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