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