home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / shadow / part03 / chage.c next >
C/C++ Source or Header  |  1993-08-14  |  22KB  |  1,007 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 <sys/types.h>
  16. #include <stdio.h>
  17. #include <fcntl.h>
  18. #include <signal.h>
  19. #include <ctype.h>
  20. #include <time.h>
  21.  
  22. #ifndef    lint
  23. static    char    sccsid[] = "@(#)chage.c    3.12    08:07:05    19 Jul 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. #include "config.h"
  42. #include "pwd.h"
  43.  
  44. /*
  45.  * chage depends on some form of aging being present.  It makes no sense
  46.  * to have a program that has no input.
  47.  */
  48.  
  49. #ifdef    SHADOWPWD
  50. #include "shadow.h"
  51. #ifndef    AGING
  52. #define    AGING
  53. #endif    /* AGING */
  54. #else    /* !SHADOWPWD */
  55. #if !defined(ATT_AGE) && defined(AGING)
  56. #undef    AGING
  57. #endif    /* !ATT_AGE && AGING */
  58. #endif    /* SHADOWPWD */
  59.  
  60. #ifdef    USE_SYSLOG
  61. #include <syslog.h>
  62.  
  63. #ifndef    LOG_WARN
  64. #define    LOG_WARN LOG_WARNING
  65. #endif    /* !LOG_WARN */
  66. #endif    /* USE_SYSLOG */
  67.  
  68. #ifdef    AGING
  69.  
  70. /*
  71.  * Global variables
  72.  */
  73.  
  74. char    *Prog;
  75. long    mindays;
  76. long    maxdays;
  77. long    lastday;
  78. #ifdef    SHADOWPWD
  79. long    warndays;
  80. long    inactdays;
  81. long    expdays;
  82. #endif
  83. void    cleanup();
  84.  
  85. /*
  86.  * External identifiers
  87.  */
  88.  
  89. extern    long    a64l();
  90. extern    char    *l64a();
  91. extern    int    pw_lock(), pw_open(),
  92.         pw_unlock(), pw_close(),
  93.         pw_update();
  94. extern    struct    passwd    *pw_locate();
  95. #ifdef    SHADOWPWD
  96. extern    int    spw_lock(), spw_open(),
  97.         spw_unlock(), spw_close(),
  98.         spw_update();
  99. extern    struct    spwd    *spw_locate();
  100. #endif
  101. extern    int    optind;
  102. extern    char    *optarg;
  103. extern    char    *getlogin ();
  104. #ifdef    NDBM
  105. extern    int    pw_dbm_mode;
  106. #ifdef    SHADOWPWD
  107. extern    int    sp_dbm_mode;
  108. #endif
  109. #endif
  110.  
  111. /*
  112.  * Password aging constants
  113.  *
  114.  *    DAY - seconds in a day
  115.  *    WEEK - seconds in a week
  116.  *    SCALE - convert from clock to aging units
  117.  */
  118.  
  119. #define    DAY    (24L*3600L)
  120. #define    WEEK    (7*DAY)
  121.  
  122. #ifdef    ITI_AGING
  123. #define    SCALE    (1)
  124. #else
  125. #define    SCALE    (DAY)
  126. #endif
  127.  
  128. #if !defined(MDY_DATE) && !defined(DMY_DATE) && !defined(YMD_DATE)
  129. #define    MDY_DATE    1
  130. #endif
  131. #if (defined (MDY_DATE) && (defined (DMY_DATE) || defined (YMD_DATE))) || \
  132.     (defined (DMY_DATE) && (defined (MDY_DATE) || defined (YMD_DATE)))
  133. Error: You must only define one of MDY_DATE, DMY_DATE, or YMD_DATE
  134. #endif
  135.  
  136. /*
  137.  * days and juldays are used to compute the number of days in the
  138.  * current month, and the cummulative number of days in the preceding
  139.  * months.  they are declared so that january is 1, not 0.
  140.  */
  141.  
  142. static    short    days[13] = { 0,
  143.     31,    28,    31,    30,    31,    30,    /* JAN - JUN */
  144.     31,    31,    30,    31,    30,    31 };    /* JUL - DEC */
  145.  
  146. static    short    juldays[13] = { 0,
  147.     0,    31,    59,    90,    120,    151,    /* JAN - JUN */
  148.     181,    212,    243,    273,    304,    334 };    /* JUL - DEC */
  149.  
  150. /*
  151.  * #defines for messages.  This facilitates foreign language conversion
  152.  * since all messages are defined right here.
  153.  */
  154.  
  155. #ifdef    SHADOWPWD
  156. #define    USAGE \
  157. "Usage: %s [ -l ] [ -m min_days ] [ -M max_days ] [ -W warn ]\n\
  158.        [ -I inactive ] [ -E expire ] [ -d last_day ] user\n"
  159. #else
  160. #define    USAGE \
  161. "Usage: %s [ -l ] [ -m min_days ] [ -M max_days ] [ -d last_day ] user\n"
  162. #endif
  163. #define    DBMERROR    "Error updating the DBM password entry.\n"
  164. #define    UNK_USER    "%s: unknown user: %s\n"
  165. #define    NO_LFLAG    "%s: do no include \"l\" with other flags\n"
  166. #define    NO_PERM        "%s: permission denied\n"
  167. #define    NO_PWLOCK    "%s: can't lock password file\n"
  168. #define    NO_PWOPEN    "%s: can't open password file\n"
  169. #define    CHANGE_INFO    "Changing the aging information for %s\n"
  170. #define    AGE_CHANGED    "changed password expiry for %s\n"
  171. #define    FIELD_ERR    "%s: error changing fields\n"
  172. #define    NO_PWUPDATE    "%s: can't update password file\n"
  173. #define    NO_PWCLOSE    "%s: can't rewrite password file\n"
  174. #define    LOCK_FAIL    "failed locking %s\n"
  175. #define    OPEN_FAIL    "failed opening %s\n"
  176. #define    WRITE_FAIL    "failed updating %s\n"
  177. #define    CLOSE_FAIL    "failed rewriting %s\n"
  178. #ifdef    MDY_DATE
  179. #define    LAST_CHG    "Last Password Change (MM/DD/YY)"
  180. #ifdef    SHADOWPWD
  181. #define    ACCT_EXP    "Account Expiration Date (MM/DD/YY)"
  182. #endif
  183. #define    EPOCH        "12/31/69"
  184. #endif
  185. #ifdef    DMY_DATE
  186. #define    LAST_CHG    "Last Password Change (DD/MM/YY)"
  187. #ifdef    SHADOWPWD
  188. #define    ACCT_EXP    "Account Expiration Date (DD/MM/YY)"
  189. #endif
  190. #define    EPOCH        "31/12/69"
  191. #endif
  192. #ifdef    YMD_DATE
  193. #define    LAST_CHG    "Last Password Change (YY/MM/DD)"
  194. #ifdef    SHADOWPWD
  195. #define    ACCT_EXP    "Account Expiration Date (YY/MM/DD)"
  196. #endif
  197. #define    EPOCH        "69/12/31"
  198. #endif
  199. #ifdef    SHADOWPWD
  200. #define    DBMERROR2    "error updating DBM shadow entry.\n"
  201. #define    NO_SPLOCK    "%s: can't lock shadow password file\n"
  202. #define    NO_SPOPEN    "%s: can't open shadow password file\n"
  203. #define    NO_SPUPDATE    "%s: can't update shadow password file\n"
  204. #define    NO_SPCLOSE    "%s: can't rewrite shadow password file\n"
  205. #else
  206. #define    DBMERROR2    "error updating DBM passwd entry.\n"
  207. #endif
  208.  
  209. /*
  210.  * usage - print command line syntax and exit
  211.  */
  212.  
  213. void
  214. usage ()
  215. {
  216.     fprintf (stderr, USAGE, Prog);
  217.     exit (1);
  218. }
  219.  
  220. /*
  221.  * strtoday - compute the number of days since 1970.
  222.  *
  223.  * the total number of days prior to the current date is
  224.  * computed.  january 1, 1970 is used as the origin with
  225.  * it having a day number of 0.  the gmtime() routine is
  226.  * used to prevent confusion regarding time zones.
  227.  */
  228.  
  229. long
  230. strtoday (str)
  231. char    *str;
  232. {
  233.     char    slop[2];
  234.     int    month;
  235.     int    day;
  236.     int    year;
  237.     long    total;
  238.  
  239.     /*
  240.      * start by separating the month, day and year.  the order
  241.      * is compiled in ...
  242.      */
  243.  
  244. #ifdef    MDY_DATE
  245.     if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
  246.         return -1;
  247. #endif
  248. #ifdef    DMY_DATE
  249.     if (sscanf (str, "%d/%d/%d%c", &day, &month, &year, slop) != 3)
  250.         return -1;
  251. #endif
  252. #ifdef    YMD_DATE
  253.     if (sscanf (str, "%d/%d/%d%c", &year, &month, &day, slop) != 3)
  254.         return -1;
  255. #endif
  256.  
  257.     /*
  258.      * the month, day of the month, and year are checked for
  259.      * correctness and the year adjusted so it falls between
  260.      * 1970 and 2069.
  261.      */
  262.  
  263.     if (month < 1 || month > 12)
  264.         return -1;
  265.  
  266.     if (day < 1)
  267.         return -1;
  268.  
  269.     if ((month != 2 || (year % 4) != 0) && day > days[month])
  270.         return -1;
  271.     else if ((month == 2 && (year % 4) == 0) && day > 29)
  272.         return -1;
  273.  
  274.     if (year < 0)
  275.         return -1;
  276.     else if (year < 69)
  277.         year += 2000;
  278.     else if (year < 99)
  279.         year += 1900;
  280.  
  281.     if (year < 1970 || year > 2069)
  282.         return -1;
  283.  
  284.     /*
  285.      * the total number of days is the total number of days in all
  286.      * the whole years, plus the number of leap days, plus the
  287.      * number of days in the whole months preceding, plus the number
  288.      * of days so far in the month.
  289.      */
  290.  
  291.     total = ((year - 1970) * 365) + (((year + 1) - 1970) / 4);
  292.     total += juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
  293.     total += day - 1;
  294.  
  295.     return total;
  296. }
  297.  
  298. /*
  299.  * new_fields - change the user's password aging information interactively.
  300.  *
  301.  * prompt the user for all of the password age values.  set the fields
  302.  * from the user's response, or leave alone if nothing was entered.  the
  303.  * value (-1) is used to indicate the field should be removed if possible.
  304.  * any other negative value is an error.  very large positive values will
  305.  * be handled elsewhere.
  306.  */
  307.  
  308. int
  309. new_fields ()
  310. {
  311.     char    buf[BUFSIZ];
  312.     char    *cp;
  313.     long    value;
  314.     struct    tm    *tp;
  315.  
  316.     printf ("Enter the new value, or press return for the default\n\n");
  317.  
  318.     sprintf (buf, "%ld", mindays);
  319.     change_field (buf, "Minimum Password Age");
  320.     if (((mindays = strtol (buf, &cp, 10)) == 0 && *cp) || mindays < -1)
  321.         return 0;
  322.  
  323.     sprintf (buf, "%ld", maxdays);
  324.     change_field (buf, "Maximum Password Age");
  325.     if (((maxdays = strtol (buf, &cp, 10)) == 0 && *cp) || maxdays < -1)
  326.         return 0;
  327.  
  328.     value = lastday * SCALE;
  329.     tp = gmtime (&value);
  330.     sprintf (buf, "%02d/%02d/%02d",
  331. #ifdef    MDY_DATE
  332.         tp->tm_mon + 1, tp->tm_mday, tp->tm_year
  333. #endif
  334. #ifdef    DMY_DATE
  335.         tp->tm_mday, tp->tm_mon + 1, tp->tm_year
  336. #endif
  337. #ifdef    YMD_DATE
  338.         tp->tm_year, tp->tm_mon + 1, tp->tm_mday
  339. #endif
  340.         );
  341.  
  342.     change_field (buf, LAST_CHG);
  343.     if (strcmp (buf, EPOCH) == 0)
  344.         lastday = -1;
  345.     else if ((lastday = strtoday (buf)) == -1)
  346.         return 0;
  347.  
  348. #ifdef    SHADOWPWD
  349.     sprintf (buf, "%ld", warndays);
  350.     change_field (buf, "Password Expiration Warning");
  351.     if (((warndays = strtol (buf, &cp, 10)) == 0 && *cp) || warndays < -1)
  352.         return 0;
  353.  
  354.     sprintf (buf, "%ld", inactdays);
  355.     change_field (buf, "Password Inactive");
  356.     if (((inactdays = strtol (buf, &cp, 10)) == 0 && *cp) || inactdays < -1)
  357.         return 0;
  358.  
  359.     value = expdays * SCALE;
  360.     tp = gmtime (&value);
  361.     sprintf (buf, "%02d/%02d/%02d",
  362. #ifdef    MDY_DATE
  363.         tp->tm_mon + 1, tp->tm_mday, tp->tm_year
  364. #endif
  365. #ifdef    DMY_DATE
  366.         tp->tm_mday, tp->tm_mon + 1, tp->tm_year
  367. #endif
  368. #ifdef    YMD_DATE
  369.         tp->tm_year, tp->tm_mon + 1, tp->tm_mday
  370. #endif
  371.         );
  372.  
  373.     change_field (buf, ACCT_EXP);
  374.     if (strcmp (buf, EPOCH) == 0)
  375.         expdays = -1;
  376.     else if ((expdays = strtoday (buf)) == -1)
  377.         return 0;
  378. #endif    /* SHADOWPWD */
  379.  
  380.     return 1;
  381. }
  382.  
  383. /*
  384.  * list_fields - display the current values of the expiration fields
  385.  *
  386.  * display the password age information from the password fields.  date
  387.  * values will be displayed as a calendar date, or the word "Never" if
  388.  * the date is 1/1/70, which is day number 0.
  389.  */
  390.  
  391. void
  392. list_fields ()
  393. {
  394.     struct    tm    *tp;
  395.     char    *cp;
  396.     long    changed;
  397.     long    expires;
  398.  
  399.     /*
  400.      * Start with the easy numbers - the number of days before the
  401.      * password can be changed, the number of days after which the
  402.      * password must be chaged, the number of days before the
  403.      * password expires that the user is told, and the number of
  404.      * days after the password expires that the account becomes
  405.      * unusable.
  406.      */
  407.  
  408.     printf ("Minimum:\t%d\n", mindays);
  409.     printf ("Maximum:\t%d\n", maxdays);
  410. #ifdef    SHADOWPWD
  411.     printf ("Warning:\t%d\n", warndays);
  412.     printf ("Inactive:\t%d\n", inactdays);
  413. #endif
  414.  
  415.     /*
  416.      * The "last change" date is either "Never" or the date the
  417.      * password was last modified.  The date is the number of
  418.      * days since 1/1/1970.
  419.      */
  420.  
  421.     printf ("Last Change:\t\t");
  422.     if (lastday <= 0) {
  423.         printf ("Never\n");
  424.     } else {
  425.         changed = lastday * SCALE;
  426.         tp = gmtime (&changed);
  427.         cp = asctime (tp);
  428.         printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
  429.     }
  430.  
  431.     /*
  432.      * The password expiration date is determined from the last
  433.      * change date plus the number of days the password is valid
  434.      * for.
  435.      */
  436.  
  437.     printf ("Password Expires:\t");
  438.     if (lastday <= 0 || maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) {
  439.         printf ("Never\n");
  440.     } else {
  441.         expires = changed + maxdays * SCALE;
  442.         tp = gmtime (&expires);
  443.         cp = asctime (tp);
  444.         printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
  445.     }
  446.  
  447. #ifdef    SHADOWPWD
  448.     /*
  449.      * The account becomes inactive if the password is expired
  450.      * for more than "inactdays".  The expiration date is calculated
  451.      * and the number of inactive days is added.  The resulting date
  452.      * is when the active will be disabled.
  453.      */
  454.  
  455.     printf ("Password Inactive:\t");
  456.     if (lastday <= 0 || inactdays <= 0 ||
  457.             maxdays >= 10000*(DAY/SCALE) || maxdays <= 0) {
  458.         printf ("Never\n");
  459.     } else {
  460.         expires = changed + (maxdays + inactdays) * SCALE;
  461.         tp = gmtime (&expires);
  462.         cp = asctime (tp);
  463.         printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
  464.     }
  465.  
  466.     /*
  467.      * The account will expire on the given date regardless of the
  468.      * password expiring or not.
  469.      */
  470.  
  471.     printf ("Account Expires:\t");
  472.     if (expdays <= 0) {
  473.         printf ("Never\n");
  474.     } else {
  475.         expires = expdays * SCALE;
  476.         tp = gmtime (&expires);
  477.         cp = asctime (tp);
  478.         printf ("%6.6s, %4.4s\n", cp + 4, cp + 20);
  479.     }
  480. #endif
  481. }
  482.  
  483. /*
  484.  * chage - change a user's password aging information
  485.  *
  486.  *    This command controls the password aging information.
  487.  *
  488.  *    The valid options are
  489.  *
  490.  *    -m    minimum number of days before password change (*)
  491.  *    -M    maximim number of days before password change (*)
  492.  *    -d    last password change date (*)
  493.  *    -l    password aging information
  494.  *    -W    expiration warning days (*)
  495.  *    -I    password inactive after expiration (*)
  496.  *    -E    account expiration date (*)
  497.  *
  498.  *    (*) requires root permission to execute.
  499.  *
  500.  *    All of the time fields are entered in the internal format
  501.  *    which is either seconds or days.
  502.  *
  503.  *    The options -W, -I and -E all depend on the SHADOWPWD
  504.  *    macro being defined.
  505.  */
  506.  
  507. int
  508. main (argc, argv)
  509. int    argc;
  510. char    **argv;
  511. {
  512.     int    flag;
  513.     int    lflg = 0;
  514.     int    mflg = 0;
  515.     int    Mflg = 0;
  516.     int    dflg = 0;
  517. #ifdef    SHADOWPWD
  518.     int    Wflg = 0;
  519.     int    Iflg = 0;
  520.     int    Eflg = 0;
  521.     struct    spwd    *sp;
  522.     struct    spwd    spwd;
  523. #else
  524.     char    new_age[5];
  525. #endif
  526.     int    ruid = getuid ();
  527.     struct    passwd    *pw;
  528.     struct    passwd    pwent;
  529.     char    name[BUFSIZ];
  530.  
  531.     /*
  532.      * Get the program name so that error messages can use it.
  533.      */
  534.  
  535.     if (Prog = strrchr (argv[0], '/'))
  536.         Prog++;
  537.     else
  538.         Prog = argv[0];
  539.  
  540. #ifdef    USE_SYSLOG
  541.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  542. #endif
  543. #ifdef    NDBM
  544. #ifdef    SHADOWPWD
  545.     sp_dbm_mode = O_RDWR;
  546. #endif
  547.     pw_dbm_mode = O_RDWR;
  548. #endif
  549.  
  550.     /*
  551.      * Parse the flags.  The difference between password file
  552.      * formats includes the number of fields, and whether the
  553.      * dates are entered as days or weeks.  Shadow password
  554.      * file info =must= be entered in days, while regular
  555.      * password file info =must= be entered in weeks.
  556.      */
  557.  
  558. #ifdef    SHADOWPWD
  559.     while ((flag = getopt (argc, argv, "lm:M:W:I:E:d:")) != EOF)
  560. #else
  561.     while ((flag = getopt (argc, argv, "lm:M:d:")) != EOF)
  562. #endif
  563.     {
  564.         switch (flag) {
  565.             case 'l':
  566.                 lflg++;
  567.                 break;
  568.             case 'm':
  569.                 mflg++;
  570.                 mindays = strtol (optarg, 0, 10);
  571.                 break;
  572.             case 'M':
  573.                 Mflg++;
  574.                 maxdays = strtol (optarg, 0, 10);
  575.                 break;
  576.             case 'd':
  577.                 dflg++;
  578.                 if (strchr (optarg, '/'))
  579.                     lastday = strtoday (optarg);
  580.                 else
  581.                     lastday = strtol (optarg, 0, 10);
  582.                 break;
  583. #ifdef    SHADOWPWD
  584.             case 'W':
  585.                 Wflg++;
  586.                 warndays = strtol (optarg, 0, 10);
  587.                 break;
  588.             case 'I':
  589.                 Iflg++;
  590.                 inactdays = strtol (optarg, 0, 10);
  591.                 break;
  592.             case 'E':
  593.                 Eflg++;
  594.                 if (strchr (optarg, '/'))
  595.                     expdays = strtoday (optarg);
  596.                 else
  597.                     expdays = strtol (optarg, 0, 10);
  598.                 break;
  599. #endif
  600.             default:
  601.                 usage ();
  602.         }
  603.     }
  604.  
  605.     /*
  606.      * Make certain the flags do not conflict and that there is
  607.      * a user name on the command line.
  608.      */
  609.  
  610.     if (argc != optind + 1)
  611.         usage ();
  612.  
  613. #ifdef    SHADOWPWD
  614.     if (lflg && (mflg || Mflg || dflg || Wflg || Iflg || Eflg))
  615. #else
  616.     if (lflg && (mflg || Mflg || dflg))
  617. #endif
  618.     {
  619.         fprintf (stderr, NO_LFLAG, Prog);
  620. #ifdef    USE_SYSLOG
  621.         closelog ();
  622. #endif
  623.         usage ();
  624.     }
  625.  
  626.     /*
  627.      * An unprivileged user can ask for their own aging information,
  628.      * but only root can change it, or list another user's aging
  629.      * information.
  630.      */
  631.  
  632.     if (ruid != 0 && ! lflg) {
  633.         fprintf (stderr, NO_PERM, Prog);
  634. #ifdef    USE_SYSLOG
  635.         closelog ();
  636. #endif
  637.         exit (1);
  638.     }
  639.  
  640.     /*
  641.      * Lock and open the password file.  This loads all of the
  642.      * password file entries into memory.  Then we get a pointer
  643.      * to the password file entry for the requested user.
  644.      */
  645.  
  646.     if (! pw_lock ()) {
  647.         fprintf (stderr, NO_PWLOCK, Prog);
  648. #ifdef    USE_SYSLOG
  649.         syslog (LOG_ERR, LOCK_FAIL, "/etc/passwd");
  650.         closelog ();
  651. #endif
  652.         exit (1);
  653.     }
  654.     if (! pw_open (ruid != 0 || lflg ? O_RDONLY:O_RDWR)) {
  655.         fprintf (stderr, NO_PWOPEN, Prog);
  656.         cleanup (1);
  657. #ifdef    USE_SYSLOG
  658.         syslog (LOG_ERR, OPEN_FAIL, "/etc/passwd");
  659.         closelog ();
  660. #endif
  661.         exit (1);
  662.     }
  663.     if (! (pw = pw_locate (argv[optind]))) {
  664.         fprintf (stderr, UNK_USER, Prog, argv[optind]);
  665.         cleanup (1);
  666. #ifdef    USE_SYSLOG
  667.         closelog ();
  668. #endif
  669.         exit (1);
  670.     }
  671.  
  672. #ifdef    SHADOWPWD
  673.     /*
  674.      * For shadow password files we have to lock the file and
  675.      * read in the entries as was done for the password file.
  676.      * The user entries does not have to exist in this case;
  677.      * a new entry will be created for this user if one does
  678.      * not exist already.
  679.      */
  680.  
  681.     if (! spw_lock ()) {
  682.         fprintf (stderr, NO_SPLOCK, Prog);
  683.         cleanup (1);
  684. #ifdef    USE_SYSLOG
  685.         syslog (LOG_ERR, LOCK_FAIL, "/etc/shadow");
  686.         closelog ();
  687. #endif
  688.         exit (1);
  689.     }
  690.     if (! spw_open ((ruid != 0 || lflg) ? O_RDONLY:O_RDWR)) {
  691.         fprintf (stderr, NO_SPOPEN, Prog);
  692.         cleanup (2);
  693. #ifdef    USE_SYSLOG
  694.         syslog (LOG_ERR, OPEN_FAIL, "/etc/shadow");
  695.         closelog ();
  696. #endif
  697.         exit (1);
  698.     }
  699.     if (sp = spw_locate (argv[optind]))
  700.         spwd = *sp;
  701. #endif    /* SHADOWPWD */
  702.  
  703.     strcpy (name, pw->pw_name);
  704.     pwent = *pw;
  705.  
  706.     /*
  707.      * Set the fields that aren't being set from the command line
  708.      * from the password file.
  709.      */
  710.  
  711. #ifdef    SHADOWPWD
  712.     if (sp) {
  713.         if (! Mflg)
  714.             maxdays = spwd.sp_max;
  715.         if (! mflg)
  716.             mindays = spwd.sp_min;
  717.         if (! dflg)
  718.             lastday = spwd.sp_lstchg;
  719.         if (! Wflg)
  720.             warndays = spwd.sp_warn;
  721.         if (! Iflg)
  722.             inactdays = spwd.sp_inact;
  723.         if (! Eflg)
  724.             expdays = spwd.sp_expire;
  725.     }
  726. #ifdef    ATT_AGE
  727.     else
  728. #endif    /* ATT_AGE */
  729. #endif    /* SHADOWPWD */
  730. #ifdef    ATT_AGE
  731.     {
  732.         if (pwent.pw_age && strlen (pwent.pw_age) >= 2) {
  733.             if (! Mflg)
  734.                 maxdays = c64i (pwent.pw_age[0]) * (WEEK/SCALE);
  735.             if (! mflg)
  736.                 mindays = c64i (pwent.pw_age[1]) * (WEEK/SCALE);
  737.             if (! dflg && strlen (pwent.pw_age) == 4)
  738.                 lastday = a64l (pwent.pw_age+2) * (WEEK/SCALE);
  739.         } else {
  740.             mindays = 0;
  741.             maxdays = 10000L * (DAY/SCALE);
  742.             lastday = -1;
  743.         }
  744. #ifdef    SHADOWPWD
  745.         warndays = inactdays = expdays = -1;
  746. #endif    /* SHADOWPWD */
  747.     }
  748. #endif    /* ATT_AGE */
  749.  
  750.     /*
  751.      * Print out the expiration fields if the user has
  752.      * requested the list option.
  753.      */
  754.  
  755.     if (lflg) {
  756.         if (ruid != 0 && ruid != pw->pw_uid) {
  757.             fprintf (stderr, NO_PERM, Prog);
  758. #ifdef    USE_SYSLOG
  759.             closelog ();
  760. #endif
  761.             exit (1);
  762.         }
  763.         list_fields ();
  764.         cleanup (2);
  765. #ifdef    USE_SYSLOG
  766.         closelog ();
  767. #endif
  768.         exit (0);
  769.     }
  770.  
  771.     /*
  772.      * If none of the fields were changed from the command line,
  773.      * let the user interactively change them.
  774.      */
  775.  
  776. #ifdef    SHADOWPWD
  777.     if (! mflg && ! Mflg && ! dflg && ! Wflg && ! Iflg && ! Eflg)
  778. #else
  779.     if (! mflg && ! Mflg && ! dflg)
  780. #endif
  781.     {
  782.         printf (CHANGE_INFO, name);
  783.         if (! new_fields ()) {
  784.             fprintf (stderr, FIELD_ERR, Prog);
  785.             cleanup (2);
  786. #ifdef    USE_SYSLOG
  787.             closelog ();
  788. #endif
  789.             exit (1);
  790.         }
  791.     }
  792.  
  793. #ifdef    SHADOWPWD
  794.     /*
  795.      * There was no shadow entry.  The new entry will have the
  796.      * encrypted password transferred from the normal password
  797.      * file along with the aging information.
  798.      */
  799.  
  800.     if (sp == 0) {
  801.         sp = &spwd;
  802.         bzero (&spwd, sizeof spwd);
  803.  
  804.         sp->sp_namp = strdup (pw->pw_name);
  805.         sp->sp_pwdp = strdup (pw->pw_passwd);
  806.         sp->sp_flag = -1;
  807.  
  808.         pwent.pw_passwd = "!";
  809. #ifdef    ATT_AGE
  810.         pwent.pw_age = "";
  811. #endif
  812.         if (! pw_update (&pwent)) {
  813.             fprintf (stderr, NO_PWUPDATE, Prog);
  814.             cleanup (2);
  815. #ifdef    USE_SYSLOG
  816.             syslog (LOG_ERR, WRITE_FAIL, "/etc/passwd");
  817.             closelog ();
  818. #endif
  819.             exit (1);
  820.         }
  821. #if defined(DBM) || defined(NDBM)
  822.         (void) pw_dbm_update (&pwent);
  823.         endpwent ();
  824. #endif
  825.     }
  826. #endif    /* SHADOWPWD */
  827.  
  828. #ifdef    SHADOWPWD
  829.  
  830.     /*
  831.      * Copy the fields back to the shadow file entry and
  832.      * write the modified entry back to the shadow file.
  833.      * Closing the shadow and password files will commit
  834.      * any changes that have been made.
  835.      */
  836.  
  837.     sp->sp_max = maxdays;
  838.     sp->sp_min = mindays;
  839.     sp->sp_lstchg = lastday;
  840.     sp->sp_warn = warndays;
  841.     sp->sp_inact = inactdays;
  842.     sp->sp_expire = expdays;
  843.  
  844.     if (! spw_update (sp)) {
  845.         fprintf (stderr, NO_SPUPDATE, Prog);
  846.         cleanup (2);
  847. #ifdef    USE_SYSLOG
  848.         syslog (LOG_ERR, WRITE_FAIL, "/etc/shadow");
  849.         closelog ();
  850. #endif
  851.         exit (1);
  852.     }
  853. #else    /* !SHADOWPWD */
  854.  
  855.     /*
  856.      * fill in the new_age string with the new values
  857.      */
  858.  
  859.     if (maxdays > (63 * 7) && mindays == 0) {
  860.         new_age[0] = '\0';
  861.     } else {
  862.         if (maxdays > (63 * 7))
  863.             maxdays = 63 * 7;
  864.  
  865.         if (mindays > (63 * 7))
  866.             mindays = 63 * 7;
  867.  
  868.         new_age[0] = i64c (maxdays / 7);
  869.         new_age[1] = i64c ((mindays + 6) / 7);
  870.  
  871.         if (lastday == 0)
  872.             new_age[2] = '\0';
  873.         else
  874.             strcpy (new_age + 2, l64a (lastday / 7));
  875.  
  876.     }
  877.     pw->pw_age = new_age;
  878.  
  879.     if (! pw_update (pw)) {
  880.         fprintf (stderr, NO_PWUPDATE, Prog);
  881.         cleanup (2);
  882. #ifdef    USE_SYSLOG
  883.         syslog (LOG_ERR, WRITE_FAIL, "/etc/passwd");
  884.         closelog ();
  885. #endif
  886.         exit (1);
  887.     }
  888. #endif    /* SHADOWPWD */
  889.  
  890. #ifdef    NDBM
  891. #ifdef    SHADOWPWD
  892.  
  893.     /*
  894.      * See if the shadow DBM file exists and try to update it.
  895.      */
  896.  
  897.     if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) {
  898.         fprintf (stderr, DBMERROR);
  899.         cleanup (2);
  900. #ifdef    USE_SYSLOG
  901.         syslog (LOG_ERR, DBMERROR2);
  902.         closelog ();
  903. #endif
  904.         exit (1);
  905.     }
  906.     endspent ();
  907.  
  908. #else    /* !SHADOWPWD */
  909.  
  910.     /*
  911.      * See if the password DBM file exists and try to update it.
  912.      */
  913.  
  914.     if (access ("/etc/passwd.pag", 0) == 0 && ! pw_dbm_update (pw)) {
  915.         fprintf (stderr, DBMERROR);
  916.         cleanup (2);
  917. #ifdef    USE_SYSLOG
  918.         syslog (LOG_ERR, DBMERROR2);
  919.         closelog ();
  920. #endif
  921.         exit (1);
  922.     }
  923.     endpwent ();
  924. #endif    /* SHADOWPWD */
  925. #endif    /* NDBM */
  926.  
  927. #ifdef    SHADOWPWD
  928.     /*
  929.      * Now close the shadow password file, which will cause all
  930.      * of the entries to be re-written.
  931.      */
  932.  
  933.     if (! spw_close ()) {
  934.         fprintf (stderr, NO_SPCLOSE, Prog);
  935.         cleanup (2);
  936. #ifdef    USE_SYSLOG
  937.         syslog (LOG_ERR, CLOSE_FAIL, "/etc/shadow");
  938.         closelog ();
  939. #endif
  940.         exit (1);
  941.     }
  942. #endif    /* SHADOWPWD */
  943.  
  944.     /*
  945.      * Close the password file.  If any entries were modified, the
  946.      * file will be re-written.
  947.      */
  948.  
  949.     if (! pw_close ()) {
  950.         fprintf (stderr, NO_PWCLOSE, Prog);
  951.         cleanup (2);
  952. #ifdef    USE_SYSLOG
  953.         syslog (LOG_ERR, CLOSE_FAIL, "/etc/passwd");
  954.         closelog ();
  955. #endif
  956.         exit (1);
  957.     }
  958.     cleanup (2);
  959. #ifdef    USE_SYSLOG
  960.     syslog (LOG_INFO, AGE_CHANGED, name);
  961.     closelog ();
  962. #endif
  963.     exit (0);
  964.     /*NOTREACHED*/
  965. }
  966.  
  967. /*
  968.  * cleanup - unlock any locked password files
  969.  */
  970.  
  971. void
  972. cleanup (state)
  973. int    state;
  974. {
  975.     switch (state) {
  976.         case 2:
  977. #ifdef    SHADOWPWD
  978.             spw_unlock ();
  979. #endif
  980.         case 1:
  981.             pw_unlock ();
  982.         case 0:
  983.             break;
  984.     }
  985. }
  986.  
  987. #else    /* !AGING */
  988.  
  989. /*
  990.  * chage - but there is no age info!
  991.  */
  992.  
  993. main (argc, argv)
  994. int    argc;
  995. char    **argv;
  996. {
  997.     if (Prog = strrchr (argv[0], '/'))
  998.         Prog++;
  999.     else
  1000.         Prog = argv[0];
  1001.  
  1002.     fprintf (stderr, "%s: no aging information present\n", Prog);
  1003.     exit (1);
  1004. }
  1005.  
  1006. #endif    /* AGING */
  1007.