OpenSolaris_b135/lib/libgss/gssd_pname_to_uid.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <pwd.h>
#include <grp.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <thread.h>
#include <synch.h>
#include <syslog.h>
#include <deflt.h>
#include <mechglueP.h>
#include "../../cmd/gss/gsscred/gsscred.h"

static mutex_t uid_map_lock = DEFAULTMUTEX;
static int uid_map_opt = 0;

extern int _getgroupsbymember(const char *, gid_t[], int, int);

/* local function used to call a mechanisms pname_to_uid */
static OM_uint32 gss_pname_to_uid(OM_uint32*, const gss_name_t,
			const gss_OID, uid_t *);

static OM_uint32 private_gsscred_expname_to_unix_cred(const gss_buffer_t,
			uid_t *, gid_t *, gid_t **, int *);

/*
 * The gsscred functions will first attempt to call the
 * mechanism'm pname_to_uid function.  In case this function
 * returns an error or if it is not provided by a mechanism
 * then the functions will attempt to look up the principal
 * in the gsscred table.
 * It is envisioned that the pname_to_uid function will be
 * provided by only a few mechanism, which may have the principal
 * name to unix credential mapping inherently present.
 */

/*
 * Fetch gsscred options from conf file.
 */
static void
get_conf_options(int *uid_map)
{
	int  flags;
	char *ptr;
	void	*defp;
	static char *conffile = "/etc/gss/gsscred.conf";

	*uid_map = 0;
	if ((defp = defopen_r(conffile)) != NULL) {
		flags = defcntl_r(DC_GETFLAGS, 0, defp);
		/* ignore case */
		TURNOFF(flags, DC_CASE);
		(void) defcntl_r(DC_SETFLAGS, flags, defp);

		if ((ptr = defread_r("SYSLOG_UID_MAPPING=", defp)) != NULL &&
		    strcasecmp("yes", ptr) == 0) {
			*uid_map = 1;
		}
		defclose_r(defp);
	}
}

void
gsscred_set_options()
{
	int u;

	get_conf_options(&u);
	(void) mutex_lock(&uid_map_lock);
	uid_map_opt = u;
	(void) mutex_unlock(&uid_map_lock);
}

static int
get_uid_map_opt()
{
	int u;

	(void) mutex_lock(&uid_map_lock);
	u = uid_map_opt;
	(void) mutex_unlock(&uid_map_lock);
	return (u);
}

/*
 * This routine accepts a name in export name format and retrieves
 * unix credentials associated with it.
 */

