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