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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 */ 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 * 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 * 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 * 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 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 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 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 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 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 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 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