xref: /illumos-gate/usr/src/lib/libgss/gssd_pname_to_uid.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
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