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