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