/* #pragma ident "@(#)g_initialize.c 1.36 05/02/02 SMI" */ /* * Copyright 1996 by Sun Microsystems, Inc. * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Sun Microsystems not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Sun Microsystems makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * SUN MICROSYSTEMS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ /* * This function will initialize the gssapi mechglue library */ #include "mglueP.h" #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #include #include #include #include #ifndef _WIN32 #include #endif #define M_DEFAULT "default" #include "k5-thread.h" #include "k5-plugin.h" #include "osconf.h" #ifdef _GSS_STATIC_LINK #include "gssapiP_krb5.h" #include "gssapiP_spnego.h" #endif #define MECH_SYM "gss_mech_initialize" #define MECH_INTERPOSER_SYM "gss_mech_interposer" #ifndef MECH_CONF #define MECH_CONF "/etc/gss/mech" #endif #define MECH_CONF_PATTERN MECH_CONF ".d/*.conf" /* Local functions */ static void addConfigEntry(const char *oidStr, const char *oid, const char *sharedLib, const char *kernMod, const char *modOptions, const char *modType); static gss_mech_info searchMechList(gss_const_OID); static void loadConfigFile(const char *); #if defined(_WIN32) #ifndef MECH_KEY #define MECH_KEY "SOFTWARE\\gss\\mech" #endif static time_t getRegKeyModTime(HKEY hBaseKey, const char *keyPath); static time_t getRegConfigModTime(const char *keyPath); static void getRegKeyValue(HKEY key, const char *keyPath, const char *valueName, void **data, DWORD *dataLen); static void loadConfigFromRegistry(HKEY keyBase, const char *keyPath); #endif static void updateMechList(void); static void initMechList(void); static void loadInterMech(gss_mech_info aMech); static void freeMechList(void); static OM_uint32 build_mechSet(void); static void free_mechSet(void); /* * list of mechanism libraries and their entry points. * the list also maintains state of the mech libraries (loaded or not). */ static gss_mech_info g_mechList = NULL; static gss_mech_info g_mechListTail = NULL; static k5_mutex_t g_mechListLock = K5_MUTEX_PARTIAL_INITIALIZER; static time_t g_confFileModTime = (time_t)0; static time_t g_confLastCall = (time_t)0; static gss_OID_set_desc g_mechSet = { 0, NULL }; static k5_mutex_t g_mechSetLock = K5_MUTEX_PARTIAL_INITIALIZER; MAKE_INIT_FUNCTION(gssint_mechglue_init); MAKE_FINI_FUNCTION(gssint_mechglue_fini); int gssint_mechglue_init(void) { int err; #ifdef SHOW_INITFINI_FUNCS printf("gssint_mechglue_init\n"); #endif add_error_table(&et_ggss_error_table); err = k5_mutex_finish_init(&g_mechSetLock); if (err) return err; err = k5_mutex_finish_init(&g_mechListLock); if (err) return err; #ifdef _GSS_STATIC_LINK err = gss_krb5int_lib_init(); if (err) return err; err = gss_spnegoint_lib_init(); if (err) return err; #endif err = gssint_mecherrmap_init(); return err; } void gssint_mechglue_fini(void) { if (!INITIALIZER_RAN(gssint_mechglue_init) || PROGRAM_EXITING()) { #ifdef SHOW_INITFINI_FUNCS printf("gssint_mechglue_fini: skipping\n"); #endif return; } #ifdef SHOW_INITFINI_FUNCS printf("gssint_mechglue_fini\n"); #endif #ifdef _GSS_STATIC_LINK gss_spnegoint_lib_fini(); gss_krb5int_lib_fini(); #endif k5_mutex_destroy(&g_mechSetLock); k5_mutex_destroy(&g_mechListLock); free_mechSet(); freeMechList(); remove_error_table(&et_ggss_error_table); gssint_mecherrmap_destroy(); } int gssint_mechglue_initialize_library(void) { return CALL_INIT_FUNCTION(gssint_mechglue_init); } /* * function used to reclaim the memory used by a gss_OID structure. * This routine requires direct access to the mechList. */ OM_uint32 KRB5_CALLCONV gss_release_oid(minor_status, oid) OM_uint32 *minor_status; gss_OID *oid; { OM_uint32 major; gss_mech_info aMech; if (minor_status != NULL) *minor_status = 0; if (minor_status == NULL || oid == NULL) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minor_status = gssint_mechglue_initialize_library(); if (*minor_status != 0) return (GSS_S_FAILURE); k5_mutex_lock(&g_mechListLock); aMech = g_mechList; while (aMech != NULL) { /* * look through the loaded mechanism libraries for * gss_internal_release_oid until one returns success. * gss_internal_release_oid will only return success when * the OID was recognized as an internal mechanism OID. if no * mechanisms recognize the OID, then call the generic version. */ if (aMech->mech && aMech->mech->gss_internal_release_oid) { major = aMech->mech->gss_internal_release_oid( minor_status, oid); if (major == GSS_S_COMPLETE) { k5_mutex_unlock(&g_mechListLock); return (GSS_S_COMPLETE); } map_error(minor_status, aMech->mech); } aMech = aMech->next; } /* while */ k5_mutex_unlock(&g_mechListLock); return (generic_gss_release_oid(minor_status, oid)); } /* gss_release_oid */ /* * Wrapper around inquire_attrs_for_mech to determine whether a mechanism has * the deprecated attribute. Must be called without g_mechSetLock since it * will call into the mechglue. */ static int is_deprecated(gss_OID element) { OM_uint32 major, minor; gss_OID_set mech_attrs = GSS_C_NO_OID_SET; int deprecated = 0; major = gss_inquire_attrs_for_mech(&minor, element, &mech_attrs, NULL); if (major == GSS_S_COMPLETE) { gss_test_oid_set_member(&minor, (gss_OID)GSS_C_MA_DEPRECATED, mech_attrs, &deprecated); } if (mech_attrs != GSS_C_NO_OID_SET) gss_release_oid_set(&minor, &mech_attrs); return deprecated; } /* * Removes mechs with the deprecated attribute from an OID set. Must be * called without g_mechSetLock held since it calls into the mechglue. */ static void prune_deprecated(gss_OID_set mech_set) { OM_uint32 i, j; j = 0; for (i = 0; i < mech_set->count; i++) { if (!is_deprecated(&mech_set->elements[i])) mech_set->elements[j++] = mech_set->elements[i]; else gssalloc_free(mech_set->elements[i].elements); } mech_set->count = j; } /* * this function will return an oid set indicating available mechanisms. * The set returned is based on configuration file entries and * NOT on the loaded mechanisms. This function does not check if any * of these can actually be loaded. * Deprecated mechanisms will not be returned. * This routine needs direct access to the mechanism list. * To avoid reading the configuration file each call, we will save a * a mech oid set, and only update it once the file has changed. */ OM_uint32 KRB5_CALLCONV gss_indicate_mechs(minorStatus, mechSet_out) OM_uint32 *minorStatus; gss_OID_set *mechSet_out; { OM_uint32 status; /* Initialize outputs. */ if (minorStatus != NULL) *minorStatus = 0; if (mechSet_out != NULL) *mechSet_out = GSS_C_NO_OID_SET; /* Validate arguments. */ if (minorStatus == NULL || mechSet_out == NULL) return (GSS_S_CALL_INACCESSIBLE_WRITE); *minorStatus = gssint_mechglue_initialize_library(); if (*minorStatus != 0) return (GSS_S_FAILURE); if (build_mechSet()) return GSS_S_FAILURE; /* * need to lock the g_mechSet in case someone tries to update it while * I'm copying it. */ k5_mutex_lock(&g_mechSetLock); status = generic_gss_copy_oid_set(minorStatus, &g_mechSet, mechSet_out); k5_mutex_unlock(&g_mechSetLock); if (*mechSet_out != GSS_C_NO_OID_SET) prune_deprecated(*mechSet_out); return (status); } /* gss_indicate_mechs */ /* Call with g_mechSetLock held, or during final cleanup. */ static void free_mechSet(void) { unsigned int i; if (g_mechSet.count != 0) { for (i = 0; i < g_mechSet.count; i++) free(g_mechSet.elements[i].elements); free(g_mechSet.elements); g_mechSet.elements = NULL; g_mechSet.count = 0; } } static OM_uint32 build_mechSet(void) { gss_mech_info mList; size_t i; size_t count; gss_OID curItem; /* * lock the mutex since we will be updating * the mechList structure * we need to keep the lock while we build the mechanism list * since we are accessing parts of the mechList which could be * modified. */ k5_mutex_lock(&g_mechListLock); updateMechList(); /* * we need to lock the mech set so that no one else will * try to read it as we are re-creating it */ k5_mutex_lock(&g_mechSetLock); /* if the oid list already exists we must free it first */ free_mechSet(); /* determine how many elements to have in the list */ mList = g_mechList; count = 0; while (mList != NULL) { count++; mList = mList->next; } /* this should always be true, but.... */ if (count > 0) { g_mechSet.elements = (gss_OID) calloc(count, sizeof (gss_OID_desc)); if (g_mechSet.elements == NULL) { k5_mutex_unlock(&g_mechSetLock); k5_mutex_unlock(&g_mechListLock); return (GSS_S_FAILURE); } (void) memset(g_mechSet.elements, 0, count * sizeof (gss_OID_desc)); /* now copy each oid element */ count = 0; for (mList = g_mechList; mList != NULL; mList = mList->next) { /* Don't expose interposer mechanisms. */ if (mList->is_interposer) continue; curItem = &(g_mechSet.elements[count]); curItem->elements = (void*) malloc(mList->mech_type->length); if (curItem->elements == NULL) { /* * this is nasty - we must delete the * part of the array already copied */ for (i = 0; i < count; i++) { free(g_mechSet.elements[i]. elements); } free(g_mechSet.elements); g_mechSet.count = 0; g_mechSet.elements = NULL; k5_mutex_unlock(&g_mechSetLock); k5_mutex_unlock(&g_mechListLock); return (GSS_S_FAILURE); } g_OID_copy(curItem, mList->mech_type); count++; } g_mechSet.count = count; } k5_mutex_unlock(&g_mechSetLock); k5_mutex_unlock(&g_mechListLock); return GSS_S_COMPLETE; } /* * this function has been added for use by modules that need to * know what (if any) optional parameters are supplied in the * config file (MECH_CONF). * It will return the option string for a specified mechanism. * caller is responsible for freeing the memory */ char * gssint_get_modOptions(oid) const gss_OID oid; { gss_mech_info aMech; char *modOptions = NULL; if (gssint_mechglue_initialize_library() != 0) return (NULL); /* make sure we have fresh data */ k5_mutex_lock(&g_mechListLock); updateMechList(); if ((aMech = searchMechList(oid)) == NULL || aMech->optionStr == NULL) { k5_mutex_unlock(&g_mechListLock); return (NULL); } if (aMech->optionStr) modOptions = strdup(aMech->optionStr); k5_mutex_unlock(&g_mechListLock); return (modOptions); } /* gssint_get_modOptions */ /* Return the mtime of filename or its eventual symlink target (if it is a * symlink), whichever is larger. Return (time_t)-1 if lstat or stat fails. */ static time_t check_link_mtime(const char *filename, time_t *mtime_out) { struct stat st1, st2; if (lstat(filename, &st1) != 0) return (time_t)-1; if (!S_ISLNK(st1.st_mode)) return st1.st_mtime; if (stat(filename, &st2) != 0) return (time_t)-1; return (st1.st_mtime > st2.st_mtime) ? st1.st_mtime : st2.st_mtime; } /* Load pathname if it is newer than last. Update *highest to the maximum of * its current value and pathname's mod time. */ static void load_if_changed(const char *pathname, time_t last, time_t *highest) { time_t mtime; mtime = check_link_mtime(pathname, &mtime); if (mtime == (time_t)-1) return; if (mtime > *highest) *highest = mtime; if (mtime > last) loadConfigFile(pathname); } #ifndef _WIN32 /* Try to load any config files which have changed since the last call. Config * files are MECH_CONF and any files matching MECH_CONF_PATTERN. */ static void loadConfigFiles() { glob_t globbuf; time_t highest = 0, now; char **path; const char *val; /* Don't glob and stat more than once per second. */ if (time(&now) == (time_t)-1 || now == g_confLastCall) return; g_confLastCall = now; val = secure_getenv("GSS_MECH_CONFIG"); if (val != NULL) { load_if_changed(val, g_confFileModTime, &g_confFileModTime); return; } load_if_changed(MECH_CONF, g_confFileModTime, &highest); memset(&globbuf, 0, sizeof(globbuf)); if (glob(MECH_CONF_PATTERN, 0, NULL, &globbuf) == 0) { for (path = globbuf.gl_pathv; *path != NULL; path++) load_if_changed(*path, g_confFileModTime, &highest); } globfree(&globbuf); g_confFileModTime = highest; } #endif /* * determines if the mechList needs to be updated from file * and performs the update. * this functions must be called with a lock of g_mechListLock */ static void updateMechList(void) { gss_mech_info minfo; #if defined(_WIN32) time_t lastConfModTime = getRegConfigModTime(MECH_KEY); if (g_confFileModTime >= lastConfModTime) return; g_confFileModTime = lastConfModTime; loadConfigFromRegistry(HKEY_CURRENT_USER, MECH_KEY); loadConfigFromRegistry(HKEY_LOCAL_MACHINE, MECH_KEY); #else /* _WIN32 */ loadConfigFiles(); #endif /* !_WIN32 */ /* Load any unloaded interposer mechanisms immediately, to make sure we * interpose other mechanisms before they are used. */ for (minfo = g_mechList; minfo != NULL; minfo = minfo->next) { if (minfo->is_interposer && minfo->mech == NULL) loadInterMech(minfo); } } /* updateMechList */ /* Update the mech list from system configuration if we have never done so. * Must be invoked with the g_mechListLock mutex held. */ static void initMechList(void) { static int lazy_init = 0; if (lazy_init == 0) { updateMechList(); lazy_init = 1; } } static void releaseMechInfo(gss_mech_info *pCf) { gss_mech_info cf; OM_uint32 minor_status; if (*pCf == NULL) { return; } cf = *pCf; if (cf->kmodName != NULL) free(cf->kmodName); if (cf->uLibName != NULL) free(cf->uLibName); if (cf->mechNameStr != NULL) free(cf->mechNameStr); if (cf->optionStr != NULL) free(cf->optionStr); if (cf->mech_type != GSS_C_NO_OID && cf->mech_type != &cf->mech->mech_type) generic_gss_release_oid(&minor_status, &cf->mech_type); if (cf->freeMech) zapfree(cf->mech, sizeof(*cf->mech)); if (cf->dl_handle != NULL) krb5int_close_plugin(cf->dl_handle); if (cf->int_mech_type != GSS_C_NO_OID) generic_gss_release_oid(&minor_status, &cf->int_mech_type); memset(cf, 0, sizeof(*cf)); free(cf); *pCf = NULL; } #ifdef _GSS_STATIC_LINK /* * Register a mechanism. Called with g_mechListLock held. */ int gssint_register_mechinfo(gss_mech_info template) { gss_mech_info cf, new_cf; new_cf = calloc(1, sizeof(*new_cf)); if (new_cf == NULL) { return ENOMEM; } new_cf->dl_handle = template->dl_handle; /* copy mech so we can rewrite canonical mechanism OID */ new_cf->mech = (gss_mechanism)calloc(1, sizeof(struct gss_config)); if (new_cf->mech == NULL) { releaseMechInfo(&new_cf); return ENOMEM; } *new_cf->mech = *template->mech; if (template->mech_type != NULL) new_cf->mech->mech_type = *(template->mech_type); new_cf->mech_type = &new_cf->mech->mech_type; new_cf->priority = template->priority; new_cf->freeMech = 1; new_cf->next = NULL; if (template->kmodName != NULL) { new_cf->kmodName = strdup(template->kmodName); if (new_cf->kmodName == NULL) { releaseMechInfo(&new_cf); return ENOMEM; } } if (template->uLibName != NULL) { new_cf->uLibName = strdup(template->uLibName); if (new_cf->uLibName == NULL) { releaseMechInfo(&new_cf); return ENOMEM; } } if (template->mechNameStr != NULL) { new_cf->mechNameStr = strdup(template->mechNameStr); if (new_cf->mechNameStr == NULL) { releaseMechInfo(&new_cf); return ENOMEM; } } if (template->optionStr != NULL) { new_cf->optionStr = strdup(template->optionStr); if (new_cf->optionStr == NULL) { releaseMechInfo(&new_cf); return ENOMEM; } } if (g_mechList == NULL) { g_mechList = new_cf; g_mechListTail = new_cf; return 0; } else if (new_cf->priority < g_mechList->priority) { new_cf->next = g_mechList; g_mechList = new_cf; return 0; } for (cf = g_mechList; cf != NULL; cf = cf->next) { if (cf->next == NULL || new_cf->priority < cf->next->priority) { new_cf->next = cf->next; cf->next = new_cf; if (g_mechListTail == cf) { g_mechListTail = new_cf; } break; } } return 0; } #endif /* _GSS_STATIC_LINK */ #define GSS_ADD_DYNAMIC_METHOD(_dl, _mech, _symbol) \ do { \ struct errinfo errinfo; \ \ memset(&errinfo, 0, sizeof(errinfo)); \ if (krb5int_get_plugin_func(_dl, \ #_symbol, \ (void (**)())&(_mech)->_symbol, \ &errinfo) || errinfo.code) { \ (_mech)->_symbol = NULL; \ k5_clear_error(&errinfo); \ } \ } while (0) /* * If _symbol is undefined in the shared object but the shared object * is linked against the mechanism glue, it's possible for dlsym() to * return the mechanism glue implementation. Guard against that. */ #define GSS_ADD_DYNAMIC_METHOD_NOLOOP(_dl, _mech, _symbol) \ do { \ GSS_ADD_DYNAMIC_METHOD(_dl, _mech, _symbol); \ if ((_mech)->_symbol == _symbol) \ (_mech)->_symbol = NULL; \ } while (0) static gss_mechanism build_dynamicMech(void *dl, const gss_OID mech_type) { gss_mechanism mech; mech = (gss_mechanism)calloc(1, sizeof(*mech)); if (mech == NULL) { return NULL; } GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_cred); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_init_sec_context); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_accept_sec_context); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_process_context_token); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_delete_sec_context); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_context_time); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_mic); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_verify_mic); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_status); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_indicate_mechs); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_compare_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_add_cred); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_sec_context); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_sec_context); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred_by_mech); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_names_for_mech); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_context); GSS_ADD_DYNAMIC_METHOD(dl, mech, gss_internal_release_oid); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_size_limit); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_localname); GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_authorize_localname); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_duplicate_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_store_cred); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_sec_context_by_oid); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_cred_by_oid); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_sec_context_option); GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_set_cred_option); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_mech_invoke); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_aead); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap_aead); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_iov); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_unwrap_iov); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_wrap_iov_length); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_complete_auth_token); /* Services4User (introduced in 1.8) */ GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred_impersonate_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_add_cred_impersonate_name); /* Naming extensions (introduced in 1.8) */ GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_display_name_ext); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_name); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_get_name_attribute); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_name_attribute); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_delete_name_attribute); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_name_composite); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_map_name_to_any); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_release_any_name_mapping); /* RFC 4401 (introduced in 1.8) */ GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_pseudo_random); /* RFC 4178 (introduced in 1.8; gss_get_neg_mechs not implemented) */ GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_set_neg_mechs); /* draft-ietf-sasl-gs2 */ GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_saslname_for_mech); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_mech_for_saslname); /* RFC 5587 */ GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_inquire_attrs_for_mech); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_acquire_cred_from); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_store_cred_into); GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_acquire_cred_with_password); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_export_cred); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gss_import_cred); GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_sec_context_by_mech); GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_name_by_mech); GSS_ADD_DYNAMIC_METHOD(dl, mech, gssspi_import_cred_by_mech); /* draft-zhu-negoex */ GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_query_meta_data); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_exchange_meta_data); GSS_ADD_DYNAMIC_METHOD_NOLOOP(dl, mech, gssspi_query_mechanism_info); assert(mech_type != GSS_C_NO_OID); mech->mech_type = *(mech_type); return mech; } #define RESOLVE_GSSI_SYMBOL(_dl, _mech, _psym, _nsym) \ do { \ struct errinfo errinfo; \ memset(&errinfo, 0, sizeof(errinfo)); \ if (krb5int_get_plugin_func(_dl, \ "gssi" #_nsym, \ (void (**)())&(_mech)->_psym \ ## _nsym, \ &errinfo) || errinfo.code) { \ (_mech)->_psym ## _nsym = NULL; \ k5_clear_error(&errinfo); \ } \ } while (0) /* Build an interposer mechanism function table from dl. */ static gss_mechanism build_interMech(void *dl, const gss_OID mech_type) { gss_mechanism mech; mech = calloc(1, sizeof(*mech)); if (mech == NULL) { return NULL; } RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_cred); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _init_sec_context); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _accept_sec_context); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _process_context_token); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _delete_sec_context); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _context_time); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_mic); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _verify_mic); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_status); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _indicate_mechs); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _compare_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _add_cred); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_sec_context); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_sec_context); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred_by_mech); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_names_for_mech); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_context); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _internal_release_oid); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_size_limit); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _localname); RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _authorize_localname); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _duplicate_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_sec_context_by_oid); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_cred_by_oid); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_sec_context_option); RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _set_cred_option); RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _mech_invoke); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_aead); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap_aead); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_iov); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _unwrap_iov); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _wrap_iov_length); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _complete_auth_token); /* Services4User (introduced in 1.8) */ RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_impersonate_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _add_cred_impersonate_name); /* Naming extensions (introduced in 1.8) */ RESOLVE_GSSI_SYMBOL(dl, mech, gss, _display_name_ext); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_name); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _get_name_attribute); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_name_attribute); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _delete_name_attribute); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_name_composite); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _map_name_to_any); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _release_any_name_mapping); /* RFC 4401 (introduced in 1.8) */ RESOLVE_GSSI_SYMBOL(dl, mech, gss, _pseudo_random); /* RFC 4178 (introduced in 1.8; get_neg_mechs not implemented) */ RESOLVE_GSSI_SYMBOL(dl, mech, gss, _set_neg_mechs); /* draft-ietf-sasl-gs2 */ RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_saslname_for_mech); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_mech_for_saslname); /* RFC 5587 */ RESOLVE_GSSI_SYMBOL(dl, mech, gss, _inquire_attrs_for_mech); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _acquire_cred_from); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _store_cred_into); RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _acquire_cred_with_password); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _export_cred); RESOLVE_GSSI_SYMBOL(dl, mech, gss, _import_cred); RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_sec_context_by_mech); RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_name_by_mech); RESOLVE_GSSI_SYMBOL(dl, mech, gssspi, _import_cred_by_mech); mech->mech_type = *mech_type; return mech; } /* * Concatenate an interposer mech OID and a real mech OID to create an * identifier for the interposed mech. (The concatenation will not be a valid * DER OID encoding, but the OID is only used internally.) */ static gss_OID interposed_oid(gss_OID pre, gss_OID real) { gss_OID o; o = (gss_OID)malloc(sizeof(gss_OID_desc)); if (!o) return NULL; o->length = pre->length + real->length; o->elements = malloc(o->length); if (!o->elements) { free(o); return NULL; } memcpy(o->elements, pre->elements, pre->length); memcpy((char *)o->elements + pre->length, real->elements, real->length); return o; } static void loadInterMech(gss_mech_info minfo) { struct plugin_file_handle *dl = NULL; struct errinfo errinfo; gss_OID_set (*isym)(const gss_OID); gss_OID_set list; gss_OID oid; OM_uint32 min; gss_mech_info mi; size_t i; memset(&errinfo, 0, sizeof(errinfo)); if (krb5int_open_plugin(minfo->uLibName, &dl, &errinfo) != 0 || errinfo.code != 0) { return; } if (krb5int_get_plugin_func(dl, MECH_INTERPOSER_SYM, (void (**)())&isym, &errinfo) != 0) goto cleanup; /* Get a list of mechs to interpose. */ list = (*isym)(minfo->mech_type); if (!list) goto cleanup; minfo->mech = build_interMech(dl, minfo->mech_type); if (minfo->mech == NULL) goto cleanup; minfo->freeMech = 1; /* Add interposer fields for each interposed mech. */ for (i = 0; i < list->count; i++) { /* Skip this mech if it doesn't exist or is already * interposed. */ oid = &list->elements[i]; mi = searchMechList(oid); if (mi == NULL || mi->int_mech_type != NULL) continue; /* Construct a special OID to represent the interposed mech. */ mi->int_mech_type = interposed_oid(minfo->mech_type, oid); if (mi->int_mech_type == NULL) continue; /* Save an alias to the interposer's function table. */ mi->int_mech = minfo->mech; } (void)gss_release_oid_set(&min, &list); minfo->dl_handle = dl; dl = NULL; cleanup: if (dl != NULL) krb5int_close_plugin(dl); k5_clear_error(&errinfo); } static void freeMechList(void) { gss_mech_info cf, next_cf; for (cf = g_mechList; cf != NULL; cf = next_cf) { next_cf = cf->next; releaseMechInfo(&cf); } } /* * Determine the mechanism to use for a caller-specified mech OID. For the * real mech OID of an interposed mech, return the interposed OID. For an * interposed mech OID (which an interposer mech uses when re-entering the * mechglue), return the real mech OID. The returned OID is an alias and * should not be modified or freed. */ OM_uint32 gssint_select_mech_type(OM_uint32 *minor, gss_const_OID oid, gss_OID *selected_oid) { gss_mech_info minfo; OM_uint32 status; *selected_oid = GSS_C_NO_OID; if (gssint_mechglue_initialize_library() != 0) return GSS_S_FAILURE; k5_mutex_lock(&g_mechListLock); /* Read conf file at least once so that interposer plugins have a * chance of getting initialized. */ initMechList(); minfo = g_mechList; if (oid == GSS_C_NULL_OID) oid = minfo->mech_type; while (minfo != NULL) { if (g_OID_equal(minfo->mech_type, oid)) { if (minfo->int_mech_type != GSS_C_NO_OID) *selected_oid = minfo->int_mech_type; else *selected_oid = minfo->mech_type; status = GSS_S_COMPLETE; goto done; } else if ((minfo->int_mech_type != GSS_C_NO_OID) && (g_OID_equal(minfo->int_mech_type, oid))) { *selected_oid = minfo->mech_type; status = GSS_S_COMPLETE; goto done; } minfo = minfo->next; } status = GSS_S_BAD_MECH; done: k5_mutex_unlock(&g_mechListLock); return status; } /* If oid is an interposed OID, return the corresponding real mech OID. If * it's a real mech OID, return it unmodified. Otherwised return null. */ gss_OID gssint_get_public_oid(gss_const_OID oid) { gss_mech_info minfo; gss_OID public_oid = GSS_C_NO_OID; /* if oid is null -> then get default which is the first in the list */ if (oid == GSS_C_NO_OID) return GSS_C_NO_OID; if (gssint_mechglue_initialize_library() != 0) return GSS_C_NO_OID; k5_mutex_lock(&g_mechListLock); for (minfo = g_mechList; minfo != NULL; minfo = minfo->next) { if (minfo->is_interposer) continue; if (g_OID_equal(minfo->mech_type, oid) || ((minfo->int_mech_type != GSS_C_NO_OID) && (g_OID_equal(minfo->int_mech_type, oid)))) { public_oid = minfo->mech_type; break; } } k5_mutex_unlock(&g_mechListLock); return public_oid; } /* Translate a vector of oids (as from a union cred struct) into a set of * public OIDs using gssint_get_public_oid. */ OM_uint32 gssint_make_public_oid_set(OM_uint32 *minor_status, gss_OID oids, int count, gss_OID_set *public_set) { OM_uint32 status, tmpmin; gss_OID_set set; gss_OID public_oid; int i; *public_set = GSS_C_NO_OID_SET; status = generic_gss_create_empty_oid_set(minor_status, &set); if (GSS_ERROR(status)) return status; for (i = 0; i < count; i++) { public_oid = gssint_get_public_oid(&oids[i]); if (public_oid == GSS_C_NO_OID) continue; status = generic_gss_add_oid_set_member(minor_status, public_oid, &set); if (GSS_ERROR(status)) { (void) generic_gss_release_oid_set(&tmpmin, &set); return status; } } *public_set = set; return GSS_S_COMPLETE; } /* * Register a mechanism. Called with g_mechListLock held. */ /* * given the mechanism type, return the mechanism structure * containing the mechanism library entry points. * will return NULL if mech type is not found * This function will also trigger the loading of the mechanism * module if it has not been already loaded. */ gss_mechanism gssint_get_mechanism(gss_const_OID oid) { gss_mech_info aMech; gss_mechanism (*sym)(const gss_OID); struct plugin_file_handle *dl; struct errinfo errinfo; if (gssint_mechglue_initialize_library() != 0) return (NULL); k5_mutex_lock(&g_mechListLock); /* Check if the mechanism is already loaded. */ aMech = g_mechList; if (oid == GSS_C_NULL_OID) oid = aMech->mech_type; while (aMech != NULL) { if (g_OID_equal(aMech->mech_type, oid) && aMech->mech) { k5_mutex_unlock(&g_mechListLock); return aMech->mech; } else if (aMech->int_mech_type != GSS_C_NO_OID && g_OID_equal(aMech->int_mech_type, oid)) { k5_mutex_unlock(&g_mechListLock); return aMech->int_mech; } aMech = aMech->next; } /* * might need to re-read the configuration file before loading * the mechanism to ensure we have the latest info. */ updateMechList(); aMech = searchMechList(oid); /* is the mechanism present in the list ? */ if (aMech == NULL) { k5_mutex_unlock(&g_mechListLock); return ((gss_mechanism)NULL); } /* has another thread loaded the mech */ if (aMech->mech) { k5_mutex_unlock(&g_mechListLock); return (aMech->mech); } memset(&errinfo, 0, sizeof(errinfo)); if (krb5int_open_plugin(aMech->uLibName, &dl, &errinfo) != 0 || errinfo.code != 0) { k5_clear_error(&errinfo); k5_mutex_unlock(&g_mechListLock); return ((gss_mechanism)NULL); } if (krb5int_get_plugin_func(dl, MECH_SYM, (void (**)())&sym, &errinfo) == 0) { /* Call the symbol to get the mechanism table */ aMech->mech = (*sym)(aMech->mech_type); } else { /* Try dynamic dispatch table */ k5_clear_error(&errinfo); aMech->mech = build_dynamicMech(dl, aMech->mech_type); aMech->freeMech = 1; } if (aMech->mech == NULL) { (void) krb5int_close_plugin(dl); k5_mutex_unlock(&g_mechListLock); return ((gss_mechanism)NULL); } aMech->dl_handle = dl; k5_mutex_unlock(&g_mechListLock); return (aMech->mech); } /* gssint_get_mechanism */ /* * this routine is used for searching the list of mechanism data. * * this needs to be called with g_mechListLock held. */ static gss_mech_info searchMechList(gss_const_OID oid) { gss_mech_info aMech = g_mechList; /* if oid is null -> then get default which is the first in the list */ if (oid == GSS_C_NULL_OID) return (aMech); while (aMech != NULL) { if (g_OID_equal(aMech->mech_type, oid)) return (aMech); aMech = aMech->next; } /* none found */ return ((gss_mech_info) NULL); } /* searchMechList */ /* Return the first non-whitespace character starting from str. */ static char * skip_whitespace(char *str) { while (isspace(*str)) str++; return str; } /* Truncate str at the first whitespace character and return the first * non-whitespace character after that point. */ static char * delimit_ws(char *str) { while (*str != '\0' && !isspace(*str)) str++; if (*str != '\0') *str++ = '\0'; return skip_whitespace(str); } /* Truncate str at the first occurrence of delimiter and return the first * non-whitespace character after that point. */ static char * delimit(char *str, char delimiter) { while (*str != '\0' && *str != delimiter) str++; if (*str != '\0') *str++ = '\0'; return skip_whitespace(str); } /* * loads the configuration file * this is called while having a mutex lock on the mechanism list * entries for libraries that have been loaded can't be modified * mechNameStr and mech_type fields are not updated during updates */ static void loadConfigFile(const char *fileName) { char *sharedLib, *kernMod, *modOptions, *modType, *oid, *next; char buffer[BUFSIZ], *oidStr; FILE *confFile; if ((confFile = fopen(fileName, "r")) == NULL) { return; } (void) memset(buffer, 0, sizeof (buffer)); while (fgets(buffer, BUFSIZ, confFile) != NULL) { /* ignore lines beginning with # */ if (*buffer == '#') continue; /* Parse out the name, oid, and shared library path. */ oidStr = buffer; oid = delimit_ws(oidStr); if (*oid == '\0') continue; sharedLib = delimit_ws(oid); if (*sharedLib == '\0') continue; next = delimit_ws(sharedLib); /* Parse out the kernel module name if present. */ if (*next != '\0' && *next != '[' && *next != '<') { kernMod = next; next = delimit_ws(kernMod); } else { kernMod = NULL; } /* Parse out the module options if present. */ if (*next == '[') { modOptions = next + 1; next = delimit(modOptions, ']'); } else { modOptions = NULL; } /* Parse out the module type if present. */ if (*next == '<') { modType = next + 1; (void)delimit(modType, '>'); } else { modType = NULL; } addConfigEntry(oidStr, oid, sharedLib, kernMod, modOptions, modType); } /* while */ (void) fclose(confFile); } /* loadConfigFile */ #if defined(_WIN32) static time_t filetimeToTimet(const FILETIME *ft) { ULARGE_INTEGER ull; ull.LowPart = ft->dwLowDateTime; ull.HighPart = ft->dwHighDateTime; return (time_t)(ull.QuadPart / 10000000ULL - 11644473600ULL); } static time_t getRegConfigModTime(const char *keyPath) { time_t currentUserModTime = getRegKeyModTime(HKEY_CURRENT_USER, keyPath); time_t localMachineModTime = getRegKeyModTime(HKEY_LOCAL_MACHINE, keyPath); return currentUserModTime > localMachineModTime ? currentUserModTime : localMachineModTime; } static time_t getRegKeyModTime(HKEY hBaseKey, const char *keyPath) { HKEY hConfigKey; HRESULT rc; int iSubKey = 0; time_t modTime = 0, keyModTime; FILETIME keyLastWriteTime; char subKeyName[256]; if ((rc = RegOpenKeyEx(hBaseKey, keyPath, 0, KEY_ENUMERATE_SUB_KEYS, &hConfigKey)) != ERROR_SUCCESS) { /* TODO: log error message */ return 0; } do { int subKeyNameSize=sizeof(subKeyName)/sizeof(subKeyName[0]); if ((rc = RegEnumKeyEx(hConfigKey, iSubKey++, subKeyName, &subKeyNameSize, NULL, NULL, NULL, &keyLastWriteTime)) != ERROR_SUCCESS) { break; } keyModTime = filetimeToTimet(&keyLastWriteTime); if (modTime < keyModTime) { modTime = keyModTime; } } while (1); RegCloseKey(hConfigKey); return modTime; } static void getRegKeyValue(HKEY hKey, const char *keyPath, const char *valueName, void **data, DWORD* dataLen) { DWORD sizeRequired=*dataLen; HRESULT hr; /* Get data length required */ if ((hr = RegGetValue(hKey, keyPath, valueName, RRF_RT_REG_SZ, NULL, NULL, &sizeRequired)) != ERROR_SUCCESS) { /* TODO: LOG registry error */ return; } /* adjust data buffer size if necessary */ if (*dataLen < sizeRequired) { *dataLen = sizeRequired; *data = realloc(*data, sizeRequired); if (!*data) { *dataLen = 0; /* TODO: LOG OOM ERROR! */ return; } } /* get data */ if ((hr = RegGetValue(hKey, keyPath, valueName, RRF_RT_REG_SZ, NULL, *data, &sizeRequired)) != ERROR_SUCCESS) { /* LOG registry error */ return; } } static void loadConfigFromRegistry(HKEY hBaseKey, const char *keyPath) { HKEY hConfigKey; DWORD iSubKey, nSubKeys, maxSubKeyNameLen, modTypeLen; char *oidStr = NULL, *oid = NULL, *sharedLib = NULL, *kernMod = NULL; char *modOptions = NULL, *modType = NULL; DWORD oidStrLen = 0, oidLen = 0, sharedLibLen = 0, kernModLen = 0; DWORD modOptionsLen = 0; HRESULT rc; if ((rc = RegOpenKeyEx(hBaseKey, keyPath, 0, KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE, &hConfigKey)) != ERROR_SUCCESS) { /* TODO: log registry error */ return; } if ((rc = RegQueryInfoKey(hConfigKey, NULL, /* lpClass */ NULL, /* lpcClass */ NULL, /* lpReserved */ &nSubKeys, &maxSubKeyNameLen, NULL, /* lpcMaxClassLen */ NULL, /* lpcValues */ NULL, /* lpcMaxValueNameLen */ NULL, /* lpcMaxValueLen */ NULL, /* lpcbSecurityDescriptor */ NULL /* lpftLastWriteTime */ )) != ERROR_SUCCESS) { goto cleanup; } oidStr = malloc(++maxSubKeyNameLen); if (!oidStr) { goto cleanup; } for (iSubKey=0; iSubKeymech) { generic_gss_release_oid(&minor, &mechOid); return; } /* * If that's all, then this is a corrupt entry. Skip it. */ if (! *sharedLib) { generic_gss_release_oid(&minor, &mechOid); return; } #if defined(_WIN32) sharedPath = sharedLib; #else if (sharedLib[0] == '/') snprintf(sharedPath, sizeof(sharedPath), "%s", sharedLib); else snprintf(sharedPath, sizeof(sharedPath), "%s%s", MECH_LIB_PREFIX, sharedLib); #endif /* * are we creating a new mechanism entry or * just modifying existing (non loaded) mechanism entry */ if (aMech) { /* * delete any old values and set new * mechNameStr and mech_type are not modified */ if (aMech->kmodName) { free(aMech->kmodName); aMech->kmodName = NULL; } if (aMech->optionStr) { free(aMech->optionStr); aMech->optionStr = NULL; } if ((tmpStr = strdup(sharedPath)) != NULL) { if (aMech->uLibName) free(aMech->uLibName); aMech->uLibName = tmpStr; } if (kernMod) /* this is an optional parameter */ aMech->kmodName = strdup(kernMod); if (modOptions) /* optional module options */ aMech->optionStr = strdup(modOptions); /* the oid is already set */ generic_gss_release_oid(&minor, &mechOid); return; } /* adding a new entry */ aMech = calloc(1, sizeof (struct gss_mech_config)); if (aMech == NULL) { generic_gss_release_oid(&minor, &mechOid); return; } aMech->mech_type = mechOid; aMech->uLibName = strdup(sharedPath); aMech->mechNameStr = strdup(oidStr); aMech->freeMech = 0; /* check if any memory allocations failed - bad news */ if (aMech->uLibName == NULL || aMech->mechNameStr == NULL) { if (aMech->uLibName) free(aMech->uLibName); if (aMech->mechNameStr) free(aMech->mechNameStr); generic_gss_release_oid(&minor, &mechOid); free(aMech); return; } if (kernMod) /* this is an optional parameter */ aMech->kmodName = strdup(kernMod); if (modOptions) aMech->optionStr = strdup(modOptions); if (modType && strcmp(modType, "interposer") == 0) aMech->is_interposer = 1; /* * add the new entry to the end of the list - make sure * that only complete entries are added because other * threads might currently be searching the list. */ tmp = g_mechListTail; g_mechListTail = aMech; if (tmp != NULL) tmp->next = aMech; if (g_mechList == NULL) g_mechList = aMech; }