diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 | 
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 | 
| commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
| tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/lib/gssapi/mechglue/g_initialize.c | |
Diffstat (limited to 'src/lib/gssapi/mechglue/g_initialize.c')
| -rw-r--r-- | src/lib/gssapi/mechglue/g_initialize.c | 1615 | 
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; +} + | 
