home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / shadow-3.1.4 / chage.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-11-26  |  17.0 KB  |  779 lines

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