/* * 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 #include #include #include #include #include #include #include #include #include #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 : "", nameStr ? nameStr : "", *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 : "", nameStr ? nameStr : "", *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 : "", nameStr ? nameStr : ""); 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 : "", s ? s : "", *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 : "", nameStr ? nameStr : "", *uidOut); else syslog(LOG_AUTH|LOG_DEBUG, "%s: gsscred tbl could NOT" " provide local name mapping (%s, %s)", whoami, mechStr ? mechStr : "", nameStr ? nameStr : ""); 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 */