xref: /illumos-gate/usr/src/lib/libgss/g_initialize.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * This file contains functions to initialize the gssapi library and
30  * load mechanism libraries.
31  *
32  * It also contain functions requiring direct access to the mechanism's
33  * list (gss_inidicate_mechs and gss_release_oid) as well as support
34  * functions which translate the mechanism strings to oids and vise versa.
35  *
36  * The mechanism libraries are loaded on demand.  This is triggered
37  * through the get_mechanism function call.
38  *
39  * Updates to the mechList are performed with the following restrictions:
40  *	- once a library is loaded, none of the fields are updated
41  *	- existing entiries for non-loaded mechs, will have the
42  *		library and kernel module names updated only
43  *		(i.e. the mech oid and mech name will not be updated)
44  */
45 
46 #include <mechglueP.h>
47 #include <stdio.h>
48 #include <syslog.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <sys/stat.h>
52 #include <ctype.h>
53 #include <errno.h>
54 #include <synch.h>
55 #include <dlfcn.h>
56 #include <libintl.h>
57 
58 
59 #ifndef TEXT_DOMAIN
60 #error TEXT_DOMAIN not defined
61 #endif
62 
63 #define	MECH_CONF "/etc/gss/mech"
64 
65 #define	MECH_LIB_PREFIX1	"/usr/lib/"
66 
67 /*
68  * This #ifdef mess figures out if we are to be compiled into
69  * a sparcv9/lp64 binary for the purposes of figuring the absolute location
70  * of gss-api mechanism modules.
71  */
72 #ifdef	_LP64
73 
74 #ifdef	__sparc
75 
76 #define	MECH_LIB_PREFIX2	"sparcv9/"
77 
78 #elif defined(__amd64)
79 
80 #define	MECH_LIB_PREFIX2	"amd64/"
81 
82 #else	/* __sparc */
83 
84 you need to define where under /usr the LP64 libraries live for this platform
85 
86 #endif	/* __sparc */
87 
88 #else	/* _LP64 */
89 
90 #define	MECH_LIB_PREFIX2	""
91 
92 #endif	/* _LP64 */
93 
94 #define	MECH_LIB_DIR		"gss/"
95 
96 #define	MECH_LIB_PREFIX	MECH_LIB_PREFIX1 MECH_LIB_PREFIX2 MECH_LIB_DIR
97 
98 
99 #ifndef	MECH_SYM
100 #define	MECH_SYM "gss_mech_initialize"
101 #endif
102 
103 #define	M_DEFAULT	"default"
104 
105 /* Local functions */
106 static gss_mech_info searchMechList(const gss_OID);
107 static void loadConfigFile(const char *);
108 static void updateMechList(void);
109 
110 
111 /*
112  * list of mechanism libraries and their entry points.
113  * the list also maintains state of the mech libraries (loaded or not).
114  */
115 static gss_mech_info g_mechList = NULL;
116 static gss_mech_info g_mechListTail = NULL;
117 static mutex_t g_mechListLock;
118 static time_t g_confFileModTime = (time_t)0;
119 
120 /*
121  * function used to reclaim the memory used by a gss_OID structure.
122  * This routine requires direct access to the mechList.
123  */
124 OM_uint32
125 gss_release_oid(minor_status, oid)
126 OM_uint32 *minor_status;
127 gss_OID *oid;
128 {
129 	OM_uint32 major;
130 	gss_mech_info aMech = g_mechList;
131 
132 	if (minor_status == NULL)
133 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
134 
135 	*minor_status = 0;
136 
137 	while (aMech != NULL) {
138 
139 		/*
140 		 * look through the loaded mechanism libraries for
141 		 * gss_internal_release_oid until one returns success.
142 		 * gss_internal_release_oid will only return success when
143 		 * the OID was recognized as an internal mechanism OID. if no
144 		 * mechanisms recognize the OID, then call the generic version.
145 		 */
146 
147 		/*
148 		 * we can walk the mechanism list without a mutex, because we
149 		 * are only looking at fields which once read will never change.
150 		 * Mechanism entries are always added to the end, and as
151 		 * complete entries.
152 		 */
153 		if (aMech->mech && aMech->mech->gss_internal_release_oid) {
154 			major = aMech->mech->gss_internal_release_oid(
155 					aMech->mech->context,
156 					minor_status, oid);
157 			if (major == GSS_S_COMPLETE)
158 				return (GSS_S_COMPLETE);
159 		}
160 		aMech = aMech->next;
161 	} /* while */
162 
163 	return (generic_gss_release_oid(minor_status, oid));
164 } /* gss_release_oid */
165 
166 
167 /*
168  * this function will return an oid set indicating available mechanisms.
169  * The set returned is based on configuration file entries and
170  * NOT on the loaded mechanisms.  This function does not check if any
171  * of these can actually be loaded.
172  * This routine needs direct access to the mechanism list.
173  * To avoid reading the configuration file each call, we will save a
174  * a mech oid set, and only update it once the file has changed.
175  */
176 static time_t g_mechSetTime = (time_t)0;
177 static gss_OID_set_desc g_mechSet = { 0, NULL };
178 static mutex_t g_mechSetLock;
179 
180 
181 OM_uint32
182 gss_indicate_mechs(minorStatus, mechSet)
183 OM_uint32 *minorStatus;
184 gss_OID_set *mechSet;
185 {
186 	gss_mech_info mList;
187 	char *fileName;
188 	struct stat fileInfo;
189 	int count, i, j;
190 	gss_OID curItem;
191 
192 	if (!minorStatus)
193 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
194 
195 	*minorStatus = 0;
196 
197 
198 	/* check output parameter */
199 	if (mechSet == NULL)
200 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
201 
202 	fileName = MECH_CONF;
203 
204 	/*
205 	 * If we have already computed the mechanisms supported and if it
206 	 * is still valid; make a copy and return to caller,
207 	 * otherwise build it first.
208 	 */
209 	if ((stat(fileName, &fileInfo) == 0 &&
210 		fileInfo.st_mtime > g_mechSetTime)) {
211 		/*
212 		 * lock the mutex since we will be updating
213 		 * the mechList structure
214 		 * we need to keep the lock while we build the mechanism list
215 		 * since we are accessing parts of the mechList which could be
216 		 * modified.
217 		 */
218 		(void) mutex_lock(&g_mechListLock);
219 
220 		/*
221 		 * this checks for the case when we need to re-construct the
222 		 * g_mechSet structure, but the mechanism list is upto date
223 		 * (because it has been read by someone calling
224 		 * __gss_get_mechanism)
225 		 */
226 		if (fileInfo.st_mtime > g_confFileModTime)
227 		{
228 			g_confFileModTime = fileInfo.st_mtime;
229 			loadConfigFile(fileName);
230 		}
231 
232 		/*
233 		 * we need to lock the mech set so that no one else will
234 		 * try to read it as we are re-creating it
235 		 */
236 		(void) mutex_lock(&g_mechSetLock);
237 
238 		/* if the oid list already exists we must free it first */
239 		if (g_mechSet.count != 0) {
240 			for (i = 0; i < g_mechSet.count; i++)
241 				free(g_mechSet.elements[i].elements);
242 			free(g_mechSet.elements);
243 			g_mechSet.elements = NULL;
244 			g_mechSet.count = 0;
245 		}
246 
247 		/* determine how many elements to have in the list */
248 		mList = g_mechList;
249 		count = 0;
250 		while (mList != NULL) {
251 			count++;
252 			mList = mList->next;
253 		}
254 
255 		/* this should always be true, but.... */
256 		if (count > 0) {
257 			g_mechSet.elements =
258 				(gss_OID) calloc(count, sizeof (gss_OID_desc));
259 			if (g_mechSet.elements == NULL) {
260 				(void) mutex_unlock(&g_mechSetLock);
261 				(void) mutex_unlock(&g_mechListLock);
262 				return (GSS_S_FAILURE);
263 			}
264 
265 			(void) memset(g_mechSet.elements, 0,
266 				count * sizeof (gss_OID_desc));
267 
268 			/* now copy each oid element */
269 			g_mechSet.count = count;
270 			count = 0;
271 			mList = g_mechList;
272 			while (mList != NULL) {
273 				curItem = &(g_mechSet.elements[count]);
274 				curItem->elements = (void*)
275 					malloc(mList->mech_type->length);
276 				if (curItem->elements == NULL) {
277 					/*
278 					 * this is nasty - we must delete the
279 					 * part of the array already copied
280 					 */
281 					for (i = 0; i < count; i++) {
282 						free(g_mechSet.elements[i].
283 							elements);
284 					}
285 					free(g_mechSet.elements);
286 					g_mechSet.count = 0;
287 					g_mechSet.elements = NULL;
288 					(void) mutex_unlock(&g_mechSetLock);
289 					(void) mutex_unlock(&g_mechListLock);
290 					return (GSS_S_FAILURE);
291 				}
292 				g_OID_copy(curItem, mList->mech_type);
293 				count++;
294 				mList = mList->next;
295 			}
296 		}
297 
298 		g_mechSetTime = fileInfo.st_mtime;
299 		(void) mutex_unlock(&g_mechSetLock);
300 		(void) mutex_unlock(&g_mechListLock);
301 	} /* if g_mechSet is out of date or not initialized */
302 
303 	/*
304 	 * the mech set is created and it is up to date
305 	 * so just copy it to caller
306 	 */
307 	if ((*mechSet =
308 		(gss_OID_set) malloc(sizeof (gss_OID_set_desc))) == NULL)
309 	{
310 		return (GSS_S_FAILURE);
311 	}
312 
313 	/*
314 	 * need to lock the g_mechSet in case someone tries to update it while
315 	 * I'm copying it.
316 	 */
317 	(void) mutex_lock(&g_mechSetLock);
318 
319 	/* allocate space for the oid structures */
320 	if (((*mechSet)->elements =
321 		(void*) calloc(g_mechSet.count, sizeof (gss_OID_desc)))
322 		== NULL)
323 	{
324 		(void) mutex_unlock(&g_mechSetLock);
325 		free(*mechSet);
326 		*mechSet = NULL;
327 		return (GSS_S_FAILURE);
328 	}
329 
330 	/* now copy the oid structures */
331 	(void) memcpy((*mechSet)->elements, g_mechSet.elements,
332 		g_mechSet.count * sizeof (gss_OID_desc));
333 
334 	(*mechSet)->count = g_mechSet.count;
335 
336 	/* still need to copy each of the oid elements arrays */
337 	for (i = 0; i < (*mechSet)->count; i++) {
338 		curItem = &((*mechSet)->elements[i]);
339 		curItem->elements =
340 			(void *) malloc(g_mechSet.elements[i].length);
341 		if (curItem->elements == NULL) {
342 			(void) mutex_unlock(&g_mechSetLock);
343 			/*
344 			 * must still free the allocated elements for
345 			 * each allocated gss_OID_desc
346 			 */
347 			for (j = 0; j < i; j++) {
348 				free((*mechSet)->elements[j].elements);
349 			}
350 			free((*mechSet)->elements);
351 			free(mechSet);
352 			*mechSet = NULL;
353 			return (GSS_S_FAILURE);
354 		}
355 		g_OID_copy(curItem, &g_mechSet.elements[i]);
356 	}
357 	(void) mutex_unlock(&g_mechSetLock);
358 	return (GSS_S_COMPLETE);
359 } /* gss_indicate_mechs */
360 
361 /*
362  * this function has been added for use by modules that need to
363  * know what (if any) optional parameters are supplied in the
364  * config file (MECH_CONF).
365  * It will return the option string for a specified mechanism.
366  * caller is responsible for freeing the memory
367  */
368 char *
369 __gss_get_modOptions(oid)
370 const gss_OID oid;
371 {
372 	gss_mech_info aMech;
373 	char *modOptions = NULL;
374 
375 	/* make sure we have fresh data */
376 	(void) mutex_lock(&g_mechListLock);
377 	updateMechList();
378 	(void) mutex_unlock(&g_mechListLock);
379 
380 	/* searching the list does not require a lock */
381 	if ((aMech = searchMechList(oid)) == NULL ||
382 		aMech->optionStr == NULL) {
383 		return (NULL);
384 	}
385 
386 	/*
387 	 * need to obtain a lock on this structure in case someone else
388 	 * will try to update it during the copy
389 	 */
390 	(void) mutex_lock(&g_mechListLock);
391 	if (aMech->optionStr)
392 		modOptions = strdup(aMech->optionStr);
393 	(void) mutex_unlock(&g_mechListLock);
394 
395 	return (modOptions);
396 } /* __gss_get_modOptions */
397 
398 /*
399  * this function has been added for use by gssd.
400  * It will return the kernel module name for a specified mechanism.
401  * caller is responsible for freeing the memory
402  */
403 char *
404 __gss_get_kmodName(oid)
405 const gss_OID oid;
406 {
407 	gss_mech_info aMech;
408 	char *kmodName = NULL;
409 
410 	/* make sure we have fresh data */
411 	(void) mutex_lock(&g_mechListLock);
412 	updateMechList();
413 	(void) mutex_unlock(&g_mechListLock);
414 
415 	/* searching the list does not require a lock */
416 	if ((aMech = searchMechList(oid)) == NULL || aMech->kmodName == NULL) {
417 		return (NULL);
418 	}
419 
420 	/*
421 	 * need to obtain a lock on this structure in case someone else
422 	 * will try to update it during the copy
423 	 */
424 	(void) mutex_lock(&g_mechListLock);
425 	if (aMech->kmodName)
426 		kmodName = strdup(aMech->kmodName);
427 	(void) mutex_unlock(&g_mechListLock);
428 
429 	return (kmodName);
430 } /* __gss_get_kmodName */
431 
432 
433 /*
434  * given a mechanism string return the mechanism oid
435  */
436 OM_uint32
437 __gss_mech_to_oid(const char *mechStr, gss_OID* oid)
438 {
439 	gss_mech_info aMech;
440 
441 	if (oid == NULL)
442 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
443 
444 	*oid = GSS_C_NULL_OID;
445 
446 	if ((mechStr == NULL) || (strlen(mechStr) == 0) ||
447 		(strcasecmp(mechStr, M_DEFAULT) == 0))
448 		return (GSS_S_COMPLETE);
449 
450 	/* ensure we have fresh data */
451 	(void) mutex_lock(&g_mechListLock);
452 	updateMechList();
453 	(void) mutex_unlock(&g_mechListLock);
454 
455 	aMech = g_mechList;
456 
457 	/* no lock required - only looking at fields that are not updated */
458 	while (aMech != NULL) {
459 		if ((aMech->mechNameStr) &&
460 			strcmp(aMech->mechNameStr, mechStr) == 0) {
461 			*oid = aMech->mech_type;
462 			return (GSS_S_COMPLETE);
463 		}
464 		aMech = aMech->next;
465 	}
466 	return (GSS_S_FAILURE);
467 } /* __gss_mech_to_oid */
468 
469 
470 /*
471  * Given the mechanism oid, return the readable mechanism name
472  * associated with that oid from the mech config file
473  * (/etc/gss/mech).
474  */
475 const char *
476 __gss_oid_to_mech(const gss_OID oid)
477 {
478 	gss_mech_info aMech;
479 
480 	if (oid == GSS_C_NULL_OID)
481 		return (M_DEFAULT);
482 
483 	/* ensure we have fresh data */
484 	(void) mutex_lock(&g_mechListLock);
485 	updateMechList();
486 	(void) mutex_unlock(&g_mechListLock);
487 
488 	if ((aMech = searchMechList(oid)) == NULL)
489 		return (NULL);
490 
491 	return (aMech->mechNameStr);
492 } /* __gss_oid_to_mech */
493 
494 
495 /*
496  * return a list of mechanism strings supported
497  * upon return the array is terminated with a NULL entry
498  */
499 OM_uint32
500 __gss_get_mechanisms(char *mechArray[], int arrayLen)
501 {
502 	gss_mech_info aMech;
503 	int i;
504 
505 	if (mechArray == NULL || arrayLen < 1)
506 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
507 
508 	/* ensure we have fresh data */
509 	(void) mutex_lock(&g_mechListLock);
510 	updateMechList();
511 	(void) mutex_unlock(&g_mechListLock);
512 
513 	aMech = g_mechList;
514 
515 	/* no lock required - only looking at fields that are not updated */
516 	for (i = 1; i < arrayLen; i++) {
517 		if (aMech != NULL) {
518 			*mechArray = aMech->mechNameStr;
519 			mechArray++;
520 			aMech = aMech->next;
521 		} else
522 			break;
523 	}
524 	*mechArray = NULL;
525 	return (GSS_S_COMPLETE);
526 } /* gss_get_mechanisms */
527 
528 
529 /*
530  * determines if the mechList needs to be updated from file
531  * and performs the update.
532  * this functions must be called with a lock of g_mechListLock
533  */
534 static void
535 updateMechList(void)
536 {
537 	char *fileName;
538 	struct stat fileInfo;
539 
540 	fileName = MECH_CONF;
541 
542 	/* check if mechList needs updating */
543 	if (stat(fileName, &fileInfo) == 0 &&
544 		(fileInfo.st_mtime > g_confFileModTime)) {
545 		loadConfigFile(fileName);
546 		g_confFileModTime = fileInfo.st_mtime;
547 	}
548 } /* updateMechList */
549 
550 
551 /*
552  * given the mechanism type, return the mechanism structure
553  * containing the mechanism library entry points.
554  * will return NULL if mech type is not found
555  * This function will also trigger the loading of the mechanism
556  * module if it has not been already loaded.
557  */
558 gss_mechanism
559 __gss_get_mechanism(oid)
560 const gss_OID oid;
561 {
562 	gss_mech_info aMech;
563 	gss_mechanism (*sym)(const gss_OID);
564 	void *dl;
565 
566 	/* check if the mechanism is already loaded */
567 	if ((aMech = searchMechList(oid)) != NULL && aMech->mech) {
568 		return (aMech->mech);
569 	}
570 
571 	/*
572 	 * might need to re-read the configuration file before loading
573 	 * the mechanism to ensure we have the latest info.
574 	 */
575 	(void) mutex_lock(&g_mechListLock);
576 	updateMechList();
577 
578 	aMech = searchMechList(oid);
579 
580 	/* is the mechanism present in the list ? */
581 	if (aMech == NULL) {
582 		(void) mutex_unlock(&g_mechListLock);
583 		return ((gss_mechanism)NULL);
584 	}
585 
586 	/* has another thread loaded the mech */
587 	if (aMech->mech) {
588 		(void) mutex_unlock(&g_mechListLock);
589 		return (aMech->mech);
590 	}
591 
592 	/* we found the mechanism, but it is not loaded */
593 	if ((dl = dlopen(aMech->uLibName, RTLD_NOW)) == NULL) {
594 		(void) syslog(LOG_INFO, "libgss dlopen(%s): %s\n",
595 				aMech->uLibName, dlerror());
596 		(void) mutex_unlock(&g_mechListLock);
597 		return ((gss_mechanism)NULL);
598 	}
599 
600 	if ((sym = (gss_mechanism (*)(const gss_OID))dlsym(dl, MECH_SYM))
601 			== NULL) {
602 		(void) dlclose(dl);
603 		(void) syslog(LOG_INFO, "unable to initialize mechanism"
604 				" library [%s]\n", aMech->uLibName);
605 		(void) mutex_unlock(&g_mechListLock);
606 		return ((gss_mechanism)NULL);
607 	}
608 
609 	/* Call the symbol to get the mechanism table */
610 	aMech->mech = (*sym)(aMech->mech_type);
611 
612 	if (aMech->mech == NULL) {
613 		(void) dlclose(dl);
614 		(void) syslog(LOG_INFO, "unable to initialize mechanism"
615 				" library [%s]\n", aMech->uLibName);
616 		(void) mutex_unlock(&g_mechListLock);
617 		return ((gss_mechanism)NULL);
618 	}
619 
620 	aMech->dl_handle = dl;
621 
622 	(void) mutex_unlock(&g_mechListLock);
623 	return (aMech->mech);
624 } /* __gss_get_mechanism */
625 
626 gss_mechanism_ext
627 __gss_get_mechanism_ext(oid)
628 const gss_OID oid;
629 {
630 	gss_mech_info aMech;
631 	gss_mechanism_ext mech_ext;
632 
633 	/* check if the mechanism is already loaded */
634 	if ((aMech = searchMechList(oid)) != NULL && aMech->mech_ext != NULL)
635 		return (aMech->mech_ext);
636 
637 	if (__gss_get_mechanism(oid) == NULL)
638 		return (NULL);
639 
640 	if (aMech->dl_handle == NULL)
641 		return (NULL);
642 
643 	/* Load the gss_config_ext struct for this mech */
644 
645 	mech_ext = (gss_mechanism_ext)malloc(sizeof (struct gss_config_ext));
646 
647 	if (mech_ext == NULL)
648 		return (NULL);
649 
650 	/*
651 	 * dlsym() the mech's 'method' functions for the extended APIs
652 	 *
653 	 * NOTE:  Until the void *context argument is removed from the
654 	 * SPI method functions' signatures it will be necessary to have
655 	 * different function pointer typedefs and function names for
656 	 * the SPI methods than for the API.  When this argument is
657 	 * removed it will be possible to rename gss_*_sfct to gss_*_fct
658 	 * and and gssspi_* to gss_*.
659 	 */
660 	mech_ext->gss_acquire_cred_with_password =
661 		(gss_acquire_cred_with_password_sfct)dlsym(aMech->dl_handle,
662 			"gssspi_acquire_cred_with_password");
663 
664 	/* Set aMech->mech_ext */
665 	(void) mutex_lock(&g_mechListLock);
666 
667 	if (aMech->mech_ext == NULL)
668 		aMech->mech_ext = mech_ext;
669 	else
670 		free(mech_ext);	/* we raced and lost; don't leak */
671 
672 	(void) mutex_unlock(&g_mechListLock);
673 
674 	return (aMech->mech_ext);
675 
676 } /* __gss_get_mechanism_ext */
677 
678 
679 /*
680  * this routine is used for searching the list of mechanism data.
681  * it needs not be mutex protected because we only add new structures
682  * from the end and they are fully initialized before being added.
683  */
684 static gss_mech_info searchMechList(oid)
685 const gss_OID oid;
686 {
687 	gss_mech_info aMech = g_mechList;
688 
689 	/* if oid is null -> then get default which is the first in the list */
690 	if (oid == GSS_C_NULL_OID)
691 		return (aMech);
692 
693 	while (aMech != NULL) {
694 		if (g_OID_equal(aMech->mech_type, oid))
695 			return (aMech);
696 		aMech = aMech->next;
697 	}
698 
699 	/* none found */
700 	return ((gss_mech_info) NULL);
701 } /* searchMechList */
702 
703 
704 /*
705  * loads the configuration file
706  * this is called while having a mutex lock on the mechanism list
707  * entries for libraries that have been loaded can't be modified
708  * mechNameStr and mech_type fields are not updated during updates
709  */
710 static void loadConfigFile(fileName)
711 const char *fileName;
712 {
713 	char buffer[BUFSIZ], *oidStr, *oid, *sharedLib, *kernMod, *endp;
714 	char *modOptions;
715 	char sharedPath[sizeof (MECH_LIB_PREFIX) + BUFSIZ];
716 	char *tmpStr;
717 	FILE *confFile;
718 	gss_OID mechOid;
719 	gss_mech_info aMech, tmp;
720 	OM_uint32 minor;
721 	gss_buffer_desc oidBuf;
722 
723 	if ((confFile = fopen(fileName, "rF")) == NULL) {
724 		return;
725 	}
726 
727 	(void) memset(buffer, 0, sizeof (buffer));
728 	while (fgets(buffer, BUFSIZ, confFile) != NULL) {
729 
730 		/* ignore lines beginning with # */
731 		if (*buffer == '#')
732 			continue;
733 
734 		/*
735 		 * find the first white-space character after
736 		 * the mechanism name
737 		 */
738 		oidStr = buffer;
739 		for (oid = buffer; *oid && !isspace(*oid); oid++);
740 
741 		/* Now find the first non-white-space character */
742 		if (*oid) {
743 			*oid = '\0';
744 			oid++;
745 			while (*oid && isspace(*oid))
746 				oid++;
747 		}
748 
749 		/*
750 		 * If that's all, then this is a corrupt entry. Skip it.
751 		 */
752 		if (! *oid)
753 			continue;
754 
755 		/* Find the end of the oid and make sure it is NULL-ended */
756 		for (endp = oid; *endp && !isspace(*endp); endp++)
757 			;
758 
759 		if (*endp) {
760 			*endp = '\0';
761 		}
762 
763 		/*
764 		 * check if an entry for this oid already exists
765 		 * if it does, and the library is already loaded then
766 		 * we can't modify it, so skip it
767 		 */
768 		oidBuf.value = (void *)oid;
769 		oidBuf.length = strlen(oid);
770 		if (generic_gss_str_to_oid(&minor, &oidBuf, &mechOid)
771 			!= GSS_S_COMPLETE) {
772 			(void) syslog(LOG_INFO, "invalid mechanism oid"
773 					" [%s] in configuration file", oid);
774 			continue;
775 		}
776 
777 		aMech = searchMechList(mechOid);
778 		if (aMech && aMech->mech) {
779 			free(mechOid->elements);
780 			free(mechOid);
781 			continue;
782 		}
783 
784 		/* Find the start of the shared lib name */
785 		for (sharedLib = endp+1; *sharedLib && isspace(*sharedLib);
786 			sharedLib++)
787 			;
788 
789 		/*
790 		 * If that's all, then this is a corrupt entry. Skip it.
791 		 */
792 		if (! *sharedLib) {
793 			free(mechOid->elements);
794 			free(mechOid);
795 			continue;
796 		}
797 
798 		/*
799 		 * Find the end of the shared lib name and make sure it is
800 		 *  NULL-terminated.
801 		 */
802 		for (endp = sharedLib; *endp && !isspace(*endp); endp++)
803 			;
804 
805 		if (*endp) {
806 			*endp = '\0';
807 		}
808 
809 		/* Find the start of the optional kernel module lib name */
810 		for (kernMod = endp+1; *kernMod && isspace(*kernMod);
811 			kernMod++)
812 			;
813 
814 		/*
815 		 * If this item starts with a bracket "[", then
816 		 * it is not a kernel module, but is a list of
817 		 * options for the user module to parse later.
818 		 */
819 		if (*kernMod && *kernMod != '[') {
820 			/*
821 			 * Find the end of the shared lib name and make sure
822 			 * it is NULL-terminated.
823 			 */
824 			for (endp = kernMod; *endp && !isspace(*endp); endp++)
825 				;
826 
827 			if (*endp) {
828 				*endp = '\0';
829 			}
830 		} else
831 			kernMod = NULL;
832 
833 		/* Find the start of the optional module options list */
834 		for (modOptions = endp+1; *modOptions && isspace(*modOptions);
835 			modOptions++);
836 
837 		if (*modOptions == '[')  {
838 			/* move past the opening bracket */
839 			for (modOptions = modOptions+1;
840 			    *modOptions && isspace(*modOptions);
841 			    modOptions++);
842 
843 			/* Find the closing bracket */
844 			for (endp = modOptions;
845 				*endp && *endp != ']'; endp++);
846 
847 			if (endp)
848 				*endp = '\0';
849 
850 		} else {
851 			modOptions = NULL;
852 		}
853 
854 		(void) strcpy(sharedPath, MECH_LIB_PREFIX);
855 		(void) strcat(sharedPath, sharedLib);
856 
857 		/*
858 		 * are we creating a new mechanism entry or
859 		 * just modifying existing (non loaded) mechanism entry
860 		 */
861 		if (aMech) {
862 			/*
863 			 * delete any old values and set new
864 			 * mechNameStr and mech_type are not modified
865 			 */
866 			if (aMech->kmodName) {
867 				free(aMech->kmodName);
868 				aMech->kmodName = NULL;
869 			}
870 
871 			if (aMech->optionStr) {
872 				free(aMech->optionStr);
873 				aMech->optionStr = NULL;
874 			}
875 
876 			if ((tmpStr = strdup(sharedPath)) != NULL) {
877 				if (aMech->uLibName)
878 					free(aMech->uLibName);
879 				aMech->uLibName = tmpStr;
880 			}
881 
882 			if (kernMod) /* this is an optional parameter */
883 				aMech->kmodName = strdup(kernMod);
884 
885 			if (modOptions) /* optional module options */
886 				aMech->optionStr = strdup(modOptions);
887 
888 			/* the oid is already set */
889 			free(mechOid->elements);
890 			free(mechOid);
891 			continue;
892 		}
893 
894 		/* adding a new entry */
895 		aMech = malloc(sizeof (struct gss_mech_config));
896 		if (aMech == NULL) {
897 			free(mechOid->elements);
898 			free(mechOid);
899 			continue;
900 		}
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);
905 
906 		/* check if any memory allocations failed - bad news */
907 		if (aMech->uLibName == NULL || aMech->mechNameStr == NULL) {
908 			if (aMech->uLibName)
909 				free(aMech->uLibName);
910 			if (aMech->mechNameStr)
911 				free(aMech->mechNameStr);
912 			free(mechOid->elements);
913 			free(mechOid);
914 			free(aMech);
915 			continue;
916 		}
917 		if (kernMod)	/* this is an optional parameter */
918 			aMech->kmodName = strdup(kernMod);
919 
920 		if (modOptions)
921 			aMech->optionStr = strdup(modOptions);
922 		/*
923 		 * add the new entry to the end of the list - make sure
924 		 * that only complete entries are added because other
925 		 * threads might currently be searching the list.
926 		 */
927 		tmp = g_mechListTail;
928 		g_mechListTail = aMech;
929 
930 		if (tmp != NULL)
931 			tmp->next = aMech;
932 
933 		if (g_mechList == NULL)
934 			g_mechList = aMech;
935 	} /* while */
936 	(void) fclose(confFile);
937 } /* loadConfigFile */
938