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