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 * Copyright (c) 2016 by Delphix. All rights reserved. 25 * Copyright 2024 OmniOS Community Edition (OmniOSce) Association. 26 */ 27 28 #include <sys/types.h> 29 #include <fcntl.h> 30 #include <errno.h> 31 #include <stdlib.h> 32 #include <sys/stat.h> 33 #include <pwd.h> 34 #include <shadow.h> 35 #include <string.h> 36 #include <strings.h> 37 #include <stdlib.h> 38 #include <unistd.h> 39 #include <nss_dbdefs.h> 40 #include <macros.h> 41 #include <syslog.h> 42 43 #include <limits.h> /* LOGNAME_MAX -- max Solaris user name */ 44 45 #include "passwdutil.h" 46 47 int files_lock(void); 48 int files_unlock(void); 49 int files_checkhistory(const char *user, const char *passwd, 50 pwu_repository_t *rep); 51 int files_getattr(const char *name, attrlist *item, pwu_repository_t *rep); 52 int files_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep, 53 void **buf); 54 int files_update(attrlist *items, pwu_repository_t *rep, void *buf); 55 int files_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep, 56 void *buf); 57 int files_user_to_authenticate(const char *name, pwu_repository_t *rep, 58 char **auth_user, int *privileged); 59 60 static int files_update_history(const char *name, struct spwd *spwd); 61 62 /* 63 * files function pointer table, used by passwdutil_init to initialize 64 * the global Repository-OPerations table "rops" 65 */ 66 struct repops files_repops = { 67 files_checkhistory, 68 files_getattr, 69 files_getpwnam, 70 files_update, 71 files_putpwnam, 72 files_user_to_authenticate, 73 files_lock, 74 files_unlock 75 }; 76 77 /* 78 * this structure defines the buffer used to keep state between 79 * get/update/put calls 80 */ 81 struct pwbuf { 82 int update_history; 83 struct passwd *pwd; 84 char *pwd_scratch; 85 struct spwd *spwd; 86 char *spwd_scratch; 87 char *new_sp_pwdp; 88 }; 89 90 /* 91 * We should use sysconf, but there is no sysconf name for SHADOW 92 * so we use these from nss_dbdefs 93 */ 94 #define PWD_SCRATCH_SIZE NSS_LINELEN_PASSWD 95 #define SPW_SCRATCH_SIZE NSS_LINELEN_SHADOW 96 97 /* 98 * lock functions for files repository 99 */ 100 int 101 files_lock(void) 102 { 103 int res; 104 105 if (lckpwdf()) { 106 switch (errno) { 107 case EINTR: 108 res = PWU_BUSY; 109 break; 110 case EACCES: 111 res = PWU_DENIED; 112 break; 113 case 0: 114 res = PWU_SUCCESS; 115 break; 116 } 117 } else 118 res = PWU_SUCCESS; 119 120 return (res); 121 } 122 123 int 124 files_unlock(void) 125 { 126 if (ulckpwdf()) 127 return (PWU_SYSTEM_ERROR); 128 129 return (PWU_SUCCESS); 130 } 131 132 /* 133 * files_privileged 134 * 135 * Are we a privileged user with regard to the files repository? 136 */ 137 int 138 files_privileged(void) 139 { 140 return (getuid() == 0); 141 } 142 143 /* 144 * 145 * private_getpwnam_r() 146 * 147 * A private implementation of getpwnam_r which does *not* fall back to 148 * other services possibly defined in nsswitch.conf 149 * 150 * behaves like getpwnam_r(). 151 */ 152 struct passwd * 153 private_getpwnam_r(const char *name, struct passwd *result, char *buffer, 154 int buflen) 155 { 156 FILE *fp; 157 int found; 158 159 if ((fp = fopen(PASSWD, "rF")) == NULL) 160 return (NULL); 161 162 found = 0; 163 while (!found && fgetpwent_r(fp, result, buffer, buflen) != NULL) { 164 if (strcmp(name, result->pw_name) == 0) 165 found = 1; 166 } 167 168 (void) fclose(fp); 169 170 if (!found) { 171 (void) memset(buffer, 0, buflen); 172 (void) memset(result, 0, sizeof (*result)); 173 return (NULL); 174 } 175 176 return (result); 177 } 178 179 /* 180 * private_getspnam_r() 181 * 182 * A private implementation of getspnam_r which does *not* fall back to 183 * other services possibly defined in nsswitch.conf. 184 * 185 * Behaves like getspnam_r(). Since we use fgetspent_t(), all numeric 186 * fields that are undefined in /etc/shadow will be set to -1. 187 * 188 */ 189 struct spwd * 190 private_getspnam_r(const char *name, struct spwd *result, char *buffer, 191 int buflen) 192 { 193 FILE *fp; 194 int found; 195 196 fp = fopen(SHADOW, "rF"); 197 if (fp == NULL) 198 return (NULL); 199 200 found = 0; 201 while (!found && fgetspent_r(fp, result, buffer, buflen) != NULL) { 202 if (strcmp(name, result->sp_namp) == 0) 203 found = 1; 204 } 205 206 (void) fclose(fp); 207 208 if (!found) { 209 (void) memset(buffer, 0, buflen); 210 (void) memset(result, 0, sizeof (*result)); 211 return (NULL); 212 } 213 return (result); 214 } 215 216 /* 217 * files_getpwnam(name, items, rep, buf) 218 * 219 */ 220 /*ARGSUSED*/ 221 int 222 files_getpwnam(const char *name, attrlist *items, pwu_repository_t *rep, 223 void **buf) 224 { 225 attrlist *p; 226 struct pwbuf *pwbuf; 227 int err = PWU_SUCCESS; 228 229 *buf = calloc(1, sizeof (struct pwbuf)); 230 pwbuf = (struct pwbuf *)*buf; 231 if (pwbuf == NULL) 232 return (PWU_NOMEM); 233 234 /* 235 * determine which password structure (/etc/passwd or /etc/shadow) 236 * we need for the items we need to update 237 */ 238 for (p = items; p != NULL; p = p->next) { 239 switch (p->type) { 240 case ATTR_NAME: 241 case ATTR_UID: 242 case ATTR_GID: 243 case ATTR_AGE: 244 case ATTR_COMMENT: 245 case ATTR_GECOS: 246 case ATTR_HOMEDIR: 247 case ATTR_SHELL: 248 if (pwbuf->pwd == NULL) { 249 pwbuf->pwd = malloc(sizeof (struct passwd)); 250 if (pwbuf->pwd == NULL) { 251 err = PWU_NOMEM; 252 goto error; 253 } 254 } 255 break; 256 case ATTR_PASSWD: 257 case ATTR_PASSWD_SERVER_POLICY: 258 case ATTR_LSTCHG: 259 case ATTR_MIN: 260 case ATTR_MAX: 261 case ATTR_WARN: 262 case ATTR_INACT: 263 case ATTR_EXPIRE: 264 case ATTR_FLAG: 265 case ATTR_LOCK_ACCOUNT: 266 case ATTR_EXPIRE_PASSWORD: 267 case ATTR_FAILED_LOGINS: 268 case ATTR_INCR_FAILED_LOGINS: 269 case ATTR_RST_FAILED_LOGINS: 270 case ATTR_NOLOGIN_ACCOUNT: 271 case ATTR_UNLOCK_ACCOUNT: 272 if (pwbuf->spwd == NULL) { 273 pwbuf->spwd = malloc(sizeof (struct spwd)); 274 if (pwbuf->spwd == NULL) { 275 err = PWU_NOMEM; 276 goto error; 277 } 278 } 279 break; 280 default: 281 /* 282 * Some other repository might have different values 283 * so we ignore those. 284 */ 285 break; 286 } 287 } 288 289 if (pwbuf->pwd) { 290 if ((pwbuf->pwd_scratch = malloc(PWD_SCRATCH_SIZE)) == NULL) { 291 err = PWU_NOMEM; 292 goto error; 293 } 294 if (private_getpwnam_r(name, pwbuf->pwd, pwbuf->pwd_scratch, 295 PWD_SCRATCH_SIZE) == NULL) { 296 err = PWU_NOT_FOUND; 297 goto error; 298 } 299 } 300 301 if (pwbuf->spwd) { 302 if ((pwbuf->spwd_scratch = malloc(SPW_SCRATCH_SIZE)) == NULL) { 303 err = PWU_NOMEM; 304 goto error; 305 } 306 if (private_getspnam_r(name, pwbuf->spwd, pwbuf->spwd_scratch, 307 SPW_SCRATCH_SIZE) == NULL) { 308 err = PWU_NOT_FOUND; 309 goto error; 310 } 311 } 312 313 return (PWU_SUCCESS); 314 error: 315 if (pwbuf->pwd) free(pwbuf->pwd); 316 if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 317 if (pwbuf->spwd) free(pwbuf->spwd); 318 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 319 free(pwbuf); 320 *buf = NULL; 321 322 return (err); 323 } 324 325 /* 326 * int files_user_to_authenticate(name, rep, auth_user, privileged) 327 * Determine which user needs to be authenticated. For files, the 328 * possible return values are: 329 * PWU_NOT_FOUND 330 * PWU_SUCCESS and (auth_user == NULL || auth_user = user) 331 * PWU_DENIED 332 * PWU_NOMEM 333 */ 334 /*ARGSUSED*/ 335 int 336 files_user_to_authenticate(const char *user, pwu_repository_t *rep, 337 char **auth_user, int *privileged) 338 { 339 struct pwbuf *pwbuf; 340 int res; 341 attrlist attr_tmp[1] = { { ATTR_UID, NULL, NULL } }; 342 343 /* check to see if target user is present in files */ 344 res = files_getpwnam(user, &attr_tmp[0], rep, (void **)&pwbuf); 345 if (res != PWU_SUCCESS) 346 return (res); 347 348 if (files_privileged()) { 349 *auth_user = NULL; 350 *privileged = 1; 351 res = PWU_SUCCESS; 352 } else { 353 *privileged = 0; 354 if (getuid() == pwbuf->pwd->pw_uid) { 355 if ((*auth_user = strdup(user)) == NULL) { 356 res = PWU_NOMEM; 357 } else { 358 res = PWU_SUCCESS; 359 } 360 } else { 361 res = PWU_DENIED; 362 } 363 } 364 365 if (pwbuf->pwd) free(pwbuf->pwd); 366 if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 367 if (pwbuf->spwd) free(pwbuf->spwd); 368 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 369 free(pwbuf); 370 371 return (res); 372 } 373 374 /* 375 * Password history file format: 376 * user:crypw1: ... crypwn: such that n <= MAXHISTORY 377 */ 378 #define HISTORY "/etc/security/passhistory" 379 #define HISTEMP "/etc/security/pwhistemp" 380 #define OHISTORY "/etc/security/opwhistory" 381 #define HISTMODE S_IRUSR /* mode to create history file */ 382 /* 383 * XXX 384 * 3*LOGNAME_MAX just in case there are long user names. 385 * Traditionally Solaris LOGNAME_MAX (_POSIX_LOGIN_NAME_MAX) is 13, 386 * but some sites often user more. 387 * If LOGNAME_MAX ever becomes reasonable (128) and actually enforced, 388 * fix up here. 389 * XXX 390 */ 391 #define MAX_LOGNAME (3 * LOGNAME_MAX) 392 393 /* 394 * files_checkhistory - check if a user's new password is in the user's 395 * old password history. 396 * 397 * Entry 398 * user = username. 399 * passwd = new clear text password. 400 * 401 * Exit 402 * PWU_SUCCESS, passwd found in user's old password history. 403 * The caller should only be interested and fail if 404 * PWU_SUCCESS is returned. 405 * PWU_NOT_FOUND, passwd not in user's old password history. 406 * PWU_errors, PWU_ errors from other routines. 407 * 408 */ 409 int 410 files_checkhistory(const char *user, const char *passwd, pwu_repository_t *rep) 411 { 412 attrlist attr; 413 int res; 414 415 attr.type = ATTR_HISTORY; 416 attr.data.val_s = NULL; 417 attr.next = NULL; 418 419 debug("files_checkhistory(user=%s)", user); 420 421 /* 422 * XXX 423 * This depends on the underlying files_getattr implementation 424 * treating user not found in backing store or no history as 425 * an error. 426 * XXX 427 */ 428 429 if ((res = files_getattr(user, &attr, rep)) == PWU_SUCCESS) { 430 char *s; 431 char *crypt_passwd; 432 int histsize; 433 char *last = attr.data.val_s; 434 435 if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 436 debug("files_checkhistory: no history requested"); 437 res = PWU_NOT_FOUND; 438 goto out; 439 } 440 441 debug("files_checkhistory: histsize = %d", histsize); 442 if (histsize > MAXHISTORY) 443 histsize = MAXHISTORY; 444 445 debug("line to test\n\t%s", last); 446 447 /* compare crypt_passwd to attr.data.val_s strings. */ 448 res = PWU_NOT_FOUND; 449 while ((histsize-- > 0) && 450 (((s = strtok_r(NULL, ":", &last)) != NULL) && 451 (*s != '\n'))) { 452 453 crypt_passwd = crypt(passwd, s); 454 debug("files_checkhistory: user_pw=%s, history_pw=%s", 455 crypt_passwd, s); 456 if (strcmp(crypt_passwd, s) == 0) { 457 res = PWU_SUCCESS; 458 break; 459 } 460 } 461 debug("files_checkhistory(%s, %s) = %d", user, crypt_passwd, 462 res); 463 } 464 out: 465 if (attr.data.val_s != NULL) 466 free(attr.data.val_s); 467 468 return (res); 469 } 470 471 /* 472 * files_getattr(name, items, rep) 473 * 474 * Get attributes specified in list 'items' 475 */ 476 int 477 files_getattr(const char *name, attrlist *items, pwu_repository_t *rep) 478 { 479 struct pwbuf *pwbuf; 480 struct passwd *pw; 481 struct spwd *spw; 482 attrlist *w; 483 int res; 484 485 res = files_getpwnam(name, items, rep, (void **)&pwbuf); 486 if (res != PWU_SUCCESS) 487 return (res); 488 489 pw = pwbuf->pwd; 490 spw = pwbuf->spwd; 491 492 for (w = items; res == PWU_SUCCESS && w != NULL; w = w->next) { 493 switch (w->type) { 494 case ATTR_NAME: 495 if ((w->data.val_s = strdup(pw->pw_name)) == NULL) 496 res = PWU_NOMEM; 497 break; 498 case ATTR_COMMENT: 499 if ((w->data.val_s = strdup(pw->pw_comment)) == NULL) 500 res = PWU_NOMEM; 501 break; 502 case ATTR_GECOS: 503 if ((w->data.val_s = strdup(pw->pw_gecos)) == NULL) 504 res = PWU_NOMEM; 505 break; 506 case ATTR_HOMEDIR: 507 if ((w->data.val_s = strdup(pw->pw_dir)) == NULL) 508 res = PWU_NOMEM; 509 break; 510 case ATTR_SHELL: 511 if ((w->data.val_s = strdup(pw->pw_shell)) == NULL) 512 res = PWU_NOMEM; 513 break; 514 /* 515 * Nothing special needs to be done for 516 * server policy 517 */ 518 case ATTR_PASSWD: 519 case ATTR_PASSWD_SERVER_POLICY: 520 if ((w->data.val_s = strdup(spw->sp_pwdp)) == NULL) 521 res = PWU_NOMEM; 522 break; 523 case ATTR_AGE: 524 if ((w->data.val_s = strdup(pw->pw_age)) == NULL) 525 res = PWU_NOMEM; 526 break; 527 case ATTR_REP_NAME: 528 if ((w->data.val_s = strdup("files")) == NULL) 529 res = PWU_NOMEM; 530 break; 531 case ATTR_HISTORY: { 532 FILE *history; 533 char buf[MAX_LOGNAME + MAXHISTORY + 534 (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 535 char *s, *s1; 536 537 debug("files_getattr: Get password history for %s ", 538 name); 539 540 if ((history = fopen(HISTORY, "rF")) == NULL) { 541 debug("files_getattr: %s not found", HISTORY); 542 res = PWU_OPEN_FAILED; 543 goto getattr_exit; 544 } 545 res = PWU_NOT_FOUND; 546 while ((s = fgets(buf, sizeof (buf), history)) != 547 NULL) { 548 s1 = strchr(s, ':'); 549 if (s1 != NULL) { 550 *s1 = '\0'; 551 } else { 552 res = PWU_NOT_FOUND; 553 break; 554 } 555 #ifdef DEBUG 556 debug("got history line for %s", s); 557 #endif /* DEBUG */ 558 if (strcmp(s, name) == 0) { 559 /* found user */ 560 if ((items->data.val_s = 561 strdup(s1+1)) == NULL) 562 res = PWU_NOMEM; 563 else 564 res = PWU_SUCCESS; 565 break; 566 } 567 } 568 (void) fclose(history); 569 break; 570 } 571 572 /* integer values */ 573 case ATTR_UID: 574 w->data.val_i = pw->pw_uid; 575 break; 576 case ATTR_GID: 577 w->data.val_i = pw->pw_gid; 578 break; 579 case ATTR_LSTCHG: 580 w->data.val_i = spw->sp_lstchg; 581 break; 582 case ATTR_MIN: 583 w->data.val_i = spw->sp_min; 584 break; 585 case ATTR_MAX: 586 w->data.val_i = spw->sp_max; 587 break; 588 case ATTR_WARN: 589 w->data.val_i = spw->sp_warn; 590 break; 591 case ATTR_INACT: 592 w->data.val_i = spw->sp_inact; 593 break; 594 case ATTR_EXPIRE: 595 w->data.val_i = spw->sp_expire; 596 break; 597 case ATTR_FLAG: 598 w->data.val_i = spw->sp_flag; 599 break; 600 case ATTR_FAILED_LOGINS: 601 w->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 602 break; 603 default: 604 break; 605 } 606 } 607 608 getattr_exit: 609 if (pwbuf->pwd) free(pwbuf->pwd); 610 if (pwbuf->pwd_scratch) free(pwbuf->pwd_scratch); 611 if (pwbuf->spwd) free(pwbuf->spwd); 612 if (pwbuf->spwd_scratch) free(pwbuf->spwd_scratch); 613 free(pwbuf); 614 615 return (res); 616 } 617 618 /* 619 * max_present(list) 620 * 621 * see if attribute ATTR_MAX, with value != -1, is present in 622 * attribute-list "list". 623 * 624 * returns 1 if present, 0 otherwise. 625 */ 626 static int 627 max_present(attrlist *list) 628 { 629 while (list != NULL) 630 if (list->type == ATTR_MAX && list->data.val_i != -1) 631 return (1); 632 else 633 list = list->next; 634 635 return (0); 636 } 637 638 /* 639 * files_update(items, rep, buf) 640 * 641 * update the information in buf with the attributes specified in 642 * items. 643 */ 644 /*ARGSUSED*/ 645 int 646 files_update(attrlist *items, pwu_repository_t *rep, void *buf) 647 { 648 struct pwbuf *pwbuf = (struct pwbuf *)buf; 649 struct passwd *pw; 650 struct spwd *spw; 651 attrlist *p; 652 int aging_needed = 0; 653 int aging_set = 0; 654 int disable_aging; 655 char *pword; 656 int len; 657 658 pw = pwbuf->pwd; 659 spw = pwbuf->spwd; 660 pwbuf->update_history = 0; 661 662 /* 663 * if sp_max==0 : disable passwd aging after updating the password 664 */ 665 disable_aging = (spw != NULL && spw->sp_max == 0); 666 667 for (p = items; p != NULL; p = p->next) { 668 switch (p->type) { 669 case ATTR_NAME: 670 break; /* We are able to handle this, but... */ 671 case ATTR_UID: 672 pw->pw_uid = (uid_t)p->data.val_i; 673 break; 674 case ATTR_GID: 675 pw->pw_gid = (gid_t)p->data.val_i; 676 break; 677 case ATTR_AGE: 678 pw->pw_age = p->data.val_s; 679 break; 680 case ATTR_COMMENT: 681 pw->pw_comment = p->data.val_s; 682 break; 683 case ATTR_GECOS: 684 pw->pw_gecos = p->data.val_s; 685 break; 686 case ATTR_HOMEDIR: 687 pw->pw_dir = p->data.val_s; 688 break; 689 case ATTR_SHELL: 690 pw->pw_shell = p->data.val_s; 691 break; 692 693 /* 694 * Nothing special needs to be done for 695 * server policy 696 */ 697 case ATTR_PASSWD: 698 case ATTR_PASSWD_SERVER_POLICY: 699 /* 700 * There is a special case only for files: if the 701 * password is to be deleted (-d to passwd), 702 * p->data.val_s will be NULL. 703 */ 704 if (p->data.val_s == NULL) { 705 spw->sp_pwdp = ""; 706 } else { 707 char *salt = NULL; 708 char *hash = NULL; 709 710 salt = crypt_gensalt(spw->sp_pwdp, pw); 711 712 if (salt == NULL) { 713 if (errno == ENOMEM) 714 return (PWU_NOMEM); 715 /* algorithm problem? */ 716 syslog(LOG_AUTH | LOG_ALERT, 717 "passwdutil: crypt_gensalt %m"); 718 return (PWU_UPDATE_FAILED); 719 } 720 hash = crypt(p->data.val_s, salt); 721 free(salt); 722 if (hash == NULL) { 723 errno = ENOMEM; 724 return (PWU_NOMEM); 725 } 726 pword = strdup(hash); 727 if (pword == NULL) { 728 errno = ENOMEM; 729 return (PWU_NOMEM); 730 } 731 732 if (pwbuf->new_sp_pwdp) 733 free(pwbuf->new_sp_pwdp); 734 pwbuf->new_sp_pwdp = pword; 735 spw->sp_pwdp = pword; 736 aging_needed = 1; 737 pwbuf->update_history = 1; 738 } 739 spw->sp_flag &= ~FAILCOUNT_MASK; /* reset count */ 740 spw->sp_lstchg = DAY_NOW_32; 741 break; 742 case ATTR_LOCK_ACCOUNT: 743 if (spw->sp_pwdp == NULL) { 744 spw->sp_pwdp = LOCKSTRING; 745 } else if ((strncmp(spw->sp_pwdp, LOCKSTRING, 746 sizeof (LOCKSTRING)-1) != 0) && 747 (strcmp(spw->sp_pwdp, NOLOGINSTRING) != 0)) { 748 len = sizeof (LOCKSTRING)-1 + 749 strlen(spw->sp_pwdp) + 1; 750 pword = malloc(len); 751 if (pword == NULL) { 752 errno = ENOMEM; 753 return (PWU_NOMEM); 754 } 755 (void) strlcpy(pword, LOCKSTRING, len); 756 (void) strlcat(pword, spw->sp_pwdp, len); 757 if (pwbuf->new_sp_pwdp) 758 free(pwbuf->new_sp_pwdp); 759 pwbuf->new_sp_pwdp = pword; 760 spw->sp_pwdp = pword; 761 } 762 spw->sp_lstchg = DAY_NOW_32; 763 break; 764 case ATTR_UNLOCK_ACCOUNT: 765 if (spw->sp_pwdp != NULL && 766 strncmp(spw->sp_pwdp, LOCKSTRING, 767 sizeof (LOCKSTRING)-1) == 0) { 768 (void) strcpy(spw->sp_pwdp, spw->sp_pwdp + 769 sizeof (LOCKSTRING)-1); 770 } 771 spw->sp_lstchg = DAY_NOW_32; 772 break; 773 case ATTR_NOLOGIN_ACCOUNT: 774 spw->sp_pwdp = NOLOGINSTRING; 775 if (pwbuf->new_sp_pwdp) { 776 free(pwbuf->new_sp_pwdp); 777 pwbuf->new_sp_pwdp = NULL; 778 } 779 spw->sp_lstchg = DAY_NOW_32; 780 break; 781 case ATTR_EXPIRE_PASSWORD: 782 spw->sp_lstchg = 0; 783 break; 784 case ATTR_LSTCHG: 785 spw->sp_lstchg = p->data.val_i; 786 break; 787 case ATTR_MIN: 788 if (spw->sp_max == -1 && 789 p->data.val_i != -1 && max_present(p->next) == 0) 790 return (PWU_AGING_DISABLED); 791 spw->sp_min = p->data.val_i; 792 aging_set = 1; 793 break; 794 case ATTR_MAX: 795 if (p->data.val_i == -1) { 796 /* Turn aging off -> Reset min and warn too */ 797 798 spw->sp_min = -1; 799 spw->sp_warn = -1; 800 } else { 801 /* Turn aging on */ 802 803 if (spw->sp_min == -1) { 804 /* 805 * If minage has not been set with 806 * a command-line option, we set it 807 * to zero. 808 */ 809 spw->sp_min = 0; 810 } 811 812 /* 813 * If aging was turned off, we update lstchg. 814 * 815 * We take care not to update lstchg if the 816 * user has no password, otherwise the user 817 * might not be required to provide a password 818 * the next time they log-in. 819 * 820 * Also, if lstchg != -1 (i.e., not set in 821 * /etc/shadow), we keep the old value. 822 */ 823 if (spw->sp_max == -1 && 824 spw->sp_pwdp != NULL && *spw->sp_pwdp && 825 spw->sp_lstchg == -1) { 826 spw->sp_lstchg = DAY_NOW_32; 827 } 828 } 829 830 spw->sp_max = p->data.val_i; 831 832 aging_set = 1; 833 834 break; 835 case ATTR_WARN: 836 if (spw->sp_max == -1 && p->data.val_i != -1 && 837 max_present(p->next) == 0) 838 return (PWU_AGING_DISABLED); 839 spw->sp_warn = p->data.val_i; 840 break; 841 case ATTR_INACT: 842 spw->sp_inact = p->data.val_i; 843 break; 844 case ATTR_EXPIRE: 845 spw->sp_expire = p->data.val_i; 846 break; 847 case ATTR_FLAG: 848 spw->sp_flag = p->data.val_i; 849 break; 850 case ATTR_INCR_FAILED_LOGINS: 851 { 852 int count = (spw->sp_flag & FAILCOUNT_MASK) + 1; 853 spw->sp_flag &= ~FAILCOUNT_MASK; 854 spw->sp_flag |= min(FAILCOUNT_MASK, count); 855 p->data.val_i = count; 856 } 857 break; 858 case ATTR_RST_FAILED_LOGINS: 859 p->data.val_i = spw->sp_flag & FAILCOUNT_MASK; 860 spw->sp_flag &= ~FAILCOUNT_MASK; 861 break; 862 default: 863 break; 864 } 865 } 866 867 /* 868 * What should the new aging values look like? 869 * 870 * There are a number of different conditions 871 * 872 * a) aging is already configured: don't touch it 873 * 874 * b) disable_aging is set: disable aging 875 * 876 * c) aging is not configured: turn on default aging; 877 * 878 * b) and c) of course only if aging_needed and !aging_set. 879 * (i.e., password changed, and aging values not changed) 880 */ 881 882 if (spw != NULL && spw->sp_max <= 0) { 883 /* a) aging not yet configured */ 884 if (aging_needed && !aging_set) { 885 if (disable_aging) { 886 /* b) turn off aging */ 887 spw->sp_min = spw->sp_max = spw->sp_warn = -1; 888 } else { 889 /* c) */ 890 turn_on_default_aging(spw); 891 } 892 } 893 } 894 895 return (PWU_SUCCESS); 896 } 897 898 /* 899 * files_update_shadow(char *name, struct spwd *spwd) 900 * 901 * update the shadow password file SHADOW to contain the spwd structure 902 * "spwd" for user "name" 903 */ 904 int 905 files_update_shadow(const char *name, struct spwd *spwd) 906 { 907 struct stat64 stbuf; 908 FILE *dst; 909 FILE *src; 910 struct spwd cur; 911 char buf[SPW_SCRATCH_SIZE]; 912 int tempfd; 913 mode_t filemode; 914 int result = -1; 915 int err = PWU_SUCCESS; 916 917 /* Mode of the shadow file should be 400 or 000 */ 918 if (stat64(SHADOW, &stbuf) < 0) { 919 err = PWU_STAT_FAILED; 920 goto shadow_exit; 921 } 922 923 /* copy mode from current shadow file (0400 or 0000) */ 924 filemode = stbuf.st_mode & S_IRUSR; 925 926 /* 927 * we can't specify filemodes to fopen(), and we SHOULD NOT 928 * set umask in multi-thread safe libraries, so we use 929 * a combination of open() and fdopen() 930 */ 931 tempfd = open(SHADTEMP, O_WRONLY|O_CREAT|O_TRUNC, filemode); 932 if (tempfd < 0) { 933 err = PWU_OPEN_FAILED; 934 goto shadow_exit; 935 } 936 (void) fchown(tempfd, (uid_t)0, stbuf.st_gid); 937 938 if ((dst = fdopen(tempfd, "wF")) == NULL) { 939 err = PWU_OPEN_FAILED; 940 goto shadow_exit; 941 } 942 943 if ((src = fopen(SHADOW, "rF")) == NULL) { 944 err = PWU_OPEN_FAILED; 945 (void) fclose(dst); 946 (void) unlink(SHADTEMP); 947 goto shadow_exit; 948 } 949 950 /* 951 * copy old shadow to temporary file while replacing the entry 952 * that matches "name". 953 */ 954 while (fgetspent_r(src, &cur, buf, sizeof (buf)) != NULL) { 955 956 if (strcmp(cur.sp_namp, name) == 0) 957 result = putspent(spwd, dst); 958 else 959 result = putspent(&cur, dst); 960 961 if (result != 0) { 962 err = PWU_WRITE_FAILED; 963 (void) fclose(src); 964 (void) fclose(dst); 965 goto shadow_exit; 966 } 967 } 968 969 (void) fclose(src); 970 971 if (fclose(dst) != 0) { 972 /* 973 * Something went wrong (ENOSPC for example). Don't 974 * use the resulting temporary file! 975 */ 976 err = PWU_CLOSE_FAILED; 977 (void) unlink(SHADTEMP); 978 goto shadow_exit; 979 } 980 981 /* 982 * Rename stmp to shadow: 983 * 1. make sure /etc/oshadow is gone 984 * 2. ln /etc/shadow /etc/oshadow 985 * 3. mv /etc/stmp /etc/shadow 986 */ 987 if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) { 988 err = PWU_UPDATE_FAILED; 989 (void) unlink(SHADTEMP); 990 goto shadow_exit; 991 } 992 993 if (link(SHADOW, OSHADOW) == -1) { 994 err = PWU_UPDATE_FAILED; 995 (void) unlink(SHADTEMP); 996 goto shadow_exit; 997 } 998 999 if (rename(SHADTEMP, SHADOW) == -1) { 1000 err = PWU_UPDATE_FAILED; 1001 (void) unlink(SHADTEMP); 1002 goto shadow_exit; 1003 } 1004 (void) unlink(OSHADOW); 1005 1006 shadow_exit: 1007 return (err); 1008 } 1009 1010 int 1011 files_update_passwd(const char *name, struct passwd *pwd) 1012 { 1013 struct stat64 stbuf; 1014 FILE *src, *dst; 1015 int tempfd; 1016 struct passwd cur; 1017 char buf[PWD_SCRATCH_SIZE]; 1018 int result; 1019 int err = PWU_SUCCESS; 1020 1021 if (stat64(PASSWD, &stbuf) < 0) { 1022 err = PWU_STAT_FAILED; 1023 goto passwd_exit; 1024 } 1025 1026 /* see files_update_shadow() for open()+fdopen() rationale */ 1027 1028 if ((tempfd = open(PASSTEMP, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { 1029 err = PWU_OPEN_FAILED; 1030 goto passwd_exit; 1031 } 1032 if ((dst = fdopen(tempfd, "wF")) == NULL) { 1033 err = PWU_OPEN_FAILED; 1034 goto passwd_exit; 1035 } 1036 if ((src = fopen(PASSWD, "rF")) == NULL) { 1037 err = PWU_OPEN_FAILED; 1038 (void) fclose(dst); 1039 (void) unlink(PASSTEMP); 1040 goto passwd_exit; 1041 } 1042 1043 /* 1044 * copy old password entries to temporary file while replacing 1045 * the entry that matches "name" 1046 */ 1047 while (fgetpwent_r(src, &cur, buf, sizeof (buf)) != NULL) { 1048 if (strcmp(cur.pw_name, name) == 0) 1049 result = putpwent(pwd, dst); 1050 else 1051 result = putpwent(&cur, dst); 1052 if (result != 0) { 1053 err = PWU_WRITE_FAILED; 1054 (void) fclose(src); 1055 (void) fclose(dst); 1056 goto passwd_exit; 1057 } 1058 } 1059 1060 (void) fclose(src); 1061 if (fclose(dst) != 0) { 1062 err = PWU_CLOSE_FAILED; 1063 goto passwd_exit; /* Don't trust the temporary file */ 1064 } 1065 1066 /* Rename temp to passwd */ 1067 if (unlink(OPASSWD) && access(OPASSWD, 0) == 0) { 1068 err = PWU_UPDATE_FAILED; 1069 (void) unlink(PASSTEMP); 1070 goto passwd_exit; 1071 } 1072 1073 if (link(PASSWD, OPASSWD) == -1) { 1074 err = PWU_UPDATE_FAILED; 1075 (void) unlink(PASSTEMP); 1076 goto passwd_exit; 1077 } 1078 1079 if (rename(PASSTEMP, PASSWD) == -1) { 1080 err = PWU_UPDATE_FAILED; 1081 (void) unlink(PASSTEMP); 1082 goto passwd_exit; 1083 } 1084 1085 (void) chmod(PASSWD, 0644); 1086 1087 passwd_exit: 1088 return (err); 1089 1090 } 1091 1092 /* 1093 * files_putpwnam(name, oldpw, rep, buf) 1094 * 1095 * store the password attributes contained in "buf" in /etc/passwd and 1096 * /etc/shadow. 1097 */ 1098 /*ARGSUSED*/ 1099 int 1100 files_putpwnam(const char *name, const char *oldpw, pwu_repository_t *rep, 1101 void *buf) 1102 { 1103 struct pwbuf *pwbuf = (struct pwbuf *)buf; 1104 int result = PWU_SUCCESS; 1105 1106 if (pwbuf->pwd) { 1107 result = files_update_passwd(name, pwbuf->pwd); 1108 } 1109 1110 if (result == PWU_SUCCESS && pwbuf->spwd) { 1111 if (pwbuf->update_history != 0) { 1112 debug("update_history = %d", pwbuf->update_history); 1113 result = files_update_history(name, pwbuf->spwd); 1114 } else { 1115 debug("no password change"); 1116 } 1117 if (result == PWU_SUCCESS) { 1118 result = files_update_shadow(name, pwbuf->spwd); 1119 } 1120 } 1121 1122 if (pwbuf->pwd) { 1123 (void) memset(pwbuf->pwd, 0, sizeof (struct passwd)); 1124 (void) memset(pwbuf->pwd_scratch, 0, PWD_SCRATCH_SIZE); 1125 free(pwbuf->pwd); 1126 free(pwbuf->pwd_scratch); 1127 } 1128 if (pwbuf->spwd) { 1129 (void) memset(pwbuf->spwd, 0, sizeof (struct spwd)); 1130 (void) memset(pwbuf->spwd_scratch, 0, SPW_SCRATCH_SIZE); 1131 free(pwbuf->spwd); 1132 free(pwbuf->spwd_scratch); 1133 } 1134 if (pwbuf->new_sp_pwdp) { 1135 free(pwbuf->new_sp_pwdp); 1136 } 1137 1138 return (result); 1139 } 1140 1141 /* 1142 * NOTE: This is all covered under the repository lock held for updating 1143 * passwd(5) and shadow(5). 1144 */ 1145 int 1146 files_update_history(const char *name, struct spwd *spwd) 1147 { 1148 int histsize; 1149 int tmpfd; 1150 FILE *src; /* history database file */ 1151 FILE *dst; /* temp history database being updated */ 1152 struct stat64 statbuf; 1153 char buf[MAX_LOGNAME + MAXHISTORY + 1154 (MAXHISTORY * CRYPT_MAXCIPHERTEXTLEN)+1]; 1155 int found; 1156 1157 if ((histsize = def_getint("HISTORY=", DEFHISTORY)) == 0) { 1158 debug("files_update_history(%s) no history, unlinking", name); 1159 (void) unlink(HISTORY); 1160 return (PWU_SUCCESS); /* no history update defined */ 1161 } 1162 debug("files_update_history(%s, %s) histsize = %d", name, spwd->sp_pwdp, 1163 histsize); 1164 1165 if (histsize > MAXHISTORY) 1166 histsize = MAXHISTORY; 1167 if ((tmpfd = open(HISTEMP, O_WRONLY|O_CREAT|O_TRUNC, HISTMODE)) < 0) { 1168 return (PWU_OPEN_FAILED); 1169 } 1170 (void) fchown(tmpfd, (uid_t)0, (gid_t)0); 1171 1172 /* get ready to copy */ 1173 if (((src = fopen(HISTORY, "rF")) == NULL) && 1174 (errno != ENOENT)) { 1175 (void) unlink(HISTEMP); 1176 return (PWU_OPEN_FAILED); 1177 } 1178 if ((dst = fdopen(tmpfd, "wF")) == NULL) { 1179 (void) fclose(src); 1180 (void) unlink(HISTEMP); 1181 return (PWU_OPEN_FAILED); 1182 } 1183 1184 /* Copy and update if found. Add if not found. */ 1185 1186 found = 0; 1187 1188 while ((src != NULL) && 1189 (fgets(buf, sizeof (buf), src) != NULL)) { 1190 char *user; 1191 char *last; 1192 1193 /* get username field */ 1194 user = strtok_r(buf, ":", &last); 1195 1196 #ifdef DEBUG 1197 debug("files_update_history: read=\"%s\"", user); 1198 #endif /* DEBUG */ 1199 1200 if (strcmp(user, name) == 0) { 1201 char *crypt; 1202 int i; 1203 1204 /* found user, update */ 1205 found++; 1206 (void) fprintf(dst, "%s:%s:", name, spwd->sp_pwdp); 1207 debug("files_update_history: update user\n" 1208 "\t%s:%s:", name, spwd->sp_pwdp); 1209 1210 /* get old crypted password history */ 1211 for (i = 0; i < MAXHISTORY-1; i++) { 1212 crypt = strtok_r(NULL, ":", &last); 1213 if (crypt == NULL || 1214 *crypt == '\n') { 1215 break; 1216 } 1217 (void) fprintf(dst, "%s:", crypt); 1218 debug("\t%d = %s:", i+1, crypt); 1219 } 1220 (void) fprintf(dst, "\n"); 1221 } else { 1222 1223 /* copy other users to updated file */ 1224 (void) fprintf(dst, "%s:%s", user, last); 1225 #ifdef DEBUG 1226 debug("files_update_history: copy line %s", 1227 user); 1228 #endif /* DEBUG */ 1229 } 1230 } 1231 1232 if (found == 0) { 1233 1234 /* user not found, add to history file */ 1235 (void) fprintf(dst, "%s:%s:\n", name, spwd->sp_pwdp); 1236 debug("files_update_history: add line\n" 1237 "\t%s:%s:", name, spwd->sp_pwdp); 1238 } 1239 1240 (void) fclose(src); 1241 1242 /* If something messed up in file system, loose the update */ 1243 if (fclose(dst) != 0) { 1244 1245 debug("files_update_history: update file close failed %d", 1246 errno); 1247 (void) unlink(HISTEMP); 1248 return (PWU_CLOSE_FAILED); 1249 } 1250 1251 /* 1252 * rename history to ohistory, 1253 * rename tmp to history, 1254 * unlink ohistory. 1255 */ 1256 1257 (void) unlink(OHISTORY); 1258 1259 if (stat64(OHISTORY, &statbuf) == 0 || 1260 ((src != NULL) && (link(HISTORY, OHISTORY) != 0)) || 1261 rename(HISTEMP, HISTORY) != 0) { 1262 1263 /* old history won't go away, loose the update */ 1264 debug("files_update_history: update file rename failed %d", 1265 errno); 1266 (void) unlink(HISTEMP); 1267 return (PWU_UPDATE_FAILED); 1268 } 1269 1270 (void) unlink(OHISTORY); 1271 return (PWU_SUCCESS); 1272 } 1273