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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association. 26 */ 27 28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 32 /* All Rights Reserved */ 33 34 /* 35 * passwd is a program whose sole purpose is to manage 36 * the password file, map, or table. It allows system administrator 37 * to add, change and display password attributes. 38 * Non privileged user can change password or display 39 * password attributes which corresponds to their login name. 40 */ 41 42 #include <stdio.h> 43 #include <pwd.h> 44 #include <sys/types.h> 45 #include <errno.h> 46 #include <unistd.h> 47 #include <stdlib.h> 48 #include <locale.h> 49 #include <stdarg.h> 50 #include <errno.h> 51 #include <string.h> 52 #include <security/pam_appl.h> 53 #include <security/pam_modules.h> 54 #include <security/pam_impl.h> 55 #include <rpcsvc/nis.h> 56 #undef GROUP 57 #include <syslog.h> 58 #include <userdefs.h> 59 #include <passwdutil.h> 60 61 #include <nss_dbdefs.h> 62 63 #include <deflt.h> 64 65 #undef GROUP 66 #include <bsm/adt.h> 67 #include <bsm/adt_event.h> 68 69 /* 70 * flags indicate password attributes to be modified 71 */ 72 73 #define LFLAG 0x001 /* lock user's password */ 74 #define DFLAG 0x002 /* delete user's password */ 75 #define MFLAG 0x004 /* set max field -- # of days passwd is valid */ 76 #define NFLAG 0x008 /* set min field -- # of days between */ 77 /* password changes */ 78 #define SFLAG 0x010 /* display password attributes */ 79 #define FFLAG 0x020 /* expire user's password */ 80 #define AFLAG 0x040 /* display password attributes for all users */ 81 #define SAFLAG (SFLAG|AFLAG) /* display password attributes for all users */ 82 #define WFLAG 0x100 /* warn user to change passwd */ 83 #define OFLAG 0x200 /* domain name */ 84 #define EFLAG 0x400 /* change shell */ 85 #define GFLAG 0x800 /* change gecos information */ 86 #define HFLAG 0x1000 /* change home directory */ 87 #define XFLAG 0x2000 /* no login */ 88 #define UFLAG 0x4000 /* unlock user's password */ 89 90 #define NONAGEFLAG (EFLAG | GFLAG | HFLAG) 91 #define AGEFLAG (LFLAG | FFLAG | MFLAG | NFLAG | WFLAG | XFLAG | UFLAG) 92 #define MUTEXFLAG (DFLAG | LFLAG | XFLAG | UFLAG | SAFLAG) 93 94 95 /* 96 * exit code 97 */ 98 99 #define SUCCESS 0 /* succeeded */ 100 #define NOPERM 1 /* No permission */ 101 #define BADOPT 2 /* Invalid combination of option */ 102 #define FMERR 3 /* File/table manipulation error */ 103 #define FATAL 4 /* Old file/table can not be recovered */ 104 #define FBUSY 5 /* Lock file/table busy */ 105 #define BADSYN 6 /* Incorrect syntax */ 106 #define BADAGE 7 /* Aging is disabled */ 107 #define NOMEM 8 /* No memory */ 108 #define SYSERR 9 /* System error */ 109 #define EXPIRED 10 /* Account expired */ 110 111 /* 112 * define error messages 113 */ 114 #define MSG_NP "Permission denied" 115 #define MSG_BS "Invalid combination of options" 116 #define MSG_FE "Unexpected failure. Password file/table unchanged." 117 #define MSG_FF "Unexpected failure. Password file/table missing." 118 #define MSG_FB "Password file/table busy. Try again later." 119 #define MSG_NV "Invalid argument to option" 120 #define MSG_AD "Password aging is disabled" 121 #define MSG_RS "Cannot change from restricted shell %s\n" 122 #define MSG_NM "Out of memory." 123 #define MSG_UNACCEPT "%s is unacceptable as a new shell\n" 124 #define MSG_UNAVAIL "warning: %s is unavailable on this machine\n" 125 #define MSG_COLON "':' is not allowed.\n" 126 #define MSG_MAXLEN "Maximum number of characters allowed is %d." 127 #define MSG_CONTROL "Control characters are not allowed.\n" 128 #define MSG_SHELL_UNCHANGED "Login shell unchanged.\n" 129 #define MSG_GECOS_UNCHANGED "Finger information unchanged.\n" 130 #define MSG_DIR_UNCHANGED "Homedir information unchanged.\n" 131 #define MSG_NAME "\nName [%s]: " 132 #define MSG_HOMEDIR "\nHome Directory [%s]: " 133 #define MSG_OLDSHELL "Old shell: %s\n" 134 #define MSG_NEWSHELL "New shell: " 135 #define MSG_AGAIN "\nPlease try again\n" 136 #define MSG_INPUTHDR "Default values are printed inside of '[]'.\n" \ 137 "To accept the default, type <return>.\n" \ 138 "To have a blank entry, type the word 'none'.\n" 139 #define MSG_UNKNOWN "%s: User unknown: %s\n" 140 #define MSG_ACCOUNT_EXP "User account has expired: %s\n" 141 #define MSG_AUTHTOK_EXP "Your password has been expired for too long.\n" \ 142 "Please contact the system administrator.\n" 143 #define MSG_NIS_HOMEDIR "-h does not apply to NIS" 144 #define MSG_CUR_PASS "Enter existing login password: " 145 #define MSG_CUR_PASS_UNAME "Enter %s's existing login password: " 146 #define MSG_SUCCESS "%s: password information changed for %s\n" 147 #define MSG_SORRY "%s: Sorry, wrong passwd\n" 148 #define MSG_INFO "%s: Changing password for %s\n" 149 150 151 /* 152 * return code from ckarg() routine 153 */ 154 #define FAIL -1 155 156 /* 157 * defind password file name 158 */ 159 #define PASSWD "/etc/passwd" 160 161 #define MAX_INPUT_LEN 512 162 163 #define DEF_ATTEMPTS 3 164 165 /* Number of characters in that make up an encrypted password (for now) */ 166 #define NUMCP 13 167 168 #ifdef DEBUG 169 #define dprintf1 printf 170 #else 171 #define dprintf1(w, x) 172 #endif 173 174 extern int optind; 175 176 static int retval = SUCCESS; 177 static int pam_retval = PAM_SUCCESS; 178 static uid_t uid; 179 static char *prognamep; 180 static long maxdate; /* password aging information */ 181 static int passwd_conv(int, const struct pam_message **, 182 struct pam_response **, void *); 183 static struct pam_conv pam_conv = {passwd_conv, NULL}; 184 static pam_handle_t *pamh; /* Authentication handle */ 185 static char *usrname; /* user whose attribute we update */ 186 static adt_session_data_t *ah; /* audit session handle */ 187 static adt_event_data_t *event = NULL; /* event to be generated */ 188 189 static pam_repository_t auth_rep; 190 static pwu_repository_t repository; 191 static pwu_repository_t __REPFILES = { "files", NULL, 0 }; 192 193 /* 194 * Function Declarations 195 */ 196 197 extern void setusershell(void); 198 extern char *getusershell(void); 199 extern void endusershell(void); 200 201 static void passwd_exit(int retcode) __NORETURN; 202 static void rusage(void); 203 static int ckuid(void); 204 static int ckarg(int argc, char **argv, attrlist **attributes); 205 206 static int get_namelist(pwu_repository_t, char ***, int *); 207 static int get_namelist_files(char ***, int *); 208 static int get_namelist_local(char ***, int *); 209 static int get_attr(char *, pwu_repository_t *, attrlist **); 210 static void display_attr(char *, attrlist *); 211 static void free_attr(attrlist *); 212 static void attrlist_add(attrlist **, attrtype, char *); 213 static void attrlist_reorder(attrlist **); 214 static char *userinput(char *, pwu_repository_t *, attrtype); 215 static char *getresponse(char *); 216 217 /* 218 * main(): 219 * The main routine will call ckarg() to parse the command line 220 * arguments and call the appropriate functions to perform the 221 * tasks specified by the arguments. It allows system 222 * administrator to add, change and display password attributes. 223 * Non privileged user can change password or display 224 * password attributes which corresponds to their login name. 225 */ 226 227 int 228 main(int argc, char *argv[]) 229 { 230 231 int flag; 232 char **namelist; 233 int num_user; 234 int i; 235 attrlist *attributes = NULL; 236 char *input; 237 int tries = 1; 238 int updated_reps; 239 240 241 if ((prognamep = strrchr(argv[0], '/')) != NULL) 242 ++prognamep; 243 else 244 prognamep = argv[0]; 245 246 auth_rep.type = NULL; 247 auth_rep.scope = NULL; 248 repository.type = NULL; 249 repository.scope = NULL; 250 repository.scope_len = 0; 251 252 253 /* initialization for variables, set locale and textdomain */ 254 i = 0; 255 flag = 0; 256 257 uid = getuid(); /* get the user id */ 258 (void) setlocale(LC_ALL, ""); 259 260 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 261 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 262 #endif 263 (void) textdomain(TEXT_DOMAIN); 264 265 /* 266 * ckarg() parses the arguments. In case of an error, 267 * it sets the retval and returns FAIL (-1). 268 */ 269 270 flag = ckarg(argc, argv, &attributes); 271 dprintf1("flag is %0x\n", flag); 272 if (flag == FAIL) 273 passwd_exit(retval); 274 275 argc -= optind; 276 277 if (argc < 1) { 278 if ((usrname = getlogin()) == NULL) { 279 struct passwd *pass = getpwuid(uid); 280 if (pass != NULL) 281 usrname = pass->pw_name; 282 else { 283 rusage(); 284 exit(NOPERM); 285 } 286 } else if (flag == 0) { 287 /* 288 * If flag is zero, change passwd. 289 * Otherwise, it will display or 290 * modify password aging attributes 291 */ 292 (void) fprintf(stderr, gettext(MSG_INFO), prognamep, 293 usrname); 294 } 295 } else { 296 usrname = argv[optind]; 297 } 298 299 if (pam_start("passwd", usrname, &pam_conv, &pamh) != PAM_SUCCESS) { 300 passwd_exit(NOPERM); 301 } 302 303 auth_rep.type = repository.type; 304 auth_rep.scope = repository.scope; 305 auth_rep.scope_len = repository.scope_len; 306 307 if (auth_rep.type != NULL) { 308 if (pam_set_item(pamh, PAM_REPOSITORY, (void *)&auth_rep) 309 != PAM_SUCCESS) { 310 passwd_exit(NOPERM); 311 } 312 } 313 314 if (flag == SAFLAG) { /* display password attributes for all users */ 315 retval = get_namelist(repository, &namelist, &num_user); 316 if (retval != SUCCESS) 317 (void) passwd_exit(retval); 318 319 if (num_user == 0) { 320 (void) fprintf(stderr, "%s: %s\n", prognamep, 321 gettext(MSG_FF)); 322 passwd_exit(FATAL); 323 } 324 i = 0; 325 while (namelist[i] != NULL) { 326 (void) get_attr(namelist[i], &repository, 327 &attributes); 328 (void) display_attr(namelist[i], attributes); 329 (void) free(namelist[i]); 330 (void) free_attr(attributes); 331 i++; 332 } 333 (void) free(namelist); 334 passwd_exit(SUCCESS); 335 } else if (flag == SFLAG) { /* display password attributes by user */ 336 if (get_attr(usrname, &repository, &attributes) == 337 PWU_SUCCESS) { 338 (void) display_attr(usrname, attributes); 339 (void) free_attr(attributes); 340 } 341 passwd_exit(SUCCESS); 342 /* NOT REACHED */ 343 } 344 345 346 switch (pam_authenticate(pamh, 0)) { 347 case PAM_SUCCESS: 348 break; 349 case PAM_USER_UNKNOWN: 350 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep, 351 usrname); 352 passwd_exit(NOPERM); 353 break; 354 case PAM_PERM_DENIED: 355 passwd_exit(NOPERM); 356 break; 357 case PAM_AUTH_ERR: 358 (void) fprintf(stderr, gettext(MSG_SORRY), prognamep); 359 passwd_exit(NOPERM); 360 break; 361 default: 362 /* system error */ 363 passwd_exit(FMERR); 364 break; 365 } 366 367 if (flag == 0) { /* changing user password */ 368 int chk_authtok = 0; /* check password strength */ 369 370 dprintf1("call pam_chauthtok() repository name =%s\n", 371 repository.type); 372 373 /* Set up for Audit */ 374 if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) { 375 perror("adt_start_session"); 376 passwd_exit(SYSERR); 377 } 378 if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) { 379 perror("adt_alloc_event"); 380 passwd_exit(NOMEM); 381 } 382 383 /* Don't check account expiration when invoked by root */ 384 if (ckuid() != SUCCESS) { 385 pam_retval = pam_acct_mgmt(pamh, PAM_SILENT); 386 switch (pam_retval) { 387 case PAM_ACCT_EXPIRED: 388 (void) fprintf(stderr, 389 gettext(MSG_ACCOUNT_EXP), usrname); 390 passwd_exit(EXPIRED); 391 break; 392 case PAM_AUTHTOK_EXPIRED: 393 (void) fprintf(stderr, 394 gettext(MSG_AUTHTOK_EXP)); 395 passwd_exit(NOPERM); 396 break; 397 case PAM_NEW_AUTHTOK_REQD: 398 /* valid error when changing passwords */ 399 break; 400 case PAM_SUCCESS: 401 /* Ok to change password */ 402 break; 403 default: 404 passwd_exit(NOPERM); 405 } 406 } 407 408 409 pam_retval = PAM_AUTHTOK_ERR; 410 tries = 1; 411 if (ckuid() == SUCCESS) { 412 /* bypass password strength checks */ 413 chk_authtok = PAM_NO_AUTHTOK_CHECK; 414 } 415 416 while (pam_retval == PAM_AUTHTOK_ERR && tries <= DEF_ATTEMPTS) { 417 if (tries > 1) 418 (void) printf(gettext(MSG_AGAIN)); 419 pam_retval = pam_chauthtok(pamh, chk_authtok); 420 if (pam_retval == PAM_TRY_AGAIN) { 421 (void) sleep(1); 422 pam_retval = pam_chauthtok(pamh, chk_authtok); 423 } 424 tries++; 425 } 426 427 switch (pam_retval) { 428 case PAM_SUCCESS: 429 retval = SUCCESS; 430 break; 431 case PAM_AUTHTOK_DISABLE_AGING: 432 retval = BADAGE; 433 break; 434 case PAM_AUTHTOK_LOCK_BUSY: 435 retval = FBUSY; 436 break; 437 case PAM_TRY_AGAIN: 438 retval = FBUSY; 439 break; 440 case PAM_AUTHTOK_ERR: 441 case PAM_AUTHTOK_RECOVERY_ERR: 442 default: 443 retval = NOPERM; 444 break; 445 } 446 447 (void) passwd_exit(retval); 448 /* NOT REACHED */ 449 } else { /* changing attributes */ 450 switch (flag) { 451 case EFLAG: /* changing user password attributes */ 452 input = userinput(usrname, &repository, ATTR_SHELL); 453 if (input) 454 attrlist_add(&attributes, ATTR_SHELL, input); 455 else 456 (void) printf(gettext(MSG_SHELL_UNCHANGED)); 457 break; 458 case GFLAG: 459 input = userinput(usrname, &repository, ATTR_GECOS); 460 if (input) 461 attrlist_add(&attributes, ATTR_GECOS, input); 462 else 463 (void) printf(gettext(MSG_GECOS_UNCHANGED)); 464 break; 465 case HFLAG: 466 input = userinput(usrname, &repository, ATTR_HOMEDIR); 467 if (input) 468 attrlist_add(&attributes, ATTR_HOMEDIR, input); 469 else 470 (void) printf(gettext(MSG_DIR_UNCHANGED)); 471 break; 472 } 473 474 if (attributes != NULL) { 475 retval = __set_authtoken_attr(usrname, 476 pamh->ps_item[PAM_AUTHTOK].pi_addr, 477 &repository, attributes, &updated_reps); 478 switch (retval) { 479 case PWU_SUCCESS: 480 for (i = 1; i <= REP_LAST; i <<= 1) { 481 if ((updated_reps & i) == 0) 482 continue; 483 (void) printf(gettext(MSG_SUCCESS), 484 prognamep, usrname); 485 } 486 retval = SUCCESS; 487 break; 488 case PWU_AGING_DISABLED: 489 retval = BADAGE; 490 break; 491 default: 492 retval = NOPERM; 493 break; 494 } 495 } else { 496 retval = SUCCESS; /* nothing to change won't fail */ 497 } 498 (void) passwd_exit(retval); 499 } 500 /* NOTREACHED */ 501 return (0); 502 } 503 504 /* 505 * Get a line of input from the user. 506 * 507 * If the line is empty, or the input equals 'oldval', NULL is returned. 508 * therwise, a malloced string containing the input (minus the trailing 509 * newline) is returned. 510 */ 511 char * 512 getresponse(char *oldval) 513 { 514 char resp[MAX_INPUT_LEN]; 515 char *retval = NULL; 516 int resplen; 517 518 (void) fgets(resp, sizeof (resp) - 1, stdin); 519 resplen = strlen(resp) - 1; 520 if (resp[resplen] == '\n') 521 resp[resplen] = '\0'; 522 if (*resp != '\0' && strcmp(resp, oldval) != 0) 523 retval = strdup(resp); 524 return (retval); 525 } 526 527 /* 528 * char *userinput(item) 529 * 530 * user conversation function. The old value of attribute "item" is 531 * displayed while the user is asked to provide a new value. 532 * 533 * returns a malloc()-ed string if the user actualy provided input 534 * or NULL if the user simply hit return or the input equals the old 535 * value (not changed). 536 */ 537 char * 538 userinput(char *name, pwu_repository_t *rep, attrtype type) 539 { 540 attrlist oldattr; 541 char *oldval; /* shorthand for oldattr.data.val_s */ 542 char *valid; /* points to valid shells */ 543 char *response; 544 char *cp; 545 546 oldattr.type = type; 547 oldattr.next = NULL; 548 549 if (__get_authtoken_attr(name, rep, &oldattr) != PWU_SUCCESS) 550 passwd_exit(FMERR); 551 552 oldval = oldattr.data.val_s; 553 554 if (type == ATTR_SHELL) { 555 /* No current shell: set DEFSHL as default choice */ 556 if (*oldval == '\0') { 557 free(oldval); 558 oldval = strdup(DEFSHL); 559 } 560 561 if (ckuid() != SUCCESS) { 562 /* User must currently have a valid shell */ 563 setusershell(); 564 valid = getusershell(); 565 while (valid && strcmp(valid, oldval) != 0) 566 valid = getusershell(); 567 endusershell(); 568 569 if (valid == NULL) { 570 (void) fprintf(stderr, gettext(MSG_RS), oldval); 571 free(oldval); 572 return (NULL); 573 } 574 } 575 (void) printf(gettext(MSG_OLDSHELL), oldval); 576 (void) printf(gettext(MSG_NEWSHELL)); 577 (void) fflush(stdout); 578 579 response = getresponse(oldval); 580 free(oldval); /* We don't need the old value anymore */ 581 582 if (response == NULL || *response == '\0') 583 return (NULL); 584 585 /* Make sure new shell is listed */ 586 setusershell(); 587 valid = getusershell(); 588 while (valid) { 589 char *cp; 590 591 /* Allow user to give shell without path */ 592 if (*response == '/') { 593 cp = valid; 594 } else { 595 if ((cp = strrchr(valid, '/')) == NULL) 596 cp = valid; 597 else 598 cp++; 599 } 600 if (strcmp(cp, response) == 0) { 601 if (*response != '/') { 602 /* take shell name including path */ 603 free(response); 604 response = strdup(valid); 605 } 606 break; 607 } 608 valid = getusershell(); 609 } 610 endusershell(); 611 612 if (valid == NULL) { /* No valid shell matches */ 613 (void) fprintf(stderr, gettext(MSG_UNACCEPT), response); 614 return (NULL); 615 } 616 617 if (access(response, X_OK) < 0) 618 (void) fprintf(stderr, gettext(MSG_UNAVAIL), response); 619 return (response); 620 /* NOT REACHED */ 621 } 622 /* 623 * if type == SHELL, we have returned by now. Only GECOS and 624 * HOMEDIR get to this point. 625 */ 626 (void) printf(gettext(MSG_INPUTHDR)); 627 628 /* 629 * PRE: oldval points to malloced string with Old Value 630 * INV: oldval remains unchanged 631 * POST:response points to valid string or NULL. 632 */ 633 for (;;) { 634 if (type == ATTR_GECOS) 635 (void) printf(gettext(MSG_NAME), oldval); 636 else if (type == ATTR_HOMEDIR) 637 (void) printf(gettext(MSG_HOMEDIR), oldval); 638 639 response = getresponse(oldval); 640 641 if (response && strcmp(response, "none") == 0) 642 *response = '\0'; 643 644 /* No-change or empty string are OK */ 645 if (response == NULL || *response == '\0') 646 break; 647 648 /* Check for illegal characters */ 649 if (strchr(response, ':')) { 650 (void) fprintf(stderr, "%s", gettext(MSG_COLON)); 651 free(response); 652 } else if (strlen(response) > MAX_INPUT_LEN - 1) { 653 (void) fprintf(stderr, gettext(MSG_MAXLEN), 654 MAX_INPUT_LEN); 655 free(response); 656 } else { 657 /* don't allow control characters */ 658 for (cp = response; *cp >= 040; cp++) 659 ; 660 if (*cp != '\0') { 661 (void) fprintf(stderr, gettext(MSG_CONTROL)); 662 free(response); 663 } else 664 break; /* response is a valid string */ 665 } 666 /* 667 * We only get here if the input was invalid. 668 * In that case, we again ask the user for input. 669 */ 670 } 671 free(oldval); 672 return (response); 673 } 674 /* 675 * ckarg(): 676 * This function parses and verifies the 677 * arguments. It takes three parameters: 678 * argc => # of arguments 679 * argv => pointer to an argument 680 * attrlist => pointer to list of password attributes 681 */ 682 683 static int 684 ckarg(int argc, char **argv, attrlist **attributes) 685 { 686 extern char *optarg; 687 char *char_p; 688 int opt; 689 int flag; 690 691 flag = 0; 692 693 while ((opt = getopt(argc, argv, "r:aldefghsux:n:w:N")) != EOF) { 694 switch (opt) { 695 696 case 'r': /* Repository Specified */ 697 /* repository: this option should be specified first */ 698 699 if (repository.type != NULL) { 700 (void) fprintf(stderr, gettext( 701 "Repository is already defined or specified.\n")); 702 rusage(); 703 retval = BADSYN; 704 return (FAIL); 705 } 706 if (strcmp(optarg, "nis") == 0) { 707 repository.type = optarg; 708 } else if (strcmp(optarg, "ldap") == 0) { 709 repository.type = optarg; 710 } else if (strcmp(optarg, "files") == 0) { 711 repository.type = optarg; 712 } else { 713 (void) fprintf(stderr, 714 gettext("invalid repository: %s\n"), 715 optarg); 716 rusage(); 717 retval = BADSYN; 718 return (FAIL); 719 } 720 break; 721 722 case 'd': /* Delete Auth Token */ 723 /* if no repository the default for -d is files */ 724 if (repository.type == NULL) 725 repository = __REPFILES; 726 727 /* 728 * Delete the password - only privileged processes 729 * can execute this for FILES or LDAP 730 */ 731 if (IS_FILES(repository) == FALSE && 732 IS_LDAP(repository) == FALSE) { 733 (void) fprintf(stderr, gettext( 734 "-d only applies to files " 735 "or ldap repository\n")); 736 rusage(); /* exit */ 737 retval = BADSYN; 738 return (FAIL); 739 } 740 741 if (ckuid() != SUCCESS) { 742 retval = NOPERM; 743 return (FAIL); 744 } 745 if (flag & (LFLAG|SAFLAG|DFLAG|XFLAG|UFLAG)) { 746 rusage(); 747 retval = BADOPT; 748 return (FAIL); 749 } 750 flag |= DFLAG; 751 attrlist_add(attributes, ATTR_PASSWD, NULL); 752 break; 753 754 case 'N': /* set account to be "no login" */ 755 756 /* if no repository the default for -N is files */ 757 if (repository.type == NULL) 758 repository = __REPFILES; 759 760 if (IS_FILES(repository) == FALSE && 761 IS_LDAP(repository) == FALSE) { 762 (void) fprintf(stderr, gettext( 763 "-N only applies to files or ldap " 764 "repository\n")); 765 rusage(); /* exit */ 766 retval = BADOPT; 767 return (FAIL); 768 } 769 770 /* 771 * Only privileged processes can execute this 772 * for FILES or LDAP 773 */ 774 if ((IS_FILES(repository) || IS_LDAP(repository)) && 775 ((retval = ckuid()) != SUCCESS)) 776 return (FAIL); 777 if (flag & (MUTEXFLAG|NONAGEFLAG)) { 778 rusage(); /* exit */ 779 retval = BADOPT; 780 return (FAIL); 781 } 782 flag |= XFLAG; 783 attrlist_add(attributes, ATTR_NOLOGIN_ACCOUNT, NULL); 784 break; 785 786 case 'l': /* lock the password */ 787 788 /* if no repository the default for -l is files */ 789 if (repository.type == NULL) 790 repository = __REPFILES; 791 792 if (IS_FILES(repository) == FALSE && 793 IS_LDAP(repository) == FALSE) { 794 (void) fprintf(stderr, gettext( 795 "-l only applies to files or ldap " 796 "repository\n")); 797 rusage(); /* exit */ 798 retval = BADOPT; 799 return (FAIL); 800 } 801 802 /* 803 * Only privileged processes can execute this 804 * for FILES or LDAP 805 */ 806 if ((IS_FILES(repository) || IS_LDAP(repository)) && 807 ((retval = ckuid()) != SUCCESS)) 808 return (FAIL); 809 if (flag & (MUTEXFLAG|NONAGEFLAG)) { 810 rusage(); /* exit */ 811 retval = BADOPT; 812 return (FAIL); 813 } 814 flag |= LFLAG; 815 attrlist_add(attributes, ATTR_LOCK_ACCOUNT, NULL); 816 break; 817 818 case 'u': /* unlock the password */ 819 820 /* if no repository the default for -u is files */ 821 if (repository.type == NULL) 822 repository = __REPFILES; 823 824 if (IS_FILES(repository) == FALSE && 825 IS_LDAP(repository) == FALSE) { 826 (void) fprintf(stderr, gettext( 827 "-u only applies to files or ldap " 828 "repository\n")); 829 rusage(); /* exit */ 830 retval = BADOPT; 831 return (FAIL); 832 } 833 834 /* 835 * Only privileged processes can execute this 836 * for FILES or LDAP 837 */ 838 if ((IS_FILES(repository) || IS_LDAP(repository)) && 839 ((retval = ckuid()) != SUCCESS)) 840 return (FAIL); 841 if (flag & (MUTEXFLAG|NONAGEFLAG)) { 842 rusage(); /* exit */ 843 retval = BADOPT; 844 return (FAIL); 845 } 846 flag |= UFLAG; 847 attrlist_add(attributes, ATTR_UNLOCK_ACCOUNT, NULL); 848 attrlist_add(attributes, ATTR_RST_FAILED_LOGINS, NULL); 849 break; 850 851 case 'x': /* set the max date */ 852 853 /* if no repository the default for -x is files */ 854 if (repository.type == NULL) 855 repository = __REPFILES; 856 857 if (IS_FILES(repository) == FALSE && 858 IS_LDAP(repository) == FALSE) { 859 (void) fprintf(stderr, gettext( 860 "-x only applies to files or ldap " 861 "repository\n")); 862 rusage(); /* exit */ 863 retval = BADSYN; 864 return (FAIL); 865 } 866 867 /* 868 * Only privileged process can execute this 869 * for FILES or LDAP 870 */ 871 if ((IS_FILES(repository) || IS_LDAP(repository)) && 872 (ckuid() != SUCCESS)) { 873 retval = NOPERM; 874 return (FAIL); 875 } 876 if (flag & (SAFLAG|MFLAG|NONAGEFLAG)) { 877 retval = BADOPT; 878 return (FAIL); 879 } 880 flag |= MFLAG; 881 if ((int)strlen(optarg) <= 0 || 882 (maxdate = strtol(optarg, &char_p, 10)) < -1 || 883 *char_p != '\0') { 884 (void) fprintf(stderr, "%s: %s -x\n", 885 prognamep, gettext(MSG_NV)); 886 retval = BADSYN; 887 return (FAIL); 888 } 889 attrlist_add(attributes, ATTR_MAX, optarg); 890 break; 891 892 case 'n': /* set the min date */ 893 894 /* if no repository the default for -n is files */ 895 if (repository.type == NULL) 896 repository = __REPFILES; 897 898 if (IS_FILES(repository) == FALSE && 899 IS_LDAP(repository) == FALSE) { 900 (void) fprintf(stderr, gettext( 901 "-n only applies to files or ldap " 902 "repository\n")); 903 rusage(); /* exit */ 904 retval = BADSYN; 905 return (FAIL); 906 } 907 908 /* 909 * Only privileged process can execute this 910 * for FILES or LDAP 911 */ 912 if ((IS_FILES(repository) || IS_LDAP(repository)) && 913 ((retval = ckuid()) != SUCCESS)) 914 return (FAIL); 915 if (flag & (SAFLAG|NFLAG|NONAGEFLAG)) { 916 retval = BADOPT; 917 return (FAIL); 918 } 919 flag |= NFLAG; 920 if ((int)strlen(optarg) <= 0 || 921 (strtol(optarg, &char_p, 10)) < 0 || 922 *char_p != '\0') { 923 (void) fprintf(stderr, "%s: %s -n\n", 924 prognamep, gettext(MSG_NV)); 925 retval = BADSYN; 926 return (FAIL); 927 } 928 attrlist_add(attributes, ATTR_MIN, optarg); 929 break; 930 931 case 'w': /* set the warning field */ 932 933 /* if no repository the default for -w is files */ 934 if (repository.type == NULL) 935 repository = __REPFILES; 936 937 if (IS_FILES(repository) == FALSE && 938 IS_LDAP(repository) == FALSE) { 939 (void) fprintf(stderr, gettext( 940 "-w only applies to files or ldap " 941 "repository\n")); 942 rusage(); /* exit */ 943 retval = BADSYN; 944 return (FAIL); 945 } 946 947 /* 948 * Only privileged process can execute this 949 * for FILES or LDAP 950 */ 951 if ((IS_FILES(repository) || IS_LDAP(repository)) && 952 (ckuid() != SUCCESS)) { 953 retval = NOPERM; 954 return (FAIL); 955 } 956 if (flag & (SAFLAG|WFLAG|NONAGEFLAG)) { 957 retval = BADOPT; 958 return (FAIL); 959 } 960 flag |= WFLAG; 961 if ((int)strlen(optarg) <= 0 || 962 (strtol(optarg, &char_p, 10)) < 0 || 963 *char_p != '\0') { 964 (void) fprintf(stderr, "%s: %s -w\n", 965 prognamep, gettext(MSG_NV)); 966 retval = BADSYN; 967 return (FAIL); 968 } 969 attrlist_add(attributes, ATTR_WARN, optarg); 970 break; 971 972 case 's': /* display password attributes */ 973 974 /* if no repository the default for -s is files */ 975 if (repository.type == NULL) 976 repository = __REPFILES; 977 978 979 /* display password attributes */ 980 if (IS_FILES(repository) == FALSE && 981 IS_LDAP(repository) == FALSE) { 982 (void) fprintf(stderr, gettext( 983 "-s only applies to files or ldap " 984 "repository\n")); 985 rusage(); /* exit */ 986 retval = BADSYN; 987 return (FAIL); 988 } 989 990 /* 991 * Only privileged process can execute this 992 * for FILES or LDAP 993 */ 994 if ((IS_FILES(repository) || IS_LDAP(repository)) && 995 ((retval = ckuid()) != SUCCESS)) 996 return (FAIL); 997 if (flag && (flag != AFLAG)) { 998 retval = BADOPT; 999 return (FAIL); 1000 } 1001 flag |= SFLAG; 1002 break; 1003 1004 case 'a': /* display password attributes */ 1005 1006 /* if no repository the default for -a is files */ 1007 if (repository.type == NULL) 1008 repository = __REPFILES; 1009 1010 if (IS_FILES(repository) == FALSE && 1011 IS_LDAP(repository) == FALSE) { 1012 (void) fprintf(stderr, gettext( 1013 "-a only applies to files or ldap " 1014 "repository\n")); 1015 rusage(); /* exit */ 1016 retval = BADSYN; 1017 return (FAIL); 1018 } 1019 1020 /* 1021 * Only privileged process can execute this 1022 * for FILES or LDAP 1023 */ 1024 if ((IS_FILES(repository) || IS_LDAP(repository)) && 1025 ((retval = ckuid()) != SUCCESS)) 1026 return (FAIL); 1027 if (flag && (flag != SFLAG)) { 1028 retval = BADOPT; 1029 return (FAIL); 1030 } 1031 flag |= AFLAG; 1032 break; 1033 1034 case 'f': /* expire password attributes */ 1035 1036 /* if no repository the default for -f is files */ 1037 if (repository.type == NULL) 1038 repository = __REPFILES; 1039 1040 if (IS_FILES(repository) == FALSE && 1041 IS_LDAP(repository) == FALSE) { 1042 (void) fprintf(stderr, gettext( 1043 "-f only applies to files or ldap " 1044 "repository\n")); 1045 rusage(); /* exit */ 1046 retval = BADSYN; 1047 return (FAIL); 1048 } 1049 1050 /* 1051 * Only privileged process can execute this 1052 * for FILES or LDAP 1053 */ 1054 if ((IS_FILES(repository) || IS_LDAP(repository)) && 1055 ((retval = ckuid()) != SUCCESS)) 1056 return (FAIL); 1057 if (flag & (SAFLAG|FFLAG|NONAGEFLAG)) { 1058 retval = BADOPT; 1059 return (FAIL); 1060 } 1061 flag |= FFLAG; 1062 attrlist_add(attributes, ATTR_EXPIRE_PASSWORD, NULL); 1063 break; 1064 1065 case 'e': /* change login shell */ 1066 1067 /* if no repository the default for -e is files */ 1068 if (repository.type == NULL) 1069 repository = __REPFILES; 1070 1071 if (flag & (EFLAG|SAFLAG|AGEFLAG)) { 1072 retval = BADOPT; 1073 return (FAIL); 1074 } 1075 flag |= EFLAG; 1076 break; 1077 1078 case 'g': /* change gecos information */ 1079 1080 /* if no repository the default for -g is files */ 1081 if (repository.type == NULL) 1082 repository = __REPFILES; 1083 1084 /* 1085 * Only privileged process can execute this 1086 * for FILES 1087 */ 1088 if (IS_FILES(repository) && (ckuid() != SUCCESS)) { 1089 retval = NOPERM; 1090 return (FAIL); 1091 } 1092 if (flag & (GFLAG|SAFLAG|AGEFLAG)) { 1093 retval = BADOPT; 1094 return (FAIL); 1095 } 1096 flag |= GFLAG; 1097 break; 1098 1099 case 'h': /* change home dir */ 1100 1101 /* if no repository the default for -h is files */ 1102 if (repository.type == NULL) 1103 repository = __REPFILES; 1104 /* 1105 * Only privileged process can execute this 1106 * for FILES 1107 */ 1108 if (IS_FILES(repository) && (ckuid() != SUCCESS)) { 1109 retval = NOPERM; 1110 return (FAIL); 1111 } 1112 if (IS_NIS(repository)) { 1113 (void) fprintf(stderr, "%s\n", 1114 gettext(MSG_NIS_HOMEDIR)); 1115 retval = BADSYN; 1116 return (FAIL); 1117 } 1118 1119 if (flag & (HFLAG|SAFLAG|AGEFLAG)) { 1120 retval = BADOPT; 1121 return (FAIL); 1122 } 1123 flag |= HFLAG; 1124 break; 1125 1126 case '?': 1127 rusage(); 1128 retval = BADOPT; 1129 return (FAIL); 1130 } 1131 } 1132 1133 argc -= optind; 1134 if (argc > 1) { 1135 rusage(); 1136 retval = BADSYN; 1137 return (FAIL); 1138 } 1139 1140 /* Make sure (EXPIRE comes after (MAX comes after MIN)) */ 1141 attrlist_reorder(attributes); 1142 1143 /* If no options are specified or only the show option */ 1144 /* is specified, return because no option error checking */ 1145 /* is needed */ 1146 if (!flag || (flag == SFLAG)) 1147 return (flag); 1148 1149 /* AFLAG must be used with SFLAG */ 1150 if (flag == AFLAG) { 1151 rusage(); 1152 retval = BADSYN; 1153 return (FAIL); 1154 } 1155 1156 if (flag != SAFLAG && argc < 1) { 1157 /* 1158 * user name is not specified (argc<1), it can't be 1159 * aging info update. 1160 */ 1161 if (!(flag & NONAGEFLAG)) { 1162 rusage(); 1163 retval = BADSYN; 1164 return (FAIL); 1165 } 1166 } 1167 1168 /* user name(s) may not be specified when SAFLAG is used. */ 1169 if (flag == SAFLAG && argc >= 1) { 1170 rusage(); 1171 retval = BADSYN; 1172 return (FAIL); 1173 } 1174 1175 /* 1176 * If aging is being turned off (maxdate == -1), mindate may not 1177 * be specified. 1178 */ 1179 if ((maxdate == -1) && (flag & NFLAG)) { 1180 (void) fprintf(stderr, "%s: %s -n\n", 1181 prognamep, gettext(MSG_NV)); 1182 retval = BADOPT; 1183 return (FAIL); 1184 } 1185 1186 return (flag); 1187 } 1188 1189 /* 1190 * 1191 * ckuid(): 1192 * This function returns SUCCESS if the caller is root, else 1193 * it returns NOPERM. 1194 * 1195 */ 1196 1197 static int 1198 ckuid(void) 1199 { 1200 if (uid != 0) { 1201 return (retval = NOPERM); 1202 } 1203 return (SUCCESS); 1204 } 1205 1206 1207 /* 1208 * get_attr() 1209 */ 1210 int 1211 get_attr(char *username, pwu_repository_t *repository, attrlist **attributes) 1212 { 1213 int res; 1214 1215 attrlist_add(attributes, ATTR_PASSWD, NULL); 1216 attrlist_add(attributes, ATTR_LSTCHG, "0"); 1217 attrlist_add(attributes, ATTR_MIN, "0"); 1218 attrlist_add(attributes, ATTR_MAX, "0"); 1219 attrlist_add(attributes, ATTR_WARN, "0"); 1220 1221 res = __get_authtoken_attr(username, repository, *attributes); 1222 1223 if (res == PWU_SUCCESS) { 1224 retval = SUCCESS; 1225 return (PWU_SUCCESS); 1226 } 1227 1228 if (res == PWU_NOT_FOUND) 1229 (void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep, 1230 username); 1231 1232 retval = NOPERM; 1233 passwd_exit(retval); 1234 /*NOTREACHED*/ 1235 } 1236 1237 /* 1238 * display_attr(): 1239 * This function prints out the password attributes of a usr 1240 * onto standand output. 1241 */ 1242 void 1243 display_attr(char *usrname, attrlist *attributes) 1244 { 1245 char *status = NULL; 1246 char *passwd; 1247 long lstchg; 1248 int min = 0, max = 0, warn = 0; 1249 1250 while (attributes) { 1251 switch (attributes->type) { 1252 case ATTR_PASSWD: 1253 passwd = attributes->data.val_s; 1254 if (passwd == NULL || *passwd == '\0') 1255 status = "NP "; 1256 else if (strncmp(passwd, LOCKSTRING, 1257 sizeof (LOCKSTRING)-1) == 0) 1258 status = "LK "; 1259 else if (strncmp(passwd, NOLOGINSTRING, 1260 sizeof (NOLOGINSTRING)-1) == 0) 1261 status = "NL "; 1262 else if ((strlen(passwd) == 13 && passwd[0] != '$') || 1263 passwd[0] == '$') 1264 status = "PS "; 1265 else 1266 status = "UN "; 1267 break; 1268 case ATTR_LSTCHG: 1269 lstchg = attributes->data.val_i * DAY; 1270 break; 1271 case ATTR_MIN: 1272 min = attributes->data.val_i; 1273 break; 1274 case ATTR_MAX: 1275 max = attributes->data.val_i; 1276 break; 1277 case ATTR_WARN: 1278 warn = attributes->data.val_i; 1279 break; 1280 default: 1281 break; 1282 } 1283 attributes = attributes->next; 1284 } 1285 (void) fprintf(stdout, "%-8s ", usrname); 1286 1287 if (status) 1288 (void) fprintf(stdout, "%s ", status); 1289 1290 if (max != -1) { 1291 if (lstchg == 0) { 1292 (void) fprintf(stdout, "00/00/00 "); 1293 } else { 1294 struct tm *tmp; 1295 tmp = gmtime(&lstchg); 1296 (void) fprintf(stdout, "%.2d/%.2d/%.2d ", 1297 tmp->tm_mon + 1, 1298 tmp->tm_mday, 1299 tmp->tm_year % 100); 1300 } 1301 (void) fprintf(stdout, (min >= 0) ? "%4d " : " ", min); 1302 (void) fprintf(stdout, "%4d ", max); 1303 (void) fprintf(stdout, (warn > 0) ? "%4d " : " ", warn); 1304 } 1305 (void) fprintf(stdout, "\n"); 1306 } 1307 1308 void 1309 free_attr(attrlist *attributes) 1310 { 1311 while (attributes) { 1312 if (attributes->type == ATTR_PASSWD) 1313 free(attributes->data.val_s); 1314 attributes = attributes->next; 1315 } 1316 } 1317 1318 /* 1319 * 1320 * get_namelist_files(): 1321 * This function gets a list of user names on the system from 1322 * the /etc/passwd file. 1323 * 1324 */ 1325 int 1326 get_namelist_files(char ***namelist_p, int *num_user) 1327 { 1328 FILE *pwfp; 1329 struct passwd *pwd; 1330 int max_user; 1331 int nuser; 1332 char **nl; 1333 1334 nuser = 0; 1335 errno = 0; 1336 pwd = NULL; 1337 1338 if ((pwfp = fopen(PASSWD, "r")) == NULL) 1339 return (NOPERM); 1340 1341 /* 1342 * find out the actual number of entries in the PASSWD file 1343 */ 1344 max_user = 1; /* need one slot for terminator NULL */ 1345 while ((pwd = fgetpwent(pwfp)) != NULL) 1346 max_user++; 1347 1348 /* 1349 * reset the file stream pointer 1350 */ 1351 rewind(pwfp); 1352 1353 nl = (char **)calloc(max_user, (sizeof (char *))); 1354 if (nl == NULL) { 1355 (void) fclose(pwfp); 1356 return (FMERR); 1357 } 1358 1359 while ((pwd = fgetpwent(pwfp)) != NULL) { 1360 if ((nl[nuser] = strdup(pwd->pw_name)) == NULL) { 1361 (void) fclose(pwfp); 1362 return (FMERR); 1363 } 1364 nuser++; 1365 } 1366 1367 nl[nuser] = NULL; 1368 *num_user = nuser; 1369 *namelist_p = nl; 1370 (void) fclose(pwfp); 1371 return (SUCCESS); 1372 } 1373 1374 /* 1375 * get_namelist_local 1376 * 1377 */ 1378 1379 /* 1380 * Our private version of the switch frontend for getspent. We want 1381 * to search just the ldap sp file, so we want to bypass 1382 * normal nsswitch.conf based processing. This implementation 1383 * compatible with version 2 of the name service switch. 1384 */ 1385 #define NSS_LDAP_ONLY "ldap" 1386 1387 extern int str2spwd(const char *, int, void *, char *, int); 1388 1389 static DEFINE_NSS_DB_ROOT(db_root); 1390 static DEFINE_NSS_GETENT(context); 1391 1392 static char *local_config; 1393 static void 1394 _lc_nss_initf_shadow(nss_db_params_t *p) 1395 { 1396 p->name = NSS_DBNAM_SHADOW; 1397 p->config_name = NSS_DBNAM_PASSWD; /* Use config for "passwd" */ 1398 p->default_config = local_config; /* Use ldap only */ 1399 p->flags = NSS_USE_DEFAULT_CONFIG; 1400 } 1401 1402 static void 1403 _lc_setspent(void) 1404 { 1405 nss_setent(&db_root, _lc_nss_initf_shadow, &context); 1406 } 1407 1408 static void 1409 _lc_endspent(void) 1410 { 1411 nss_endent(&db_root, _lc_nss_initf_shadow, &context); 1412 nss_delete(&db_root); 1413 } 1414 1415 static struct spwd * 1416 _lc_getspent_r(struct spwd *result, char *buffer, int buflen) 1417 { 1418 nss_XbyY_args_t arg; 1419 char *nam; 1420 1421 /* In getXXent_r(), protect the unsuspecting caller from +/- entries */ 1422 1423 do { 1424 NSS_XbyY_INIT(&arg, result, buffer, buflen, str2spwd); 1425 /* No key to fill in */ 1426 (void) nss_getent(&db_root, _lc_nss_initf_shadow, &context, 1427 &arg); 1428 } while (arg.returnval != 0 && 1429 (nam = ((struct spwd *)arg.returnval)->sp_namp) != 0 && 1430 (*nam == '+' || *nam == '-')); 1431 1432 return (struct spwd *)NSS_XbyY_FINI(&arg); 1433 } 1434 1435 static nss_XbyY_buf_t *buffer; 1436 1437 static struct spwd * 1438 _lc_getspent(void) 1439 { 1440 nss_XbyY_buf_t *b; 1441 1442 b = NSS_XbyY_ALLOC(&buffer, sizeof (struct spwd), NSS_BUFLEN_SHADOW); 1443 1444 return (b == 0 ? 0 : _lc_getspent_r(b->result, b->buffer, b->buflen)); 1445 } 1446 1447 int 1448 get_namelist_local(char ***namelist_p, int *num_user) 1449 { 1450 int nuser = 0; 1451 int alloced = 100; 1452 char **nl; 1453 struct spwd *p; 1454 1455 1456 if ((nl = calloc(alloced, sizeof (*nl))) == NULL) 1457 return (FMERR); 1458 1459 (void) _lc_setspent(); 1460 while ((p = _lc_getspent()) != NULL) { 1461 if ((nl[nuser] = strdup(p->sp_namp)) == NULL) { 1462 _lc_endspent(); 1463 return (FMERR); 1464 } 1465 if (++nuser == alloced) { 1466 alloced += 100; 1467 nl = realloc(nl, alloced * (sizeof (*nl))); 1468 if (nl == NULL) { 1469 _lc_endspent(); 1470 return (FMERR); 1471 } 1472 } 1473 } 1474 (void) _lc_endspent(); 1475 nl[nuser] = NULL; 1476 1477 *namelist_p = nl; 1478 *num_user = nuser; /* including NULL */ 1479 1480 return (SUCCESS); 1481 } 1482 1483 int 1484 get_namelist(pwu_repository_t repository, char ***namelist, int *num_user) 1485 { 1486 if (IS_LDAP(repository)) { 1487 local_config = NSS_LDAP_ONLY; 1488 return (get_namelist_local(namelist, num_user)); 1489 } else if (IS_FILES(repository)) 1490 return (get_namelist_files(namelist, num_user)); 1491 1492 rusage(); 1493 return (BADSYN); 1494 } 1495 1496 /* 1497 * 1498 * passwd_exit(): 1499 * This function will call exit() with appropriate exit code 1500 * according to the input "retcode" value. 1501 * It also calls pam_end() to clean-up buffers before exit. 1502 * 1503 */ 1504 1505 void 1506 passwd_exit(int retcode) 1507 { 1508 1509 if (pamh) 1510 (void) pam_end(pamh, pam_retval); 1511 1512 switch (retcode) { 1513 case SUCCESS: 1514 break; 1515 case NOPERM: 1516 (void) fprintf(stderr, "%s\n", gettext(MSG_NP)); 1517 break; 1518 case BADOPT: 1519 (void) fprintf(stderr, "%s\n", gettext(MSG_BS)); 1520 break; 1521 case FMERR: 1522 (void) fprintf(stderr, "%s\n", gettext(MSG_FE)); 1523 break; 1524 case FATAL: 1525 (void) fprintf(stderr, "%s\n", gettext(MSG_FF)); 1526 break; 1527 case FBUSY: 1528 (void) fprintf(stderr, "%s\n", gettext(MSG_FB)); 1529 break; 1530 case BADSYN: 1531 (void) fprintf(stderr, "%s\n", gettext(MSG_NV)); 1532 break; 1533 case BADAGE: 1534 (void) fprintf(stderr, "%s\n", gettext(MSG_AD)); 1535 break; 1536 case NOMEM: 1537 (void) fprintf(stderr, "%s\n", gettext(MSG_NM)); 1538 break; 1539 default: 1540 (void) fprintf(stderr, "%s\n", gettext(MSG_NP)); 1541 retcode = NOPERM; 1542 break; 1543 } 1544 /* write password record */ 1545 if (event != NULL) { 1546 struct passwd *pass; 1547 1548 if ((pass = getpwnam(usrname)) == NULL) { 1549 /* unlikely to ever get here, but ... */ 1550 event->adt_passwd.username = usrname; 1551 } else if (pass->pw_uid != uid) { 1552 /* save target user */ 1553 event->adt_passwd.uid = pass->pw_uid; 1554 event->adt_passwd.username = pass->pw_name; 1555 } 1556 1557 if (adt_put_event(event, 1558 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAILURE, 1559 retcode == SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM + 1560 pam_retval) != 0) { 1561 adt_free_event(event); 1562 (void) adt_end_session(ah); 1563 perror("adt_put_event"); 1564 exit(retcode); 1565 } 1566 adt_free_event(event); 1567 } 1568 (void) adt_end_session(ah); 1569 exit(retcode); 1570 } 1571 1572 /* 1573 * 1574 * passwd_conv(): 1575 * This is the conv (conversation) function called from 1576 * a PAM authentication module to print error messages 1577 * or garner information from the user. 1578 * 1579 */ 1580 1581 static int 1582 passwd_conv(int num_msg, const struct pam_message **msg, 1583 struct pam_response **response, void *appdata_ptr) 1584 { 1585 const struct pam_message *m; 1586 struct pam_response *r; 1587 char *temp; 1588 int k, i; 1589 1590 if (num_msg <= 0) 1591 return (PAM_CONV_ERR); 1592 1593 *response = (struct pam_response *)calloc(num_msg, 1594 sizeof (struct pam_response)); 1595 if (*response == NULL) 1596 return (PAM_BUF_ERR); 1597 1598 k = num_msg; 1599 m = *msg; 1600 r = *response; 1601 while (k--) { 1602 1603 switch (m->msg_style) { 1604 1605 case PAM_PROMPT_ECHO_OFF: 1606 temp = getpassphrase(m->msg); 1607 if (temp != NULL) { 1608 r->resp = strdup(temp); 1609 (void) memset(temp, 0, strlen(temp)); 1610 if (r->resp == NULL) { 1611 /* free responses */ 1612 r = *response; 1613 for (i = 0; i < num_msg; i++, r++) { 1614 if (r->resp) 1615 free(r->resp); 1616 } 1617 free(*response); 1618 *response = NULL; 1619 return (PAM_BUF_ERR); 1620 } 1621 } 1622 m++; 1623 r++; 1624 break; 1625 1626 case PAM_PROMPT_ECHO_ON: 1627 if (m->msg != NULL) { 1628 (void) fputs(m->msg, stdout); 1629 } 1630 r->resp = (char *)calloc(PAM_MAX_RESP_SIZE, 1631 sizeof (char)); 1632 if (r->resp == NULL) { 1633 /* free responses */ 1634 r = *response; 1635 for (i = 0; i < num_msg; i++, r++) { 1636 if (r->resp) 1637 free(r->resp); 1638 } 1639 free(*response); 1640 *response = NULL; 1641 return (PAM_BUF_ERR); 1642 } 1643 if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) { 1644 int len = strlen(r->resp); 1645 if (r->resp[len-1] == '\n') 1646 r->resp[len-1] = '\0'; 1647 } 1648 m++; 1649 r++; 1650 break; 1651 1652 case PAM_ERROR_MSG: 1653 if (m->msg != NULL) { 1654 (void) fputs(m->msg, stderr); 1655 (void) fputs("\n", stderr); 1656 } 1657 m++; 1658 r++; 1659 break; 1660 case PAM_TEXT_INFO: 1661 if (m->msg != NULL) { 1662 (void) fputs(m->msg, stdout); 1663 (void) fputs("\n", stdout); 1664 } 1665 m++; 1666 r++; 1667 break; 1668 1669 default: 1670 break; 1671 } 1672 } 1673 return (PAM_SUCCESS); 1674 } 1675 1676 /* 1677 * Utilities Functions 1678 */ 1679 1680 /* 1681 * int attrlist_add(attrlist **l, attrtype type, char *val) 1682 * add an item, with type "type" and value "val", at the tail of list l. 1683 * This functions exits the application on OutOfMem error. 1684 */ 1685 void 1686 attrlist_add(attrlist **l, attrtype type, char *val) 1687 { 1688 attrlist **w; 1689 1690 /* tail insert */ 1691 for (w = l; *w != NULL; w = &(*w)->next) 1692 ; 1693 1694 if ((*w = malloc(sizeof (**w))) == NULL) 1695 passwd_exit(NOMEM); 1696 1697 (*w)->type = type; 1698 (*w)->next = NULL; 1699 1700 switch (type) { 1701 case ATTR_MIN: 1702 case ATTR_WARN: 1703 case ATTR_MAX: 1704 (*w)->data.val_i = atoi(val); 1705 break; 1706 default: 1707 (*w)->data.val_s = val; 1708 break; 1709 } 1710 } 1711 1712 /* 1713 * attrlist_reorder(attrlist **l) 1714 * Make sure that 1715 * - if EXPIRE and MAX or MIN is set, EXPIRE comes after MAX/MIN 1716 * - if both MIN and MAX are set, MAX comes before MIN. 1717 */ 1718 1719 static void 1720 attrlist_reorder(attrlist **l) 1721 { 1722 attrlist **w; 1723 attrlist *exp = NULL; /* ATTR_EXPIRE_PASSWORD, if found */ 1724 attrlist *max = NULL; /* ATTR_MAX, if found */ 1725 1726 if (*l == NULL || (*l)->next == NULL) 1727 return; /* order of list with <= one item is ok */ 1728 1729 /* 1730 * We simply walk the list, take off the EXPIRE and MAX items if 1731 * they appear, and put them (first MAX, them EXPIRE) at the end 1732 * of the list. 1733 */ 1734 w = l; 1735 while (*w != NULL) { 1736 if ((*w)->type == ATTR_EXPIRE_PASSWORD) { 1737 exp = *w; 1738 *w = (*w)->next; 1739 } else if ((*w)->type == ATTR_MAX) { 1740 max = *w; 1741 *w = (*w)->next; 1742 } else 1743 w = &(*w)->next; 1744 } 1745 1746 /* 'w' points to the address of the 'next' field of the last element */ 1747 1748 if (max) { 1749 *w = max; 1750 w = &max->next; 1751 } 1752 if (exp) { 1753 *w = exp; 1754 w = &exp->next; 1755 } 1756 *w = NULL; 1757 } 1758 1759 void 1760 rusage(void) 1761 { 1762 1763 #define MSG(a) (void) fprintf(stderr, gettext((a))); 1764 1765 MSG("usage:\n"); 1766 MSG("\tpasswd [-r files | -r nis | -r ldap] [name]\n"); 1767 MSG("\tpasswd [-r files] [-egh] [name]\n"); 1768 MSG("\tpasswd [-r files] -sa\n"); 1769 MSG("\tpasswd [-r files] -s [name]\n"); 1770 MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] " 1771 "[-x max] name\n"); 1772 MSG("\tpasswd -r nis [-eg] [name]\n"); 1773 MSG("\t\t[-x max] name\n"); 1774 MSG("\tpasswd -r ldap [-egh] [name]\n"); 1775 MSG("\tpasswd -r ldap -sa\n"); 1776 MSG("\tpasswd -r ldap -s [name]\n"); 1777 MSG("\tpasswd -r ldap [-l|-N|-u] [-f] [-n min] [-w warn] " 1778 "[-x max] name\n"); 1779 #undef MSG 1780 } 1781