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