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