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