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
get_conf_options(int * uid_map)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
gsscred_set_options()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
get_uid_map_opt()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
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)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
gsscred_expname_to_unix_cred(const gss_buffer_t expName,uid_t * uidOut,gid_t * gidOut,gid_t * gids[],int * gidsLen)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
private_gsscred_expname_to_unix_cred(expName,uidOut,gidOut,gids,gidsLen)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
make_name_str(const gss_name_t intName,const gss_OID mechType)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
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)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
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)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
gss_get_group_info(uid,gidOut,gids,gidsLen)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
gss_pname_to_uid(minor,name,mech_type,uidOut)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