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