| /* |
| * 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 */ |