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