home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3339 / passwd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-17  |  16.7 KB  |  719 lines

  1. /*
  2.  * Copyright 1989, 1990, 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 <time.h>
  14. #include <stdio.h>
  15. #include <fcntl.h>
  16. #include <signal.h>
  17. #include <syslog.h>
  18.  
  19. #ifndef    lint
  20. static    char    sccsid[] = "@(#)passwd.c    3.1    09:00:47    2/8/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 "lastlog.h"
  41. #include "shadow.h"
  42.  
  43. /*
  44.  * Password aging constants
  45.  *
  46.  *    DAY - seconds in a day
  47.  *    WEEK - seconds in a week
  48.  *    SCALE - convert from clock to aging units
  49.  */
  50.  
  51. #define    DAY    (24L*3600L)
  52. #define    WEEK    (7L*DAY)
  53.  
  54. #ifdef    ITI_AGING
  55. #define    SCALE    (1)
  56. #else
  57. #define    SCALE    DAY
  58. #endif
  59.  
  60. /*
  61.  * Global variables
  62.  */
  63.  
  64. char    name[32];        /* The user's name */
  65. char    *Prog;            /* Program name */
  66. int    amroot;            /* The real UID was 0 */
  67.  
  68. /*
  69.  * External identifiers
  70.  */
  71.  
  72. extern    char    *getpass();
  73. extern    char    *pw_encrypt();
  74. extern    char    *getlogin();
  75. extern    int    optind;        /* Index into argv[] for current option */
  76. extern    char    *optarg;    /* Pointer to current option value */
  77. #ifdef    NDBM
  78. extern    int    sp_dbm_mode;
  79. extern    int    pw_dbm_mode;
  80. #endif
  81.  
  82. /*
  83.  * #defines for messages.  This facilities foreign language conversion
  84.  * since all messages are defined right here.
  85.  */
  86.  
  87. #define    USAGE        "usage: %s [ -f | -s ] [ name ]\n"
  88. #define    ADMUSAGE \
  89.     "       %s [ -x max ] [ -n min ] [ -w warn ] [ -i inact ] name\n"
  90. #define    ADMUSAGE2 \
  91.     "       %s { -l | -d | -S } name\n"
  92. #define    OLDPASS        "Old Password:"
  93. #define    NEWPASSMSG \
  94. "Enter the new password (minimum of 5 characters)\n\
  95. Please use a combination of upper and lower case letters and numbers.\n"
  96. #define NEWPASS        "New Password:"
  97. #define    NEWPASS2    "Re-enter new password:"
  98. #define    WRONGPWD    "Incorrect password for %s.\n"
  99. #define    WRONGPWD2    "incorrect password for `%s'\n"
  100. #define    NOMATCH        "They don't match; try again.\n"
  101. #define    CANTCHANGE    "The password for %s cannot be changed.\n"
  102. #define    CANTCHANGE2    "password locked for `%s'\n"
  103. #define    TOOSOON        "Sorry, the password for %s cannot be changed yet.\n"
  104. #define    TOOSOON2    "now < sp_min for `%s'\n"
  105. #define    EXECFAILED    "%s: Cannot execute %s"
  106. #define    EXECFAILED2    "cannot execute %s\n"
  107. #define    WHOAREYOU    "%s: Cannot determine you user name.\n"
  108. #define    UNKUSER        "%s: Unknown user %s\n"
  109. #define    NOPERM        "You may not change the password for %s.\n"
  110. #define    NOPERM2        "can't change pwd for `%s'\n"
  111. #define    UNCHANGED    "The password for %s is unchanged.\n"
  112. #define    SPWDBUSY    "Cannot lock the password file; try again later.\n"
  113. #define    SPWDBUSY2    "can't lock /etc/shadow\n"
  114. #define    OPNERROR    "Cannot open the password file.\n"
  115. #define    OPNERROR2    "can't open /etc/shadow\n"
  116. #define    UPDERROR    "Error updating the password entry.\n"
  117. #define    UPDERROR2    "error updating shadow entry\n"
  118. #define    DBMERROR    "Error updating the DBM password entry.\n"
  119. #define    DBMERROR2    "error updating DBM shadow entry.\n"
  120. #define    NOTROOT        "Cannot change ID to root.\n"
  121. #define    NOTROOT2    "can't setuid(0).\n"
  122. #define    CLSERROR    "Cannot commit shadow file changes.\n"
  123. #define    CLSERROR2    "can't rewrite /etc/shadow.\n"
  124. #define    UNLKERROR    "Cannot unlock the shadow file.\n"
  125. #define    UNLKERROR2    "can't unlock /etc/shadow.\n"
  126. #define    TRYAGAIN    "Try again.\n"
  127. #define    CHGPASSWD    "changed password for `%s'\n"
  128.  
  129. /*
  130.  * usage - print command usage and exit
  131.  */
  132.  
  133. void
  134. usage ()
  135. {
  136.     fprintf (stderr, USAGE, Prog);
  137.     if (amroot) {
  138.         fprintf (stderr, ADMUSAGE, Prog);
  139.         fprintf (stderr, ADMUSAGE2, Prog);
  140.     }
  141.     exit (1);
  142. }
  143.  
  144. /*
  145.  * new_password - validate old password and replace with new
  146.  */
  147.  
  148. int
  149. new_password (pw, sp)
  150. struct    passwd    *pw;
  151. struct    spwd    *sp;
  152. {
  153.     char    *clear;        /* Pointer to clear text */
  154.     char    *cipher;    /* Pointer to cipher text */
  155.     char    *cp;        /* Pointer to getpass() response */
  156.     char    orig[BUFSIZ];    /* Original password */
  157.     char    pass[BUFSIZ];    /* New password */
  158.     int    i;        /* Counter for retries */
  159.  
  160.     /*
  161.      * Authenticate the user.  The user will be prompted for their
  162.      * own password.
  163.      */
  164.  
  165.     if (! amroot && sp->sp_pwdp[0]) {
  166.         bzero (orig, sizeof orig);
  167.  
  168.         if (! (clear = getpass (OLDPASS)))
  169.             return -1;
  170.  
  171.         cipher = pw_encrypt (clear, sp->sp_pwdp);
  172.         if (strcmp (cipher, sp->sp_pwdp) != 0) {
  173.             sleep (1);
  174.             fprintf (stderr, WRONGPWD, sp->sp_namp);
  175.             syslog (LOG_WARN, WRONGPWD2, sp->sp_namp);
  176.             return -1;
  177.         }
  178.         strcpy (orig, clear);
  179.     }
  180.  
  181.     /*
  182.      * Get the new password.  The user is prompted for the new password
  183.      * and has three tries to get it right.  The password will be tested
  184.      * for strength, unless it is the root user.  This provides an escape
  185.      * for initial login passwords.
  186.      */
  187.  
  188.     printf (NEWPASSMSG);
  189.     for (i = 0;i < 3;i++) {
  190.         if (! (cp = getpass (NEWPASS)))
  191.             return -1;
  192.         else
  193.             strcpy (pass, cp);
  194.  
  195.         if (! amroot && ! obscure (orig, pass)) {
  196.             printf (TRYAGAIN);
  197.             continue;
  198.         }
  199.         if (! (cp = getpass (NEWPASS2)))
  200.             return -1;
  201.  
  202.         if (strcmp (cp, pass))
  203.             fprintf (stderr, NOMATCH);
  204.         else
  205.             break;
  206.     }
  207.     if (i == 3)
  208.         return -1;
  209.  
  210.     /*
  211.      * Encrypt the password.  The new password is encrypted and
  212.      * the shadow password structure updated to reflect the change.
  213.      */
  214.  
  215.     sp->sp_pwdp = pw_encrypt (pass, (char *) 0);
  216.     sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  217.  
  218.     return 0;
  219. }
  220.  
  221. /*
  222.  * check_password - test a password to see if it can be changed
  223.  *
  224.  *    check_password() sees if the invoker has permission to change the
  225.  *    password for the given user.
  226.  */
  227.  
  228. void
  229. check_password (pw, sp)
  230. struct    passwd    *pw;
  231. struct    spwd    *sp;
  232. {
  233.     time_t    now = time ((time_t *) 0) / SCALE;
  234.  
  235.     /*
  236.      * Root can change any password any time.
  237.      */
  238.  
  239.     if (amroot)
  240.         return;
  241.  
  242.     /*
  243.      * Expired accounts cannot be changed ever.  Passwords
  244.      * which are locked may not be changed.  Passwords where
  245.      * min > max may not be changed.  Passwords which have
  246.      * been inactive too long cannot be changed.
  247.      */
  248.  
  249.     if ((sp->sp_expire > 0 && now >= sp->sp_expire) ||
  250.         (sp->sp_inact >= 0 && sp->sp_max >= 0 &&
  251.         now >= (sp->sp_lstchg + sp->sp_inact + sp->sp_max)) ||
  252.             strcmp (sp->sp_pwdp, "!") == 0 ||
  253.             sp->sp_min > sp->sp_max) {
  254.         fprintf (stderr, CANTCHANGE, sp->sp_namp);
  255.         syslog (LOG_WARN, CANTCHANGE2, sp->sp_namp);
  256.         exit (1);
  257.     }
  258.  
  259.     /*
  260.      * Passwords may only be changed after sp_min time is up.
  261.      */
  262.  
  263.     if (sp->sp_min >= 0 && now < (sp->sp_lstchg + sp->sp_min)) {
  264.         fprintf (stderr, TOOSOON, sp->sp_namp);
  265.         syslog (LOG_WARN, TOOSOON2, sp->sp_namp);
  266.         exit (1);
  267.     }
  268. }
  269.  
  270. /*
  271.  * pwd_to_spwd - create entries for new spwd structure
  272.  *
  273.  *    pwd_to_spwd() creates a new (struct spwd) containing the
  274.  *    information in the pointed-to (struct passwd).
  275.  */
  276.  
  277. void
  278. pwd_to_spwd (pw, sp)
  279. struct    passwd    *pw;
  280. struct    spwd    *sp;
  281. {
  282.     time_t    t;
  283.  
  284.     /*
  285.      * Nice, easy parts first.  The name and passwd map directly
  286.      * from the old password structure to the new one.
  287.      */
  288.  
  289.     sp->sp_namp = strdup (pw->pw_name);
  290.     sp->sp_pwdp = strdup (pw->pw_passwd);
  291. #ifdef    ATT_AGE
  292.  
  293.     /*
  294.      * AT&T-style password aging maps the sp_min, sp_max, and
  295.      * sp_lstchg information from the pw_age field, which appears
  296.      * after the encrypted password.
  297.      */
  298.  
  299.     if (pw->pw_age[0]) {
  300.         t = (c64i (pw->pw_age[0]) * WEEK) / SCALE;
  301.         sp->sp_max = t;
  302.  
  303.         if (pw->pw_age[1]) {
  304.             t = (c64i (pw->pw_age[1]) * WEEK) / SCALE;
  305.             sp->sp_min = t;
  306.         } else
  307.             sp->sp_min = (10000L * DAY) / SCALE;
  308.  
  309.         if (pw->pw_age[1] && pw->pw_age[2]) {
  310.             t = (a64l (pw->pw_age + 2) * WEEK) / SCALE;
  311.             sp->sp_lstchg = t;
  312.         } else
  313.             sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  314.     } else {
  315.         sp->sp_min = 0;
  316.         sp->sp_max = (10000L * DAY) / SCALE;
  317.         sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  318.     }
  319. #else
  320.     /*
  321.      * BSD does not use the pw_age field and has no aging information
  322.      * anywheres.  The default values are used to initialize the
  323.      * fields which are in the missing pw_age field;
  324.      */
  325.  
  326.     sp->sp_min = 0;
  327.     sp->sp_max = (10000L * DAY) / SCALE;
  328.     sp->sp_lstchg = time ((time_t *) 0) / SCALE;
  329. #endif
  330.  
  331.     /*
  332.      * These fields have no corresponding information in the password
  333.      * file.  They are set to uninitialized values.
  334.      */
  335.  
  336.     sp->sp_warn = -1;
  337.     sp->sp_inact = -1;
  338.     sp->sp_expire = -1;
  339.     sp->sp_flag = -1;
  340. }
  341.  
  342. /*
  343.  * print_status - print current password status
  344.  */
  345.  
  346. void
  347. print_status (sp)
  348. struct    spwd    *sp;
  349. {
  350.     struct    tm    *tm;
  351.     time_t    time;
  352.  
  353.     time = sp->sp_lstchg * SCALE;
  354.     tm = gmtime (&time);
  355.  
  356.     printf ("%s ", sp->sp_namp);
  357.     printf ("%s ",
  358.         sp->sp_pwdp[0] ? (sp->sp_pwdp[0] == '!' ? "L":"P"):"NP");
  359.     printf ("%02.2d/%02.2d/%02.2d ",
  360.         tm->tm_mon + 1, tm->tm_mday, tm->tm_year % 100);
  361.     printf ("%d %d %d %d\n",
  362.         (sp->sp_min * SCALE) / DAY, (sp->sp_max * SCALE) / DAY,
  363.         (sp->sp_warn * SCALE) / DAY, (sp->sp_inact * SCALE) / DAY);
  364. }
  365.  
  366. /*
  367.  * passwd - change a user's password file information
  368.  *
  369.  *    This command controls the password file and commands which are
  370.  *     used to modify it.
  371.  *
  372.  *    The valid options are
  373.  *
  374.  *    -l    lock the named account (*)
  375.  *    -d    delete the password for the named account (*)
  376.  *    -x #    set sp_max to # days (*)
  377.  *    -n #    set sp_min to # days (*)
  378.  *    -w #    set sp_warn to # days (*)
  379.  *    -i #    set sp_inact to # days (*)
  380.  *    -S    show password status of named account (*)
  381.  *    -g    execute gpasswd command to interpret flags
  382.  *    -f    execute chfn command to interpret flags
  383.  *    -s    execute chsh command to interpret flags
  384.  *
  385.  *    (*) requires root permission to execute.
  386.  *
  387.  *    All of the time fields are entered in days and converted to the
  388.  *     appropriate internal format.  For finer resolute the chage
  389.  *    command must be used.
  390.  */
  391.  
  392. int
  393. main (argc, argv)
  394. int    argc;
  395. char    **argv;
  396. {
  397.     char    buf[BUFSIZ];        /* I/O buffer for messages, etc.      */
  398.     char    *cp;            /* Miscellaneous character pointing   */
  399.     time_t    min;            /* Minimum days before change         */
  400.     time_t    max;            /* Maximum days until change          */
  401.     time_t    warn;            /* Warning days before change         */
  402.     time_t    inact;            /* Days without change before locked  */
  403.     int    i;            /* Loop control variable              */
  404.     int    flag;            /* Current option to process          */
  405.     int    lflg = 0;        /* -l - lock account option           */
  406.     int    dflg = 0;        /* -d - delete password option        */
  407.     int    xflg = 0;        /* -x - set maximum days              */
  408.     int    nflg = 0;        /* -n - set minimum days              */
  409.     int    wflg = 0;        /* -w - set warning days              */
  410.     int    iflg = 0;        /* -i - set inactive days             */
  411.     int    Sflg = 0;        /* -S - show password status          */
  412.     struct    passwd    *pw;        /* Password file entry for user       */
  413.     struct    spwd    *sp;        /* Shadow file entry for user         */
  414.     struct    spwd    tspwd;        /* New shadow file entry if none      */
  415.  
  416.     /*
  417.      * The program behaves differently when executed by root
  418.      * than when executed by a normal user.
  419.      */
  420.  
  421.     amroot = getuid () == 0;
  422. #ifdef    NDBM
  423.     sp_dbm_mode = O_RDWR;
  424.     pw_dbm_mode = O_RDWR;
  425. #endif
  426.  
  427.     /*
  428.      * Get the program name.  The program name is used as a
  429.      * prefix to most error messages.  It is also used as input
  430.      * to the openlog() function for error logging.
  431.      */
  432.  
  433.     if (Prog = strrchr (argv[0], '/'))
  434.         Prog++;
  435.     else
  436.         Prog = argv[0];
  437.  
  438.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  439.  
  440.     /*
  441.      * Start with the flags which cause another command to be
  442.      * executed.  The effective UID will be set back to the
  443.      * real UID and the new command executed with the flags
  444.      */
  445.  
  446.     if (argc > 1 && argv[1][0] == '-' && strchr ("gfs", argv[1][1])) {
  447.         setuid (getuid ());
  448.         switch (argv[1][1]) {
  449.             case 'g':
  450.                 argv[1] = "gpasswd";
  451.                 execv ("/bin/gpasswd", &argv[1]);
  452.                 break;
  453.             case 'f':
  454.                 argv[1] = "chfn";
  455.                 execv ("/bin/chfn", &argv[1]);
  456.                 break;
  457.             case 's':
  458.                 argv[1] = "chsh";
  459.                 execv ("/bin/chsh", &argv[1]);
  460.                 break;
  461.             default:
  462.                 usage ();
  463.         }
  464.         sprintf (buf, EXECFAILED, Prog, argv[1]);
  465.         perror (buf);
  466.         syslog (LOG_CRIT, EXECFAILED2, argv[1]);
  467.         exit (1);
  468.     }
  469.  
  470.     /* 
  471.      * The remaining arguments will be processed one by one and
  472.      * executed by this command.  The name is the last argument
  473.      * if it does not begin with a "-", otherwise the name is
  474.      * determined from the environment and must agree with the
  475.      * real UID.  Also, the UID will be checked for any commands
  476.      * which are restricted to root only.
  477.      */
  478.  
  479.     while ((flag = getopt (argc, argv, "ldx:n:w:i:S")) != EOF) {
  480.         switch (flag) {
  481.             case 'x':
  482.                 max = strtol (optarg, &cp, 10);
  483.                 if (*cp || getuid ())
  484.                     usage ();
  485.  
  486.                 xflg++;
  487.                 break;
  488.             case 'n':
  489.                 min = strtol (optarg, &cp, 10);
  490.                 if (*cp || getuid ())
  491.                     usage ();
  492.  
  493.                 nflg++;
  494.                 break;
  495.             case 'w':
  496.                 warn = strtol (optarg, &cp, 10);
  497.                 if (*cp || getuid ())
  498.                     usage ();
  499.  
  500.                 wflg++;
  501.                 break;
  502.             case 'i':
  503.                 inact = strtol (optarg, &cp, 10);
  504.                 if (*cp || getuid ())
  505.                     usage ();
  506.  
  507.                 iflg++;
  508.                 break;
  509.             case 'S':
  510.                 if (getuid ())
  511.                     usage ();
  512.  
  513.                 Sflg++;
  514.                 break;
  515.             case 'd':
  516.                 dflg++;
  517.                 break;
  518.             case 'l':
  519.                 lflg++;
  520.                 break;
  521.             default:
  522.                 usage ();
  523.         }
  524.     }
  525.  
  526.     /*
  527.      * If any of the flags were given, a user name must be supplied
  528.      * on the command line.  Only an unadorned command line doesn't
  529.      * require the user's name be given.  Also, on -x, -n, -m, and
  530.      * -i may appear with each other.  -d, -l and -S must appear alone.
  531.      */
  532.  
  533.     if ((dflg || lflg || xflg || nflg ||
  534.                 wflg || iflg || Sflg) && optind >= argc)
  535.         usage ();
  536.  
  537.     if ((dflg + lflg + (xflg || nflg || wflg || iflg) + Sflg) > 1)
  538.         usage ();
  539.  
  540.     /*
  541.      * Now I have to get the user name.  The name will be gotten 
  542.      * from the command line if possible.  Otherwise it is figured
  543.      * out from the environment.
  544.      */
  545.  
  546.     if (optind < argc) {
  547.         strncpy (name, argv[optind], sizeof name);
  548.         name[sizeof name - 1] = '\0';
  549.     } else if (cp = getlogin ()) {
  550.         strncpy (name, cp, sizeof name);
  551.         name[sizeof name - 1] = '\0';
  552.     } else {
  553.         fprintf (stderr, WHOAREYOU, Prog);
  554.         exit (1);
  555.     }
  556.  
  557.     /*
  558.      * Now I have a name, let's see if the UID for the name
  559.      * matches the current real UID.
  560.      */
  561.  
  562.     if (! (pw = getpwnam (name))) {
  563.         fprintf (stderr, UNKUSER, Prog, name);
  564.         exit (1);
  565.     }
  566.     if (! amroot && pw->pw_uid != getuid ()) {
  567.         fprintf (stderr, NOPERM, name);
  568.         syslog (LOG_WARN, NOPERM2, name);
  569.         exit (1);
  570.     }
  571.  
  572.     /*
  573.      * The user name is valid, so let's get the shadow file
  574.      * entry.
  575.      */
  576.  
  577.     if (! (sp = getspnam (name)))
  578.         pwd_to_spwd (pw, sp = &tspwd);
  579.  
  580.     /*
  581.      * Save the shadow entry off to the side so it doesn't
  582.      * get changed by any of the following code.
  583.      */
  584.  
  585.     if (sp != &tspwd) {
  586.         tspwd = *sp;
  587.         sp = &tspwd;
  588.     }
  589.     tspwd.sp_namp = strdup (sp->sp_namp);
  590.     tspwd.sp_pwdp = strdup (sp->sp_pwdp);
  591.  
  592.     if (Sflg) {
  593.         print_status (sp);
  594.         exit (0);
  595.     }
  596.  
  597.     /*
  598.      * If there are no other flags, just change the password.
  599.      */
  600.  
  601.     if (! (dflg || lflg || xflg || nflg || wflg || iflg)) {
  602.  
  603.         /*
  604.          * See if the user is permitted to change the password.
  605.          * Otherwise, go ahead and set a new password.
  606.          */
  607.  
  608.         check_password (pw, sp);
  609.  
  610.         if (new_password (pw, sp)) {
  611.             fprintf (stderr, UNCHANGED, name);
  612.             exit (1);
  613.         }
  614.     }
  615.  
  616.     /*
  617.      * The other options are incredibly simple.  Just modify the
  618.      * field in the shadow file entry.
  619.      */
  620.  
  621.     if (dflg)            /* Set password to blank */
  622.         sp->sp_pwdp = "";
  623.  
  624.     if (lflg)            /* Set password to "locked" value */
  625.         sp->sp_pwdp = "!";
  626.  
  627.     if (xflg)
  628.         sp->sp_max = (max * DAY) / SCALE;
  629.  
  630.     if (nflg)
  631.         sp->sp_min = (min * DAY) / SCALE;
  632.  
  633.     if (wflg)
  634.         sp->sp_warn = (warn * DAY) / SCALE;
  635.  
  636.     if (iflg)
  637.         sp->sp_inact = (inact * DAY) / SCALE;
  638.  
  639.     /*
  640.      * Before going any further, raise the ulimit to prevent
  641.      * colliding into a lowered ulimit, and set the real UID
  642.      * to root to protect against unexpected signals.  Any
  643.      * keyboard signals are set to be ignored.
  644.      */
  645.  
  646.     ulimit (2, 30000);
  647.     if (setuid (0)) {
  648.         fprintf (stderr, NOTROOT);
  649.         syslog (LOG_ERR, NOTROOT2);
  650.         exit (1);
  651.     }
  652.     signal (SIGHUP, SIG_IGN);
  653.     signal (SIGINT, SIG_IGN);
  654.     signal (SIGQUIT, SIG_IGN);
  655. #ifdef    SIGTSTP
  656.     signal (SIGTSTP, SIG_IGN);
  657. #endif
  658.  
  659.     /*
  660.      * The shadow entry is now ready to be committed back to
  661.      * the shadow file.  Get a lock on the file and open it.
  662.      */
  663.  
  664.     for (i = 0;i < 30;i++)
  665.         if (spw_lock ())
  666.             break;
  667.  
  668.     if (i == 30) {
  669.         fprintf (stderr, SPWDBUSY);
  670.         syslog (LOG_WARN, SPWDBUSY2);
  671.         exit (1);
  672.     }
  673.     if (! spw_open (O_RDWR)) {
  674.         fprintf (stderr, OPNERROR);
  675.         syslog (LOG_ERR, OPNERROR2);
  676.         (void) spw_unlock ();
  677.         exit (1);
  678.     }
  679.  
  680.     /*
  681.      * Update the shadow file entry.  If there is a DBM file,
  682.      * update that entry as well.
  683.      */
  684.  
  685.     if (! spw_update (sp)) {
  686.         fprintf (stderr, UPDERROR);
  687.         syslog (LOG_ERR, UPDERROR2);
  688.         (void) spw_unlock ();
  689.         exit (1);
  690.     }
  691. #ifdef    NDBM
  692.     if (access ("/etc/shadow.pag", 0) == 0 && ! sp_dbm_update (sp)) {
  693.         fprintf (stderr, DBMERROR);
  694.         syslog (LOG_ERR, DBMERROR2);
  695.         (void) spw_unlock ();
  696.         exit (1);
  697.     }
  698. #endif
  699.  
  700.     /*
  701.      * Changes have all been made, so commit them and unlock the
  702.      * file.
  703.      */
  704.  
  705.     if (! spw_close ()) {
  706.         fprintf (stderr, CLSERROR);
  707.         syslog (LOG_ERR, CLSERROR2);
  708.         (void) spw_unlock ();
  709.         exit (1);
  710.     }
  711.     if (! spw_unlock ()) {
  712.         fprintf (stderr, UNLKERROR);
  713.         syslog (LOG_ERR, UNLKERROR2);
  714.         exit (1);
  715.     }
  716.     syslog (LOG_INFO, CHGPASSWD, name);
  717.     exit (0);
  718. }
  719.