4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
26 * This file contains functions to initialize the gssapi library and
27 * load mechanism libraries.
29 * It also contain functions requiring direct access to the mechanism's
30 * list (gss_inidicate_mechs and gss_release_oid) as well as support
31 * functions which translate the mechanism strings to oids and vise versa.
33 * The mechanism libraries are loaded on demand. This is triggered
34 * through the get_mechanism function call.
36 * Updates to the mechList are performed with the following restrictions:
37 * - once a library is loaded, none of the fields are updated
38 * - existing entiries for non-loaded mechs, will have the
39 * library and kernel module names updated only
40 * (i.e. the mech oid and mech name will not be updated)
43 #include <mechglueP.h>
44 #include "gssapiP_generic.h"
58 #error TEXT_DOMAIN not defined
61 #define MECH_CONF "/etc/gss/mech"
63 #define MECH_LIB_PREFIX1 "/usr/lib/"
66 * This #ifdef mess figures out if we are to be compiled into
67 * a sparcv9/lp64 binary for the purposes of figuring the absolute location
68 * of gss-api mechanism modules.
74 #define MECH_LIB_PREFIX2 "sparcv9/"
76 #elif defined(__amd64)
78 #define MECH_LIB_PREFIX2 "amd64/"
82 you need to define where under
/usr the LP64 libraries live
for this platform
88 #define MECH_LIB_PREFIX2 ""
92 #define MECH_LIB_DIR "gss/"
94 #define MECH_LIB_PREFIX MECH_LIB_PREFIX1 MECH_LIB_PREFIX2 MECH_LIB_DIR
98 #define MECH_SYM "gss_mech_initialize"
101 #define M_DEFAULT "default"
103 /* Local functions */
104 static gss_mech_info
searchMechList(const gss_OID
);
105 static void loadConfigFile(const char *);
106 static void updateMechList(void);
110 * list of mechanism libraries and their entry points.
111 * the list also maintains state of the mech libraries (loaded or not).
113 static gss_mech_info g_mechList
= NULL
;
114 static gss_mech_info g_mechListTail
= NULL
;
115 static mutex_t g_mechListLock
;
116 static time_t g_confFileModTime
= (time_t)0;
119 * function used to reclaim the memory used by a gss_OID structure.
120 * This routine requires direct access to the mechList.
123 gss_release_oid(minor_status
, oid
)
124 OM_uint32
*minor_status
;
128 gss_mech_info aMech
= g_mechList
;
130 if (minor_status
== NULL
)
131 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
135 while (aMech
!= NULL
) {
138 * look through the loaded mechanism libraries for
139 * gss_internal_release_oid until one returns success.
140 * gss_internal_release_oid will only return success when
141 * the OID was recognized as an internal mechanism OID. if no
142 * mechanisms recognize the OID, then call the generic version.
146 * we can walk the mechanism list without a mutex, because we
147 * are only looking at fields which once read will never change.
148 * Mechanism entries are always added to the end, and as
151 if (aMech
->mech
&& aMech
->mech
->gss_internal_release_oid
) {
152 major
= aMech
->mech
->gss_internal_release_oid(
153 aMech
->mech
->context
,
155 if (major
== GSS_S_COMPLETE
)
156 return (GSS_S_COMPLETE
);
157 map_error(minor_status
, aMech
->mech
);
162 return (generic_gss_release_oid(minor_status
, oid
));
163 } /* gss_release_oid */
167 * this function will return an oid set indicating available mechanisms.
168 * The set returned is based on configuration file entries and
169 * NOT on the loaded mechanisms. This function does not check if any
170 * of these can actually be loaded.
171 * This routine needs direct access to the mechanism list.
172 * To avoid reading the configuration file each call, we will save a
173 * a mech oid set, and only update it once the file has changed.
175 static time_t g_mechSetTime
= (time_t)0;
176 static gss_OID_set_desc g_mechSet
= { 0, NULL
};
177 static mutex_t g_mechSetLock
;
181 gss_indicate_mechs(minorStatus
, mechSet
)
182 OM_uint32
*minorStatus
;
183 gss_OID_set
*mechSet
;
187 struct stat fileInfo
;
191 /* Initialize outputs. */
193 if (minorStatus
!= NULL
)
197 *mechSet
= GSS_C_NO_OID_SET
;
199 /* Validate arguments. */
200 if (minorStatus
== NULL
|| mechSet
== NULL
)
201 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
203 fileName
= MECH_CONF
;
206 * If we have already computed the mechanisms supported and if it
207 * is still valid; make a copy and return to caller,
208 * otherwise build it first.
210 if ((stat(fileName
, &fileInfo
) == 0 &&
211 fileInfo
.st_mtime
> g_mechSetTime
)) {
213 * lock the mutex since we will be updating
214 * the mechList structure
215 * we need to keep the lock while we build the mechanism list
216 * since we are accessing parts of the mechList which could be
219 (void) mutex_lock(&g_mechListLock
);
222 * this checks for the case when we need to re-construct the
223 * g_mechSet structure, but the mechanism list is upto date
224 * (because it has been read by someone calling
225 * __gss_get_mechanism)
227 if (fileInfo
.st_mtime
> g_confFileModTime
)
229 g_confFileModTime
= fileInfo
.st_mtime
;
230 loadConfigFile(fileName
);
234 * we need to lock the mech set so that no one else will
235 * try to read it as we are re-creating it
237 (void) mutex_lock(&g_mechSetLock
);
239 /* if the oid list already exists we must free it first */
240 if (g_mechSet
.count
!= 0) {
241 for (i
= 0; i
< g_mechSet
.count
; i
++)
242 free(g_mechSet
.elements
[i
].elements
);
243 free(g_mechSet
.elements
);
244 g_mechSet
.elements
= NULL
;
248 /* determine how many elements to have in the list */
251 while (mList
!= NULL
) {
256 /* this should always be true, but.... */
259 (gss_OID
) calloc(count
, sizeof (gss_OID_desc
));
260 if (g_mechSet
.elements
== NULL
) {
261 (void) mutex_unlock(&g_mechSetLock
);
262 (void) mutex_unlock(&g_mechListLock
);
263 return (GSS_S_FAILURE
);
266 (void) memset(g_mechSet
.elements
, 0,
267 count
* sizeof (gss_OID_desc
));
269 /* now copy each oid element */
270 g_mechSet
.count
= count
;
273 while (mList
!= NULL
) {
274 curItem
= &(g_mechSet
.elements
[count
]);
275 curItem
->elements
= (void*)
276 malloc(mList
->mech_type
->length
);
277 if (curItem
->elements
== NULL
) {
279 * this is nasty - we must delete the
280 * part of the array already copied
282 for (i
= 0; i
< count
; i
++) {
283 free(g_mechSet
.elements
[i
].
286 free(g_mechSet
.elements
);
288 g_mechSet
.elements
= NULL
;
289 (void) mutex_unlock(&g_mechSetLock
);
290 (void) mutex_unlock(&g_mechListLock
);
291 return (GSS_S_FAILURE
);
293 g_OID_copy(curItem
, mList
->mech_type
);
299 g_mechSetTime
= fileInfo
.st_mtime
;
300 (void) mutex_unlock(&g_mechSetLock
);
301 (void) mutex_unlock(&g_mechListLock
);
302 } /* if g_mechSet is out of date or not initialized */
305 * the mech set is created and it is up to date
306 * so just copy it to caller
309 (gss_OID_set
) malloc(sizeof (gss_OID_set_desc
))) == NULL
)
311 return (GSS_S_FAILURE
);
315 * need to lock the g_mechSet in case someone tries to update it while
318 (void) mutex_lock(&g_mechSetLock
);
320 /* allocate space for the oid structures */
321 if (((*mechSet
)->elements
=
322 (void*) calloc(g_mechSet
.count
, sizeof (gss_OID_desc
)))
325 (void) mutex_unlock(&g_mechSetLock
);
328 return (GSS_S_FAILURE
);
331 /* now copy the oid structures */
332 (void) memcpy((*mechSet
)->elements
, g_mechSet
.elements
,
333 g_mechSet
.count
* sizeof (gss_OID_desc
));
335 (*mechSet
)->count
= g_mechSet
.count
;
337 /* still need to copy each of the oid elements arrays */
338 for (i
= 0; i
< (*mechSet
)->count
; i
++) {
339 curItem
= &((*mechSet
)->elements
[i
]);
341 (void *) malloc(g_mechSet
.elements
[i
].length
);
342 if (curItem
->elements
== NULL
) {
343 (void) mutex_unlock(&g_mechSetLock
);
345 * must still free the allocated elements for
346 * each allocated gss_OID_desc
348 for (j
= 0; j
< i
; j
++) {
349 free((*mechSet
)->elements
[j
].elements
);
351 free((*mechSet
)->elements
);
354 return (GSS_S_FAILURE
);
356 g_OID_copy(curItem
, &g_mechSet
.elements
[i
]);
358 (void) mutex_unlock(&g_mechSetLock
);
359 return (GSS_S_COMPLETE
);
360 } /* gss_indicate_mechs */
363 * this function has been added for use by modules that need to
364 * know what (if any) optional parameters are supplied in the
365 * config file (MECH_CONF).
366 * It will return the option string for a specified mechanism.
367 * caller is responsible for freeing the memory
370 __gss_get_modOptions(oid
)
374 char *modOptions
= NULL
;
376 /* make sure we have fresh data */
377 (void) mutex_lock(&g_mechListLock
);
379 (void) mutex_unlock(&g_mechListLock
);
381 /* searching the list does not require a lock */
382 if ((aMech
= searchMechList(oid
)) == NULL
||
383 aMech
->optionStr
== NULL
) {
388 * need to obtain a lock on this structure in case someone else
389 * will try to update it during the copy
391 (void) mutex_lock(&g_mechListLock
);
392 if (aMech
->optionStr
)
393 modOptions
= strdup(aMech
->optionStr
);
394 (void) mutex_unlock(&g_mechListLock
);
397 } /* __gss_get_modOptions */
400 * this function has been added for use by gssd.
401 * It will return the kernel module name for a specified mechanism.
402 * caller is responsible for freeing the memory
405 __gss_get_kmodName(oid
)
409 char *kmodName
= NULL
;
411 /* make sure we have fresh data */
412 (void) mutex_lock(&g_mechListLock
);
414 (void) mutex_unlock(&g_mechListLock
);
416 /* searching the list does not require a lock */
417 if ((aMech
= searchMechList(oid
)) == NULL
|| aMech
->kmodName
== NULL
) {
422 * need to obtain a lock on this structure in case someone else
423 * will try to update it during the copy
425 (void) mutex_lock(&g_mechListLock
);
427 kmodName
= strdup(aMech
->kmodName
);
428 (void) mutex_unlock(&g_mechListLock
);
431 } /* __gss_get_kmodName */
435 * given a mechanism string return the mechanism oid
438 __gss_mech_to_oid(const char *mechStr
, gss_OID
* oid
)
443 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
445 *oid
= GSS_C_NULL_OID
;
447 if ((mechStr
== NULL
) || (strlen(mechStr
) == 0) ||
448 (strcasecmp(mechStr
, M_DEFAULT
) == 0))
449 return (GSS_S_COMPLETE
);
451 /* ensure we have fresh data */
452 (void) mutex_lock(&g_mechListLock
);
454 (void) mutex_unlock(&g_mechListLock
);
458 /* no lock required - only looking at fields that are not updated */
459 while (aMech
!= NULL
) {
460 if ((aMech
->mechNameStr
) &&
461 strcmp(aMech
->mechNameStr
, mechStr
) == 0) {
462 *oid
= aMech
->mech_type
;
463 return (GSS_S_COMPLETE
);
467 return (GSS_S_FAILURE
);
468 } /* __gss_mech_to_oid */
472 * Given the mechanism oid, return the readable mechanism name
473 * associated with that oid from the mech config file
477 __gss_oid_to_mech(const gss_OID oid
)
481 if (oid
== GSS_C_NULL_OID
)
484 /* ensure we have fresh data */
485 (void) mutex_lock(&g_mechListLock
);
487 (void) mutex_unlock(&g_mechListLock
);
489 if ((aMech
= searchMechList(oid
)) == NULL
)
492 return (aMech
->mechNameStr
);
493 } /* __gss_oid_to_mech */
497 * return a list of mechanism strings supported
498 * upon return the array is terminated with a NULL entry
501 __gss_get_mechanisms(char *mechArray
[], int arrayLen
)
506 if (mechArray
== NULL
|| arrayLen
< 1)
507 return (GSS_S_CALL_INACCESSIBLE_WRITE
);
509 /* ensure we have fresh data */
510 (void) mutex_lock(&g_mechListLock
);
512 (void) mutex_unlock(&g_mechListLock
);
516 /* no lock required - only looking at fields that are not updated */
517 for (i
= 1; i
< arrayLen
; i
++) {
519 *mechArray
= aMech
->mechNameStr
;
526 return (GSS_S_COMPLETE
);
527 } /* gss_get_mechanisms */
531 * determines if the mechList needs to be updated from file
532 * and performs the update.
533 * this functions must be called with a lock of g_mechListLock
539 struct stat fileInfo
;
541 fileName
= MECH_CONF
;
543 /* check if mechList needs updating */
544 if (stat(fileName
, &fileInfo
) == 0 &&
545 (fileInfo
.st_mtime
> g_confFileModTime
)) {
546 loadConfigFile(fileName
);
547 g_confFileModTime
= fileInfo
.st_mtime
;
549 } /* updateMechList */
553 * given the mechanism type, return the mechanism structure
554 * containing the mechanism library entry points.
555 * will return NULL if mech type is not found
556 * This function will also trigger the loading of the mechanism
557 * module if it has not been already loaded.
560 __gss_get_mechanism(oid
)
564 gss_mechanism (*sym
)(const gss_OID
);
567 /* check if the mechanism is already loaded */
568 if ((aMech
= searchMechList(oid
)) != NULL
&& aMech
->mech
) {
569 return (aMech
->mech
);
573 * might need to re-read the configuration file before loading
574 * the mechanism to ensure we have the latest info.
576 (void) mutex_lock(&g_mechListLock
);
579 aMech
= searchMechList(oid
);
581 /* is the mechanism present in the list ? */
583 (void) mutex_unlock(&g_mechListLock
);
584 return ((gss_mechanism
)NULL
);
587 /* has another thread loaded the mech */
589 (void) mutex_unlock(&g_mechListLock
);
590 return (aMech
->mech
);
593 /* we found the mechanism, but it is not loaded */
594 if ((dl
= dlopen(aMech
->uLibName
, RTLD_NOW
)) == NULL
) {
595 (void) syslog(LOG_INFO
, "libgss dlopen(%s): %s\n",
596 aMech
->uLibName
, dlerror());
597 (void) mutex_unlock(&g_mechListLock
);
598 return ((gss_mechanism
)NULL
);
601 if ((sym
= (gss_mechanism (*)(const gss_OID
))dlsym(dl
, MECH_SYM
))
604 (void) syslog(LOG_INFO
, "unable to initialize mechanism"
605 " library [%s]\n", aMech
->uLibName
);
606 (void) mutex_unlock(&g_mechListLock
);
607 return ((gss_mechanism
)NULL
);
610 /* Call the symbol to get the mechanism table */
611 aMech
->mech
= (*sym
)(aMech
->mech_type
);
613 if (aMech
->mech
== NULL
) {
615 (void) syslog(LOG_INFO
, "unable to initialize mechanism"
616 " library [%s]\n", aMech
->uLibName
);
617 (void) mutex_unlock(&g_mechListLock
);
618 return ((gss_mechanism
)NULL
);
621 aMech
->dl_handle
= dl
;
623 (void) mutex_unlock(&g_mechListLock
);
624 return (aMech
->mech
);
625 } /* __gss_get_mechanism */
628 __gss_get_mechanism_ext(oid
)
632 gss_mechanism_ext mech_ext
;
634 /* check if the mechanism is already loaded */
635 if ((aMech
= searchMechList(oid
)) != NULL
&& aMech
->mech_ext
!= NULL
)
636 return (aMech
->mech_ext
);
638 if (__gss_get_mechanism(oid
) == NULL
)
641 if (aMech
->dl_handle
== NULL
)
644 /* Load the gss_config_ext struct for this mech */
646 mech_ext
= (gss_mechanism_ext
)malloc(sizeof (struct gss_config_ext
));
648 if (mech_ext
== NULL
)
652 * dlsym() the mech's 'method' functions for the extended APIs
654 * NOTE: Until the void *context argument is removed from the
655 * SPI method functions' signatures it will be necessary to have
656 * different function pointer typedefs and function names for
657 * the SPI methods than for the API. When this argument is
658 * removed it will be possible to rename gss_*_sfct to gss_*_fct
659 * and and gssspi_* to gss_*.
661 mech_ext
->gss_acquire_cred_with_password
=
662 (gss_acquire_cred_with_password_sfct
)dlsym(aMech
->dl_handle
,
663 "gssspi_acquire_cred_with_password");
665 /* Set aMech->mech_ext */
666 (void) mutex_lock(&g_mechListLock
);
668 if (aMech
->mech_ext
== NULL
)
669 aMech
->mech_ext
= mech_ext
;
671 free(mech_ext
); /* we raced and lost; don't leak */
673 (void) mutex_unlock(&g_mechListLock
);
675 return (aMech
->mech_ext
);
677 } /* __gss_get_mechanism_ext */
681 * this routine is used for searching the list of mechanism data.
682 * it needs not be mutex protected because we only add new structures
683 * from the end and they are fully initialized before being added.
685 static gss_mech_info
searchMechList(oid
)
688 gss_mech_info aMech
= g_mechList
;
690 /* if oid is null -> then get default which is the first in the list */
691 if (oid
== GSS_C_NULL_OID
)
694 while (aMech
!= NULL
) {
695 if (g_OID_equal(aMech
->mech_type
, oid
))
701 return ((gss_mech_info
) NULL
);
702 } /* searchMechList */
706 * loads the configuration file
707 * this is called while having a mutex lock on the mechanism list
708 * entries for libraries that have been loaded can't be modified
709 * mechNameStr and mech_type fields are not updated during updates
711 static void loadConfigFile(fileName
)
712 const char *fileName
;
714 char buffer
[BUFSIZ
], *oidStr
, *oid
, *sharedLib
, *kernMod
, *endp
;
716 char sharedPath
[sizeof (MECH_LIB_PREFIX
) + BUFSIZ
];
720 gss_mech_info aMech
, tmp
;
722 gss_buffer_desc oidBuf
;
724 if ((confFile
= fopen(fileName
, "rF")) == NULL
) {
728 (void) memset(buffer
, 0, sizeof (buffer
));
729 while (fgets(buffer
, BUFSIZ
, confFile
) != NULL
) {
731 /* ignore lines beginning with # */
736 * find the first white-space character after
740 for (oid
= buffer
; *oid
&& !isspace(*oid
); oid
++);
742 /* Now find the first non-white-space character */
746 while (*oid
&& isspace(*oid
))
751 * If that's all, then this is a corrupt entry. Skip it.
756 /* Find the end of the oid and make sure it is NULL-ended */
757 for (endp
= oid
; *endp
&& !isspace(*endp
); endp
++)
765 * check if an entry for this oid already exists
766 * if it does, and the library is already loaded then
767 * we can't modify it, so skip it
769 oidBuf
.value
= (void *)oid
;
770 oidBuf
.length
= strlen(oid
);
771 if (generic_gss_str_to_oid(&minor
, &oidBuf
, &mechOid
)
773 (void) syslog(LOG_INFO
, "invalid mechanism oid"
774 " [%s] in configuration file", oid
);
778 aMech
= searchMechList(mechOid
);
779 if (aMech
&& aMech
->mech
) {
780 free(mechOid
->elements
);
785 /* Find the start of the shared lib name */
786 for (sharedLib
= endp
+1; *sharedLib
&& isspace(*sharedLib
);
791 * If that's all, then this is a corrupt entry. Skip it.
794 free(mechOid
->elements
);
800 * Find the end of the shared lib name and make sure it is
803 for (endp
= sharedLib
; *endp
&& !isspace(*endp
); endp
++)
810 /* Find the start of the optional kernel module lib name */
811 for (kernMod
= endp
+1; *kernMod
&& isspace(*kernMod
);
816 * If this item starts with a bracket "[", then
817 * it is not a kernel module, but is a list of
818 * options for the user module to parse later.
820 if (*kernMod
&& *kernMod
!= '[') {
822 * Find the end of the shared lib name and make sure
823 * it is NULL-terminated.
825 for (endp
= kernMod
; *endp
&& !isspace(*endp
); endp
++)
834 /* Find the start of the optional module options list */
835 for (modOptions
= endp
+1; *modOptions
&& isspace(*modOptions
);
838 if (*modOptions
== '[') {
839 /* move past the opening bracket */
840 for (modOptions
= modOptions
+1;
841 *modOptions
&& isspace(*modOptions
);
844 /* Find the closing bracket */
845 for (endp
= modOptions
;
846 *endp
&& *endp
!= ']'; endp
++);
855 (void) strcpy(sharedPath
, MECH_LIB_PREFIX
);
856 (void) strcat(sharedPath
, sharedLib
);
859 * are we creating a new mechanism entry or
860 * just modifying existing (non loaded) mechanism entry
864 * delete any old values and set new
865 * mechNameStr and mech_type are not modified
867 if (aMech
->kmodName
) {
868 free(aMech
->kmodName
);
869 aMech
->kmodName
= NULL
;
872 if (aMech
->optionStr
) {
873 free(aMech
->optionStr
);
874 aMech
->optionStr
= NULL
;
877 if ((tmpStr
= strdup(sharedPath
)) != NULL
) {
878 free(aMech
->uLibName
);
879 aMech
->uLibName
= tmpStr
;
882 if (kernMod
) /* this is an optional parameter */
883 aMech
->kmodName
= strdup(kernMod
);
885 if (modOptions
) /* optional module options */
886 aMech
->optionStr
= strdup(modOptions
);
888 /* the oid is already set */
889 free(mechOid
->elements
);
894 /* adding a new entry */
895 aMech
= malloc(sizeof (struct gss_mech_config
));
897 free(mechOid
->elements
);
901 (void) memset(aMech
, 0, sizeof (struct gss_mech_config
));
902 aMech
->mech_type
= mechOid
;
903 aMech
->uLibName
= strdup(sharedPath
);
904 aMech
->mechNameStr
= strdup(oidStr
);
906 /* check if any memory allocations failed - bad news */
907 if (aMech
->uLibName
== NULL
|| aMech
->mechNameStr
== NULL
) {
908 free(aMech
->uLibName
);
909 free(aMech
->mechNameStr
);
910 free(mechOid
->elements
);
915 if (kernMod
) /* this is an optional parameter */
916 aMech
->kmodName
= strdup(kernMod
);
919 aMech
->optionStr
= strdup(modOptions
);
921 * add the new entry to the end of the list - make sure
922 * that only complete entries are added because other
923 * threads might currently be searching the list.
925 tmp
= g_mechListTail
;
926 g_mechListTail
= aMech
;
931 if (g_mechList
== NULL
)
934 (void) fclose(confFile
);
935 } /* loadConfigFile */