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