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