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 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 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 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 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 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 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 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 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 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 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 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