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