OM_uint32
gsscred_expname_to_unix_cred_ext(
	const gss_buffer_t expName,
	uid_t *uidOut,
	gid_t *gidOut,
	gid_t *gids[],
	int *gidsLen,
	int try_mech)
{
	gss_name_t intName;
	OM_uint32 minor, major;
	const char *mechStr = NULL;
	char *nameStr = NULL;
	char *whoami = "gsscred_expname_to_unix_cred";
	gss_buffer_desc namebuf;
	int debug = get_uid_map_opt();

	if (uidOut == NULL)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	if (expName == NULL)
		return (GSS_S_CALL_INACCESSIBLE_READ);

	/* first check the mechanism for the mapping */
	if (gss_import_name(&minor, expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
	    &intName) == GSS_S_COMPLETE) {

		if (debug) {
			gss_union_name_t uintName = (gss_union_name_t)intName;

			if (uintName->mech_type)
				mechStr = __gss_oid_to_mech(
				    uintName->mech_type);

			major = gss_display_name(&minor, intName,
			    &namebuf, NULL);
			if (major == GSS_S_COMPLETE) {
				nameStr = strdup(namebuf.value);
				(void) gss_release_buffer(&minor, &namebuf);
			}
		}

		if (try_mech) {
			major = gss_pname_to_uid(&minor, intName,
			    NULL, uidOut);
			if (major == GSS_S_COMPLETE) {

				if (debug) {
					syslog(LOG_AUTH|LOG_DEBUG,
					    "%s: mech provided local name"
					    " mapping (%s, %s, %d)", whoami,
					    mechStr ? mechStr : "<null>",
					    nameStr ? nameStr : "<null>",
					    *uidOut);
					free(nameStr);
				}

				(void) gss_release_name(&minor, &intName);
				if (gids && gidsLen && gidOut)
					return (gss_get_group_info(*uidOut,
					    gidOut, gids, gidsLen));
				return (GSS_S_COMPLETE);
			}
		}

		(void) gss_release_name(&minor, &intName);
	}

	/*
	 * we fall back onto the gsscred table to provide the mapping
	 * start by making sure that the expName is an export name buffer
	 */
	major = private_gsscred_expname_to_unix_cred(expName, uidOut, gidOut,
	    gids, gidsLen);

	if (debug && major == GSS_S_COMPLETE) {
		syslog(LOG_AUTH|LOG_DEBUG,
		    "%s: gsscred tbl provided"
		    " local name mapping (%s, %s, %d)",
		    whoami,
		    mechStr ? mechStr : "<unknown>",
		    nameStr ? nameStr : "<unknown>",
		    *uidOut);
		free(nameStr);
	} else if (debug) {
		syslog(LOG_AUTH|LOG_DEBUG,
		    "%s: gsscred tbl could NOT"
		    " provide local name mapping (%s, %s)",
		    whoami,
		    mechStr ? mechStr : "<unknown>",
		    nameStr ? nameStr : "<unknown>");
		free(nameStr);
	}

	return (major);

} /* gsscred_expname_to_unix_cred */

OM_uint32
gsscred_expname_to_unix_cred(
	const gss_buffer_t expName,
	uid_t *uidOut,
	gid_t *gidOut,
	gid_t *gids[],
	int *gidsLen)
{
	return (gsscred_expname_to_unix_cred_ext(expName, uidOut, gidOut, gids,
	    gidsLen, 1));
}


static const char *expNameTokId = "\x04\x01";
static const int expNameTokIdLen = 2;
/*
 * private routine added to be called from gsscred_name_to_unix_cred
 * and gsscred_expName_to_unix_cred.
 */
static OM_uint32
private_gsscred_expname_to_unix_cred(expName, uidOut, gidOut, gids, gidsLen)
const gss_buffer_t expName;
uid_t *uidOut;
gid_t *gidOut;
gid_t *gids[];
int *gidsLen;
{

	if (expName->length < expNameTokIdLen ||
		(memcmp(expName->value, expNameTokId, expNameTokIdLen) != 0))
		return (GSS_S_DEFECTIVE_TOKEN);

	if (!gss_getGssCredEntry(expName, uidOut))
		return (GSS_S_FAILURE);

	/* did caller request group info also ? */
	if (gids && gidsLen && gidOut)
		return (gss_get_group_info(*uidOut, gidOut, gids, gidsLen));

	return (GSS_S_COMPLETE);
}

/*
 * Return a string of the authenticated name.
 * It's a bit of hack/workaround/longroad but the current intName
 * passed to gss_display_name insists on returning an empty string.
 *
 * Caller must free string memory.
 */
static
char *make_name_str(
	const gss_name_t intName,
	const gss_OID mechType)

{
	gss_buffer_desc expName = GSS_C_EMPTY_BUFFER;
	OM_uint32 major, minor;
	gss_name_t canonName;
	gss_name_t iName;
	gss_buffer_desc namebuf;

	if (major = gss_canonicalize_name(&minor, intName,
				mechType, &canonName))
		return (NULL);

	major = gss_export_name(&minor, canonName, &expName);
	(void) gss_release_name(&minor, &canonName);
	if (major)
		return (NULL);

	if (gss_import_name(&minor, &expName,
			    (gss_OID)GSS_C_NT_EXPORT_NAME,
			    &iName) == GSS_S_COMPLETE) {

		major = gss_display_name(&minor, iName, &namebuf, NULL);
		if (major == GSS_S_COMPLETE) {
			char *s;

			if (namebuf.value)
				s = strdup(namebuf.value);

			(void) gss_release_buffer(&minor, &namebuf);
			(void) gss_release_buffer(&minor, &expName);
			(void) gss_release_buffer(&minor, (gss_buffer_t)iName);
			return (s);
		}
		(void) gss_release_buffer(&minor, (gss_buffer_t)iName);
	}

	(void) gss_release_buffer(&minor, &expName);
	return (NULL);
}

