xref: /illumos-gate/usr/src/lib/libgss/g_initialize.c (revision 1a2d662a91cee3bf82f41cd47c7ae6f3825d9db2)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 /*
26  * This file contains functions to initialize the gssapi library and
27  * load mechanism libraries.
28  *
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.
32  *
33  * The mechanism libraries are loaded on demand.  This is triggered
34  * through the get_mechanism function call.
35  *
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)
41  */
42 
43 #include <mechglueP.h>
44 #include "gssapiP_generic.h"
45 #include <stdio.h>
46 #include <syslog.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sys/stat.h>
50 #include <ctype.h>
51 #include <errno.h>
52 #include <synch.h>
53 #include <dlfcn.h>
54 #include <libintl.h>
55 
56 
57 #ifndef TEXT_DOMAIN
58 #error TEXT_DOMAIN not defined
59 #endif
60 
61 #define	MECH_CONF "/etc/gss/mech"
62 
63 #define	MECH_LIB_PREFIX1	"/usr/lib/"
64 
65 /*
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.
69  */
70 #ifdef	_LP64
71 
72 #ifdef	__sparc
73 
74 #define	MECH_LIB_PREFIX2	"sparcv9/"
75 
76 #elif defined(__amd64)
77 
78 #define	MECH_LIB_PREFIX2	"amd64/"
79 
80 #else	/* __sparc */
81 
82 you need to define where under /usr the LP64 libraries live for this platform
83 
84 #endif	/* __sparc */
85 
86 #else	/* _LP64 */
87 
88 #define	MECH_LIB_PREFIX2	""
89 
90 #endif	/* _LP64 */
91 
92 #define	MECH_LIB_DIR		"gss/"
93 
94 #define	MECH_LIB_PREFIX	MECH_LIB_PREFIX1 MECH_LIB_PREFIX2 MECH_LIB_DIR
95 
96 
97 #ifndef	MECH_SYM
98 #define	MECH_SYM "gss_mech_initialize"
99 #endif
100 
101 #define	M_DEFAULT	"default"
102 
103 /* Local functions */
104 static gss_mech_info searchMechList(const gss_OID);
105 static void loadConfigFile(const char *);
106 static void updateMechList(void);
107 
108 
109 /*
110  * list of mechanism libraries and their entry points.
111  * the list also maintains state of the mech libraries (loaded or not).
112  */
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;
117 
118 /*
119  * function used to reclaim the memory used by a gss_OID structure.
120  * This routine requires direct access to the mechList.
121  */
122 OM_uint32
123 gss_release_oid(minor_status, oid)
124 OM_uint32 *minor_status;
125 gss_OID *oid;
126 {
127 	OM_uint32 major;
128 	gss_mech_info aMech = g_mechList;
129 
130 	if (minor_status == NULL)
131 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
132 
133 	*minor_status = 0;
134 
135 	while (aMech != NULL) {
136 
137 		/*
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.
143 		 */
144 
145 		/*
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
149 		 * complete entries.
150 		 */
151 		if (aMech->mech && aMech->mech->gss_internal_release_oid) {
152 			major = aMech->mech->gss_internal_release_oid(
153 					aMech->mech->context,
154 					minor_status, oid);
155 			if (major == GSS_S_COMPLETE)
156 				return (GSS_S_COMPLETE);
157 			map_error(minor_status, aMech->mech);
158 		}
159 		aMech = aMech->next;
160 	} /* while */
161 
162 	return (generic_gss_release_oid(minor_status, oid));
163 } /* gss_release_oid */
164 
165 
166 /*
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.
174  */
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;
178 
179 
180 OM_uint32
181 gss_indicate_mechs(minorStatus, mechSet)
182 OM_uint32 *minorStatus;
183 gss_OID_set *mechSet;
184 {
185 	gss_mech_info mList;
186 	char *fileName;
187 	struct stat fileInfo;
188 	int count, i, j;
189 	gss_OID curItem;
190 
191 	/* Initialize outputs. */
192 
193 	if (minorStatus != NULL)
194 		*minorStatus = 0;
195 
196 	if (mechSet != NULL)
197 		*mechSet = GSS_C_NO_OID_SET;
198 
199 	/* Validate arguments. */
200 	if (minorStatus == NULL || mechSet == NULL)
201 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
202 
203 	fileName = MECH_CONF;
204 
205 	/*
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.
209 	 */
210 	if ((stat(fileName, &fileInfo) == 0 &&
211 		fileInfo.st_mtime > g_mechSetTime)) {
212 		/*
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
217 		 * modified.
218 		 */
219 		(void) mutex_lock(&g_mechListLock);
220 
221 		/*
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)
226 		 */
227 		if (fileInfo.st_mtime > g_confFileModTime)
228 		{
229 			g_confFileModTime = fileInfo.st_mtime;
230 			loadConfigFile(fileName);
231 		}
232 
233 		/*
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
236 		 */
237 		(void) mutex_lock(&g_mechSetLock);
238 
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;
245 			g_mechSet.count = 0;
246 		}
247 
248 		/* determine how many elements to have in the list */
249 		mList = g_mechList;
250 		count = 0;
251 		while (mList != NULL) {
252 			count++;
253 			mList = mList->next;
254 		}
255 
256 		/* this should always be true, but.... */
257 		if (count > 0) {
258 			g_mechSet.elements =
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);
264 			}
265 
266 			(void) memset(g_mechSet.elements, 0,
267 				count * sizeof (gss_OID_desc));
268 
269 			/* now copy each oid element */
270 			g_mechSet.count = count;
271 			count = 0;
272 			mList = g_mechList;
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) {
278 					/*
279 					 * this is nasty - we must delete the
280 					 * part of the array already copied
281 					 */
282 					for (i = 0; i < count; i++) {
283 						free(g_mechSet.elements[i].
284 							elements);
285 					}
286 					free(g_mechSet.elements);
287 					g_mechSet.count = 0;
288 					g_mechSet.elements = NULL;
289 					(void) mutex_unlock(&g_mechSetLock);
290 					(void) mutex_unlock(&g_mechListLock);
291 					return (GSS_S_FAILURE);
292 				}
293 				g_OID_copy(curItem, mList->mech_type);
294 				count++;
295 				mList = mList->next;
296 			}
297 		}
298 
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 */
303 
304 	/*
305 	 * the mech set is created and it is up to date
306 	 * so just copy it to caller
307 	 */
308 	if ((*mechSet =
309 		(gss_OID_set) malloc(sizeof (gss_OID_set_desc))) == NULL)
310 	{
311 		return (GSS_S_FAILURE);
312 	}
313 
314 	/*
315 	 * need to lock the g_mechSet in case someone tries to update it while
316 	 * I'm copying it.
317 	 */
318 	(void) mutex_lock(&g_mechSetLock);
319 
320 	/* allocate space for the oid structures */
321 	if (((*mechSet)->elements =
322 		(void*) calloc(g_mechSet.count, sizeof (gss_OID_desc)))
323 		== NULL)
324 	{
325 		(void) mutex_unlock(&g_mechSetLock);
326 		free(*mechSet);
327 		*mechSet = NULL;
328 		return (GSS_S_FAILURE);
329 	}
330 
331 	/* now copy the oid structures */
332 	(void) memcpy((*mechSet)->elements, g_mechSet.elements,
333 		g_mechSet.count * sizeof (gss_OID_desc));
334 
335 	(*mechSet)->count = g_mechSet.count;
336 
337 	/* still need to copy each of the oid elements arrays */
338 	for (i = 0; i < (*mechSet)->count; i++) {
339 		curItem = &((*mechSet)->elements[i]);
340 		curItem->elements =
341 			(void *) malloc(g_mechSet.elements[i].length);
342 		if (curItem->elements == NULL) {
343 			(void) mutex_unlock(&g_mechSetLock);
344 			/*
345 			 * must still free the allocated elements for
346 			 * each allocated gss_OID_desc
347 			 */
348 			for (j = 0; j < i; j++) {
349 				free((*mechSet)->elements[j].elements);
350 			}
351 			free((*mechSet)->elements);
352 			free(mechSet);
353 			*mechSet = NULL;
354 			return (GSS_S_FAILURE);
355 		}
356 		g_OID_copy(curItem, &g_mechSet.elements[i]);
357 	}
358 	(void) mutex_unlock(&g_mechSetLock);
359 	return (GSS_S_COMPLETE);
360 } /* gss_indicate_mechs */
361 
362 /*
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
368  */
369 char *
370 __gss_get_modOptions(oid)
371 const gss_OID oid;
372 {
373 	gss_mech_info aMech;
374 	char *modOptions = NULL;
375 
376 	/* make sure we have fresh data */
377 	(void) mutex_lock(&g_mechListLock);
378 	updateMechList();
379 	(void) mutex_unlock(&g_mechListLock);
380 
381 	/* searching the list does not require a lock */
382 	if ((aMech = searchMechList(oid)) == NULL ||
383 		aMech->optionStr == NULL) {
384 		return (NULL);
385 	}
386 
387 	/*
388 	 * need to obtain a lock on this structure in case someone else
389 	 * will try to update it during the copy
390 	 */
391 	(void) mutex_lock(&g_mechListLock);
392 	if (aMech->optionStr)
393 		modOptions = strdup(aMech->optionStr);
394 	(void) mutex_unlock(&g_mechListLock);
395 
396 	return (modOptions);
397 } /* __gss_get_modOptions */
398 
399 /*
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
403  */
404 char *
405 __gss_get_kmodName(oid)
406 const gss_OID oid;
407 {
408 	gss_mech_info aMech;
409 	char *kmodName = NULL;
410 
411 	/* make sure we have fresh data */
412 	(void) mutex_lock(&g_mechListLock);
413 	updateMechList();
414 	(void) mutex_unlock(&g_mechListLock);
415 
416 	/* searching the list does not require a lock */
417 	if ((aMech = searchMechList(oid)) == NULL || aMech->kmodName == NULL) {
418 		return (NULL);
419 	}
420 
421 	/*
422 	 * need to obtain a lock on this structure in case someone else
423 	 * will try to update it during the copy
424 	 */
425 	(void) mutex_lock(&g_mechListLock);
426 	if (aMech->kmodName)
427 		kmodName = strdup(aMech->kmodName);
428 	(void) mutex_unlock(&g_mechListLock);
429 
430 	return (kmodName);
431 } /* __gss_get_kmodName */
432 
433 
434 /*
435  * given a mechanism string return the mechanism oid
436  */
437 OM_uint32
438 __gss_mech_to_oid(const char *mechStr, gss_OID* oid)
439 {
440 	gss_mech_info aMech;
441 
442 	if (oid == NULL)
443 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
444 
445 	*oid = GSS_C_NULL_OID;
446 
447 	if ((mechStr == NULL) || (strlen(mechStr) == 0) ||
448 		(strcasecmp(mechStr, M_DEFAULT) == 0))
449 		return (GSS_S_COMPLETE);
450 
451 	/* ensure we have fresh data */
452 	(void) mutex_lock(&g_mechListLock);
453 	updateMechList();
454 	(void) mutex_unlock(&g_mechListLock);
455 
456 	aMech = g_mechList;
457 
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);
464 		}
465 		aMech = aMech->next;
466 	}
467 	return (GSS_S_FAILURE);
468 } /* __gss_mech_to_oid */
469 
470 
471 /*
472  * Given the mechanism oid, return the readable mechanism name
473  * associated with that oid from the mech config file
474  * (/etc/gss/mech).
475  */
476 const char *
477 __gss_oid_to_mech(const gss_OID oid)
478 {
479 	gss_mech_info aMech;
480 
481 	if (oid == GSS_C_NULL_OID)
482 		return (M_DEFAULT);
483 
484 	/* ensure we have fresh data */
485 	(void) mutex_lock(&g_mechListLock);
486 	updateMechList();
487 	(void) mutex_unlock(&g_mechListLock);
488 
489 	if ((aMech = searchMechList(oid)) == NULL)
490 		return (NULL);
491 
492 	return (aMech->mechNameStr);
493 } /* __gss_oid_to_mech */
494 
495 
496 /*
497  * return a list of mechanism strings supported
498  * upon return the array is terminated with a NULL entry
499  */
500 OM_uint32
501 __gss_get_mechanisms(char *mechArray[], int arrayLen)
502 {
503 	gss_mech_info aMech;
504 	int i;
505 
506 	if (mechArray == NULL || arrayLen < 1)
507 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
508 
509 	/* ensure we have fresh data */
510 	(void) mutex_lock(&g_mechListLock);
511 	updateMechList();
512 	(void) mutex_unlock(&g_mechListLock);
513 
514 	aMech = g_mechList;
515 
516 	/* no lock required - only looking at fields that are not updated */
517 	for (i = 1; i < arrayLen; i++) {
518 		if (aMech != NULL) {
519 			*mechArray = aMech->mechNameStr;
520 			mechArray++;
521 			aMech = aMech->next;
522 		} else
523 			break;
524 	}
525 	*mechArray = NULL;
526 	return (GSS_S_COMPLETE);
527 } /* gss_get_mechanisms */
528 
529 
530 /*
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
534  */
535 static void
536 updateMechList(void)
537 {
538 	char *fileName;
539 	struct stat fileInfo;
540 
541 	fileName = MECH_CONF;
542 
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;
548 	}
549 } /* updateMechList */
550 
551 
552 /*
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.
558  */
559 gss_mechanism
560 __gss_get_mechanism(oid)
561 const gss_OID oid;
562 {
563 	gss_mech_info aMech;
564 	gss_mechanism (*sym)(const gss_OID);
565 	void *dl;
566 
567 	/* check if the mechanism is already loaded */
568 	if ((aMech = searchMechList(oid)) != NULL && aMech->mech) {
569 		return (aMech->mech);
570 	}
571 
572 	/*
573 	 * might need to re-read the configuration file before loading
574 	 * the mechanism to ensure we have the latest info.
575 	 */
576 	(void) mutex_lock(&g_mechListLock);
577 	updateMechList();
578 
579 	aMech = searchMechList(oid);
580 
581 	/* is the mechanism present in the list ? */
582 	if (aMech == NULL) {
583 		(void) mutex_unlock(&g_mechListLock);
584 		return ((gss_mechanism)NULL);
585 	}
586 
587 	/* has another thread loaded the mech */
588 	if (aMech->mech) {
589 		(void) mutex_unlock(&g_mechListLock);
590 		return (aMech->mech);
591 	}
592 
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);
599 	}
600 
601 	if ((sym = (gss_mechanism (*)(const gss_OID))dlsym(dl, MECH_SYM))
602 			== NULL) {
603 		(void) dlclose(dl);
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);
608 	}
609 
610 	/* Call the symbol to get the mechanism table */
611 	aMech->mech = (*sym)(aMech->mech_type);
612 
613 	if (aMech->mech == NULL) {
614 		(void) dlclose(dl);
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);
619 	}
620 
621 	aMech->dl_handle = dl;
622 
623 	(void) mutex_unlock(&g_mechListLock);
624 	return (aMech->mech);
625 } /* __gss_get_mechanism */
626 
627 gss_mechanism_ext
628 __gss_get_mechanism_ext(oid)
629 const gss_OID oid;
630 {
631 	gss_mech_info aMech;
632 	gss_mechanism_ext mech_ext;
633 
634 	/* check if the mechanism is already loaded */
635 	if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext != NULL)
636 		return (aMech->mech_ext);
637 
638 	if (__gss_get_mechanism(oid) == NULL)
639 		return (NULL);
640 
641 	if (aMech->dl_handle == NULL)
642 		return (NULL);
643 
644 	/* Load the gss_config_ext struct for this mech */
645 
646 	mech_ext = (gss_mechanism_ext)malloc(sizeof (struct gss_config_ext));
647 
648 	if (mech_ext == NULL)
649 		return (NULL);
650 
651 	/*
652 	 * dlsym() the mech's 'method' functions for the extended APIs
653 	 *
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_*.
660 	 */
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");
664 
665 	/* Set aMech->mech_ext */
666 	(void) mutex_lock(&g_mechListLock);
667 
668 	if (aMech->mech_ext == NULL)
669 		aMech->mech_ext = mech_ext;
670 	else
671 		free(mech_ext);	/* we raced and lost; don't leak */
672 
673 	(void) mutex_unlock(&g_mechListLock);
674 
675 	return (aMech->mech_ext);
676 
677 } /* __gss_get_mechanism_ext */
678 
679 
680 /*
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.
684  */
685 static gss_mech_info searchMechList(oid)
686 const gss_OID oid;
687 {
688 	gss_mech_info aMech = g_mechList;
689 
690 	/* if oid is null -> then get default which is the first in the list */
691 	if (oid == GSS_C_NULL_OID)
692 		return (aMech);
693 
694 	while (aMech != NULL) {
695 		if (g_OID_equal(aMech->mech_type, oid))
696 			return (aMech);
697 		aMech = aMech->next;
698 	}
699 
700 	/* none found */
701 	return ((gss_mech_info) NULL);
702 } /* searchMechList */
703 
704 
705 /*
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
710  */
711 static void loadConfigFile(fileName)
712 const char *fileName;
713 {
714 	char buffer[BUFSIZ], *oidStr, *oid, *sharedLib, *kernMod, *endp;
715 	char *modOptions;
716 	char sharedPath[sizeof (MECH_LIB_PREFIX) + BUFSIZ];
717 	char *tmpStr;
718 	FILE *confFile;
719 	gss_OID mechOid;
720 	gss_mech_info aMech, tmp;
721 	OM_uint32 minor;
722 	gss_buffer_desc oidBuf;
723 
724 	if ((confFile = fopen(fileName, "rF")) == NULL) {
725 		return;
726 	}
727 
728 	(void) memset(buffer, 0, sizeof (buffer));
729 	while (fgets(buffer, BUFSIZ, confFile) != NULL) {
730 
731 		/* ignore lines beginning with # */
732 		if (*buffer == '#')
733 			continue;
734 
735 		/*
736 		 * find the first white-space character after
737 		 * the mechanism name
738 		 */
739 		oidStr = buffer;
740 		for (oid = buffer; *oid && !isspace(*oid); oid++);
741 
742 		/* Now find the first non-white-space character */
743 		if (*oid) {
744 			*oid = '\0';
745 			oid++;
746 			while (*oid && isspace(*oid))
747 				oid++;
748 		}
749 
750 		/*
751 		 * If that's all, then this is a corrupt entry. Skip it.
752 		 */
753 		if (! *oid)
754 			continue;
755 
756 		/* Find the end of the oid and make sure it is NULL-ended */
757 		for (endp = oid; *endp && !isspace(*endp); endp++)
758 			;
759 
760 		if (*endp) {
761 			*endp = '\0';
762 		}
763 
764 		/*
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
768 		 */
769 		oidBuf.value = (void *)oid;
770 		oidBuf.length = strlen(oid);
771 		if (generic_gss_str_to_oid(&minor, &oidBuf, &mechOid)
772 			!= GSS_S_COMPLETE) {
773 			(void) syslog(LOG_INFO, "invalid mechanism oid"
774 					" [%s] in configuration file", oid);
775 			continue;
776 		}
777 
778 		aMech = searchMechList(mechOid);
779 		if (aMech && aMech->mech) {
780 			free(mechOid->elements);
781 			free(mechOid);
782 			continue;
783 		}
784 
785 		/* Find the start of the shared lib name */
786 		for (sharedLib = endp+1; *sharedLib && isspace(*sharedLib);
787 			sharedLib++)
788 			;
789 
790 		/*
791 		 * If that's all, then this is a corrupt entry. Skip it.
792 		 */
793 		if (! *sharedLib) {
794 			free(mechOid->elements);
795 			free(mechOid);
796 			continue;
797 		}
798 
799 		/*
800 		 * Find the end of the shared lib name and make sure it is
801 		 *  NULL-terminated.
802 		 */
803 		for (endp = sharedLib; *endp && !isspace(*endp); endp++)
804 			;
805 
806 		if (*endp) {
807 			*endp = '\0';
808 		}
809 
810 		/* Find the start of the optional kernel module lib name */
811 		for (kernMod = endp+1; *kernMod && isspace(*kernMod);
812 			kernMod++)
813 			;
814 
815 		/*
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.
819 		 */
820 		if (*kernMod && *kernMod != '[') {
821 			/*
822 			 * Find the end of the shared lib name and make sure
823 			 * it is NULL-terminated.
824 			 */
825 			for (endp = kernMod; *endp && !isspace(*endp); endp++)
826 				;
827 
828 			if (*endp) {
829 				*endp = '\0';
830 			}
831 		} else
832 			kernMod = NULL;
833 
834 		/* Find the start of the optional module options list */
835 		for (modOptions = endp+1; *modOptions && isspace(*modOptions);
836 			modOptions++);
837 
838 		if (*modOptions == '[')  {
839 			/* move past the opening bracket */
840 			for (modOptions = modOptions+1;
841 			    *modOptions && isspace(*modOptions);
842 			    modOptions++);
843 
844 			/* Find the closing bracket */
845 			for (endp = modOptions;
846 				*endp && *endp != ']'; endp++);
847 
848 			if (endp)
849 				*endp = '\0';
850 
851 		} else {
852 			modOptions = NULL;
853 		}
854 
855 		(void) strcpy(sharedPath, MECH_LIB_PREFIX);
856 		(void) strcat(sharedPath, sharedLib);
857 
858 		/*
859 		 * are we creating a new mechanism entry or
860 		 * just modifying existing (non loaded) mechanism entry
861 		 */
862 		if (aMech) {
863 			/*
864 			 * delete any old values and set new
865 			 * mechNameStr and mech_type are not modified
866 			 */
867 			if (aMech->kmodName) {
868 				free(aMech->kmodName);
869 				aMech->kmodName = NULL;
870 			}
871 
872 			if (aMech->optionStr) {
873 				free(aMech->optionStr);
874 				aMech->optionStr = NULL;
875 			}
876 
877 			if ((tmpStr = strdup(sharedPath)) != NULL) {
878 				if (aMech->uLibName)
879 					free(aMech->uLibName);
880 				aMech->uLibName = tmpStr;
881 			}
882 
883 			if (kernMod) /* this is an optional parameter */
884 				aMech->kmodName = strdup(kernMod);
885 
886 			if (modOptions) /* optional module options */
887 				aMech->optionStr = strdup(modOptions);
888 
889 			/* the oid is already set */
890 			free(mechOid->elements);
891 			free(mechOid);
892 			continue;
893 		}
894 
895 		/* adding a new entry */
896 		aMech = malloc(sizeof (struct gss_mech_config));
897 		if (aMech == NULL) {
898 			free(mechOid->elements);
899 			free(mechOid);
900 			continue;
901 		}
902 		(void) memset(aMech, 0, sizeof (struct gss_mech_config));
903 		aMech->mech_type = mechOid;
904 		aMech->uLibName = strdup(sharedPath);
905 		aMech->mechNameStr = strdup(oidStr);
906 
907 		/* check if any memory allocations failed - bad news */
908 		if (aMech->uLibName == NULL || aMech->mechNameStr == NULL) {
909 			if (aMech->uLibName)
910 				free(aMech->uLibName);
911 			if (aMech->mechNameStr)
912 				free(aMech->mechNameStr);
913 			free(mechOid->elements);
914 			free(mechOid);
915 			free(aMech);
916 			continue;
917 		}
918 		if (kernMod)	/* this is an optional parameter */
919 			aMech->kmodName = strdup(kernMod);
920 
921 		if (modOptions)
922 			aMech->optionStr = strdup(modOptions);
923 		/*
924 		 * add the new entry to the end of the list - make sure
925 		 * that only complete entries are added because other
926 		 * threads might currently be searching the list.
927 		 */
928 		tmp = g_mechListTail;
929 		g_mechListTail = aMech;
930 
931 		if (tmp != NULL)
932 			tmp->next = aMech;
933 
934 		if (g_mechList == NULL)
935 			g_mechList = aMech;
936 	} /* while */
937 	(void) fclose(confFile);
938 } /* loadConfigFile */
939