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