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 (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <nss_dbdefs.h> 26 #include <pwd.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <syslog.h> 30 #include <unistd.h> 31 #include <auth_attr.h> 32 #include <deflt.h> 33 #include <priv.h> 34 #include <secdb.h> 35 #include <user_attr.h> 36 #include <sys/task.h> 37 #include <libintl.h> 38 #include <project.h> 39 #include <errno.h> 40 #include <alloca.h> 41 42 #include <bsm/adt.h> 43 #include <bsm/adt_event.h> /* adt_get_auid() */ 44 45 #include <security/pam_appl.h> 46 #include <security/pam_modules.h> 47 #include <security/pam_impl.h> 48 49 #define PROJECT "project=" 50 #define PROJSZ (sizeof (PROJECT) - 1) 51 52 /* 53 * unix_cred - PAM auth modules must contain both pam_sm_authenticate 54 * and pam_sm_setcred. Some other auth module is responsible 55 * for authentication (e.g., pam_unix_auth.so), this module 56 * only implements pam_sm_setcred so that the authentication 57 * can be separated without knowledge of the Solaris Unix style 58 * credential setting. 59 * Solaris Unix style credential setting includes initializing 60 * the audit characteristics if not already initialized and 61 * setting the user's default and limit privileges. 62 */ 63 64 /* 65 * unix_cred - pam_sm_authenticate 66 * 67 * Returns PAM_IGNORE. 68 */ 69 70 /*ARGSUSED*/ 71 int 72 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 73 { 74 return (PAM_IGNORE); 75 } 76 77 /* 78 * Obtain a privilege set "keyname" from userattr; if none is present, 79 * fall back to the default, "defname". 80 */ 81 static int 82 getset(char *keyname, char *defname, userattr_t *ua, priv_set_t **res, 83 void *defp) 84 { 85 char *str; 86 priv_set_t *tmp; 87 char *badp; 88 int len; 89 90 if ((ua == NULL || ua->attr == NULL || 91 (str = kva_match(ua->attr, keyname)) == NULL) && 92 (defp == NULL || (str = defread_r(defname, defp)) == NULL)) 93 return (0); 94 95 len = strlen(str) + 1; 96 badp = alloca(len); 97 (void) memset(badp, '\0', len); 98 do { 99 const char *q, *endp; 100 tmp = priv_str_to_set(str, ",", &endp); 101 if (tmp == NULL) { 102 if (endp == NULL) 103 break; 104 105 /* Now remove the bad privilege endp points to */ 106 q = strchr(endp, ','); 107 if (q == NULL) 108 q = endp + strlen(endp); 109 110 if (*badp != '\0') 111 (void) strlcat(badp, ",", len); 112 /* Memset above guarantees NUL termination */ 113 /* LINTED */ 114 (void) strncat(badp, endp, q - endp); 115 /* excise bad privilege; strtok ignores 2x sep */ 116 (void) memmove((void *)endp, q, strlen(q) + 1); 117 } 118 } while (tmp == NULL && *str != '\0'); 119 120 if (tmp == NULL) { 121 syslog(LOG_AUTH|LOG_ERR, 122 "pam_setcred: can't parse privilege specification: %m\n"); 123 return (-1); 124 } else if (*badp != '\0') { 125 syslog(LOG_AUTH|LOG_DEBUG, 126 "pam_setcred: unrecognized privilege(s): %s\n", badp); 127 } 128 *res = tmp; 129 return (0); 130 } 131 132 /* 133 * unix_cred - pam_sm_setcred 134 * 135 * Entry flags = PAM_ESTABLISH_CRED, set up Solaris Unix cred. 136 * PAM_DELETE_CRED, NOP, return PAM_SUCCESS. 137 * PAM_REINITIALIZE_CRED, set up Solaris Unix cred, 138 * or merge the current context with the new 139 * user. 140 * PAM_REFRESH_CRED, set up Solaris Unix cred. 141 * PAM_SILENT, print no messages to user. 142 * 143 * Returns PAM_SUCCESS, if all successful. 144 * PAM_CRED_ERR, if unable to set credentials. 145 * PAM_USER_UNKNOWN, if PAM_USER not set, or unable to find 146 * user in databases. 147 * PAM_SYSTEM_ERR, if no valid flag, or unable to get/set 148 * user's audit state. 149 */ 150 151 int 152 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 153 { 154 int i; 155 int debug = 0; 156 uint_t nowarn = flags & PAM_SILENT; 157 int ret = PAM_SUCCESS; 158 char *user; 159 char *auser; 160 char *rhost; 161 char *tty; 162 au_id_t auid; 163 adt_session_data_t *ah; 164 adt_termid_t *termid = NULL; 165 userattr_t *ua; 166 priv_set_t *lim, *def, *tset; 167 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE]; 168 char buf[PROJECT_BUFSZ]; 169 struct project proj, *pproj; 170 int error; 171 char *projname; 172 char *kvs; 173 struct passwd pwd; 174 char pwbuf[NSS_BUFLEN_PASSWD]; 175 void *defp; 176 177 for (i = 0; i < argc; i++) { 178 if (strcmp(argv[i], "debug") == 0) 179 debug = 1; 180 else if (strcmp(argv[i], "nowarn") == 0) 181 nowarn |= 1; 182 } 183 184 if (debug) 185 syslog(LOG_AUTH | LOG_DEBUG, 186 "pam_unix_cred: pam_sm_setcred(flags = %x, argc= %d)", 187 flags, argc); 188 189 (void) pam_get_item(pamh, PAM_USER, (void **)&user); 190 191 if (user == NULL || *user == '\0') { 192 syslog(LOG_AUTH | LOG_ERR, 193 "pam_unix_cred: USER NULL or empty!\n"); 194 return (PAM_USER_UNKNOWN); 195 } 196 (void) pam_get_item(pamh, PAM_AUSER, (void **)&auser); 197 (void) pam_get_item(pamh, PAM_RHOST, (void **)&rhost); 198 (void) pam_get_item(pamh, PAM_TTY, (void **)&tty); 199 if (debug) 200 syslog(LOG_AUTH | LOG_DEBUG, 201 "pam_unix_cred: user = %s, auser = %s, rhost = %s, " 202 "tty = %s", user, 203 (auser == NULL) ? "NULL" : (*auser == '\0') ? "ZERO" : 204 auser, 205 (rhost == NULL) ? "NULL" : (*rhost == '\0') ? "ZERO" : 206 rhost, 207 (tty == NULL) ? "NULL" : (*tty == '\0') ? "ZERO" : 208 tty); 209 210 /* validate flags */ 211 switch (flags & (PAM_ESTABLISH_CRED | PAM_DELETE_CRED | 212 PAM_REINITIALIZE_CRED | PAM_REFRESH_CRED)) { 213 case 0: 214 /* set default flag */ 215 flags |= PAM_ESTABLISH_CRED; 216 break; 217 case PAM_ESTABLISH_CRED: 218 case PAM_REINITIALIZE_CRED: 219 case PAM_REFRESH_CRED: 220 break; 221 case PAM_DELETE_CRED: 222 return (PAM_SUCCESS); 223 default: 224 syslog(LOG_AUTH | LOG_ERR, 225 "pam_unix_cred: invalid flags %x", flags); 226 return (PAM_SYSTEM_ERR); 227 } 228 229 /* 230 * if auditing on and process audit state not set, 231 * setup audit context for process. 232 */ 233 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) { 234 syslog(LOG_AUTH | LOG_ERR, 235 "pam_unix_cred: cannot create start audit session %m"); 236 return (PAM_SYSTEM_ERR); 237 } 238 adt_get_auid(ah, &auid); 239 if (debug) { 240 int auditstate; 241 242 if (auditon(A_GETCOND, (caddr_t)&auditstate, 243 sizeof (auditstate)) != 0) { 244 auditstate = AUC_DISABLED; 245 } 246 syslog(LOG_AUTH | LOG_DEBUG, 247 "pam_unix_cred: state = %d, auid = %d", auditstate, 248 auid); 249 } 250 if (getpwnam_r(user, &pwd, pwbuf, sizeof (pwbuf)) == NULL) { 251 syslog(LOG_AUTH | LOG_ERR, 252 "pam_unix_cred: cannot get passwd entry for user = %s", 253 user); 254 ret = PAM_USER_UNKNOWN; 255 goto adt_done; 256 } 257 258 if ((auid == AU_NOAUDITID) && 259 (flags & PAM_ESTABLISH_CRED)) { 260 struct passwd apwd; 261 char apwbuf[NSS_BUFLEN_PASSWD]; 262 263 errno = 0; 264 if ((rhost == NULL || *rhost == '\0')) { 265 if (adt_load_ttyname(tty, &termid) != 0) { 266 if (errno == ENETDOWN) { 267 /* 268 * tolerate not being able to 269 * translate local hostname 270 * to a termid -- it will be 271 * "loopback". 272 */ 273 syslog(LOG_AUTH | LOG_ERR, 274 "pam_unix_cred: cannot load " 275 "ttyname: %m, continuing."); 276 goto adt_setuser; 277 } else if (errno != 0) { 278 syslog(LOG_AUTH | LOG_ERR, 279 "pam_unix_cred: cannot load " 280 "ttyname: %m."); 281 } else { 282 syslog(LOG_AUTH | LOG_ERR, 283 "pam_unix_cred: cannot load " 284 "ttyname."); 285 } 286 ret = PAM_SYSTEM_ERR; 287 goto adt_done; 288 } 289 } else { 290 if (adt_load_hostname(rhost, &termid) != 0) { 291 if (errno != 0) { 292 syslog(LOG_AUTH | LOG_ERR, 293 "pam_unix_cred: cannot load " 294 "hostname: %m."); 295 } else { 296 syslog(LOG_AUTH | LOG_ERR, 297 "pam_unix_cred: cannot load " 298 "hostname."); 299 } 300 ret = PAM_SYSTEM_ERR; 301 goto adt_done; 302 } 303 } 304 adt_setuser: 305 if ((auser != NULL) && (*auser != '\0') && 306 (getpwnam_r(auser, &apwd, apwbuf, 307 sizeof (apwbuf)) != NULL)) { 308 /* 309 * set up the initial audit for user coming 310 * from another user 311 */ 312 if (adt_set_user(ah, apwd.pw_uid, apwd.pw_gid, 313 apwd.pw_uid, apwd.pw_gid, termid, ADT_NEW) != 0) { 314 syslog(LOG_AUTH | LOG_ERR, 315 "pam_unix_cred: cannot set auser audit " 316 "%m"); 317 ret = PAM_SYSTEM_ERR; 318 goto adt_done; 319 } 320 if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid, 321 pwd.pw_uid, pwd.pw_gid, NULL, 322 ADT_UPDATE) != 0) { 323 syslog(LOG_AUTH | LOG_ERR, 324 "pam_unix_cred: cannot merge user audit " 325 "%m"); 326 ret = PAM_SYSTEM_ERR; 327 goto adt_done; 328 } 329 if (debug) { 330 syslog(LOG_AUTH | LOG_DEBUG, 331 "pam_unix_cred: new audit set for %d:%d", 332 apwd.pw_uid, pwd.pw_uid); 333 } 334 } else { 335 /* 336 * No authenticated user or authenticated user is 337 * not a local user, no remote attribution, set 338 * up the initial audit as for direct user login 339 */ 340 if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid, 341 pwd.pw_uid, pwd.pw_gid, termid, ADT_NEW) != 0) { 342 syslog(LOG_AUTH | LOG_ERR, 343 "pam_unix_cred: cannot set user audit %m"); 344 ret = PAM_SYSTEM_ERR; 345 goto adt_done; 346 } 347 } 348 if (adt_set_proc(ah) != 0) { 349 syslog(LOG_AUTH | LOG_ERR, 350 "pam_unix_cred: cannot set process audit %m"); 351 ret = PAM_CRED_ERR; 352 goto adt_done; 353 } 354 if (debug) { 355 syslog(LOG_AUTH | LOG_DEBUG, 356 "pam_unix_cred: new audit set for %d", 357 pwd.pw_uid); 358 } 359 } else if ((auid != AU_NOAUDITID) && 360 (flags & PAM_REINITIALIZE_CRED)) { 361 if (adt_set_user(ah, pwd.pw_uid, pwd.pw_gid, pwd.pw_uid, 362 pwd.pw_gid, NULL, ADT_UPDATE) != 0) { 363 syslog(LOG_AUTH | LOG_ERR, 364 "pam_unix_cred: cannot set user audit %m"); 365 ret = PAM_SYSTEM_ERR; 366 goto adt_done; 367 } 368 if (adt_set_proc(ah) != 0) { 369 syslog(LOG_AUTH | LOG_ERR, 370 "pam_unix_cred: cannot set process audit %m"); 371 ret = PAM_CRED_ERR; 372 goto adt_done; 373 } 374 if (debug) { 375 syslog(LOG_AUTH | LOG_DEBUG, 376 "pam_unix_cred: audit merged for %d:%d", 377 auid, pwd.pw_uid); 378 } 379 } else if (debug) { 380 syslog(LOG_AUTH | LOG_DEBUG, 381 "pam_unix_cred: audit already set for %d", auid); 382 } 383 adt_done: 384 if (termid != NULL) 385 free(termid); 386 if (adt_end_session(ah) != 0) { 387 syslog(LOG_AUTH | LOG_ERR, 388 "pam_unix_cred: unable to end audit session"); 389 } 390 391 if (ret != PAM_SUCCESS) 392 return (ret); 393 394 /* Initialize the user's project */ 395 (void) pam_get_item(pamh, PAM_RESOURCE, (void **)&kvs); 396 if (kvs != NULL) { 397 char *tmp, *lasts, *tok; 398 399 kvs = tmp = strdup(kvs); 400 if (kvs == NULL) 401 return (PAM_BUF_ERR); 402 403 while ((tok = strtok_r(tmp, ";", &lasts)) != NULL) { 404 if (strncmp(tok, PROJECT, PROJSZ) == 0) { 405 projname = tok + PROJSZ; 406 break; 407 } 408 tmp = NULL; 409 } 410 } else { 411 projname = NULL; 412 } 413 414 if (projname == NULL || *projname == '\0') { 415 pproj = getdefaultproj(user, &proj, (void *)&buf, 416 PROJECT_BUFSZ); 417 } else { 418 pproj = getprojbyname(projname, &proj, (void *)&buf, 419 PROJECT_BUFSZ); 420 } 421 /* projname points into kvs, so this is the first opportunity to free */ 422 if (kvs != NULL) 423 free(kvs); 424 if (pproj == NULL) { 425 syslog(LOG_AUTH | LOG_ERR, 426 "pam_unix_cred: no default project for user %s", user); 427 if (!nowarn) { 428 (void) snprintf(messages[0], sizeof (messages[0]), 429 dgettext(TEXT_DOMAIN, "No default project!")); 430 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 431 1, messages, NULL); 432 } 433 return (PAM_SYSTEM_ERR); 434 } 435 if ((error = setproject(proj.pj_name, user, TASK_NORMAL)) != 0) { 436 kva_t *kv_array; 437 438 switch (error) { 439 case SETPROJ_ERR_TASK: 440 if (errno == EAGAIN) { 441 syslog(LOG_AUTH | LOG_ERR, 442 "pam_unix_cred: project \"%s\" resource " 443 "control limit has been reached", 444 proj.pj_name); 445 (void) snprintf(messages[0], 446 sizeof (messages[0]), dgettext( 447 TEXT_DOMAIN, 448 "Resource control limit has been " 449 "reached")); 450 } else { 451 syslog(LOG_AUTH | LOG_ERR, 452 "pam_unix_cred: user %s could not join " 453 "project \"%s\": %m", user, proj.pj_name); 454 (void) snprintf(messages[0], 455 sizeof (messages[0]), dgettext( 456 TEXT_DOMAIN, 457 "Could not join default project")); 458 } 459 if (!nowarn) 460 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, 461 messages, NULL); 462 break; 463 case SETPROJ_ERR_POOL: 464 (void) snprintf(messages[0], sizeof (messages[0]), 465 dgettext(TEXT_DOMAIN, 466 "Could not bind to resource pool")); 467 switch (errno) { 468 case EACCES: 469 syslog(LOG_AUTH | LOG_ERR, 470 "pam_unix_cred: project \"%s\" could not " 471 "bind to resource pool: No resource pool " 472 "accepting default bindings exists", 473 proj.pj_name); 474 (void) snprintf(messages[1], 475 sizeof (messages[1]), 476 dgettext(TEXT_DOMAIN, 477 "No resource pool accepting " 478 "default bindings exists")); 479 break; 480 case ESRCH: 481 syslog(LOG_AUTH | LOG_ERR, 482 "pam_unix_cred: project \"%s\" could not " 483 "bind to resource pool: The resource pool " 484 "is unknown", proj.pj_name); 485 (void) snprintf(messages[1], 486 sizeof (messages[1]), 487 dgettext(TEXT_DOMAIN, 488 "The specified resource pool " 489 "is unknown")); 490 break; 491 default: 492 (void) snprintf(messages[1], 493 sizeof (messages[1]), 494 dgettext(TEXT_DOMAIN, 495 "Failure during pool binding")); 496 syslog(LOG_AUTH | LOG_ERR, 497 "pam_unix_cred: project \"%s\" could not " 498 "bind to resource pool: %m", proj.pj_name); 499 } 500 if (!nowarn) 501 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 502 2, messages, NULL); 503 break; 504 default: 505 /* 506 * Resource control assignment failed. Unlike 507 * newtask(1m), we treat this as an error. 508 */ 509 if (error < 0) { 510 /* 511 * This isn't supposed to happen, but in 512 * case it does, this error message 513 * doesn't use error as an index, like 514 * the others might. 515 */ 516 syslog(LOG_AUTH | LOG_ERR, 517 "pam_unix_cred: unkwown error joining " 518 "project \"%s\" (%d)", proj.pj_name, error); 519 (void) snprintf(messages[0], 520 sizeof (messages[0]), 521 dgettext(TEXT_DOMAIN, 522 "unkwown error joining project \"%s\"" 523 " (%d)"), proj.pj_name, error); 524 } else if ((kv_array = _str2kva(proj.pj_attr, KV_ASSIGN, 525 KV_DELIMITER)) != NULL) { 526 syslog(LOG_AUTH | LOG_ERR, 527 "pam_unix_cred: %s resource control " 528 "assignment failed for project \"%s\"", 529 kv_array->data[error - 1].key, 530 proj.pj_name); 531 (void) snprintf(messages[0], 532 sizeof (messages[0]), 533 dgettext(TEXT_DOMAIN, 534 "%s resource control assignment failed for " 535 "project \"%s\""), 536 kv_array->data[error - 1].key, 537 proj.pj_name); 538 _kva_free(kv_array); 539 } else { 540 syslog(LOG_AUTH | LOG_ERR, 541 "pam_unix_cred: resource control " 542 "assignment failed for project \"%s\"" 543 "attribute %d", proj.pj_name, error); 544 (void) snprintf(messages[0], 545 sizeof (messages[0]), 546 dgettext(TEXT_DOMAIN, 547 "resource control assignment failed for " 548 "project \"%s\" attribute %d"), 549 proj.pj_name, error); 550 } 551 if (!nowarn) 552 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 553 1, messages, NULL); 554 } 555 return (PAM_SYSTEM_ERR); 556 } 557 558 ua = getusernam(user); 559 560 defp = defopen_r(AUTH_POLICY); 561 562 tset = def = lim = NULL; 563 564 if (getset(USERATTR_LIMPRIV_KW, DEF_LIMITPRIV, ua, &lim, defp) != 0 || 565 getset(USERATTR_DFLTPRIV_KW, DEF_DFLTPRIV, ua, &def, defp) != 0) { 566 ret = PAM_SYSTEM_ERR; 567 goto out; 568 } 569 570 if (def == NULL) { 571 def = priv_str_to_set("basic", ",", NULL); 572 errno = 0; 573 if ((pathconf("/", _PC_CHOWN_RESTRICTED) == -1) && (errno == 0)) 574 (void) priv_addset(def, PRIV_FILE_CHOWN_SELF); 575 } 576 /* 577 * Silently limit the privileges to those actually available 578 * in the current zone. 579 */ 580 tset = priv_allocset(); 581 if (tset == NULL) { 582 ret = PAM_SYSTEM_ERR; 583 goto out; 584 } 585 if (getppriv(PRIV_PERMITTED, tset) != 0) { 586 ret = PAM_SYSTEM_ERR; 587 goto out; 588 } 589 if (!priv_issubset(def, tset)) 590 priv_intersect(tset, def); 591 /* 592 * We set privilege awareness here so that I gets copied to 593 * P & E when the final setuid(uid) happens. 594 */ 595 (void) setpflags(PRIV_AWARE, 1); 596 if (setppriv(PRIV_SET, PRIV_INHERITABLE, def) != 0) { 597 syslog(LOG_AUTH | LOG_ERR, 598 "pam_setcred: setppriv(defaultpriv) failed: %m"); 599 ret = PAM_CRED_ERR; 600 } 601 602 if (lim != NULL) { 603 /* 604 * Silently limit the privileges to the limit set available. 605 */ 606 if (getppriv(PRIV_LIMIT, tset) != 0) { 607 ret = PAM_SYSTEM_ERR; 608 goto out; 609 } 610 if (!priv_issubset(lim, tset)) 611 priv_intersect(tset, lim); 612 if (setppriv(PRIV_SET, PRIV_LIMIT, lim) != 0) { 613 syslog(LOG_AUTH | LOG_ERR, 614 "pam_setcred: setppriv(limitpriv) failed: %m"); 615 ret = PAM_CRED_ERR; 616 goto out; 617 } 618 /* 619 * In order not to surprise certain applications, we 620 * need to get rid of privilege awareness and thus we must 621 * set this flag which will cause a reset on set*uid(). 622 */ 623 (void) setpflags(PRIV_AWARE_RESET, 1); 624 } 625 /* 626 * This may fail but we do not care as this will be reset later 627 * when the uids are set to their final values. 628 */ 629 (void) setpflags(PRIV_AWARE, 0); 630 631 out: 632 if (defp != NULL) 633 defclose_r(defp); 634 635 if (ua != NULL) 636 free_userattr(ua); 637 if (lim != NULL) 638 priv_freeset(lim); 639 if (def != NULL) 640 priv_freeset(def); 641 if (tset != NULL) 642 priv_freeset(tset); 643 644 return (ret); 645 } 646