2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <pwd.h>
2N/A#include <grp.h>
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <unistd.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A#include <syslog.h>
2N/A#include <deflt.h>
2N/A#include <mglueP.h>
2N/A#include "../../cmd/gss/gsscred/gsscred.h"
2N/A
2N/Astatic mutex_t uid_map_lock = DEFAULTMUTEX;
2N/Astatic int uid_map_opt = 0;
2N/A
2N/Aextern int _getgroupsbymember(const char *, gid_t[], int, int);
2N/A
2N/A/* local function used to call a mechanisms pname_to_uid */
2N/Astatic OM_uint32 gss_pname_to_uid(OM_uint32*, const gss_name_t,
2N/A const gss_OID, uid_t *);
2N/A
2N/Astatic OM_uint32 private_gsscred_expname_to_unix_cred(const gss_buffer_t,
2N/A uid_t *, gid_t *, gid_t **, int *);
2N/A
2N/A/*
2N/A * The gsscred functions will first attempt to call the
2N/A * mechanism'm pname_to_uid function. In case this function
2N/A * returns an error or if it is not provided by a mechanism
2N/A * then the functions will attempt to look up the principal
2N/A * in the gsscred table.
2N/A * It is envisioned that the pname_to_uid function will be
2N/A * provided by only a few mechanism, which may have the principal
2N/A * name to unix credential mapping inherently present.
2N/A */
2N/A
2N/A/*
2N/A * Fetch gsscred options from conf file.
2N/A */
2N/Astatic void
2N/Aget_conf_options(int *uid_map)
2N/A{
2N/A int flags;
2N/A char *ptr;
2N/A void *defp;
2N/A static char *conffile = "/etc/gss/gsscred.conf";
2N/A
2N/A *uid_map = 0;
2N/A if ((defp = defopen_r(conffile)) != NULL) {
2N/A flags = defcntl_r(DC_GETFLAGS, 0, defp);
2N/A /* ignore case */
2N/A TURNOFF(flags, DC_CASE);
2N/A (void) defcntl_r(DC_SETFLAGS, flags, defp);
2N/A
2N/A if ((ptr = defread_r("SYSLOG_UID_MAPPING=", defp)) != NULL &&
2N/A strcasecmp("yes", ptr) == 0) {
2N/A *uid_map = 1;
2N/A }
2N/A defclose_r(defp);
2N/A }
2N/A}
2N/A
2N/Avoid
2N/Agsscred_set_options()
2N/A{
2N/A int u;
2N/A
2N/A get_conf_options(&u);
2N/A (void) mutex_lock(&uid_map_lock);
2N/A uid_map_opt = u;
2N/A (void) mutex_unlock(&uid_map_lock);
2N/A}
2N/A
2N/Astatic int
2N/Aget_uid_map_opt()
2N/A{
2N/A int u;
2N/A
2N/A (void) mutex_lock(&uid_map_lock);
2N/A u = uid_map_opt;
2N/A (void) mutex_unlock(&uid_map_lock);
2N/A return (u);
2N/A}
2N/A
2N/A/*
2N/A * This routine accepts a name in export name format and retrieves
2N/A * unix credentials associated with it.
2N/A */
2N/A
2N/AOM_uint32
2N/Agsscred_expname_to_unix_cred_ext(
2N/A const gss_buffer_t expName,
2N/A uid_t *uidOut,
2N/A gid_t *gidOut,
2N/A gid_t *gids[],
2N/A int *gidsLen,
2N/A int try_mech)
2N/A{
2N/A gss_name_t intName;
2N/A OM_uint32 minor, major;
2N/A const char *mechStr = NULL;
2N/A char *nameStr = NULL;
2N/A char *whoami = "gsscred_expname_to_unix_cred";
2N/A gss_buffer_desc namebuf;
2N/A int debug = get_uid_map_opt();
2N/A
2N/A if (uidOut == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A if (expName == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_READ);
2N/A
2N/A /* first check the mechanism for the mapping */
2N/A if (gss_import_name(&minor, expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
2N/A &intName) == GSS_S_COMPLETE) {
2N/A
2N/A if (debug) {
2N/A gss_union_name_t uintName = (gss_union_name_t)intName;
2N/A
2N/A if (uintName->mech_type)
2N/A mechStr = __gss_oid_to_mech(
2N/A uintName->mech_type);
2N/A
2N/A major = gss_display_name(&minor, intName,
2N/A &namebuf, NULL);
2N/A if (major == GSS_S_COMPLETE) {
2N/A nameStr = strdup(namebuf.value);
2N/A (void) gss_release_buffer(&minor, &namebuf);
2N/A }
2N/A }
2N/A
2N/A if (try_mech) {
2N/A major = gss_pname_to_uid(&minor, intName,
2N/A NULL, uidOut);
2N/A if (major == GSS_S_COMPLETE) {
2N/A
2N/A if (debug) {
2N/A syslog(LOG_AUTH|LOG_DEBUG,
2N/A "%s: mech provided local name"
2N/A " mapping (%s, %s, %d)", whoami,
2N/A mechStr ? mechStr : "<null>",
2N/A nameStr ? nameStr : "<null>",
2N/A *uidOut);
2N/A free(nameStr);
2N/A }
2N/A
2N/A (void) gss_release_name(&minor, &intName);
2N/A if (gids && gidsLen && gidOut)
2N/A return (gss_get_group_info(*uidOut,
2N/A gidOut, gids, gidsLen));
2N/A return (GSS_S_COMPLETE);
2N/A }
2N/A }
2N/A
2N/A (void) gss_release_name(&minor, &intName);
2N/A }
2N/A
2N/A /*
2N/A * we fall back onto the gsscred table to provide the mapping
2N/A * start by making sure that the expName is an export name buffer
2N/A */
2N/A major = private_gsscred_expname_to_unix_cred(expName, uidOut, gidOut,
2N/A gids, gidsLen);
2N/A
2N/A if (debug && major == GSS_S_COMPLETE) {
2N/A syslog(LOG_AUTH|LOG_DEBUG,
2N/A "%s: gsscred tbl provided"
2N/A " local name mapping (%s, %s, %d)",
2N/A whoami,
2N/A mechStr ? mechStr : "<unknown>",
2N/A nameStr ? nameStr : "<unknown>",
2N/A *uidOut);
2N/A free(nameStr);
2N/A } else if (debug) {
2N/A syslog(LOG_AUTH|LOG_DEBUG,
2N/A "%s: gsscred tbl could NOT"
2N/A " provide local name mapping (%s, %s)",
2N/A whoami,
2N/A mechStr ? mechStr : "<unknown>",
2N/A nameStr ? nameStr : "<unknown>");
2N/A free(nameStr);
2N/A }
2N/A
2N/A return (major);
2N/A
2N/A} /* gsscred_expname_to_unix_cred */
2N/A
2N/AOM_uint32
2N/Agsscred_expname_to_unix_cred(
2N/A const gss_buffer_t expName,
2N/A uid_t *uidOut,
2N/A gid_t *gidOut,
2N/A gid_t *gids[],
2N/A int *gidsLen)
2N/A{
2N/A return (gsscred_expname_to_unix_cred_ext(expName, uidOut, gidOut, gids,
2N/A gidsLen, 1));
2N/A}
2N/A
2N/A
2N/Astatic const char *expNameTokId = "\x04\x01";
2N/Astatic const int expNameTokIdLen = 2;
2N/A/*
2N/A * private routine added to be called from gsscred_name_to_unix_cred
2N/A * and gsscred_expName_to_unix_cred.
2N/A */
2N/Astatic OM_uint32
2N/Aprivate_gsscred_expname_to_unix_cred(expName, uidOut, gidOut, gids, gidsLen)
2N/Aconst gss_buffer_t expName;
2N/Auid_t *uidOut;
2N/Agid_t *gidOut;
2N/Agid_t *gids[];
2N/Aint *gidsLen;
2N/A{
2N/A
2N/A if (expName->length < expNameTokIdLen ||
2N/A (memcmp(expName->value, expNameTokId, expNameTokIdLen) != 0))
2N/A return (GSS_S_DEFECTIVE_TOKEN);
2N/A
2N/A if (!gss_getGssCredEntry(expName, uidOut))
2N/A return (GSS_S_FAILURE);
2N/A
2N/A /* did caller request group info also ? */
2N/A if (gids && gidsLen && gidOut)
2N/A return (gss_get_group_info(*uidOut, gidOut, gids, gidsLen));
2N/A
2N/A return (GSS_S_COMPLETE);
2N/A}
2N/A
2N/A/*
2N/A * Return a string of the authenticated name.
2N/A * It's a bit of hack/workaround/longroad but the current intName
2N/A * passed to gss_display_name insists on returning an empty string.
2N/A *
2N/A * Caller must free string memory.
2N/A */
2N/Astatic
2N/Achar *make_name_str(
2N/A const gss_name_t intName,
2N/A const gss_OID mechType)
2N/A
2N/A{
2N/A gss_buffer_desc expName = GSS_C_EMPTY_BUFFER;
2N/A OM_uint32 major, minor;
2N/A gss_name_t canonName;
2N/A gss_name_t iName;
2N/A gss_buffer_desc namebuf;
2N/A
2N/A if (major = gss_canonicalize_name(&minor, intName,
2N/A mechType, &canonName))
2N/A return (NULL);
2N/A
2N/A major = gss_export_name(&minor, canonName, &expName);
2N/A (void) gss_release_name(&minor, &canonName);
2N/A if (major)
2N/A return (NULL);
2N/A
2N/A if (gss_import_name(&minor, &expName,
2N/A (gss_OID)GSS_C_NT_EXPORT_NAME,
2N/A &iName) == GSS_S_COMPLETE) {
2N/A
2N/A major = gss_display_name(&minor, iName, &namebuf, NULL);
2N/A if (major == GSS_S_COMPLETE) {
2N/A char *s;
2N/A
2N/A if (namebuf.value)
2N/A s = strdup(namebuf.value);
2N/A
2N/A (void) gss_release_buffer(&minor, &namebuf);
2N/A (void) gss_release_buffer(&minor, &expName);
2N/A (void) gss_release_buffer(&minor, (gss_buffer_t)iName);
2N/A return (s);
2N/A }
2N/A (void) gss_release_buffer(&minor, (gss_buffer_t)iName);
2N/A }
2N/A
2N/A (void) gss_release_buffer(&minor, &expName);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * This routine accepts a name in gss internal name format together with
2N/A * a mechanim OID and retrieves a unix credentials for that entity.
2N/A */
2N/AOM_uint32
2N/Agsscred_name_to_unix_cred_ext(
2N/A const gss_name_t intName,
2N/A const gss_OID mechType,
2N/A uid_t *uidOut,
2N/A gid_t *gidOut,
2N/A gid_t *gids[],
2N/A int *gidsLen,
2N/A int try_mech)
2N/A{
2N/A gss_name_t canonName;
2N/A gss_buffer_desc expName = GSS_C_EMPTY_BUFFER;
2N/A OM_uint32 major, minor;
2N/A int debug = get_uid_map_opt();
2N/A
2N/A const char *mechStr;
2N/A char *whoami = "gsscred_name_to_unix_cred";
2N/A gss_buffer_desc namebuf;
2N/A
2N/A if (intName == NULL || mechType == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_READ);
2N/A
2N/A if (uidOut == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A mechStr = __gss_oid_to_mech(mechType);
2N/A
2N/A /* first try the mechanism provided mapping */
2N/A if (try_mech && gss_pname_to_uid(&minor, intName, mechType, uidOut)
2N/A == GSS_S_COMPLETE) {
2N/A
2N/A if (debug) {
2N/A char *s = make_name_str(intName, mechType);
2N/A syslog(LOG_AUTH|LOG_DEBUG,
2N/A "%s: mech provided local name"
2N/A " mapping (%s, %s, %d)", whoami,
2N/A mechStr ? mechStr : "<null>",
2N/A s ? s : "<null>",
2N/A *uidOut);
2N/A free(s);
2N/A }
2N/A
2N/A if (gids && gidsLen && gidOut)
2N/A return (gss_get_group_info(*uidOut, gidOut, gids,
2N/A gidsLen));
2N/A return (GSS_S_COMPLETE);
2N/A }
2N/A /*
2N/A * falling back onto the gsscred table to provide the mapping
2N/A * start by canonicalizing the passed in name and then export it
2N/A */
2N/A if (major = gss_canonicalize_name(&minor, intName,
2N/A mechType, &canonName))
2N/A return (major);
2N/A
2N/A major = gss_export_name(&minor, canonName, &expName);
2N/A (void) gss_release_name(&minor, &canonName);
2N/A if (major)
2N/A return (major);
2N/A
2N/A major = private_gsscred_expname_to_unix_cred(&expName, uidOut, gidOut,
2N/A gids, gidsLen);
2N/A
2N/A
2N/A if (debug) {
2N/A gss_name_t iName;
2N/A OM_uint32 maj;
2N/A char *nameStr = NULL;
2N/A
2N/A if (gss_import_name(&minor, &expName,
2N/A (gss_OID)GSS_C_NT_EXPORT_NAME, &iName) == GSS_S_COMPLETE) {
2N/A
2N/A maj = gss_display_name(&minor, iName, &namebuf,
2N/A NULL);
2N/A (void) gss_release_buffer(&minor, (gss_buffer_t)iName);
2N/A if (maj == GSS_S_COMPLETE) {
2N/A nameStr = strdup(namebuf.value);
2N/A (void) gss_release_buffer(&minor, &namebuf);
2N/A }
2N/A }
2N/A
2N/A if (major == GSS_S_COMPLETE)
2N/A syslog(LOG_AUTH|LOG_DEBUG,
2N/A "%s: gsscred tbl provided"
2N/A " local name mapping (%s, %s, %d)",
2N/A whoami,
2N/A mechStr ? mechStr : "<unknown>",
2N/A nameStr ? nameStr : "<unknown>",
2N/A *uidOut);
2N/A else
2N/A syslog(LOG_AUTH|LOG_DEBUG,
2N/A "%s: gsscred tbl could NOT"
2N/A " provide local name mapping (%s, %s)",
2N/A whoami,
2N/A mechStr ? mechStr : "<unknown>",
2N/A nameStr ? nameStr : "<unknown>");
2N/A
2N/A free(nameStr);
2N/A }
2N/A
2N/A (void) gss_release_buffer(&minor, &expName);
2N/A return (major);
2N/A} /* gsscred_name_to_unix_cred */
2N/A
2N/A
2N/AOM_uint32
2N/Agsscred_name_to_unix_cred(
2N/A const gss_name_t intName,
2N/A const gss_OID mechType,
2N/A uid_t *uidOut,
2N/A gid_t *gidOut,
2N/A gid_t *gids[],
2N/A int *gidsLen)
2N/A{
2N/A return (gsscred_name_to_unix_cred_ext(intName, mechType,
2N/A uidOut, gidOut, gids, gidsLen, 1));
2N/A}
2N/A
2N/A
2N/A
2N/A/*
2N/A * This routine accepts a unix uid, and retrieves the group id
2N/A * and supplementary group ids for that uid.
2N/A * Callers should be aware that the supplamentary group ids
2N/A * array may be empty even when this function returns success.
2N/A */
2N/AOM_uint32
2N/Agss_get_group_info(uid, gidOut, gids, gidsLen)
2N/Aconst uid_t uid;
2N/Agid_t *gidOut;
2N/Agid_t *gids[];
2N/Aint *gidsLen;
2N/A{
2N/A struct passwd *pw;
2N/A int maxgroups;
2N/A
2N/A /* check for output parameters */
2N/A if (gidOut == NULL || gids == NULL || gidsLen == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A *gids = NULL;
2N/A *gidsLen = 0;
2N/A
2N/A /* determine maximum number of groups possible */
2N/A maxgroups = sysconf(_SC_NGROUPS_MAX);
2N/A if (maxgroups < 1)
2N/A maxgroups = 16;
2N/A
2N/A if ((pw = getpwuid(uid)) == NULL)
2N/A return (GSS_S_FAILURE);
2N/A
2N/A /*
2N/A * we allocate for the maximum number of groups
2N/A * we do not reclaim the space when the actual number
2N/A * is lower, just set the size approprately.
2N/A */
2N/A *gids = (gid_t *)calloc(maxgroups, sizeof (gid_t));
2N/A if (*gids == NULL)
2N/A return (GSS_S_FAILURE);
2N/A
2N/A *gidOut = pw->pw_gid;
2N/A (*gids)[0] = pw->pw_gid;
2N/A *gidsLen = _getgroupsbymember(pw->pw_name, *gids, maxgroups, 1);
2N/A /*
2N/A * we will try to remove the duplicate entry from the groups
2N/A * array. This can cause the group array to be empty.
2N/A */
2N/A if (*gidsLen < 1)
2N/A {
2N/A free(*gids);
2N/A *gids = NULL;
2N/A return (GSS_S_FAILURE);
2N/A } else if (*gidsLen == 1) {
2N/A free(*gids);
2N/A *gids = NULL;
2N/A *gidsLen = 0;
2N/A } else {
2N/A /* length is atleast 2 */
2N/A *gidsLen = *gidsLen -1;
2N/A (*gids)[0] = (*gids)[*gidsLen];
2N/A }
2N/A
2N/A return (GSS_S_COMPLETE);
2N/A} /* gss_get_group_info */
2N/A
2N/A
2N/Astatic OM_uint32
2N/Agss_pname_to_uid(minor, name, mech_type, uidOut)
2N/AOM_uint32 *minor;
2N/Aconst gss_name_t name;
2N/Aconst gss_OID mech_type;
2N/Auid_t *uidOut;
2N/A{
2N/A gss_mechanism mech;
2N/A gss_union_name_t intName;
2N/A gss_name_t mechName = NULL;
2N/A OM_uint32 major, tmpMinor;
2N/A
2N/A if (!minor)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A *minor = 0;
2N/A
2N/A if (uidOut == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_WRITE);
2N/A
2N/A if (name == NULL)
2N/A return (GSS_S_CALL_INACCESSIBLE_READ);
2N/A
2N/A intName = (gss_union_name_t)name;
2N/A
2N/A if (mech_type != NULL)
2N/A mech = __gss_get_mechanism(mech_type);
2N/A else {
2N/A /*
2N/A * if this is a MN, then try using the mech
2N/A * from the name; otherwise ask for default
2N/A */
2N/A mech = __gss_get_mechanism(intName->mech_type);
2N/A }
2N/A
2N/A if (mech == NULL || mech->pname_to_uid == NULL)
2N/A return (GSS_S_UNAVAILABLE);
2N/A
2N/A /* may need to import the name if this is not MN */
2N/A if (intName->mech_type == NULL) {
2N/A major = __gss_import_internal_name(minor,
2N/A mech_type, intName,
2N/A &mechName);
2N/A if (major != GSS_S_COMPLETE)
2N/A return (major);
2N/A } else
2N/A mechName = intName->mech_name;
2N/A
2N/A
2N/A /* now call the mechanism's pname function to do the work */
2N/A major = mech->pname_to_uid(minor, mechName, uidOut);
2N/A
2N/A if (intName->mech_name != mechName)
2N/A (void) __gss_release_internal_name(&tmpMinor, &mech->mech_type,
2N/A &mechName);
2N/A
2N/A return (major);
2N/A} /* gss_pname_to_uid */