/*
 * This routine accepts a name in gss internal name format together with
 * a mechanim OID and retrieves a unix credentials for that entity.
 */
OM_uint32
gsscred_name_to_unix_cred_ext(
	const gss_name_t intName,
	const gss_OID mechType,
	uid_t *uidOut,
	gid_t *gidOut,
	gid_t *gids[],
	int *gidsLen,
	int try_mech)
{
	gss_name_t canonName;
	gss_buffer_desc expName = GSS_C_EMPTY_BUFFER;
	OM_uint32 major, minor;
	int debug = get_uid_map_opt();

	const char *mechStr;
	char *whoami = "gsscred_name_to_unix_cred";
	gss_buffer_desc namebuf;

	if (intName == NULL || mechType == NULL)
		return (GSS_S_CALL_INACCESSIBLE_READ);

	if (uidOut == NULL)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	mechStr = __gss_oid_to_mech(mechType);

	/* first try the mechanism provided mapping */
	if (try_mech && gss_pname_to_uid(&minor, intName, mechType, uidOut)
	    == GSS_S_COMPLETE) {

		if (debug) {
			char *s = make_name_str(intName, mechType);
			syslog(LOG_AUTH|LOG_DEBUG,
			    "%s: mech provided local name"
			    " mapping (%s, %s, %d)", whoami,
			    mechStr ? mechStr : "<null>",
			    s ? s : "<null>",
			    *uidOut);
			free(s);
		}

		if (gids && gidsLen && gidOut)
			return (gss_get_group_info(*uidOut, gidOut, gids,
			    gidsLen));
		return (GSS_S_COMPLETE);
	}
	/*
	 * falling back onto the gsscred table to provide the mapping
	 * start by canonicalizing the passed in name and then export it
	 */
	if (major = gss_canonicalize_name(&minor, intName,
	    mechType, &canonName))
		return (major);

	major = gss_export_name(&minor, canonName, &expName);
	(void) gss_release_name(&minor, &canonName);
	if (major)
		return (major);

	major = private_gsscred_expname_to_unix_cred(&expName, uidOut, gidOut,
	    gids, gidsLen);


	if (debug) {
		gss_name_t iName;
		OM_uint32 maj;
		char *nameStr = NULL;

		if (gss_import_name(&minor, &expName,
		    (gss_OID)GSS_C_NT_EXPORT_NAME, &iName) == GSS_S_COMPLETE) {

			maj = gss_display_name(&minor, iName, &namebuf,
			    NULL);
			(void) gss_release_buffer(&minor, (gss_buffer_t)iName);
			if (maj == GSS_S_COMPLETE) {
				nameStr = strdup(namebuf.value);
				(void) gss_release_buffer(&minor, &namebuf);
			}
		}

		if (major == GSS_S_COMPLETE)
			syslog(LOG_AUTH|LOG_DEBUG,
			    "%s: gsscred tbl provided"
			    " local name mapping (%s, %s, %d)",
			    whoami,
			    mechStr ? mechStr : "<unknown>",
			    nameStr ? nameStr : "<unknown>",
			    *uidOut);
		else
			syslog(LOG_AUTH|LOG_DEBUG,
			    "%s: gsscred tbl could NOT"
			    " provide local name mapping (%s, %s)",
			    whoami,
			    mechStr ? mechStr : "<unknown>",
			    nameStr ? nameStr : "<unknown>");

		free(nameStr);
	}

	(void) gss_release_buffer(&minor, &expName);
	return (major);
} /* gsscred_name_to_unix_cred */


