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