xref: /freebsd/crypto/krb5/src/lib/gssapi/mechglue/g_initialize.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* #pragma ident	"@(#)g_initialize.c	1.36	05/02/02 SMI" */
2 
3 /*
4  * Copyright 1996 by Sun Microsystems, Inc.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software
7  * and its documentation for any purpose is hereby granted without fee,
8  * provided that the above copyright notice appears in all copies and
9  * that both that copyright notice and this permission notice appear in
10  * supporting documentation, and that the name of Sun Microsystems not be used
11  * in advertising or publicity pertaining to distribution of the software
12  * without specific, written prior permission. Sun Microsystems makes no
13  * representations about the suitability of this software for any
14  * purpose.  It is provided "as is" without express or implied warranty.
15  *
16  * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18  * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
20  * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
21  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22  * PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 /*
26  * This function will initialize the gssapi mechglue library
27  */
28 
29 #include "mglueP.h"
30 #ifdef HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif
33 #ifdef HAVE_SYS_STAT_H
34 #include <sys/stat.h>
35 #endif
36 #ifdef HAVE_SYS_PARAM_H
37 #include <sys/param.h>
38 #endif
39 
40 #include <stdio.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #ifndef _WIN32
45 #include <glob.h>
46 #endif
47 
48 #define	M_DEFAULT	"default"
49 
50 #include "k5-thread.h"
51 #include "k5-plugin.h"
52 #include "osconf.h"
53 #ifdef _GSS_STATIC_LINK
54 #include "gssapiP_krb5.h"
55 #include "gssapiP_spnego.h"
56 #endif
57 
58 #define MECH_SYM "gss_mech_initialize"
59 #define MECH_INTERPOSER_SYM "gss_mech_interposer"
60 
61 #ifndef MECH_CONF
62 #define	MECH_CONF "/etc/gss/mech"
63 #endif
64 #define MECH_CONF_PATTERN MECH_CONF ".d/*.conf"
65 
66 /* Local functions */
67 static void addConfigEntry(const char *oidStr, const char *oid,
68 			   const char *sharedLib, const char *kernMod,
69 			   const char *modOptions, const char *modType);
70 static gss_mech_info searchMechList(gss_const_OID);
71 static void loadConfigFile(const char *);
72 #if defined(_WIN32)
73 #ifndef MECH_KEY
74 #define MECH_KEY "SOFTWARE\\gss\\mech"
75 #endif
76 static time_t getRegKeyModTime(HKEY hBaseKey, const char *keyPath);
77 static time_t getRegConfigModTime(const char *keyPath);
78 static void getRegKeyValue(HKEY key, const char *keyPath, const char *valueName, void **data, DWORD *dataLen);
79 static void loadConfigFromRegistry(HKEY keyBase, const char *keyPath);
80 #endif
81 static void updateMechList(void);
82 static void initMechList(void);
83 static void loadInterMech(gss_mech_info aMech);
84 static void freeMechList(void);
85 
86 static OM_uint32 build_mechSet(void);
87 static void free_mechSet(void);
88 
89 /*
90  * list of mechanism libraries and their entry points.
91  * the list also maintains state of the mech libraries (loaded or not).
92  */
93 static gss_mech_info g_mechList = NULL;
94 static gss_mech_info g_mechListTail = NULL;
95 static k5_mutex_t g_mechListLock = K5_MUTEX_PARTIAL_INITIALIZER;
96 static time_t g_confFileModTime = (time_t)-1;
97 static time_t g_confLastCall = (time_t)0;
98 
99 static gss_OID_set_desc g_mechSet = { 0, NULL };
100 static k5_mutex_t g_mechSetLock = K5_MUTEX_PARTIAL_INITIALIZER;
101 
102 MAKE_INIT_FUNCTION(gssint_mechglue_init);
103 MAKE_FINI_FUNCTION(gssint_mechglue_fini);
104 
105 int
gssint_mechglue_init(void)106 gssint_mechglue_init(void)
107 {
108 	int err;
109 
110 #ifdef SHOW_INITFINI_FUNCS
111 	printf("gssint_mechglue_init\n");
112 #endif
113 
114 	add_error_table(&et_ggss_error_table);
115 
116 	err = k5_mutex_finish_init(&g_mechSetLock);
117 	if (err)
118 		return err;
119 	err = k5_mutex_finish_init(&g_mechListLock);
120 	if (err)
121 		return err;
122 
123 #ifdef _GSS_STATIC_LINK
124 	err = gss_krb5int_lib_init();
125 	if (err)
126 		return err;
127 	err = gss_spnegoint_lib_init();
128 	if (err)
129 		return err;
130 #endif
131 
132 	err = gssint_mecherrmap_init();
133 	return err;
134 }
135 
136 void
gssint_mechglue_fini(void)137 gssint_mechglue_fini(void)
138 {
139 	if (!INITIALIZER_RAN(gssint_mechglue_init) || PROGRAM_EXITING()) {
140 #ifdef SHOW_INITFINI_FUNCS
141 		printf("gssint_mechglue_fini: skipping\n");
142 #endif
143 		return;
144 	}
145 
146 #ifdef SHOW_INITFINI_FUNCS
147 	printf("gssint_mechglue_fini\n");
148 #endif
149 #ifdef _GSS_STATIC_LINK
150 	gss_spnegoint_lib_fini();
151 	gss_krb5int_lib_fini();
152 #endif
153 	k5_mutex_destroy(&g_mechSetLock);
154 	k5_mutex_destroy(&g_mechListLock);
155 	free_mechSet();
156 	freeMechList();
157 	remove_error_table(&et_ggss_error_table);
158 	gssint_mecherrmap_destroy();
159 }
160 
161 int
gssint_mechglue_initialize_library(void)162 gssint_mechglue_initialize_library(void)
163 {
164 	return CALL_INIT_FUNCTION(gssint_mechglue_init);
165 }
166 
167 /*
168  * function used to reclaim the memory used by a gss_OID structure.
169  * This routine requires direct access to the mechList.
170  */
171 OM_uint32 KRB5_CALLCONV
gss_release_oid(minor_status,oid)172 gss_release_oid(minor_status, oid)
173 OM_uint32 *minor_status;
174 gss_OID *oid;
175 {
176 	OM_uint32 major;
177 	gss_mech_info aMech;
178 
179 	if (minor_status != NULL)
180 	    *minor_status = 0;
181 
182 	if (minor_status == NULL || oid == NULL)
183 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
184 
185 	*minor_status = gssint_mechglue_initialize_library();
186 	if (*minor_status != 0)
187 		return (GSS_S_FAILURE);
188 
189 	k5_mutex_lock(&g_mechListLock);
190 	aMech = g_mechList;
191 	while (aMech != NULL) {
192 
193 		/*
194 		 * look through the loaded mechanism libraries for
195 		 * gss_internal_release_oid until one returns success.
196 		 * gss_internal_release_oid will only return success when
197 		 * the OID was recognized as an internal mechanism OID. if no
198 		 * mechanisms recognize the OID, then call the generic version.
199 		 */
200 		if (aMech->mech && aMech->mech->gss_internal_release_oid) {
201 			major = aMech->mech->gss_internal_release_oid(
202 					minor_status, oid);
203 			if (major == GSS_S_COMPLETE) {
204 				k5_mutex_unlock(&g_mechListLock);
205 				return (GSS_S_COMPLETE);
206 			}
207 			map_error(minor_status, aMech->mech);
208 		}
209 		aMech = aMech->next;
210 	} /* while */
211 	k5_mutex_unlock(&g_mechListLock);
212 
213 	return (generic_gss_release_oid(minor_status, oid));
214 } /* gss_release_oid */
215 
216 /*
217  * Wrapper around inquire_attrs_for_mech to determine whether a mechanism has
218  * the deprecated attribute.  Must be called without g_mechSetLock since it
219  * will call into the mechglue.
220  */
221 static int
is_deprecated(gss_OID element)222 is_deprecated(gss_OID element)
223 {
224 	OM_uint32 major, minor;
225 	gss_OID_set mech_attrs = GSS_C_NO_OID_SET;
226 	int deprecated = 0;
227 
228 	major = gss_inquire_attrs_for_mech(&minor, element, &mech_attrs, NULL);
229 	if (major == GSS_S_COMPLETE) {
230 		gss_test_oid_set_member(&minor, (gss_OID)GSS_C_MA_DEPRECATED,
231 					mech_attrs, &deprecated);
232 	}
233 
234 	if (mech_attrs != GSS_C_NO_OID_SET)
235 		gss_release_oid_set(&minor, &mech_attrs);
236 
237 	return deprecated;
238 }
239 
240 /*
241  * Removes mechs with the deprecated attribute from an OID set.  Must be
242  * called without g_mechSetLock held since it calls into the mechglue.
243  */
244 static void
prune_deprecated(gss_OID_set mech_set)245 prune_deprecated(gss_OID_set mech_set)
246 {
247 	OM_uint32 i, j;
248 
249 	j = 0;
250 	for (i = 0; i < mech_set->count; i++) {
251 	    if (!is_deprecated(&mech_set->elements[i]))
252 		mech_set->elements[j++] = mech_set->elements[i];
253 	    else
254 		gssalloc_free(mech_set->elements[i].elements);
255 	}
256 	mech_set->count = j;
257 }
258 
259 /*
260  * this function will return an oid set indicating available mechanisms.
261  * The set returned is based on configuration file entries and
262  * NOT on the loaded mechanisms.  This function does not check if any
263  * of these can actually be loaded.
264  * Deprecated mechanisms will not be returned.
265  * This routine needs direct access to the mechanism list.
266  * To avoid reading the configuration file each call, we will save a
267  * a mech oid set, and only update it once the file has changed.
268  */
269 OM_uint32 KRB5_CALLCONV
gss_indicate_mechs(minorStatus,mechSet_out)270 gss_indicate_mechs(minorStatus, mechSet_out)
271 OM_uint32 *minorStatus;
272 gss_OID_set *mechSet_out;
273 {
274 	OM_uint32 status;
275 
276 	/* Initialize outputs. */
277 
278 	if (minorStatus != NULL)
279 		*minorStatus = 0;
280 
281 	if (mechSet_out != NULL)
282 		*mechSet_out = GSS_C_NO_OID_SET;
283 
284 	/* Validate arguments. */
285 	if (minorStatus == NULL || mechSet_out == NULL)
286 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
287 
288 	*minorStatus = gssint_mechglue_initialize_library();
289 	if (*minorStatus != 0)
290 		return (GSS_S_FAILURE);
291 
292 	if (build_mechSet())
293 		return GSS_S_FAILURE;
294 
295 	/*
296 	 * need to lock the g_mechSet in case someone tries to update it while
297 	 * I'm copying it.
298 	 */
299 	k5_mutex_lock(&g_mechSetLock);
300 	status = generic_gss_copy_oid_set(minorStatus, &g_mechSet, mechSet_out);
301 	k5_mutex_unlock(&g_mechSetLock);
302 
303 	if (*mechSet_out != GSS_C_NO_OID_SET)
304 		prune_deprecated(*mechSet_out);
305 
306 	return (status);
307 } /* gss_indicate_mechs */
308 
309 
310 /* Call with g_mechSetLock held, or during final cleanup.  */
311 static void
free_mechSet(void)312 free_mechSet(void)
313 {
314 	unsigned int i;
315 
316 	if (g_mechSet.count != 0) {
317 		for (i = 0; i < g_mechSet.count; i++)
318 			free(g_mechSet.elements[i].elements);
319 		free(g_mechSet.elements);
320 		g_mechSet.elements = NULL;
321 		g_mechSet.count = 0;
322 	}
323 }
324 
325 static OM_uint32
build_mechSet(void)326 build_mechSet(void)
327 {
328 	gss_mech_info mList;
329 	size_t i;
330 	size_t count;
331 	gss_OID curItem;
332 
333 	/*
334 	 * lock the mutex since we will be updating
335 	 * the mechList structure
336 	 * we need to keep the lock while we build the mechanism list
337 	 * since we are accessing parts of the mechList which could be
338 	 * modified.
339 	 */
340 	k5_mutex_lock(&g_mechListLock);
341 
342 	updateMechList();
343 
344 	/*
345 	 * we need to lock the mech set so that no one else will
346 	 * try to read it as we are re-creating it
347 	 */
348 	k5_mutex_lock(&g_mechSetLock);
349 
350 	/* if the oid list already exists we must free it first */
351 	free_mechSet();
352 
353 	/* determine how many elements to have in the list */
354 	mList = g_mechList;
355 	count = 0;
356 	while (mList != NULL) {
357 		count++;
358 		mList = mList->next;
359 	}
360 
361 	/* this should always be true, but.... */
362 	if (count > 0) {
363 		g_mechSet.elements =
364 			(gss_OID) calloc(count, sizeof (gss_OID_desc));
365 		if (g_mechSet.elements == NULL) {
366 			k5_mutex_unlock(&g_mechSetLock);
367 			k5_mutex_unlock(&g_mechListLock);
368 			return (GSS_S_FAILURE);
369 		}
370 
371 		(void) memset(g_mechSet.elements, 0,
372 			      count * sizeof (gss_OID_desc));
373 
374 		/* now copy each oid element */
375 		count = 0;
376 		for (mList = g_mechList; mList != NULL; mList = mList->next) {
377 			/* Don't expose interposer mechanisms. */
378 			if (mList->is_interposer)
379 				continue;
380 			curItem = &(g_mechSet.elements[count]);
381 			curItem->elements = (void*)
382 				malloc(mList->mech_type->length);
383 			if (curItem->elements == NULL) {
384 				/*
385 				 * this is nasty - we must delete the
386 				 * part of the array already copied
387 				 */
388 				for (i = 0; i < count; i++) {
389 					free(g_mechSet.elements[i].
390 					     elements);
391 				}
392 				free(g_mechSet.elements);
393 				g_mechSet.count = 0;
394 				g_mechSet.elements = NULL;
395 				k5_mutex_unlock(&g_mechSetLock);
396 				k5_mutex_unlock(&g_mechListLock);
397 				return (GSS_S_FAILURE);
398 			}
399 			g_OID_copy(curItem, mList->mech_type);
400 			count++;
401 		}
402 		g_mechSet.count = count;
403 	}
404 
405 	k5_mutex_unlock(&g_mechSetLock);
406 	k5_mutex_unlock(&g_mechListLock);
407 
408 	return GSS_S_COMPLETE;
409 }
410 
411 
412 /*
413  * this function has been added for use by modules that need to
414  * know what (if any) optional parameters are supplied in the
415  * config file (MECH_CONF).
416  * It will return the option string for a specified mechanism.
417  * caller is responsible for freeing the memory
418  */
419 char *
gssint_get_modOptions(oid)420 gssint_get_modOptions(oid)
421 const gss_OID oid;
422 {
423 	gss_mech_info aMech;
424 	char *modOptions = NULL;
425 
426 	if (gssint_mechglue_initialize_library() != 0)
427 		return (NULL);
428 
429 	/* make sure we have fresh data */
430 	k5_mutex_lock(&g_mechListLock);
431 	updateMechList();
432 
433 	if ((aMech = searchMechList(oid)) == NULL ||
434 		aMech->optionStr == NULL) {
435 		k5_mutex_unlock(&g_mechListLock);
436 		return (NULL);
437 	}
438 
439 	if (aMech->optionStr)
440 		modOptions = strdup(aMech->optionStr);
441 	k5_mutex_unlock(&g_mechListLock);
442 
443 	return (modOptions);
444 } /* gssint_get_modOptions */
445 
446 /* Return the mtime of filename or its eventual symlink target (if it is a
447  * symlink), whichever is larger.  Return (time_t)-1 if lstat or stat fails. */
448 static time_t
check_link_mtime(const char * filename,time_t * mtime_out)449 check_link_mtime(const char *filename, time_t *mtime_out)
450 {
451 	struct stat st1, st2;
452 
453 	if (lstat(filename, &st1) != 0)
454 		return (time_t)-1;
455 	if (!S_ISLNK(st1.st_mode))
456 		return st1.st_mtime;
457 	if (stat(filename, &st2) != 0)
458 		return (time_t)-1;
459 	return (st1.st_mtime > st2.st_mtime) ? st1.st_mtime : st2.st_mtime;
460 }
461 
462 /* Load pathname if it is newer than last.  Update *highest to the maximum of
463  * its current value and pathname's mod time. */
464 static void
load_if_changed(const char * pathname,time_t last,time_t * highest)465 load_if_changed(const char *pathname, time_t last, time_t *highest)
466 {
467 	time_t mtime;
468 
469 	mtime = check_link_mtime(pathname, &mtime);
470 	if (mtime == (time_t)-1)
471 		return;
472 	if (mtime > *highest || *highest == (time_t)-1)
473 		*highest = mtime;
474 	if (mtime > last || last == (time_t)-1)
475 		loadConfigFile(pathname);
476 }
477 
478 #ifndef _WIN32
479 /* Try to load any config files which have changed since the last call.  Config
480  * files are MECH_CONF and any files matching MECH_CONF_PATTERN. */
481 static void
loadConfigFiles()482 loadConfigFiles()
483 {
484 	glob_t globbuf;
485 	time_t highest = (time_t)-1, now;
486 	char **path;
487 	const char *val;
488 
489 	/* Don't glob and stat more than once per second. */
490 	if (time(&now) == (time_t)-1 || now == g_confLastCall)
491 		return;
492 	g_confLastCall = now;
493 
494 	val = secure_getenv("GSS_MECH_CONFIG");
495 	if (val != NULL) {
496 		load_if_changed(val, g_confFileModTime, &g_confFileModTime);
497 		return;
498 	}
499 
500 	load_if_changed(MECH_CONF, g_confFileModTime, &highest);
501 
502 	memset(&globbuf, 0, sizeof(globbuf));
503 	if (glob(MECH_CONF_PATTERN, 0, NULL, &globbuf) == 0) {
504 		for (path = globbuf.gl_pathv; *path != NULL; path++)
505 			load_if_changed(*path, g_confFileModTime, &highest);
506 	}
507 	globfree(&globbuf);
508 
509 	g_confFileModTime = highest;
510 }
511 #endif
512 
513 /*
514  * determines if the mechList needs to be updated from file
515  * and performs the update.
516  * this functions must be called with a lock of g_mechListLock
517  */
518 static void
updateMechList(void)519 updateMechList(void)
520 {
521 	gss_mech_info minfo;
522 
523 #if defined(_WIN32)
524 	time_t lastConfModTime = getRegConfigModTime(MECH_KEY);
525 	if (g_confFileModTime >= lastConfModTime &&
526 	    g_confFileModTime != (time_t)-1)
527 		return;
528 	g_confFileModTime = lastConfModTime;
529 	loadConfigFromRegistry(HKEY_CURRENT_USER, MECH_KEY);
530 	loadConfigFromRegistry(HKEY_LOCAL_MACHINE, MECH_KEY);
531 #else /* _WIN32 */
532 	loadConfigFiles();
533 #endif /* !_WIN32 */
534 
535 	/* Load any unloaded interposer mechanisms immediately, to make sure we
536 	 * interpose other mechanisms before they are used. */
537 	for (minfo = g_mechList; minfo != NULL; minfo = minfo->next) {
538 		if (minfo->is_interposer && minfo->mech == NULL)
539 			loadInterMech(minfo);
540 	}
541 } /* updateMechList */
542 
543 /* Update the mech list from system configuration if we have never done so.
544  * Must be invoked with the g_mechListLock mutex held. */
545 static void
initMechList(void)546 initMechList(void)
547 {
548 	static int lazy_init = 0;
549 
550 	if (lazy_init == 0) {
551 		updateMechList();
552 		lazy_init = 1;
553 	}
554 }
555 
556 static void
releaseMechInfo(gss_mech_info * pCf)557 releaseMechInfo(gss_mech_info *pCf)
558 {
559 	gss_mech_info cf;
560 	OM_uint32 minor_status;
561 
562 	if (*pCf == NULL) {
563 		return;
564 	}
565 
566 	cf = *pCf;
567 
568 	if (cf->kmodName != NULL)
569 		free(cf->kmodName);
570 	if (cf->uLibName != NULL)
571 		free(cf->uLibName);
572 	if (cf->mechNameStr != NULL)
573 		free(cf->mechNameStr);
574 	if (cf->optionStr != NULL)
575 		free(cf->optionStr);
576 	if (cf->mech_type != GSS_C_NO_OID &&
577 	    cf->mech_type != &cf->mech->mech_type)
578 		generic_gss_release_oid(&minor_status, &cf->mech_type);
579 	if (cf->freeMech)
580 		zapfree(cf->mech, sizeof(*cf->mech));
581 	if (cf->dl_handle != NULL)
582 		krb5int_close_plugin(cf->dl_handle);
583 	if (cf->int_mech_type != GSS_C_NO_OID)
584 		generic_gss_release_oid(&minor_status, &cf->int_mech_type);
585 
586 	memset(cf, 0, sizeof(*cf));
587 	free(cf);
588 
589 	*pCf = NULL;
590 }
591 
592 #ifdef _GSS_STATIC_LINK
593 /*
594  * Register a mechanism.  Called with g_mechListLock held.
595  */
596 int
gssint_register_mechinfo(gss_mech_info template)597 gssint_register_mechinfo(gss_mech_info template)
598 {
599 	gss_mech_info cf, new_cf;
600 
601 	new_cf = calloc(1, sizeof(*new_cf));
602 	if (new_cf == NULL) {
603 		return ENOMEM;
604 	}
605 
606 	new_cf->dl_handle = template->dl_handle;
607 	/* copy mech so we can rewrite canonical mechanism OID */
608 	new_cf->mech = (gss_mechanism)calloc(1, sizeof(struct gss_config));
609 	if (new_cf->mech == NULL) {
610 		releaseMechInfo(&new_cf);
611 		return ENOMEM;
612 	}
613 	*new_cf->mech = *template->mech;
614 	if (template->mech_type != NULL)
615 		new_cf->mech->mech_type = *(template->mech_type);
616 	new_cf->mech_type = &new_cf->mech->mech_type;
617 	new_cf->priority = template->priority;
618 	new_cf->freeMech = 1;
619 	new_cf->next = NULL;
620 
621 	if (template->kmodName != NULL) {
622 		new_cf->kmodName = strdup(template->kmodName);
623 		if (new_cf->kmodName == NULL) {
624 			releaseMechInfo(&new_cf);
625 			return ENOMEM;
626 		}
627 	}
628 	if (template->uLibName != NULL) {
629 		new_cf->uLibName = strdup(template->uLibName);
630 		if (new_cf->uLibName == NULL) {
631 			releaseMechInfo(&new_cf);
632 			return ENOMEM;
633 		}
634 	}
635 	if (template->mechNameStr != NULL) {
636 		new_cf->mechNameStr = strdup(template->mechNameStr);
637 		if (new_cf->mechNameStr == NULL) {
638 			releaseMechInfo(&new_cf);
639 			return ENOMEM;
640 		}
641 	}
642 	if (template->optionStr != NULL) {
643 		new_cf->optionStr = strdup(template->optionStr);
644 		if (new_cf->optionStr == NULL) {
645 			releaseMechInfo(&new_cf);
646 			return ENOMEM;
647 		}
648 	}
649 	if (g_mechList == NULL) {
650 		g_mechList = new_cf;
651 		g_mechListTail = new_cf;
652 		return 0;
653 	} else if (new_cf->priority < g_mechList->priority) {
654 		new_cf->next = g_mechList;
655 		g_mechList = new_cf;
656 		return 0;
657 	}
658 
659 	for (cf = g_mechList; cf != NULL; cf = cf->next) {
660 		if (cf->next == NULL ||
661 		    new_cf->priority < cf->next->priority) {
662 			new_cf->next = cf->next;
663 			cf->next = new_cf;
664 			if (g_mechListTail == cf) {
665 				g_mechListTail = new_cf;
666 			}
667 			break;
668 		}
669 	}
670 
671 	return 0;
672 }
673 #endif /* _GSS_STATIC_LINK */
674 
675 #define GSS_ADD_DYNAMIC_METHOD(_dl, _mech, _symbol) \
676 	do { \
677 		struct errinfo errinfo; \
678 		\
679 		memset(&errinfo, 0, sizeof(errinfo)); \
680 		if (krb5int_get_plugin_func(_dl, \
681 					    #_symbol, \
682 					    (void (**)())&(_mech)->_symbol, \
683 					    &errinfo) || errinfo.code) {  \
684 			(_mech)->_symbol = NULL; \
685 			k5_clear_error(&errinfo); \
686 			} \
687 	} while (0)
688 
689 /*
690  * If _symbol is undefined in the shared object but the shared object
691  * is linked against the mechanism glue, it's possible for dlsym() to
692  * return the mechanism glue implementation. Guard against that.
693  */
694 #define GSS_ADD_DYNAMIC_METHOD_NOLOOP(_dl, _mech, _symbol)	\
695 	do {							\
696 		GSS_ADD_DYNAMIC_METHOD(_dl, _mech, _symbol);	\
697 		if ((_mech)->_symbol == _symbol)		\
698 		    (_mech)->_symbol = NULL;			\
699 	} while (0)
700 
701 static gss_mechanism
build_dynamicMech(void * dl,const gss_OID mech_type)702 build_dynamicMech(void *dl, const gss_OID mech_type)
703 {
704 	gss_mechanism mech;
705 
706 	mech = (gss_mechanism)calloc(1, sizeof(*mech));
707 	if (mech == NULL) {
708 		return NULL;
709 	}
710 
711 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred);
712 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_cred);
713 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_init_sec_context);
714 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_accept_sec_context);
715 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_process_context_token);
716 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_delete_sec_context);
717 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_context_time);
718 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_mic);
719 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_verify_mic);
720 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap);
721 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap);
722 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_status);
723 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_indicate_mechs);
724 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_compare_name);
725 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_name);
726 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_name);
727 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_name);
728 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred);
729 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_add_cred);
730 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_sec_context);
731 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_sec_context);
732 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred_by_mech);
733 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_names_for_mech);
734 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_context);
735 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_internal_release_oid);
736 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_size_limit);
737 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_localname);
738 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_authorize_localname);
739 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_name);
740 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_duplicate_name);
741 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_store_cred);
742 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_sec_context_by_oid);
743 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred_by_oid);
744 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_sec_context_option);
745 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_set_cred_option);
746 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_mech_invoke);
747 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_aead);
748 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap_aead);
749 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_iov);
750 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap_iov);
751 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_iov_length);
752 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_complete_auth_token);
753 	/* Services4User (introduced in 1.8) */
754 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred_impersonate_name);
755 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_add_cred_impersonate_name);
756 	/* Naming extensions (introduced in 1.8) */
757 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_name_ext);
758 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_name);
759 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_name_attribute);
760 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_name_attribute);
761 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_delete_name_attribute);
762 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_name_composite);
763 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_map_name_to_any);
764 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_any_name_mapping);
765         /* RFC 4401 (introduced in 1.8) */
766 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_pseudo_random);
767 	/* RFC 4178 (introduced in 1.8; gss_get_neg_mechs not implemented) */
768 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_neg_mechs);
769         /* draft-ietf-sasl-gs2 */
770         GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_saslname_for_mech);
771         GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_mech_for_saslname);
772         /* RFC 5587 */
773         GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_attrs_for_mech);
774 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred_from);
775 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_store_cred_into);
776 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_acquire_cred_with_password);
777 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_cred);
778 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_cred);
779 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_sec_context_by_mech);
780 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_name_by_mech);
781 	GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_cred_by_mech);
782 	/* draft-zhu-negoex */
783 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_query_meta_data);
784 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_exchange_meta_data);
785 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_query_mechanism_info);
786 	/* gss_get_mic_iov extensions (added 1.12, implementable 1.20) */
787 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_mic_iov);
788 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_verify_mic_iov);
789 	GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_mic_iov_length);
790 
791 	assert(mech_type != GSS_C_NO_OID);
792 
793 	mech->mech_type = *(mech_type);
794 
795 	return mech;
796 }
797 
798 #define RESOLVE_GSSI_SYMBOL(_dl, _mech, _psym, _nsym)			\
799 	do {								\
800 		struct errinfo errinfo;					\
801 		memset(&errinfo, 0, sizeof(errinfo));			\
802 		if (krb5int_get_plugin_func(_dl,			\
803 					    "gssi" #_nsym,		\
804 					    (void (**)())&(_mech)->_psym \
805 					    ## _nsym,			\
806 					    &errinfo) || errinfo.code) { \
807 			(_mech)->_psym ## _nsym = NULL;			\
808 			k5_clear_error(&errinfo);			\
809 		}							\
810 	} while (0)
811 
812 /* Build an interposer mechanism function table from dl. */
813 static gss_mechanism
build_interMech(void * dl,const gss_OID mech_type)814 build_interMech(void *dl, const gss_OID mech_type)
815 {
816 	gss_mechanism mech;
817 
818 	mech = calloc(1, sizeof(*mech));
819 	if (mech == NULL) {
820 		return NULL;
821 	}
822 
823 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred);
824 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_cred);
825 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _init_sec_context);
826 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _accept_sec_context);
827 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _process_context_token);
828 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _delete_sec_context);
829 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _context_time);
830 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_mic);
831 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _verify_mic);
832 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap);
833 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap);
834 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_status);
835 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _indicate_mechs);
836 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _compare_name);
837 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_name);
838 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_name);
839 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_name);
840 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred);
841 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _add_cred);
842 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_sec_context);
843 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_sec_context);
844 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred_by_mech);
845 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_names_for_mech);
846 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_context);
847 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _internal_release_oid);
848 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_size_limit);
849 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _localname);
850 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _authorize_localname);
851 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_name);
852 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _duplicate_name);
853 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred);
854 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_sec_context_by_oid);
855 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred_by_oid);
856 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_sec_context_option);
857 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _set_cred_option);
858 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _mech_invoke);
859 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_aead);
860 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap_aead);
861 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_iov);
862 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap_iov);
863 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_iov_length);
864 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _complete_auth_token);
865 	/* Services4User (introduced in 1.8) */
866 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_impersonate_name);
867 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _add_cred_impersonate_name);
868 	/* Naming extensions (introduced in 1.8) */
869 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_name_ext);
870 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_name);
871 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_name_attribute);
872 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_name_attribute);
873 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _delete_name_attribute);
874 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_name_composite);
875 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _map_name_to_any);
876 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_any_name_mapping);
877 	/* RFC 4401 (introduced in 1.8) */
878 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _pseudo_random);
879 	/* RFC 4178 (introduced in 1.8; get_neg_mechs not implemented) */
880 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_neg_mechs);
881 	/* draft-ietf-sasl-gs2 */
882 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_saslname_for_mech);
883 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_mech_for_saslname);
884 	/* RFC 5587 */
885 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_attrs_for_mech);
886 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_from);
887 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred_into);
888 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _acquire_cred_with_password);
889 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_cred);
890 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_cred);
891 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_sec_context_by_mech);
892 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_name_by_mech);
893 	RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_cred_by_mech);
894 	/* gss_get_mic_iov extensions (added 1.12, implementable 1.20) */
895 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_mic_iov);
896 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _verify_mic_iov);
897 	RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_mic_iov_length);
898 
899 	mech->mech_type = *mech_type;
900 	return mech;
901 }
902 
903 /*
904  * Concatenate an interposer mech OID and a real mech OID to create an
905  * identifier for the interposed mech.  (The concatenation will not be a valid
906  * DER OID encoding, but the OID is only used internally.)
907  */
908 static gss_OID
interposed_oid(gss_OID pre,gss_OID real)909 interposed_oid(gss_OID pre, gss_OID real)
910 {
911 	gss_OID o;
912 
913 	o = (gss_OID)malloc(sizeof(gss_OID_desc));
914 	if (!o)
915 		return NULL;
916 
917 	o->length = pre->length + real->length;
918 	o->elements = malloc(o->length);
919 	if (!o->elements) {
920 		free(o);
921 		return NULL;
922 	}
923 
924 	memcpy(o->elements, pre->elements, pre->length);
925 	memcpy((char *)o->elements + pre->length, real->elements,
926 	       real->length);
927 
928 	return o;
929 }
930 
931 static void
loadInterMech(gss_mech_info minfo)932 loadInterMech(gss_mech_info minfo)
933 {
934 	struct plugin_file_handle *dl = NULL;
935 	struct errinfo errinfo;
936 	gss_OID_set (*isym)(const gss_OID);
937 	gss_OID_set list;
938 	gss_OID oid;
939 	OM_uint32 min;
940 	gss_mech_info mi;
941 	size_t i;
942 
943 	memset(&errinfo, 0, sizeof(errinfo));
944 
945 	if (krb5int_open_plugin(minfo->uLibName, &dl, &errinfo) != 0 ||
946 	    errinfo.code != 0) {
947 		return;
948 	}
949 
950 	if (krb5int_get_plugin_func(dl, MECH_INTERPOSER_SYM,
951 				    (void (**)())&isym, &errinfo) != 0)
952 		goto cleanup;
953 
954 	/* Get a list of mechs to interpose. */
955 	list = (*isym)(minfo->mech_type);
956 	if (!list)
957 		goto cleanup;
958 	minfo->mech = build_interMech(dl, minfo->mech_type);
959 	if (minfo->mech == NULL)
960 		goto cleanup;
961 	minfo->freeMech = 1;
962 
963 	/* Add interposer fields for each interposed mech. */
964 	for (i = 0; i < list->count; i++) {
965 		/* Skip this mech if it doesn't exist or is already
966 		 * interposed. */
967 		oid = &list->elements[i];
968 		mi = searchMechList(oid);
969 		if (mi == NULL || mi->int_mech_type != NULL)
970 			continue;
971 
972 		/* Construct a special OID to represent the interposed mech. */
973 		mi->int_mech_type = interposed_oid(minfo->mech_type, oid);
974 		if (mi->int_mech_type == NULL)
975 			continue;
976 
977 		/* Save an alias to the interposer's function table. */
978 		mi->int_mech = minfo->mech;
979 	}
980 	(void)gss_release_oid_set(&min, &list);
981 
982 	minfo->dl_handle = dl;
983 	dl = NULL;
984 
985 cleanup:
986 	if (dl != NULL)
987 		krb5int_close_plugin(dl);
988 	k5_clear_error(&errinfo);
989 }
990 
991 static void
freeMechList(void)992 freeMechList(void)
993 {
994 	gss_mech_info cf, next_cf;
995 
996 	for (cf = g_mechList; cf != NULL; cf = next_cf) {
997 		next_cf = cf->next;
998 		releaseMechInfo(&cf);
999 	}
1000 }
1001 
1002 /*
1003  * Determine the mechanism to use for a caller-specified mech OID.  For the
1004  * real mech OID of an interposed mech, return the interposed OID.  For an
1005  * interposed mech OID (which an interposer mech uses when re-entering the
1006  * mechglue), return the real mech OID.  The returned OID is an alias and
1007  * should not be modified or freed.
1008  */
1009 OM_uint32
gssint_select_mech_type(OM_uint32 * minor,gss_const_OID oid,gss_OID * selected_oid)1010 gssint_select_mech_type(OM_uint32 *minor, gss_const_OID oid,
1011 			gss_OID *selected_oid)
1012 {
1013 	gss_mech_info minfo;
1014 	OM_uint32 status;
1015 
1016 	*selected_oid = GSS_C_NO_OID;
1017 
1018 	if (gssint_mechglue_initialize_library() != 0)
1019 		return GSS_S_FAILURE;
1020 
1021 	k5_mutex_lock(&g_mechListLock);
1022 
1023 	/* Read conf file at least once so that interposer plugins have a
1024 	 * chance of getting initialized. */
1025 	initMechList();
1026 
1027 	minfo = g_mechList;
1028 	if (oid == GSS_C_NULL_OID)
1029 		oid = minfo->mech_type;
1030 	while (minfo != NULL) {
1031 		if (g_OID_equal(minfo->mech_type, oid)) {
1032 			if (minfo->int_mech_type != GSS_C_NO_OID)
1033 				*selected_oid = minfo->int_mech_type;
1034 			else
1035 				*selected_oid = minfo->mech_type;
1036 			status = GSS_S_COMPLETE;
1037 			goto done;
1038 		} else if ((minfo->int_mech_type != GSS_C_NO_OID) &&
1039 			   (g_OID_equal(minfo->int_mech_type, oid))) {
1040 			*selected_oid = minfo->mech_type;
1041 			status = GSS_S_COMPLETE;
1042 			goto done;
1043 		}
1044 		minfo = minfo->next;
1045 	}
1046 	status = GSS_S_BAD_MECH;
1047 
1048 done:
1049 	k5_mutex_unlock(&g_mechListLock);
1050 	return status;
1051 }
1052 
1053 /* If oid is an interposed OID, return the corresponding real mech OID.  If
1054  * it's a real mech OID, return it unmodified.  Otherwised return null. */
1055 gss_OID
gssint_get_public_oid(gss_const_OID oid)1056 gssint_get_public_oid(gss_const_OID oid)
1057 {
1058 	gss_mech_info minfo;
1059 	gss_OID public_oid = GSS_C_NO_OID;
1060 
1061 	/* if oid is null -> then get default which is the first in the list */
1062 	if (oid == GSS_C_NO_OID)
1063 		return GSS_C_NO_OID;
1064 
1065 	if (gssint_mechglue_initialize_library() != 0)
1066 		return GSS_C_NO_OID;
1067 
1068 	k5_mutex_lock(&g_mechListLock);
1069 
1070 	for (minfo = g_mechList; minfo != NULL; minfo = minfo->next) {
1071 		if (minfo->is_interposer)
1072 			continue;
1073 		if (g_OID_equal(minfo->mech_type, oid) ||
1074 		    ((minfo->int_mech_type != GSS_C_NO_OID) &&
1075 		     (g_OID_equal(minfo->int_mech_type, oid)))) {
1076 			public_oid = minfo->mech_type;
1077 			break;
1078 		}
1079 	}
1080 
1081 	k5_mutex_unlock(&g_mechListLock);
1082 	return public_oid;
1083 }
1084 
1085 /* Translate a vector of oids (as from a union cred struct) into a set of
1086  * public OIDs using gssint_get_public_oid. */
1087 OM_uint32
gssint_make_public_oid_set(OM_uint32 * minor_status,gss_OID oids,int count,gss_OID_set * public_set)1088 gssint_make_public_oid_set(OM_uint32 *minor_status, gss_OID oids, int count,
1089 			   gss_OID_set *public_set)
1090 {
1091 	OM_uint32 status, tmpmin;
1092 	gss_OID_set set;
1093 	gss_OID public_oid;
1094 	int i;
1095 
1096 	*public_set = GSS_C_NO_OID_SET;
1097 
1098 	status = generic_gss_create_empty_oid_set(minor_status, &set);
1099 	if (GSS_ERROR(status))
1100 		return status;
1101 
1102 	for (i = 0; i < count; i++) {
1103 		public_oid = gssint_get_public_oid(&oids[i]);
1104 		if (public_oid == GSS_C_NO_OID)
1105 			continue;
1106 		status = generic_gss_add_oid_set_member(minor_status,
1107 							public_oid, &set);
1108 		if (GSS_ERROR(status)) {
1109 			(void) generic_gss_release_oid_set(&tmpmin, &set);
1110 			return status;
1111 		}
1112 	}
1113 
1114 	*public_set = set;
1115 	return GSS_S_COMPLETE;
1116 }
1117 
1118 /*
1119  * Register a mechanism.  Called with g_mechListLock held.
1120  */
1121 
1122 /*
1123  * given the mechanism type, return the mechanism structure
1124  * containing the mechanism library entry points.
1125  * will return NULL if mech type is not found
1126  * This function will also trigger the loading of the mechanism
1127  * module if it has not been already loaded.
1128  */
1129 gss_mechanism
gssint_get_mechanism(gss_const_OID oid)1130 gssint_get_mechanism(gss_const_OID oid)
1131 {
1132 	gss_mech_info aMech;
1133 	gss_mechanism (*sym)(const gss_OID);
1134 	struct plugin_file_handle *dl;
1135 	struct errinfo errinfo;
1136 
1137 	if (gssint_mechglue_initialize_library() != 0)
1138 		return (NULL);
1139 
1140 	k5_mutex_lock(&g_mechListLock);
1141 
1142 	/* Check if the mechanism is already loaded. */
1143 	aMech = g_mechList;
1144 	if (oid == GSS_C_NULL_OID)
1145 		oid = aMech->mech_type;
1146 	while (aMech != NULL) {
1147 		if (g_OID_equal(aMech->mech_type, oid) && aMech->mech) {
1148 			k5_mutex_unlock(&g_mechListLock);
1149 			return aMech->mech;
1150 		} else if (aMech->int_mech_type != GSS_C_NO_OID &&
1151 			   g_OID_equal(aMech->int_mech_type, oid)) {
1152 			k5_mutex_unlock(&g_mechListLock);
1153 			return aMech->int_mech;
1154 		}
1155 		aMech = aMech->next;
1156 	}
1157 
1158 	/*
1159 	 * might need to re-read the configuration file before loading
1160 	 * the mechanism to ensure we have the latest info.
1161 	 */
1162 	updateMechList();
1163 
1164 	aMech = searchMechList(oid);
1165 
1166 	/* is the mechanism present in the list ? */
1167 	if (aMech == NULL) {
1168 		k5_mutex_unlock(&g_mechListLock);
1169 		return ((gss_mechanism)NULL);
1170 	}
1171 
1172 	/* has another thread loaded the mech */
1173 	if (aMech->mech) {
1174 		k5_mutex_unlock(&g_mechListLock);
1175 		return (aMech->mech);
1176 	}
1177 
1178 	memset(&errinfo, 0, sizeof(errinfo));
1179 
1180 	if (krb5int_open_plugin(aMech->uLibName, &dl, &errinfo) != 0 ||
1181 	    errinfo.code != 0) {
1182 		k5_clear_error(&errinfo);
1183 		k5_mutex_unlock(&g_mechListLock);
1184 		return ((gss_mechanism)NULL);
1185 	}
1186 
1187 	if (krb5int_get_plugin_func(dl, MECH_SYM, (void (**)())&sym,
1188 				    &errinfo) == 0) {
1189 		/* Call the symbol to get the mechanism table */
1190 		aMech->mech = (*sym)(aMech->mech_type);
1191 	} else {
1192 		/* Try dynamic dispatch table */
1193 		k5_clear_error(&errinfo);
1194 		aMech->mech = build_dynamicMech(dl, aMech->mech_type);
1195 		aMech->freeMech = 1;
1196 	}
1197 	if (aMech->mech == NULL) {
1198 		(void) krb5int_close_plugin(dl);
1199 		k5_mutex_unlock(&g_mechListLock);
1200 		return ((gss_mechanism)NULL);
1201 	}
1202 
1203 	aMech->dl_handle = dl;
1204 
1205 	k5_mutex_unlock(&g_mechListLock);
1206 	return (aMech->mech);
1207 } /* gssint_get_mechanism */
1208 
1209 /*
1210  * this routine is used for searching the list of mechanism data.
1211  *
1212  * this needs to be called with g_mechListLock held.
1213  */
searchMechList(gss_const_OID oid)1214 static gss_mech_info searchMechList(gss_const_OID oid)
1215 {
1216 	gss_mech_info aMech = g_mechList;
1217 
1218 	/* if oid is null -> then get default which is the first in the list */
1219 	if (oid == GSS_C_NULL_OID)
1220 		return (aMech);
1221 
1222 	while (aMech != NULL) {
1223 		if (g_OID_equal(aMech->mech_type, oid))
1224 			return (aMech);
1225 		aMech = aMech->next;
1226 	}
1227 
1228 	/* none found */
1229 	return ((gss_mech_info) NULL);
1230 } /* searchMechList */
1231 
1232 /* Return the first non-whitespace character starting from str. */
1233 static char *
skip_whitespace(char * str)1234 skip_whitespace(char *str)
1235 {
1236 	while (isspace(*str))
1237 		str++;
1238 	return str;
1239 }
1240 
1241 /* Truncate str at the first whitespace character and return the first
1242  * non-whitespace character after that point. */
1243 static char *
delimit_ws(char * str)1244 delimit_ws(char *str)
1245 {
1246 	while (*str != '\0' && !isspace(*str))
1247 		str++;
1248 	if (*str != '\0')
1249 		*str++ = '\0';
1250 	return skip_whitespace(str);
1251 }
1252 
1253 /* Truncate str at the first occurrence of delimiter and return the first
1254  * non-whitespace character after that point. */
1255 static char *
delimit(char * str,char delimiter)1256 delimit(char *str, char delimiter)
1257 {
1258 	while (*str != '\0' && *str != delimiter)
1259 		str++;
1260 	if (*str != '\0')
1261 		*str++ = '\0';
1262 	return skip_whitespace(str);
1263 }
1264 
1265 /*
1266  * loads the configuration file
1267  * this is called while having a mutex lock on the mechanism list
1268  * entries for libraries that have been loaded can't be modified
1269  * mechNameStr and mech_type fields are not updated during updates
1270  */
1271 static void
loadConfigFile(const char * fileName)1272 loadConfigFile(const char *fileName)
1273 {
1274 	char *sharedLib, *kernMod, *modOptions, *modType, *oid, *next;
1275 	char buffer[BUFSIZ], *oidStr;
1276 	FILE *confFile;
1277 
1278 	if ((confFile = fopen(fileName, "r")) == NULL) {
1279 		return;
1280 	}
1281 
1282 	(void) memset(buffer, 0, sizeof (buffer));
1283 	while (fgets(buffer, BUFSIZ, confFile) != NULL) {
1284 
1285 		/* ignore lines beginning with # */
1286 		if (*buffer == '#')
1287 			continue;
1288 
1289 		/* Parse out the name, oid, and shared library path. */
1290 		oidStr = buffer;
1291 		oid = delimit_ws(oidStr);
1292 		if (*oid == '\0')
1293 			continue;
1294 		sharedLib = delimit_ws(oid);
1295 		if (*sharedLib == '\0')
1296 			continue;
1297 		next = delimit_ws(sharedLib);
1298 
1299 		/* Parse out the kernel module name if present. */
1300 		if (*next != '\0' && *next != '[' && *next != '<') {
1301 			kernMod = next;
1302 			next = delimit_ws(kernMod);
1303 		} else {
1304 			kernMod = NULL;
1305 		}
1306 
1307 		/* Parse out the module options if present. */
1308 		if (*next == '[') {
1309 			modOptions = next + 1;
1310 			next = delimit(modOptions, ']');
1311 		} else {
1312 			modOptions = NULL;
1313 		}
1314 
1315 		/* Parse out the module type if present. */
1316 		if (*next == '<') {
1317 			modType = next + 1;
1318 			(void)delimit(modType, '>');
1319 		} else {
1320 			modType = NULL;
1321 		}
1322 
1323 		addConfigEntry(oidStr, oid, sharedLib, kernMod, modOptions,
1324 			       modType);
1325 	} /* while */
1326 	(void) fclose(confFile);
1327 } /* loadConfigFile */
1328 
1329 #if defined(_WIN32)
1330 
1331 static time_t
filetimeToTimet(const FILETIME * ft)1332 filetimeToTimet(const FILETIME *ft)
1333 {
1334 	ULARGE_INTEGER ull;
1335 
1336 	ull.LowPart = ft->dwLowDateTime;
1337 	ull.HighPart = ft->dwHighDateTime;
1338 	return (time_t)(ull.QuadPart / 10000000ULL - 11644473600ULL);
1339 }
1340 
1341 static time_t
getRegConfigModTime(const char * keyPath)1342 getRegConfigModTime(const char *keyPath)
1343 {
1344 	time_t currentUserModTime = getRegKeyModTime(HKEY_CURRENT_USER,
1345 						     keyPath);
1346 	time_t localMachineModTime = getRegKeyModTime(HKEY_LOCAL_MACHINE,
1347 						      keyPath);
1348 
1349 	return currentUserModTime > localMachineModTime ? currentUserModTime :
1350 		localMachineModTime;
1351 }
1352 
1353 static time_t
getRegKeyModTime(HKEY hBaseKey,const char * keyPath)1354 getRegKeyModTime(HKEY hBaseKey, const char *keyPath)
1355 {
1356 	HKEY hConfigKey;
1357 	HRESULT rc;
1358 	int iSubKey = 0;
1359 	time_t modTime = 0, keyModTime;
1360 	FILETIME keyLastWriteTime;
1361 	char subKeyName[256];
1362 
1363 	if ((rc = RegOpenKeyEx(hBaseKey, keyPath, 0, KEY_ENUMERATE_SUB_KEYS,
1364 			       &hConfigKey)) != ERROR_SUCCESS) {
1365 		/* TODO: log error message */
1366 		return 0;
1367 	}
1368 	do {
1369 		int subKeyNameSize=sizeof(subKeyName)/sizeof(subKeyName[0]);
1370 		if ((rc = RegEnumKeyEx(hConfigKey, iSubKey++, subKeyName,
1371 				       &subKeyNameSize, NULL, NULL, NULL,
1372 				       &keyLastWriteTime)) != ERROR_SUCCESS) {
1373 			break;
1374 		}
1375 		keyModTime = filetimeToTimet(&keyLastWriteTime);
1376 		if (modTime < keyModTime) {
1377 			modTime = keyModTime;
1378 		}
1379 	} while (1);
1380 	RegCloseKey(hConfigKey);
1381 	return modTime;
1382 }
1383 
1384 static void
getRegKeyValue(HKEY hKey,const char * keyPath,const char * valueName,void ** data,DWORD * dataLen)1385 getRegKeyValue(HKEY hKey, const char *keyPath, const char *valueName,
1386 	       void **data, DWORD* dataLen)
1387 {
1388 	DWORD sizeRequired=*dataLen;
1389 	HRESULT hr;
1390 	/* Get data length required */
1391 	if ((hr = RegGetValue(hKey, keyPath, valueName, RRF_RT_REG_SZ, NULL,
1392 			      NULL, &sizeRequired)) != ERROR_SUCCESS) {
1393 		/* TODO: LOG registry error */
1394 		return;
1395 	}
1396 	/* adjust data buffer size if necessary */
1397 	if (*dataLen < sizeRequired) {
1398 		*dataLen = sizeRequired;
1399 		*data = realloc(*data, sizeRequired);
1400 		if (!*data) {
1401 			*dataLen = 0;
1402 			/* TODO: LOG OOM ERROR! */
1403 			return;
1404 		}
1405 	}
1406 	/* get data */
1407 	if ((hr = RegGetValue(hKey, keyPath, valueName, RRF_RT_REG_SZ, NULL,
1408 			      *data, &sizeRequired)) != ERROR_SUCCESS) {
1409 		/* LOG registry error */
1410 		return;
1411 	}
1412 }
1413 
1414 static void
loadConfigFromRegistry(HKEY hBaseKey,const char * keyPath)1415 loadConfigFromRegistry(HKEY hBaseKey, const char *keyPath)
1416 {
1417 	HKEY hConfigKey;
1418 	DWORD iSubKey, nSubKeys, maxSubKeyNameLen, modTypeLen;
1419 	char *oidStr = NULL, *oid = NULL, *sharedLib = NULL, *kernMod = NULL;
1420 	char *modOptions = NULL, *modType = NULL;
1421 	DWORD oidStrLen = 0, oidLen = 0, sharedLibLen = 0, kernModLen = 0;
1422 	DWORD modOptionsLen = 0;
1423 	HRESULT rc;
1424 
1425 	if ((rc = RegOpenKeyEx(hBaseKey, keyPath, 0,
1426 			       KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,
1427 			       &hConfigKey)) != ERROR_SUCCESS) {
1428 		/* TODO: log registry error */
1429 		return;
1430 	}
1431 
1432 	if ((rc = RegQueryInfoKey(hConfigKey,
1433 		NULL, /* lpClass */
1434 		NULL, /* lpcClass */
1435 		NULL, /* lpReserved */
1436 		&nSubKeys,
1437 		&maxSubKeyNameLen,
1438 		NULL, /* lpcMaxClassLen */
1439 		NULL, /* lpcValues */
1440 		NULL, /* lpcMaxValueNameLen */
1441 		NULL, /* lpcMaxValueLen */
1442 		NULL, /* lpcbSecurityDescriptor */
1443 		NULL  /* lpftLastWriteTime */ )) != ERROR_SUCCESS) {
1444 		goto cleanup;
1445 	}
1446 	oidStr = malloc(++maxSubKeyNameLen);
1447 	if (!oidStr) {
1448 		goto cleanup;
1449 	}
1450 	for (iSubKey=0; iSubKey<nSubKeys; iSubKey++) {
1451 		oidStrLen = maxSubKeyNameLen;
1452 		if ((rc = RegEnumKeyEx(hConfigKey, iSubKey, oidStr, &oidStrLen,
1453 				       NULL, NULL, NULL, NULL)) !=
1454 		    ERROR_SUCCESS) {
1455 			/* TODO: log registry error */
1456 			continue;
1457 		}
1458 		getRegKeyValue(hConfigKey, oidStr, "OID", &oid, &oidLen);
1459 		getRegKeyValue(hConfigKey, oidStr, "Shared Library",
1460 			       &sharedLib, &sharedLibLen);
1461 		getRegKeyValue(hConfigKey, oidStr, "Kernel Module", &kernMod,
1462 			       &kernModLen);
1463 		getRegKeyValue(hConfigKey, oidStr, "Options", &modOptions,
1464 			       &modOptionsLen);
1465 		getRegKeyValue(hConfigKey, oidStr, "Type", &modType,
1466 			       &modTypeLen);
1467 		addConfigEntry(oidStr, oid, sharedLib, kernMod, modOptions,
1468 			       modType);
1469 	}
1470 cleanup:
1471 	RegCloseKey(hConfigKey);
1472 	if (oidStr) {
1473 		free(oidStr);
1474 	}
1475 	if (oid) {
1476 		free(oid);
1477 	}
1478 	if (sharedLib) {
1479 		free(sharedLib);
1480 	}
1481 	if (kernMod) {
1482 		free(kernMod);
1483 	}
1484 	if (modOptions) {
1485 		free(modOptions);
1486 	}
1487 }
1488 #endif
1489 
1490 static void
addConfigEntry(const char * oidStr,const char * oid,const char * sharedLib,const char * kernMod,const char * modOptions,const char * modType)1491 addConfigEntry(const char *oidStr, const char *oid, const char *sharedLib,
1492 	       const char *kernMod, const char *modOptions,
1493 	       const char *modType)
1494 {
1495 #if defined(_WIN32)
1496 	const char *sharedPath;
1497 #else
1498 	char sharedPath[sizeof (MECH_LIB_PREFIX) + BUFSIZ];
1499 #endif
1500 	char *tmpStr;
1501 	gss_OID mechOid;
1502 	gss_mech_info aMech, tmp;
1503 	OM_uint32 minor;
1504 	gss_buffer_desc oidBuf;
1505 
1506 	if ((!oid) || (!oidStr)) {
1507 		return;
1508 	}
1509 	/*
1510 	 * check if an entry for this oid already exists
1511 	 * if it does, and the library is already loaded then
1512 	 * we can't modify it, so skip it
1513 	 */
1514 	oidBuf.value = (void *)oid;
1515 	oidBuf.length = strlen(oid);
1516 	if (generic_gss_str_to_oid(&minor, &oidBuf, &mechOid)
1517 		!= GSS_S_COMPLETE) {
1518 		return;
1519 	}
1520 
1521 	aMech = searchMechList(mechOid);
1522 	if (aMech && aMech->mech) {
1523 		generic_gss_release_oid(&minor, &mechOid);
1524 		return;
1525 	}
1526 
1527 	/*
1528 	 * If that's all, then this is a corrupt entry. Skip it.
1529 	 */
1530 	if (! *sharedLib) {
1531 		generic_gss_release_oid(&minor, &mechOid);
1532 		return;
1533 	}
1534 #if defined(_WIN32)
1535 	sharedPath = sharedLib;
1536 #else
1537 	if (sharedLib[0] == '/')
1538 		snprintf(sharedPath, sizeof(sharedPath), "%s", sharedLib);
1539 	else
1540 		snprintf(sharedPath, sizeof(sharedPath), "%s%s",
1541 			 MECH_LIB_PREFIX, sharedLib);
1542 #endif
1543 	/*
1544 	 * are we creating a new mechanism entry or
1545 	 * just modifying existing (non loaded) mechanism entry
1546 	 */
1547 	if (aMech) {
1548 		/*
1549 		 * delete any old values and set new
1550 		 * mechNameStr and mech_type are not modified
1551 		 */
1552 		if (aMech->kmodName) {
1553 			free(aMech->kmodName);
1554 			aMech->kmodName = NULL;
1555 		}
1556 
1557 		if (aMech->optionStr) {
1558 			free(aMech->optionStr);
1559 			aMech->optionStr = NULL;
1560 		}
1561 
1562 		if ((tmpStr = strdup(sharedPath)) != NULL) {
1563 			if (aMech->uLibName)
1564 				free(aMech->uLibName);
1565 			aMech->uLibName = tmpStr;
1566 		}
1567 
1568 		if (kernMod) /* this is an optional parameter */
1569 			aMech->kmodName = strdup(kernMod);
1570 
1571 		if (modOptions) /* optional module options */
1572 			aMech->optionStr = strdup(modOptions);
1573 
1574 		/* the oid is already set */
1575 		generic_gss_release_oid(&minor, &mechOid);
1576 		return;
1577 	}
1578 
1579 	/* adding a new entry */
1580 	aMech = calloc(1, sizeof (struct gss_mech_config));
1581 	if (aMech == NULL) {
1582 		generic_gss_release_oid(&minor, &mechOid);
1583 		return;
1584 	}
1585 	aMech->mech_type = mechOid;
1586 	aMech->uLibName = strdup(sharedPath);
1587 	aMech->mechNameStr = strdup(oidStr);
1588 	aMech->freeMech = 0;
1589 
1590 	/* check if any memory allocations failed - bad news */
1591 	if (aMech->uLibName == NULL || aMech->mechNameStr == NULL) {
1592 		if (aMech->uLibName)
1593 			free(aMech->uLibName);
1594 		if (aMech->mechNameStr)
1595 			free(aMech->mechNameStr);
1596 		generic_gss_release_oid(&minor, &mechOid);
1597 		free(aMech);
1598 		return;
1599 	}
1600 	if (kernMod)	/* this is an optional parameter */
1601 		aMech->kmodName = strdup(kernMod);
1602 
1603 	if (modOptions)
1604 		aMech->optionStr = strdup(modOptions);
1605 
1606 	if (modType && strcmp(modType, "interposer") == 0)
1607 		aMech->is_interposer = 1;
1608 
1609 	/*
1610 	 * add the new entry to the end of the list - make sure
1611 	 * that only complete entries are added because other
1612 	 * threads might currently be searching the list.
1613 	 */
1614 	tmp = g_mechListTail;
1615 	g_mechListTail = aMech;
1616 
1617 	if (tmp != NULL)
1618 		tmp->next = aMech;
1619 
1620 	if (g_mechList == NULL)
1621 		g_mechList = aMech;
1622 }
1623 
1624