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