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