home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / shadow / part03 / passwd.c < prev    next >
C/C++ Source or Header  |  1993-08-14  |  27KB  |  1,275 lines

  1. /*
  2.  * Copyright 1989, 1990, 1991, 1992, 1993, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  *
  11.  * This software is provided on an AS-IS basis and the author makes
  12.  * no warrantee of any kind.
  13.  */
  14.  
  15. #include "config.h"
  16. #include <sys/types.h>
  17. #include <time.h>
  18. #include <stdio.h>
  19. #include <fcntl.h>
  20. #include <signal.h>
  21.  
  22. #ifndef    lint
  23. static    char    sccsid[] = "@(#)passwd.c    3.11    08:57:44    10 Jun 1993";
  24. #endif
  25.  
  26. /*
  27.  * Set up some BSD defines so that all the BSD ifdef's are
  28.  * kept right here 
  29.  */
  30.  
  31. #ifndef    BSD
  32. #include <string.h>
  33. #include <memory.h>
  34. #define    bzero(a,n)    memset(a, 0, n)
  35. #else
  36. #include <strings.h>
  37. #define    strchr    index
  38. #define    strrchr    rindex
  39. #endif
  40.  
  41. #ifdef    STDLIB_H
  42. #include <stdlib.h>
  43. #endif
  44. #ifdef    UNISTD_H
  45. #include <unistd.h>
  46. #endif
  47. #ifdef    ULIMIT_H
  48. #include <ulimit.h>
  49. #endif
  50.  
  51. #ifndef UL_SFILLIM
  52. #define UL_SFILLIM    2
  53. #endif
  54.  
  55. #include "pwd.h"
  56. #include "lastlog.h"
  57. #ifdef    SHADOWPWD
  58. #include "shadow.h"
  59. #ifndef    AGING
  60. #define    AGING    0
  61. #endif
  62. #endif
  63. #include "pwauth.h"
  64.  
  65. #ifdef    USE_SYSLOG
  66. #include <syslog.h>
  67.  
  68. #ifndef    LOG_WARN
  69. #define    LOG_WARN    LOG_WARNING
  70. #endif
  71. #endif
  72. #ifdef    HAVE_RLIMIT
  73. #include <sys/resource.h>
  74.  
  75. struct    rlimit    rlimit_fsize = { RLIM_INFINITY, RLIM_INFINITY };
  76. #endif
  77.  
  78. #ifdef    AGING
  79.  
  80. /*
  81.  * Password aging constants
  82.  *
  83.  *    DAY - seconds in a day
  84.  *    WEEK - seconds in a week
  85.  *    SCALE - convert from clock to aging units
  86.  */
  87.  
  88. #ifndef    DAY
  89. #define    DAY    (24L*3600L)
  90. #endif
  91. #define    WEEK    (7L*DAY)
  92.  
  93. #ifdef    ITI_AGING
  94. #define    SCALE    (1)
  95. #else
  96. #define    SCALE    DAY
  97. #endif
  98. #endif
  99.  
  100. /*
  101.  * Global variables
  102.  */
  103.  
  104. char    name[32];        /* The user's name */
  105. char    crypt_passwd[32];    /* The "old-style" password, if present */
  106. char    *Prog;            /* Program name */
  107. int    amroot;            /* The real UID was 0 */
  108.  
  109. /*
  110.  * External identifiers
  111.  */
  112.  
  113. extern    char    *getpass();
  114. extern    char    *pw_encrypt();
  115. extern    int    pw_auth();
  116. extern    char    *getlogin();
  117. extern    char    l64a();
  118. extern    int    optind;        /* Index into argv[] for current option */
  119. extern    char    *optarg;    /* Pointer to current option value */
  120. #ifdef    NDBM
  121. extern    int    sp_dbm_mode;
  122. extern    int    pw_dbm_mode;
  123. #endif
  124.  
  125. /*
  126.  * #defines for messages.  This facilities foreign language conversion
  127.  * since all messages are defined right here.
  128.  */
  129.  
  130. #define    USAGE        "usage: %s [ -f | -s ] [ name ]\n"
  131. #define    ADMUSAGE \
  132.     "       %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n"
  133. #define    ADMUSAGE2 \
  134.     "       %s { -l | -d | -S } name\n"
  135. #define    OLDPASS        "Old Password:"
  136. #define    NEWPASSMSG \
  137. "Enter the new password (minimum of %d characters)\n\
  138. Please use a combination of upper and lower case letters and numbers.\n"
  139. #define    CHANGING    "Changing password for %s\n"
  140. #define NEWPASS        "New Password:"
  141. #define    NEWPASS2    "Re-enter new password:"
  142. #define    WRONGPWD    "Incorrect password for %s.\n"
  143. #define    WRONGPWD2    "incorrect password for `%s'\n"
  144. #define    NOMATCH        "They don't match; try again.\n"
  145. #define    CANTCHANGE    "The password for %s cannot be changed.\n"
  146. #define    CANTCHANGE2    "password locked for `%s'\n"
  147.  
  148. #ifdef    AGING
  149. #define    TOOSOON        "Sorry, the password for %s cannot be changed yet.\n"
  150. #define    TOOSOON2    "now < sp_min for `%s'\n"
  151. #endif
  152.  
  153. #define    EXECFAILED    "%s: Cannot execute %s"
  154. #define    EXECFAILED2    "cannot execute %s\n"
  155. #define    WHOAREYOU    "%s: Cannot determine you user name.\n"
  156. #define    UNKUSER        "%s: Unknown user %s\n"
  157. #define    NOPERM        "You may not change the password for %s.\n"
  158. #define    NOPERM2        "can't change pwd for `%s'\n"
  159. #define    UNCHANGED    "The password for %s is unchanged.\n"
  160.  
  161. #ifdef    SHADOWPWD
  162. #define    SPWDBUSY    "Cannot lock the password file; try again later.\n"
  163. #define    SPWDBUSY2    "can't lock /etc/shadow\n"
  164. #define    OPNERROR    "Cannot open the password file.\n"
  165. #define    OPNERROR2    "can't open /etc/shadow\n"
  166. #define    UPDERROR    "Error updating the password entry.\n"
  167. #define    UPDERROR2    "error updating shadow entry\n"
  168. #define    DBMERROR    "Error updating the DBM password entry.\n"
  169. #define    DBMERROR2    "error updating DBM shadow entry.\n"
  170. #define    CLSERROR    "Cannot commit shadow file changes.\n"
  171. #define    CLSERROR2    "can't rewrite /etc/shadow.\n"
  172. #define    UNLKERROR    "Cannot unlock the shadow file.\n"
  173. #define    UNLKERROR2    "can't unlock /etc/shadow.\n"
  174. #else
  175. #define    PWDBUSY        "Cannot lock the password file; try again later.\n"
  176. #define    PWDBUSY2    "can't lock /etc/passwd\n"
  177. #define    OPNERROR    "Cannot open the password file.\n"
  178. #define    OPNERROR2    "can't open /etc/passwd\n"
  179. #define    UPDERROR    "Error updating the password entry.\n"
  180. #define    UPDERROR2    "error updating password entry\n"
  181. #define    DBMERROR    "Error updating the DBM password entry.\n"
  182. #define    DBMERROR2    "error updating DBM password entry.\n"
  183. #define    CLSERROR    "Cannot commit password file changes.\n"
  184. #define    CLSERROR2    "can't rewrite /etc/passwd.\n"
  185. #define    UNLKERROR    "Cannot unlock the password file.\n"
  186. #define    UNLKERROR2    "can't unlock /etc/passwd.\n"
  187. #endif
  188.  
  189. #define    NOTROOT        "Cannot change ID to root.\n"
  190. #define    NOTROOT2    "can't setuid(0).\n"
  191. #define    TRYAGAIN    "Try again.\n"
  192. #define    CHGPASSWD    "changed password for `%s'\n"
  193. #define    NOCHGPASSWD    "did not change password for `%s'\n"
  194.  
  195. /*
  196.  * usage - print command usage and exit
  197.  */
  198.  
  199. void
  200. usage ()
  201. {
  202.     fprintf (stderr, USAGE, Prog);
  203.     if (amroot) {
  204.         fprintf (stderr, ADMUSAGE, Prog);
  205.         fprintf (stderr, ADMUSAGE2, Prog);
  206.     }
  207.     exit (1);
  208. }
  209.  
  210. /*
  211.  * get_password - locate encrypted password in authentication list
  212.  */
  213.  
  214. char *
  215. get_password (list)
  216. char    *list;
  217. {
  218.     char    *cp, *end;
  219.     static    char    buf[257];
  220.  
  221.     strcpy (buf, list);
  222.     for (cp = buf;cp;cp = end) {
  223.         if (end = strchr (cp, ';'))
  224.             *end++ = 0;
  225.  
  226.         if (cp[0] == '@')
  227.             continue;
  228.  
  229.         return cp;
  230.     }
  231.     return (char *) 0;
  232. }
  233.  
  234. /*
  235.  * uses_default_method - determine if "old-style" password present
  236.  *
  237.  *    uses_default_method determines if a "old-style" password is present
  238.  *    in the authentication string, and if one is present it extracts it.
  239.  */
  240.  
  241. int
  242. uses_default_method (methods)
  243. char    *methods;
  244. {
  245.     char    *cp;
  246.  
  247.     if (cp = get_password (methods)) {
  248.         strcpy (crypt_passwd, cp);
  249.         return 1;
  250.     }
  251.     return 0;
  252. }
  253.  
  254. /*
  255.  * update_age - update the "last_changed" field
  256.  */
  257.  
  258. #ifdef    AGING
  259. void
  260. #ifdef    SHADOWPWD
  261. update_age (sp)
  262. struct    spwd    *sp;
  263. {
  264.     sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  265. }
  266. #else
  267. update_age (pw)
  268. struct    passwd    *pw;
  269. {
  270. #ifdef    ATT_AGE
  271.     long    week;        /* at the office ... */
  272.     static    char    age[5];    /* Password age string */
  273.  
  274.     week = time ((time_t *) 0) / WEEK;
  275.     if (pw->pw_age[0]) {
  276.         cp = l64a (week);
  277.         age[0] = pw->pw_age[0];
  278.         age[1] = pw->pw_age[1];
  279.         age[2] = cp[0];
  280.         age[3] = cp[1];
  281.         age[4] = '\0';
  282.         pw->pw_age = age;
  283.     }
  284. #endif    /* ATT_AGE */
  285. }
  286. #endif    /* SHADOWPWD */
  287. #endif    /* AGING */
  288.  
  289. /*
  290.  * insert_crypt_passwd - add an "old-style" password to authentication string
  291.  */
  292.  
  293. insert_crypt_passwd (string, passwd, result)
  294. char    *string;
  295. char    *passwd;
  296. char    *result;
  297. {
  298.     while (*string) {
  299.         if (string[0] == ';') {
  300.             *result++ = *string++;
  301.         } else if (string[0] == '@') {
  302.             while (*string && *string != ';')
  303.                 *result++ = *string++;
  304.         } else {
  305.             while (*passwd)
  306.                 *result++ = *passwd++;
  307.  
  308.             while (*string && *string != ';')
  309.                 string++;
  310.         }
  311.     }
  312.     *result = '\0';
  313. }
  314.  
  315. /*
  316.  * new_password - validate old password and replace with new
  317.  */
  318.  
  319. /*ARGSUSED*/
  320. int
  321. #ifdef    SHADOWPWD
  322. new_password (pw, sp)
  323. struct    passwd    *pw;
  324. struct    spwd    *sp;
  325. #else
  326. new_password (pw)
  327. struct    passwd    *pw;
  328. #endif
  329. {
  330.     char    *clear;        /* Pointer to clear text */
  331.     char    *cipher;    /* Pointer to cipher text */
  332.     char    *cp;        /* Pointer to getpass() response */
  333.     char    orig[BUFSIZ];    /* Original password */
  334.     char    pass[BUFSIZ];    /* New password */
  335.     char    new_auth[257];
  336.     int    i;        /* Counter for retries */
  337. #ifdef    AGING
  338.     long    week;        /* This week in history ... */
  339. #endif
  340.  
  341.     /*
  342.      * Authenticate the user.  The user will be prompted for their
  343.      * own password.
  344.      */
  345.  
  346.     if (! amroot && crypt_passwd[0]) {
  347.         bzero (orig, sizeof orig);
  348.  
  349.         if (! (clear = getpass (OLDPASS)))
  350.             return -1;
  351.  
  352.         cipher = pw_encrypt (clear, crypt_passwd);
  353.         if (strcmp (cipher, crypt_passwd) != 0) {
  354.             sleep (1);
  355.             fprintf (stderr, WRONGPWD, pw->pw_name);
  356. #ifdef    USE_SYSLOG
  357.             syslog (LOG_WARN, WRONGPWD2, pw->pw_name);
  358. #endif
  359.             return -1;
  360.         }
  361.         strcpy (orig, clear);
  362.         bzero (cipher, strlen (cipher));
  363.         bzero (clear, strlen (clear));
  364.     }
  365.  
  366.     /*
  367.      * Get the new password.  The user is prompted for the new password
  368.      * and has three tries to get it right.  The password will be tested
  369.      * for strength, unless it is the root user.  This provides an escape
  370.      * for initial login passwords.
  371.      */
  372.  
  373.     printf (NEWPASSMSG, getdef_num ("PASS_MIN_LEN", 5));
  374.     for (i = 0;i < 3;i++) {
  375.         if (! (cp = getpass (NEWPASS))) {
  376.             bzero (orig, sizeof orig);
  377.             return -1;
  378.         } else
  379.             strcpy (pass, cp);
  380.  
  381.         if (! amroot && ! obscure (orig, pass)) {
  382.             printf (TRYAGAIN);
  383.             continue;
  384.         }
  385.         if (! (cp = getpass (NEWPASS2))) {
  386.             bzero (orig, sizeof orig);
  387.             return -1;
  388.         }
  389.         if (strcmp (cp, pass))
  390.             fprintf (stderr, NOMATCH);
  391.         else {
  392.             bzero (cp, strlen (cp));
  393.             break;
  394.         }
  395.     }
  396.     bzero (orig, sizeof orig);
  397.  
  398.     if (i == 3) {
  399.         bzero (pass, sizeof pass);
  400.         return -1;
  401.     }
  402.  
  403.     /*
  404.      * Encrypt the password.  The new password is encrypted and
  405.      * the shadow password structure updated to reflect the change.
  406.      */
  407.  
  408. #ifdef    SHADOWPWD
  409.     cp = pw_encrypt (pass, (char *) 0);
  410.     insert_crypt_passwd (sp->sp_pwdp, cp, new_auth);
  411.     cp = sp->sp_pwdp;
  412.     sp->sp_pwdp = strdup (new_auth);
  413.     update_age (sp);
  414. #else
  415.     cp = pw_encrypt (pass, (char *) 0);
  416.     insert_crypt_passwd (pw->pw_passwd, cp, new_auth);
  417.     cp = pw->pw_passwd;
  418.     pw->pw_passwd = strdup (new_auth);
  419. #ifdef    AGING
  420.     update_age (pw);
  421. #endif
  422. #endif    /* SHADOWPWD */
  423.     bzero (pass, sizeof pass);
  424.     bzero (new_auth, sizeof new_auth);
  425.     bzero (cp, strlen (cp));
  426.  
  427.     return 0;
  428. }
  429.  
  430. #ifdef    AGING
  431.  
  432. /*
  433.  * check_password - test a password to see if it can be changed
  434.  *
  435.  *    check_password() sees if the invoker has permission to change the
  436.  *    password for the given user.
  437.  */
  438.  
  439. /*ARGSUSED*/
  440. void
  441. #ifdef    SHADOWPWD
  442. check_password (pw, sp)
  443. struct    passwd    *pw;
  444. struct    spwd    *sp;
  445. #else
  446. check_password (pw)
  447. struct    passwd    *pw;
  448. #endif
  449. {
  450.     time_t    now = time ((time_t *) 0) / SCALE;
  451. #ifndef    SHADOWPWD
  452.     time_t    last;
  453.     time_t    ok;
  454. #endif
  455.  
  456.     /*
  457.      * Root can change any password any time.
  458.      */
  459.  
  460.     if (amroot)
  461.         return;
  462.  
  463.     /*
  464.      * Expired accounts cannot be changed ever.  Passwords
  465.      * which are locked may not be changed.  Passwords where
  466.      * min > max may not be changed.  Passwords which have
  467.      * been inactive too long cannot be changed.
  468.      */
  469.  
  470. #ifdef    SHADOWPWD
  471.     if ((sp->sp_expire > 0 && now >= sp->sp_expire) ||
  472.         (sp->sp_inact >= 0 && sp->sp_max >= 0 &&
  473.         now >= (sp->sp_lstchg + sp->sp_inact + sp->sp_max)) ||
  474.             strcmp (sp->sp_pwdp, "!") == 0 ||
  475.             sp->sp_min > sp->sp_max) {
  476.         fprintf (stderr, CANTCHANGE, sp->sp_namp);
  477. #ifdef    USE_SYSLOG
  478.         syslog (LOG_WARN, CANTCHANGE2, sp->sp_namp);
  479.         closelog ();
  480. #endif
  481.         exit (1);
  482.     }
  483. #endif    /* SHADOWPWD */
  484.  
  485.     /*
  486.      * Passwords may only be changed after sp_min time is up.
  487.      */
  488.  
  489. #ifdef    SHADOWPWD
  490.     if (sp->sp_min >= 0 && now < (sp->sp_lstchg + sp->sp_min)) {
  491.         fprintf (stderr, TOOSOON, sp->sp_namp);
  492. #ifdef    USE_SYSLOG
  493.         syslog (LOG_WARN, TOOSOON2, sp->sp_namp);
  494.         closelog ();
  495. #endif
  496.         exit (1);
  497.     }
  498. #else    /* !SHADOWPWD */
  499.  
  500.     /*
  501.      * Can always be changed if there is no age info
  502.      */
  503.  
  504.     if (! pw->pw_age[0])
  505.         return;
  506.  
  507.     last = a64l (pw->pw_age + 2) * WEEK;
  508.     ok = last + c64i (pw->pw_age[1]) * WEEK;
  509.  
  510.     if (now < ok) {
  511.         fprintf (stderr, TOOSOON, pw->pw_name);
  512. #ifdef    USE_SYSLOG
  513.         syslog (LOG_WARN, TOOSOON2, pw->pw_name);
  514.         closelog ();
  515. #endif
  516.         exit (1);
  517.     }
  518. #endif    /* SHADOWPWD */
  519. }
  520. #endif    /* AGING */
  521.  
  522. #ifdef    SHADOWPWD
  523.  
  524. /*
  525.  * pwd_to_spwd - create entries for new spwd structure
  526.  *
  527.  *    pwd_to_spwd() creates a new (struct spwd) containing the
  528.  *    information in the pointed-to (struct passwd).
  529.  */
  530.  
  531. void
  532. pwd_to_spwd (pw, sp)
  533. struct    passwd    *pw;
  534. struct    spwd    *sp;
  535. {
  536.     time_t    t;
  537.  
  538.     /*
  539.      * Nice, easy parts first.  The name and passwd map directly
  540.      * from the old password structure to the new one.
  541.      */
  542.  
  543.     sp->sp_namp = strdup (pw->pw_name);
  544.     sp->sp_pwdp = strdup (pw->pw_passwd);
  545. #ifdef    ATT_AGE
  546.  
  547.     /*
  548.      * AT&T-style password aging maps the sp_min, sp_max, and
  549.      * sp_lstchg information from the pw_age field, which appears
  550.      * after the encrypted password.
  551.      */
  552.  
  553.     if (pw->pw_age[0]) {
  554.         t = (c64i (pw->pw_age[0]) * WEEK) / SCALE;
  555.         sp->sp_max = t;
  556.  
  557.         if (pw->pw_age[1]) {
  558.             t = (c64i (pw->pw_age[1]) * WEEK) / SCALE;
  559.             sp->sp_min = t;
  560.         } else
  561.             sp->sp_min = (10000L * DAY) / SCALE;
  562.  
  563.         if (pw->pw_age[1] && pw->pw_age[2]) {
  564.             t = (a64l (pw->pw_age + 2) * WEEK) / SCALE;
  565.             sp->sp_lstchg = t;
  566.         } else
  567.             sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  568.     } else {
  569.         sp->sp_min = 0;
  570.         sp->sp_max = (10000L * DAY) / SCALE;
  571.         sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  572.     }
  573. #else    /* !ATT_AGE */
  574.     /*
  575.      * BSD does not use the pw_age field and has no aging information
  576.      * anywheres.  The default values are used to initialize the
  577.      * fields which are in the missing pw_age field;
  578.      */
  579.  
  580.     sp->sp_min = 0;
  581.     sp->sp_max = (10000L * DAY) / SCALE;
  582.     sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  583. #endif    /* ATT_AGE */
  584.  
  585.     /*
  586.      * These fields have no corresponding information in the password
  587.      * file.  They are set to uninitialized values.
  588.      */
  589.  
  590.     sp->sp_warn = -1;
  591.     sp->sp_inact = -1;
  592.     sp->sp_expire = -1;
  593.     sp->sp_flag = -1;
  594. }
  595.  
  596. #endif    /* SHADOWPWD */
  597.  
  598. /*
  599.  * print_status - print current password status
  600.  */
  601.  
  602. /*ARGSUSED*/
  603. void
  604. #ifdef    SHADOWPWD
  605. print_status (pw, sp)
  606. struct    passwd    *pw;
  607. struct    spwd    *sp;
  608. #else
  609. print_status (pw)
  610. struct    passwd    *pw;
  611. #endif
  612. {
  613. #ifdef    AGING
  614.     struct    tm    *tm;
  615.     time_t    last_time;
  616. #endif
  617. #ifdef    SHADOWPWD
  618.     last_time = sp->sp_lstchg * SCALE;
  619.     tm = gmtime (&last_time);
  620.  
  621.     printf ("%s ", sp->sp_namp);
  622.     printf ("%s ",
  623.         sp->sp_pwdp[0] ? (sp->sp_pwdp[0] == '!' ? "L":"P"):"NP");
  624.     printf ("%02.2d/%02.2d/%02.2d ",
  625.         tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
  626.     printf ("%d %d %d %d\n",
  627.         (sp->sp_min * SCALE) / DAY, (sp->sp_max * SCALE) / DAY,
  628.         (sp->sp_warn * SCALE) / DAY, (sp->sp_inact * SCALE) / DAY);
  629. #else
  630.     printf ("%s ", pw->pw_name);
  631.     printf ("%s",
  632.         pw->pw_passwd[0] ? (pw->pw_passwd[0] == '!' ? "L":"P"):"NP");
  633.  
  634. #ifdef    ATT_AGE
  635.     if (pw->pw_age[0])
  636.         last_time = a64l (pw->pw_age + 2);
  637.     else
  638.         last_time = 0L;
  639.  
  640.     tm = gmtime (&last_time);
  641.     printf (" %02.2d/%02.2d/%02.2d ",
  642.         tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
  643.     printf ("%d %d\n",
  644.         pw->pw_age[0] ? c64i (pw->pw_age[1]) * 7:10000,
  645.         pw->pw_age[0] ? c64i (pw->pw_age[0]) * 7:0);
  646. #else    /* !ATT_AGE */
  647.     putchar ('\n');
  648. #endif    /* ATT_AGE */
  649. #endif    /* SHADOWPWD */
  650. }
  651.  
  652. /*
  653.  * passwd - change a user's password file information
  654.  *
  655.  *    This command controls the password file and commands which are
  656.  *     used to modify it.
  657.  *
  658.  *    The valid options are
  659.  *
  660.  *    -l    lock the named account (*)
  661.  *    -d    delete the password for the named account (*)
  662.  *    -x #    set sp_max to # days (*)
  663.  *    -n #    set sp_min to # days (*)
  664.  *    -w #    set sp_warn to # days (*)
  665.  *    -i #    set sp_inact to # days (*)
  666.  *    -S    show password status of named account (*)
  667.  *    -g    execute gpasswd command to interpret flags
  668.  *    -f    execute chfn command to interpret flags
  669.  *    -s    execute chsh command to interpret flags
  670.  *
  671.  *    (*) requires root permission to execute.
  672.  *
  673.  *    All of the time fields are entered in days and converted to the
  674.  *     appropriate internal format.  For finer resolute the chage
  675.  *    command must be used.
  676.  */
  677.  
  678. int
  679. main (argc, argv)
  680. int    argc;
  681. char    **argv;
  682. {
  683.     char    buf[BUFSIZ];        /* I/O buffer for messages, etc.      */
  684.     char    new_passwd[BUFSIZ];    /* Buffer for changed passwords       */
  685.     char    *cp;            /* Miscellaneous character pointing   */
  686.     time_t    min;            /* Minimum days before change         */
  687.     time_t    max;            /* Maximum days until change          */
  688.     time_t    warn;            /* Warning days before change         */
  689.     time_t    inact;            /* Days without change before locked  */
  690.     int    i;            /* Loop control variable              */
  691.     int    flag;            /* Current option to process          */
  692.     int    lflg = 0;        /* -l - lock account option           */
  693.     int    uflg = 0;        /* -u - unlock account option         */
  694.     int    dflg = 0;        /* -d - delete password option        */
  695. #ifdef    AGING
  696.     int    xflg = 0;        /* -x - set maximum days              */
  697.     int    nflg = 0;        /* -n - set minimum days              */
  698.     int    wflg = 0;        /* -w - set warning days              */
  699.     int    iflg = 0;        /* -i - set inactive days             */
  700. #endif
  701.     int    Sflg = 0;        /* -S - show password status          */
  702.     struct    passwd    *pw;        /* Password file entry for user       */
  703. #ifdef    SHADOWPWD
  704.     struct    spwd    *sp;        /* Shadow file entry for user         */
  705.     struct    spwd    tspwd;        /* New shadow file entry if none      */
  706. #else
  707.     char    age[5];            /* New password age entry             */
  708. #endif
  709.  
  710.     /*
  711.      * The program behaves differently when executed by root
  712.      * than when executed by a normal user.
  713.      */
  714.  
  715.     amroot = getuid () == 0;
  716. #ifdef    NDBM
  717. #ifdef    SHADOWPWD
  718.     sp_dbm_mode = O_RDWR;
  719. #endif
  720.     pw_dbm_mode = O_RDWR;
  721. #endif
  722.  
  723.     /*
  724.      * Get the program name.  The program name is used as a
  725.      * prefix to most error messages.  It is also used as input
  726.      * to the openlog() function for error logging.
  727.      */
  728.  
  729.     if (Prog = strrchr (argv[0], '/'))
  730.         Prog++;
  731.     else
  732.         Prog = argv[0];
  733.  
  734. #ifdef    USE_SYSLOG
  735.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  736. #endif
  737.  
  738.     /*
  739.      * Start with the flags which cause another command to be
  740.      * executed.  The effective UID will be set back to the
  741.      * real UID and the new command executed with the flags
  742.      */
  743.  
  744.     if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
  745.         setuid (getuid ());
  746.         switch (argv[1][1]) {
  747.             case 'g':
  748.                 argv[1] = "gpasswd";
  749.                 execv ("/bin/gpasswd", &argv[1]);
  750.                 break;
  751.             case 'f':
  752.                 argv[1] = "chfn";
  753.                 execv ("/bin/chfn", &argv[1]);
  754.                 break;
  755.             case 's':
  756.                 argv[1] = "chsh";
  757.                 execv ("/bin/chsh", &argv[1]);
  758.                 break;
  759.             default:
  760.                 usage ();
  761.         }
  762.         sprintf (buf, EXECFAILED, Prog, argv[1]);
  763.         perror (buf);
  764. #ifdef    USE_SYSLOG
  765.         syslog (LOG_CRIT, EXECFAILED2, argv[1]);
  766.         closelog ();
  767. #endif
  768.         exit (1);
  769.     }
  770.  
  771.     /* 
  772.      * The remaining arguments will be processed one by one and
  773.      * executed by this command.  The name is the last argument
  774.      * if it does not begin with a "-", otherwise the name is
  775.      * determined from the environment and must agree with the
  776.      * real UID.  Also, the UID will be checked for any commands
  777.      * which are restricted to root only.
  778.      */
  779.  
  780. #ifdef    SHADOWPWD
  781.     while ((flag = getopt (argc, argv, "ludx:n:w:i:S")) != EOF)
  782. #else
  783.     while ((flag = getopt (argc, argv, "ludx:n:S")) != EOF)
  784. #endif
  785.     {
  786.         switch (flag) {
  787.             case 'x':
  788.                 max = strtol (optarg, &cp, 10);
  789. #ifndef    SHADOWPWD
  790.                 if (max > 0)
  791.                     max /= 7;
  792. #endif
  793.                 if (*cp || getuid ())
  794.                     usage ();
  795.  
  796.                 xflg++;
  797.                 break;
  798.             case 'n':
  799.                 min = strtol (optarg, &cp, 10);
  800. #ifndef    SHADOWPWD
  801.                 if (min > 0)
  802.                     min /= 7;
  803. #endif
  804.                 if (*cp || getuid ())
  805.                     usage ();
  806.  
  807.                 nflg++;
  808.                 break;
  809. #ifdef    SHADOWPWD
  810.             case 'w':
  811.                 warn = strtol (optarg, &cp, 10);
  812.                 if (*cp || getuid ())
  813.                     usage ();
  814.  
  815.                 if (warn >= -1)
  816.                     wflg++;
  817.  
  818.                 break;
  819.             case 'i':
  820.                 inact = strtol (optarg, &cp, 10);
  821.                 if (*cp || getuid ())
  822.                     usage ();
  823.  
  824.                 if (inact >= -1)
  825.                     iflg++;
  826.  
  827.                 break;
  828. #endif    /* SHADOWPWD */
  829.             case 'S':
  830.                 if (getuid ())
  831.                     usage ();
  832.  
  833.                 Sflg++;
  834.                 break;
  835.             case 'd':
  836.                 if (getuid ())
  837.                     usage ();
  838.  
  839.                 dflg++;
  840.                 break;
  841.             case 'l':
  842.                 if (getuid ())
  843.                     usage ();
  844.  
  845.                 lflg++;
  846.                 break;
  847.             case 'u':
  848.                 if (getuid ())
  849.                     usage ();
  850.  
  851.                 uflg++;
  852.                 break;
  853.             default:
  854.                 usage ();
  855.         }
  856.     }
  857.  
  858.     /*
  859.      * If any of the flags were given, a user name must be supplied
  860.      * on the command line.  Only an unadorned command line doesn't
  861.      * require the user's name be given.  Also, on -x, -n, -m, and
  862.      * -i may appear with each other.  -d, -l and -S must appear alone.
  863.      */
  864.  
  865. #ifdef    SHADOWPWD
  866.     if ((dflg || lflg || xflg || nflg ||
  867.                 wflg || iflg || Sflg) && optind >= argc)
  868. #else
  869.     if ((dflg || lflg || xflg || nflg || Sflg) && optind >= argc)
  870. #endif
  871.         usage ();
  872.  
  873. #ifdef    SHADOWPWD
  874.     if ((dflg + lflg + uflg + (xflg || nflg || wflg || iflg) + Sflg) > 1)
  875. #else
  876.     if ((dflg + lflg + uflg + (xflg || nflg) + Sflg) > 1)
  877. #endif
  878.         usage ();
  879.  
  880.     /*
  881.      * Now I have to get the user name.  The name will be gotten 
  882.      * from the command line if possible.  Otherwise it is figured
  883.      * out from the environment.
  884.      */
  885.  
  886.     if (optind < argc) {
  887.         strncpy (name, argv[optind], sizeof name);
  888.         name[sizeof name - 1] = '\0';
  889.     } else if (amroot) {
  890.         strcpy (name, "root");
  891.     } else if (cp = getlogin ()) {
  892.         strncpy (name, cp, sizeof name);
  893.         name[sizeof name - 1] = '\0';
  894.     } else {
  895.         fprintf (stderr, WHOAREYOU, Prog);
  896. #ifdef    USE_SYSLOG
  897.         closelog ();
  898. #endif
  899.         exit (1);
  900.     }
  901.  
  902.     /*
  903.      * Now I have a name, let's see if the UID for the name
  904.      * matches the current real UID.
  905.      */
  906.  
  907.     if (! (pw = getpwnam (name))) {
  908.         fprintf (stderr, UNKUSER, Prog, name);
  909. #ifdef    USE_SYSLOG
  910.         closelog ();
  911. #endif
  912.         exit (1);
  913.     }
  914.     if (! amroot && pw->pw_uid != getuid ()) {
  915.         fprintf (stderr, NOPERM, name);
  916. #ifdef    USE_SYSLOG
  917.         syslog (LOG_WARN, NOPERM2, name);
  918.         closelog ();
  919. #endif
  920.         exit (1);
  921.     }
  922.  
  923.     /*
  924.      * Let the user know whose password is being changed.
  925.      */
  926.  
  927.     if (! Sflg)
  928.         printf (CHANGING, name);
  929.  
  930. #ifdef    SHADOWPWD
  931.     /*
  932.      * The user name is valid, so let's get the shadow file
  933.      * entry.
  934.      */
  935.  
  936.     if (! (sp = getspnam (name)))
  937.         pwd_to_spwd (pw, sp = &tspwd);
  938.  
  939.     /*
  940.      * Save the shadow entry off to the side so it doesn't
  941.      * get changed by any of the following code.
  942.      */
  943.  
  944.     if (sp != &tspwd) {
  945.         tspwd = *sp;
  946.         sp = &tspwd;
  947.     }
  948.     tspwd.sp_namp = strdup (sp->sp_namp);
  949.     tspwd.sp_pwdp = strdup (sp->sp_pwdp);
  950. #endif    /* SHADOWPWD */
  951.  
  952.     if (Sflg) {
  953. #ifdef    SHADOWPWD
  954.         print_status (pw, sp);
  955. #else
  956.         print_status (pw);
  957. #endif
  958. #ifdef    USE_SYSLOG
  959.         closelog ();
  960. #endif
  961.         exit (0);
  962.     }
  963.  
  964.     /*
  965.      * If there are no other flags, just change the password.
  966.      */
  967.  
  968. #ifdef    SHADOWPWD
  969.     if (! (dflg || lflg || uflg || xflg || nflg || wflg || iflg))
  970. #else
  971.     if (! (dflg || lflg || uflg || xflg || nflg))
  972. #endif
  973.     {
  974. #ifdef    SHADOWPWD
  975.         if (strchr (sp->sp_pwdp, '@')) {
  976.             if (pw_auth (sp->sp_pwdp, name, PW_CHANGE, 0)) {
  977. #ifdef    USE_SYSLOG
  978.                 syslog (LOG_INFO, NOCHGPASSWD, name);
  979.                 closelog ();
  980. #endif
  981.                 exit (1);
  982.             } else {
  983.                 update_age (sp);
  984.  
  985.                 if (! uses_default_method (sp->sp_pwdp))
  986.                     goto done;
  987.             }
  988.         } else
  989.             strcpy (crypt_passwd, sp->sp_pwdp);
  990. #else    /* !SHADOWPWD */
  991.         if (strchr (pw->pw_passwd, '@')) {
  992.             if (pw_auth (pw->pw_passwd, name, PW_CHANGE, 0)) {
  993. #ifdef    USE_SYSLOG
  994.                 syslog (LOG_INFO, CHGPASSWD, name);
  995.                 closelog ();
  996. #endif
  997.                 exit (0);
  998.             } else {
  999.                 update_age (pw);
  1000.  
  1001.                 if (! uses_default_method (pw->pw_passwd))
  1002.                     goto done;
  1003.             }
  1004.         } else
  1005.             strcpy (crypt_passwd, pw_pw_passwd);
  1006. #endif    /* SHADOWPWD */
  1007.  
  1008.         /*
  1009.          * See if the user is permitted to change the password.
  1010.          * Otherwise, go ahead and set a new password.
  1011.          */
  1012.  
  1013. #ifdef    SHADOWPWD
  1014.         check_password (pw, sp);
  1015.         if (new_password (pw, sp))
  1016. #else
  1017. #ifdef    AGING
  1018.  
  1019.         /*
  1020.          * Only check the age when there is one to check.
  1021.          */
  1022.  
  1023.         check_password (pw);
  1024. #endif
  1025.         if (new_password (pw))
  1026. #endif
  1027.         {
  1028.             fprintf (stderr, UNCHANGED, name);
  1029. #ifdef    USE_SYSLOG
  1030.             closelog ();
  1031. #endif
  1032.             exit (1);
  1033.         }
  1034.     }
  1035.  
  1036.     /*
  1037.      * The other options are incredibly simple.  Just modify the
  1038.      * field in the shadow file entry.
  1039.      */
  1040.  
  1041.     if (dflg)            /* Set password to blank */
  1042. #ifdef    SHADOWPWD
  1043.         sp->sp_pwdp = "";
  1044. #else
  1045.         pw->pw_passwd = "";
  1046. #endif
  1047.     if (lflg) {            /* Set password to "locked" value */
  1048. #ifdef    SHADOWPWD
  1049.         if (sp->sp_pwdp && sp->sp_pwdp[0] != '!') {
  1050.             strcpy (new_passwd, "!");
  1051.             strcat (new_passwd, sp->sp_pwdp);
  1052.             sp->sp_pwdp = new_passwd;
  1053.         }
  1054. #else
  1055.         if (pw->pw_passwd & pw->pw_passwd[0] != '!') {
  1056.             strcpy (new_passwd, "!");
  1057.             strcat (new_passwd, pw->pw_passwd);
  1058.             pw->pw_passwd = new_passwd;
  1059.         }
  1060. #endif
  1061.     }
  1062.     if (uflg) {            /* Undo password "locked" value */
  1063. #ifdef    SHADOWPWD
  1064.         if (sp->sp_pwdp && sp->sp_pwdp[0] == '!') {
  1065.             strcpy (new_passwd, sp->sp_pwdp + 1);
  1066.             sp->sp_pwdp = new_passwd;
  1067.         }
  1068. #else
  1069.         if (pw->pw_passwd && pw->pw_passwd[0] == '!') {
  1070.             strcpy (new_passwd, pw->pw_passwd + 1);
  1071.             pw->pw_passwd = new_passwd;
  1072.         }
  1073. #endif
  1074.     }
  1075. #if !defined(SHADOWPWD) && defined(ATT_AGE)
  1076.     bzero (age, sizeof age);
  1077.     strcpy (age, pw->pw_age);
  1078. #endif
  1079.     if (xflg) {
  1080. #ifdef    SHADOWPWD
  1081.         sp->sp_max = (max * DAY) / SCALE;
  1082. #else
  1083.         age[0] = i64c (max);
  1084. #endif
  1085.     }
  1086.     if (nflg) {
  1087. #ifdef    SHADOWPWD
  1088.         sp->sp_min = (min * DAY) / SCALE;
  1089. #else
  1090.         if (age[0] == '\0')
  1091.             age[0] = '/';
  1092.  
  1093.         age[1] = i64c (min);
  1094. #endif
  1095.     }
  1096. #ifdef    SHADOWPWD
  1097.     if (wflg)
  1098.         sp->sp_warn = (warn * DAY) / SCALE;
  1099.  
  1100.     if (iflg)
  1101.         sp->sp_inact = (inact * DAY) / SCALE;
  1102. #endif
  1103. #if !defined(SHADOWPWD) && defined(ATT_AGE)
  1104.     pw->pw_age = age;
  1105. #endif
  1106.  
  1107. done:
  1108.     /*
  1109.      * Before going any further, raise the ulimit to prevent
  1110.      * colliding into a lowered ulimit, and set the real UID
  1111.      * to root to protect against unexpected signals.  Any
  1112.      * keyboard signals are set to be ignored.
  1113.      */
  1114.  
  1115. #ifdef    HAVE_ULIMIT
  1116.     ulimit (UL_SFILLIM, 30000);
  1117. #endif
  1118. #ifdef    HAVE_RLIMIT
  1119.     setrlimit (RLIMIT_FSIZE, &rlimit_fsize);
  1120. #endif
  1121.     if (setuid (0)) {
  1122.         fprintf (stderr, NOTROOT);
  1123. #ifdef    USE_SYSLOG
  1124.         syslog (LOG_ERR, NOTROOT2);
  1125.         closelog ();
  1126. #endif
  1127.         exit (1);
  1128.     }
  1129.     signal (SIGHUP, SIG_IGN);
  1130.     signal (SIGINT, SIG_IGN);
  1131.     signal (SIGQUIT, SIG_IGN);
  1132. #ifdef    SIGTSTP
  1133.     signal (SIGTSTP, SIG_IGN);
  1134. #endif
  1135.  
  1136.     /*
  1137.      * The shadow entry is now ready to be committed back to
  1138.      * the shadow file.  Get a lock on the file and open it.
  1139.      */
  1140.  
  1141.     for (i = 0;i < 30;i++)
  1142. #ifdef    SHADOWPWD
  1143.         if (spw_lock ())
  1144. #else
  1145.         if (pw_lock ())
  1146. #endif
  1147.             break;
  1148.  
  1149.     if (i == 30) {
  1150. #ifdef    SHADOWPWD
  1151.         fprintf (stderr, SPWDBUSY);
  1152. #else
  1153.         fprintf (stderr, PWDBUSY);
  1154. #endif
  1155. #ifdef    USE_SYSLOG
  1156. #ifdef    SHADOWPWD
  1157.         syslog (LOG_WARN, SPWDBUSY2);
  1158. #else
  1159.         syslog (LOG_WARN, PWDBUSY2);
  1160. #endif
  1161.         closelog ();
  1162. #endif
  1163.         exit (1);
  1164.     }
  1165. #ifdef    SHADOWPWD
  1166.     if (! spw_open (O_RDWR))
  1167. #else
  1168.     if (! pw_open (O_RDWR))
  1169. #endif
  1170.     {
  1171.         fprintf (stderr, OPNERROR);
  1172. #ifdef    USE_SYSLOG
  1173.         syslog (LOG_ERR, OPNERROR2);
  1174.         closelog ();
  1175. #endif
  1176. #ifdef    SHADOWPWD
  1177.         (void) spw_unlock ();
  1178. #else
  1179.         (void) pw_unlock ();
  1180. #endif
  1181.         exit (1);
  1182.     }
  1183.  
  1184.     /*
  1185.      * Update the shadow file entry.  If there is a DBM file,
  1186.      * update that entry as well.
  1187.      */
  1188.  
  1189. #ifdef    SHADOWPWD
  1190.     if (! spw_update (sp))
  1191. #else
  1192.     if (! pw_update (pw))
  1193. #endif
  1194.     {
  1195.         fprintf (stderr, UPDERROR);
  1196. #ifdef    USE_SYSLOG
  1197.         syslog (LOG_ERR, UPDERROR2);
  1198.         closelog ();
  1199. #endif
  1200. #ifdef    SHADOWPWD
  1201.         (void) spw_unlock ();
  1202. #else
  1203.         (void) pw_unlock ();
  1204. #endif
  1205.         exit (1);
  1206.     }
  1207. #ifdef    NDBM
  1208. #ifdef    SHADOWPWD
  1209.     if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp))
  1210. #else
  1211.     if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw))
  1212. #endif
  1213.     {
  1214.         fprintf (stderr, DBMERROR);
  1215. #ifdef    USE_SYSLOG
  1216.         syslog (LOG_ERR, DBMERROR2);
  1217.         closelog ();
  1218. #endif
  1219. #ifdef    SHADOWPWD
  1220.         (void) spw_unlock ();
  1221. #else
  1222.         (void) pw_unlock ();
  1223. #endif
  1224.         exit (1);
  1225.     }
  1226. #ifdef    SHADOWPWD
  1227.     endspent ();
  1228. #endif
  1229.     endpwent ();
  1230. #endif    /* NDBM */
  1231.  
  1232.     /*
  1233.      * Changes have all been made, so commit them and unlock the
  1234.      * file.
  1235.      */
  1236.  
  1237. #ifdef    SHADOWPWD
  1238.     if (! spw_close ())
  1239. #else
  1240.     if (! pw_close ())
  1241. #endif
  1242.     {
  1243.         fprintf (stderr, CLSERROR);
  1244. #ifdef    USE_SYSLOG
  1245.         syslog (LOG_ERR, CLSERROR2);
  1246.         closelog ();
  1247. #endif
  1248. #ifdef    SHADOWPWD
  1249.         (void) spw_unlock ();
  1250. #else
  1251.         (void) pw_unlock ();
  1252. #endif
  1253.         exit (1);
  1254.     }
  1255. #ifdef    SHADOWPWD
  1256.     if (! spw_unlock ())
  1257. #else
  1258.     if (! pw_unlock ())
  1259. #endif
  1260.     {
  1261.         fprintf (stderr, UNLKERROR);
  1262. #ifdef    USE_SYSLOG
  1263.         syslog (LOG_ERR, UNLKERROR2);
  1264.         closelog ();
  1265. #endif
  1266.         exit (1);
  1267.     }
  1268. #ifdef    USE_SYSLOG
  1269.     syslog (LOG_INFO, CHGPASSWD, name);
  1270.     closelog ();
  1271. #endif
  1272.     exit (0);
  1273.     /*NOTREACHED*/
  1274. }
  1275.