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