Home | History | Annotate | Download | only in libgss
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <pwd.h>
     27 #include <grp.h>
     28 #include <stdio.h>
     29 #include <stdlib.h>
     30 #include <unistd.h>
     31 #include <thread.h>
     32 #include <synch.h>
     33 #include <syslog.h>
     34 #include <deflt.h>
     35 #include <mechglueP.h>
     36 #include "../../cmd/gss/gsscred/gsscred.h"
     37 
     38 static mutex_t uid_map_lock = DEFAULTMUTEX;
     39 static int uid_map_opt = 0;
     40 
     41 extern int _getgroupsbymember(const char *, gid_t[], int, int);
     42 
     43 /* local function used to call a mechanisms pname_to_uid */
     44 static OM_uint32 gss_pname_to_uid(OM_uint32*, const gss_name_t,
     45 			const gss_OID, uid_t *);
     46 
     47 static OM_uint32 private_gsscred_expname_to_unix_cred(const gss_buffer_t,
     48 			uid_t *, gid_t *, gid_t **, int *);
     49 
     50 /*
     51  * The gsscred functions will first attempt to call the
     52  * mechanism'm pname_to_uid function.  In case this function
     53  * returns an error or if it is not provided by a mechanism
     54  * then the functions will attempt to look up the principal
     55  * in the gsscred table.
     56  * It is envisioned that the pname_to_uid function will be
     57  * provided by only a few mechanism, which may have the principal
     58  * name to unix credential mapping inherently present.
     59  */
     60 
     61 /*
     62  * Fetch gsscred options from conf file.
     63  */
     64 static void
     65 get_conf_options(int *uid_map)
     66 {
     67 	int  flags;
     68 	char *ptr;
     69 	void	*defp;
     70 	static char *conffile = "/etc/gss/gsscred.conf";
     71 
     72 	*uid_map = 0;
     73 	if ((defp = defopen_r(conffile)) != NULL) {
     74 		flags = defcntl_r(DC_GETFLAGS, 0, defp);
     75 		/* ignore case */
     76 		TURNOFF(flags, DC_CASE);
     77 		(void) defcntl_r(DC_SETFLAGS, flags, defp);
     78 
     79 		if ((ptr = defread_r("SYSLOG_UID_MAPPING=", defp)) != NULL &&
     80 		    strcasecmp("yes", ptr) == 0) {
     81 			*uid_map = 1;
     82 		}
     83 		defclose_r(defp);
     84 	}
     85 }
     86 
     87 void
     88 gsscred_set_options()
     89 {
     90 	int u;
     91 
     92 	get_conf_options(&u);
     93 	(void) mutex_lock(&uid_map_lock);
     94 	uid_map_opt = u;
     95 	(void) mutex_unlock(&uid_map_lock);
     96 }
     97 
     98 static int
     99 get_uid_map_opt()
    100 {
    101 	int u;
    102 
    103 	(void) mutex_lock(&uid_map_lock);
    104 	u = uid_map_opt;
    105 	(void) mutex_unlock(&uid_map_lock);
    106 	return (u);
    107 }
    108 
    109 /*
    110  * This routine accepts a name in export name format and retrieves
    111  * unix credentials associated with it.
    112  */
    113 
    114 OM_uint32
    115 gsscred_expname_to_unix_cred_ext(
    116 	const gss_buffer_t expName,
    117 	uid_t *uidOut,
    118 	gid_t *gidOut,
    119 	gid_t *gids[],
    120 	int *gidsLen,
    121 	int try_mech)
    122 {
    123 	gss_name_t intName;
    124 	OM_uint32 minor, major;
    125 	const char *mechStr = NULL;
    126 	char *nameStr = NULL;
    127 	char *whoami = "gsscred_expname_to_unix_cred";
    128 	gss_buffer_desc namebuf;
    129 	int debug = get_uid_map_opt();
    130 
    131 	if (uidOut == NULL)
    132 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
    133 
    134 	if (expName == NULL)
    135 		return (GSS_S_CALL_INACCESSIBLE_READ);
    136 
    137 	/* first check the mechanism for the mapping */
    138 	if (gss_import_name(&minor, expName, (gss_OID)GSS_C_NT_EXPORT_NAME,
    139 	    &intName) == GSS_S_COMPLETE) {
    140 
    141 		if (debug) {
    142 			gss_union_name_t uintName = (gss_union_name_t)intName;
    143 
    144 			if (uintName->mech_type)
    145 				mechStr = __gss_oid_to_mech(
    146 				    uintName->mech_type);
    147 
    148 			major = gss_display_name(&minor, intName,
    149 			    &namebuf, NULL);
    150 			if (major == GSS_S_COMPLETE) {
    151 				nameStr = strdup(namebuf.value);
    152 				(void) gss_release_buffer(&minor, &namebuf);
    153 			}
    154 		}
    155 
    156 		if (try_mech) {
    157 			major = gss_pname_to_uid(&minor, intName,
    158 			    NULL, uidOut);
    159 			if (major == GSS_S_COMPLETE) {
    160 
    161 				if (debug) {
    162 					syslog(LOG_AUTH|LOG_DEBUG,
    163 					    "%s: mech provided local name"
    164 					    " mapping (%s, %s, %d)", whoami,
    165 					    mechStr ? mechStr : "<null>",
    166 					    nameStr ? nameStr : "<null>",
    167 					    *uidOut);
    168 					free(nameStr);
    169 				}
    170 
    171 				(void) gss_release_name(&minor, &intName);
    172 				if (gids && gidsLen && gidOut)
    173 					return (gss_get_group_info(*uidOut,
    174 					    gidOut, gids, gidsLen));
    175 				return (GSS_S_COMPLETE);
    176 			}
    177 		}
    178 
    179 		(void) gss_release_name(&minor, &intName);
    180 	}
    181 
    182 	/*
    183 	 * we fall back onto the gsscred table to provide the mapping
    184 	 * start by making sure that the expName is an export name buffer
    185 	 */
    186 	major = private_gsscred_expname_to_unix_cred(expName, uidOut, gidOut,
    187 	    gids, gidsLen);
    188 
    189 	if (debug && major == GSS_S_COMPLETE) {
    190 		syslog(LOG_AUTH|LOG_DEBUG,
    191 		    "%s: gsscred tbl provided"
    192 		    " local name mapping (%s, %s, %d)",
    193 		    whoami,
    194 		    mechStr ? mechStr : "<unknown>",
    195 		    nameStr ? nameStr : "<unknown>",
    196 		    *uidOut);
    197 		free(nameStr);
    198 	} else if (debug) {
    199 		syslog(LOG_AUTH|LOG_DEBUG,
    200 		    "%s: gsscred tbl could NOT"
    201 		    " provide local name mapping (%s, %s)",
    202 		    whoami,
    203 		    mechStr ? mechStr : "<unknown>",
    204 		    nameStr ? nameStr : "<unknown>");
    205 		free(nameStr);
    206 	}
    207 
    208 	return (major);
    209 
    210 } /* gsscred_expname_to_unix_cred */
    211 
    212 OM_uint32
    213 gsscred_expname_to_unix_cred(
    214 	const gss_buffer_t expName,
    215 	uid_t *uidOut,
    216 	gid_t *gidOut,
    217 	gid_t *gids[],
    218 	int *gidsLen)
    219 {
    220 	return (gsscred_expname_to_unix_cred_ext(expName, uidOut, gidOut, gids,
    221 	    gidsLen, 1));
    222 }
    223 
    224 
    225 static const char *expNameTokId = "\x04\x01";
    226 static const int expNameTokIdLen = 2;
    227 /*
    228  * private routine added to be called from gsscred_name_to_unix_cred
    229  * and gsscred_expName_to_unix_cred.
    230  */
    231 static OM_uint32
    232 private_gsscred_expname_to_unix_cred(expName, uidOut, gidOut, gids, gidsLen)
    233 const gss_buffer_t expName;
    234 uid_t *uidOut;
    235 gid_t *gidOut;
    236 gid_t *gids[];
    237 int *gidsLen;
    238 {
    239 
    240 	if (expName->length < expNameTokIdLen ||
    241 		(memcmp(expName->value, expNameTokId, expNameTokIdLen) != 0))
    242 		return (GSS_S_DEFECTIVE_TOKEN);
    243 
    244 	if (!gss_getGssCredEntry(expName, uidOut))
    245 		return (GSS_S_FAILURE);
    246 
    247 	/* did caller request group info also ? */
    248 	if (gids && gidsLen && gidOut)
    249 		return (gss_get_group_info(*uidOut, gidOut, gids, gidsLen));
    250 
    251 	return (GSS_S_COMPLETE);
    252 }
    253 
    254 /*
    255  * Return a string of the authenticated name.
    256  * It's a bit of hack/workaround/longroad but the current intName
    257  * passed to gss_display_name insists on returning an empty string.
    258  *
    259  * Caller must free string memory.
    260  */
    261 static
    262 char *make_name_str(
    263 	const gss_name_t intName,
    264 	const gss_OID mechType)
    265 
    266 {
    267 	gss_buffer_desc expName = GSS_C_EMPTY_BUFFER;
    268 	OM_uint32 major, minor;
    269 	gss_name_t canonName;
    270 	gss_name_t iName;
    271 	gss_buffer_desc namebuf;
    272 
    273 	if (major = gss_canonicalize_name(&minor, intName,
    274 				mechType, &canonName))
    275 		return (NULL);
    276 
    277 	major = gss_export_name(&minor, canonName, &expName);
    278 	(void) gss_release_name(&minor, &canonName);
    279 	if (major)
    280 		return (NULL);
    281 
    282 	if (gss_import_name(&minor, &expName,
    283 			    (gss_OID)GSS_C_NT_EXPORT_NAME,
    284 			    &iName) == GSS_S_COMPLETE) {
    285 
    286 		major = gss_display_name(&minor, iName, &namebuf, NULL);
    287 		if (major == GSS_S_COMPLETE) {
    288 			char *s;
    289 
    290 			if (namebuf.value)
    291 				s = strdup(namebuf.value);
    292 
    293 			(void) gss_release_buffer(&minor, &namebuf);
    294 			(void) gss_release_buffer(&minor, &expName);
    295 			(void) gss_release_buffer(&minor, (gss_buffer_t)iName);
    296 			return (s);
    297 		}
    298 		(void) gss_release_buffer(&minor, (gss_buffer_t)iName);
    299 	}
    300 
    301 	(void) gss_release_buffer(&minor, &expName);
    302 	return (NULL);
    303 }
    304 
    305 /*
    306  * This routine accepts a name in gss internal name format together with
    307  * a mechanim OID and retrieves a unix credentials for that entity.
    308  */
    309 OM_uint32
    310 gsscred_name_to_unix_cred_ext(
    311 	const gss_name_t intName,
    312 	const gss_OID mechType,
    313 	uid_t *uidOut,
    314 	gid_t *gidOut,
    315 	gid_t *gids[],
    316 	int *gidsLen,
    317 	int try_mech)
    318 {
    319 	gss_name_t canonName;
    320 	gss_buffer_desc expName = GSS_C_EMPTY_BUFFER;
    321 	OM_uint32 major, minor;
    322 	int debug = get_uid_map_opt();
    323 
    324 	const char *mechStr;
    325 	char *whoami = "gsscred_name_to_unix_cred";
    326 	gss_buffer_desc namebuf;
    327 
    328 	if (intName == NULL || mechType == NULL)
    329 		return (GSS_S_CALL_INACCESSIBLE_READ);
    330 
    331 	if (uidOut == NULL)
    332 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
    333 
    334 	mechStr = __gss_oid_to_mech(mechType);
    335 
    336 	/* first try the mechanism provided mapping */
    337 	if (try_mech && gss_pname_to_uid(&minor, intName, mechType, uidOut)
    338 	    == GSS_S_COMPLETE) {
    339 
    340 		if (debug) {
    341 			char *s = make_name_str(intName, mechType);
    342 			syslog(LOG_AUTH|LOG_DEBUG,
    343 			    "%s: mech provided local name"
    344 			    " mapping (%s, %s, %d)", whoami,
    345 			    mechStr ? mechStr : "<null>",
    346 			    s ? s : "<null>",
    347 			    *uidOut);
    348 			free(s);
    349 		}
    350 
    351 		if (gids && gidsLen && gidOut)
    352 			return (gss_get_group_info(*uidOut, gidOut, gids,
    353 			    gidsLen));
    354 		return (GSS_S_COMPLETE);
    355 	}
    356 	/*
    357 	 * falling back onto the gsscred table to provide the mapping
    358 	 * start by canonicalizing the passed in name and then export it
    359 	 */
    360 	if (major = gss_canonicalize_name(&minor, intName,
    361 	    mechType, &canonName))
    362 		return (major);
    363 
    364 	major = gss_export_name(&minor, canonName, &expName);
    365 	(void) gss_release_name(&minor, &canonName);
    366 	if (major)
    367 		return (major);
    368 
    369 	major = private_gsscred_expname_to_unix_cred(&expName, uidOut, gidOut,
    370 	    gids, gidsLen);
    371 
    372 
    373 	if (debug) {
    374 		gss_name_t iName;
    375 		OM_uint32 maj;
    376 		char *nameStr = NULL;
    377 
    378 		if (gss_import_name(&minor, &expName,
    379 		    (gss_OID)GSS_C_NT_EXPORT_NAME, &iName) == GSS_S_COMPLETE) {
    380 
    381 			maj = gss_display_name(&minor, iName, &namebuf,
    382 			    NULL);
    383 			(void) gss_release_buffer(&minor, (gss_buffer_t)iName);
    384 			if (maj == GSS_S_COMPLETE) {
    385 				nameStr = strdup(namebuf.value);
    386 				(void) gss_release_buffer(&minor, &namebuf);
    387 			}
    388 		}
    389 
    390 		if (major == GSS_S_COMPLETE)
    391 			syslog(LOG_AUTH|LOG_DEBUG,
    392 			    "%s: gsscred tbl provided"
    393 			    " local name mapping (%s, %s, %d)",
    394 			    whoami,
    395 			    mechStr ? mechStr : "<unknown>",
    396 			    nameStr ? nameStr : "<unknown>",
    397 			    *uidOut);
    398 		else
    399 			syslog(LOG_AUTH|LOG_DEBUG,
    400 			    "%s: gsscred tbl could NOT"
    401 			    " provide local name mapping (%s, %s)",
    402 			    whoami,
    403 			    mechStr ? mechStr : "<unknown>",
    404 			    nameStr ? nameStr : "<unknown>");
    405 
    406 		free(nameStr);
    407 	}
    408 
    409 	(void) gss_release_buffer(&minor, &expName);
    410 	return (major);
    411 } /* gsscred_name_to_unix_cred */
    412 
    413 
    414 OM_uint32
    415 gsscred_name_to_unix_cred(
    416 	const gss_name_t intName,
    417 	const gss_OID mechType,
    418 	uid_t *uidOut,
    419 	gid_t *gidOut,
    420 	gid_t *gids[],
    421 	int *gidsLen)
    422 {
    423 	return (gsscred_name_to_unix_cred_ext(intName, mechType,
    424 	    uidOut, gidOut, gids, gidsLen, 1));
    425 }
    426 
    427 
    428 
    429 /*
    430  * This routine accepts a unix uid, and retrieves the group id
    431  * and supplamentery group ids for that uid.
    432  * Callers should be aware that the supplamentary group ids
    433  * array may be empty even when this function returns success.
    434  */
    435 OM_uint32
    436 gss_get_group_info(uid, gidOut, gids, gidsLen)
    437 const uid_t uid;
    438 gid_t *gidOut;
    439 gid_t *gids[];
    440 int *gidsLen;
    441 {
    442 	struct passwd *pw;
    443 	int maxgroups;
    444 
    445 	/* check for output parameters */
    446 	if (gidOut == NULL || gids == NULL || gidsLen == NULL)
    447 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
    448 
    449 	*gids = NULL;
    450 	*gidsLen = 0;
    451 
    452 	/* determine maximum number of groups possible */
    453 	maxgroups = sysconf(_SC_NGROUPS_MAX);
    454 	if (maxgroups < 1)
    455 	    maxgroups = 16;
    456 
    457 	if ((pw = getpwuid(uid)) == NULL)
    458 	    return (GSS_S_FAILURE);
    459 
    460 	/*
    461 	 * we allocate for the maximum number of groups
    462 	 * we do not reclaim the space when the actual number
    463 	 * is lower, just set the size approprately.
    464 	 */
    465 	*gids = (gid_t *)calloc(maxgroups, sizeof (gid_t));
    466 	if (*gids == NULL)
    467 	    return (GSS_S_FAILURE);
    468 
    469 	*gidOut = pw->pw_gid;
    470 	(*gids)[0] = pw->pw_gid;
    471 	*gidsLen = _getgroupsbymember(pw->pw_name, *gids, maxgroups, 1);
    472 	/*
    473 	 * we will try to remove the duplicate entry from the groups
    474 	 * array.  This can cause the group array to be empty.
    475 	 */
    476 	if (*gidsLen < 1)
    477 	{
    478 		free(*gids);
    479 		*gids = NULL;
    480 		return (GSS_S_FAILURE);
    481 	} else if (*gidsLen == 1) {
    482 		free(*gids);
    483 		*gids = NULL;
    484 		*gidsLen = 0;
    485 	} else {
    486 		/* length is atleast 2 */
    487 		*gidsLen = *gidsLen -1;
    488 		(*gids)[0] = (*gids)[*gidsLen];
    489 	}
    490 
    491 	return (GSS_S_COMPLETE);
    492 } /* gss_get_group_info */
    493 
    494 
    495 static OM_uint32
    496 gss_pname_to_uid(minor, name, mech_type, uidOut)
    497 OM_uint32 *minor;
    498 const gss_name_t name;
    499 const gss_OID mech_type;
    500 uid_t *uidOut;
    501 {
    502 	gss_mechanism mech;
    503 	gss_union_name_t intName;
    504 	gss_name_t mechName = NULL;
    505 	OM_uint32 major, tmpMinor;
    506 
    507 	if (!minor)
    508 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
    509 
    510 	*minor = 0;
    511 
    512 	if (uidOut == NULL)
    513 		return (GSS_S_CALL_INACCESSIBLE_WRITE);
    514 
    515 	if (name == NULL)
    516 		return (GSS_S_CALL_INACCESSIBLE_READ);
    517 
    518 	intName = (gss_union_name_t)name;
    519 
    520 	if (mech_type != NULL)
    521 		mech = __gss_get_mechanism(mech_type);
    522 	else {
    523 		/*
    524 		 * if this is a MN, then try using the mech
    525 		 * from the name; otherwise ask for default
    526 		 */
    527 		mech = __gss_get_mechanism(intName->mech_type);
    528 	}
    529 
    530 	if (mech == NULL || mech->pname_to_uid == NULL)
    531 		return (GSS_S_UNAVAILABLE);
    532 
    533 	/* may need to import the name if this is not MN */
    534 	if (intName->mech_type == NULL) {
    535 		major = __gss_import_internal_name(minor,
    536 				mech_type, intName,
    537 				&mechName);
    538 		if (major != GSS_S_COMPLETE)
    539 			return (major);
    540 	} else
    541 		mechName = intName->mech_name;
    542 
    543 
    544 	/* now call the mechanism's pname function to do the work */
    545 	major = mech->pname_to_uid(mech->context, minor, mechName, uidOut);
    546 
    547 	if (intName->mech_name != mechName)
    548 		(void) __gss_release_internal_name(&tmpMinor, &mech->mech_type,
    549 				&mechName);
    550 
    551 	return (major);
    552 } /* gss_pname_to_uid */
    553