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