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