summaryrefslogtreecommitdiff
path: root/src/lib/gssapi/mechglue/g_initialize.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/gssapi/mechglue/g_initialize.c')
-rw-r--r--src/lib/gssapi/mechglue/g_initialize.c1615
1 files changed, 1615 insertions, 0 deletions
diff --git a/src/lib/gssapi/mechglue/g_initialize.c b/src/lib/gssapi/mechglue/g_initialize.c
new file mode 100644
index 000000000000..9197666e1072
--- /dev/null
+++ b/src/lib/gssapi/mechglue/g_initialize.c
@@ -0,0 +1,1615 @@
+/* #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 <stdlib.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#ifndef _WIN32
+#include <glob.h>
+#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);
+ err = k5_mutex_finish_init(&g_mechListLock);
+
+#ifdef _GSS_STATIC_LINK
+ err = gss_krb5int_lib_init();
+ err = gss_spnegoint_lib_init();
+#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 || 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;
+ }
+
+#if 0
+ g_mechSetTime = fileInfo.st_mtime;
+#endif
+ 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;
+
+ /* Don't glob and stat more than once per second. */
+ if (time(&now) == (time_t)-1 || now == g_confLastCall)
+ return;
+ g_confLastCall = now;
+
+ 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);
+
+ 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) {
+#if 0
+ (void) syslog(LOG_INFO, "libgss dlopen(%s): %s\n",
+ aMech->uLibName, dlerror());
+#endif
+ 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 0
+ if (aMech->mech == NULL) {
+ (void) syslog(LOG_INFO, "unable to initialize mechanism"
+ " library [%s]\n", aMech->uLibName);
+ }
+#endif
+ 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) {
+#if 0
+ (void) syslog(LOG_INFO, "libgss dlopen(%s): %s\n",
+ aMech->uLibName, dlerror());
+#endif
+ 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 */
+ aMech->mech = build_dynamicMech(dl, aMech->mech_type);
+ aMech->freeMech = 1;
+ }
+ if (aMech->mech == NULL) {
+ (void) krb5int_close_plugin(dl);
+#if 0
+ (void) syslog(LOG_INFO, "unable to initialize mechanism"
+ " library [%s]\n", aMech->uLibName);
+#endif
+ 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; iSubKey<nSubKeys; iSubKey++) {
+ oidStrLen = maxSubKeyNameLen;
+ if ((rc = RegEnumKeyEx(hConfigKey, iSubKey, oidStr, &oidStrLen,
+ NULL, NULL, NULL, NULL)) !=
+ ERROR_SUCCESS) {
+ /* TODO: log registry error */
+ continue;
+ }
+ getRegKeyValue(hConfigKey, oidStr, "OID", &oid, &oidLen);
+ getRegKeyValue(hConfigKey, oidStr, "Shared Library",
+ &sharedLib, &sharedLibLen);
+ getRegKeyValue(hConfigKey, oidStr, "Kernel Module", &kernMod,
+ &kernModLen);
+ getRegKeyValue(hConfigKey, oidStr, "Options", &modOptions,
+ &modOptionsLen);
+ getRegKeyValue(hConfigKey, oidStr, "Type", &modType,
+ &modTypeLen);
+ addConfigEntry(oidStr, oid, sharedLib, kernMod, modOptions,
+ modType);
+ }
+cleanup:
+ RegCloseKey(hConfigKey);
+ if (oidStr) {
+ free(oidStr);
+ }
+ if (oid) {
+ free(oid);
+ }
+ if (sharedLib) {
+ free(sharedLib);
+ }
+ if (kernMod) {
+ free(kernMod);
+ }
+ if (modOptions) {
+ free(modOptions);
+ }
+}
+#endif
+
+static void
+addConfigEntry(const char *oidStr, const char *oid, const char *sharedLib,
+ const char *kernMod, const char *modOptions,
+ const char *modType)
+{
+#if defined(_WIN32)
+ const char *sharedPath;
+#else
+ char sharedPath[sizeof (MECH_LIB_PREFIX) + BUFSIZ];
+#endif
+ char *tmpStr;
+ gss_OID mechOid;
+ gss_mech_info aMech, tmp;
+ OM_uint32 minor;
+ gss_buffer_desc oidBuf;
+
+ if ((!oid) || (!oidStr)) {
+ return;
+ }
+ /*
+ * check if an entry for this oid already exists
+ * if it does, and the library is already loaded then
+ * we can't modify it, so skip it
+ */
+ oidBuf.value = (void *)oid;
+ oidBuf.length = strlen(oid);
+ if (generic_gss_str_to_oid(&minor, &oidBuf, &mechOid)
+ != GSS_S_COMPLETE) {
+#if 0
+ (void) syslog(LOG_INFO, "invalid mechanism oid"
+ " [%s] in configuration file", oid);
+#endif
+ return;
+ }
+
+ aMech = searchMechList(mechOid);
+ if (aMech && aMech->mech) {
+ 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;
+}
+