OM_uint32
gsscred_name_to_unix_cred(
	const gss_name_t intName,
	const gss_OID mechType,
	uid_t *uidOut,
	gid_t *gidOut,
	gid_t *gids[],
	int *gidsLen)
{
	return (gsscred_name_to_unix_cred_ext(intName, mechType,
	    uidOut, gidOut, gids, gidsLen, 1));
}



/*
 * This routine accepts a unix uid, and retrieves the group id
 * and supplamentery group ids for that uid.
 * Callers should be aware that the supplamentary group ids
 * array may be empty even when this function returns success.
 */
OM_uint32
gss_get_group_info(uid, gidOut, gids, gidsLen)
const uid_t uid;
gid_t *gidOut;
gid_t *gids[];
int *gidsLen;
{
	struct passwd *pw;
	int maxgroups;

	/* check for output parameters */
	if (gidOut == NULL || gids == NULL || gidsLen == NULL)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	*gids = NULL;
	*gidsLen = 0;

	/* determine maximum number of groups possible */
	maxgroups = sysconf(_SC_NGROUPS_MAX);
	if (maxgroups < 1)
	    maxgroups = 16;

	if ((pw = getpwuid(uid)) == NULL)
	    return (GSS_S_FAILURE);

	/*
	 * we allocate for the maximum number of groups
	 * we do not reclaim the space when the actual number
	 * is lower, just set the size approprately.
	 */
	*gids = (gid_t *)calloc(maxgroups, sizeof (gid_t));
	if (*gids == NULL)
	    return (GSS_S_FAILURE);

	*gidOut = pw->pw_gid;
	(*gids)[0] = pw->pw_gid;
	*gidsLen = _getgroupsbymember(pw->pw_name, *gids, maxgroups, 1);
	/*
	 * we will try to remove the duplicate entry from the groups
	 * array.  This can cause the group array to be empty.
	 */
	if (*gidsLen < 1)
	{
		free(*gids);
		*gids = NULL;
		return (GSS_S_FAILURE);
	} else if (*gidsLen == 1) {
		free(*gids);
		*gids = NULL;
		*gidsLen = 0;
	} else {
		/* length is atleast 2 */
		*gidsLen = *gidsLen -1;
		(*gids)[0] = (*gids)[*gidsLen];
	}

	return (GSS_S_COMPLETE);
} /* gss_get_group_info */


static OM_uint32
gss_pname_to_uid(minor, name, mech_type, uidOut)
OM_uint32 *minor;
const gss_name_t name;
const gss_OID mech_type;
uid_t *uidOut;
{
	gss_mechanism mech;
	gss_union_name_t intName;
	gss_name_t mechName = NULL;
	OM_uint32 major, tmpMinor;

	if (!minor)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	*minor = 0;

	if (uidOut == NULL)
		return (GSS_S_CALL_INACCESSIBLE_WRITE);

	if (name == NULL)
		return (GSS_S_CALL_INACCESSIBLE_READ);

	intName = (gss_union_name_t)name;

	if (mech_type != NULL)
		mech = __gss_get_mechanism(mech_type);
	else {
		/*
		 * if this is a MN, then try using the mech
		 * from the name; otherwise ask for default
		 */
		mech = __gss_get_mechanism(intName->mech_type);
	}

	if (mech == NULL || mech->pname_to_uid == NULL)
		return (GSS_S_UNAVAILABLE);

	/* may need to import the name if this is not MN */
	if (intName->mech_type == NULL) {
		major = __gss_import_internal_name(minor,
				mech_type, intName,
				&mechName);
		if (major != GSS_S_COMPLETE)
			return (major);
	} else
		mechName = intName->mech_name;


	/* now call the mechanism's pname function to do the work */
	major = mech->pname_to_uid(mech->context, minor, mechName, uidOut);

	if (intName->mech_name != mechName)
		(void) __gss_release_internal_name(&tmpMinor, &mech->mech_type,
				&mechName);

	return (major);
} /* gss_pname_to_uid */