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