home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume38 / shadow / part02 / usermod.c < prev   
C/C++ Source or Header  |  1993-08-14  |  36KB  |  1,730 lines

  1. /*
  2.  * Copyright 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. #ifndef lint
  16. static    char    sccsid[] = "@(#)usermod.c    3.16    08:11:52    07 May 1993";
  17. #endif
  18.  
  19. #include <sys/types.h>
  20. #include <sys/stat.h>
  21. #include <stdio.h>
  22. #include <errno.h>
  23. #include "pwd.h"
  24. #include <grp.h>
  25. #include <ctype.h>
  26. #include <fcntl.h>
  27. #include <time.h>
  28.  
  29. #ifdef    BSD
  30. #include <strings.h>
  31. #else
  32. #include <string.h>
  33. #endif
  34.  
  35. #include "config.h"
  36. #ifdef    SHADOWPWD
  37. #include "shadow.h"
  38. #endif
  39. #include "faillog.h"
  40. #include "lastlog.h"
  41. #include "pwauth.h"
  42.  
  43. #ifdef    USE_SYSLOG
  44. #include <syslog.h>
  45.  
  46. #ifndef    LOG_WARN
  47. #define    LOG_WARN LOG_WARNING
  48. #endif
  49. #endif
  50.  
  51. #ifndef    NGROUPS_MAX
  52. #define    NGROUPS_MAX    64
  53. #endif
  54.  
  55. #if defined(DIR_XENIX) || defined(DIR_BSD) || defined(DIR_SYSV)
  56. #define    DIR_ANY
  57. #endif
  58.  
  59. #if !defined(MDY_DATE) && !defined(DMY_DATE) && !defined(YMD_DATE)
  60. #define    MDY_DATE    1
  61. #endif
  62. #if (defined (MDY_DATE) && (defined (DMY_DATE) || defined (YMD_DATE))) || \
  63.     (defined (DMY_DATE) && (defined (MDY_DATE) || defined (YMD_DATE)))
  64. Error: You must only define one of MDY_DATE, DMY_DATE, or YMD_DATE
  65. #endif
  66.  
  67. #define    VALID(s)    (strcspn (s, ":\n") == strlen (s))
  68.  
  69. char    user_name[BUFSIZ];
  70. char    user_newname[BUFSIZ];
  71. char    user_auth[BUFSIZ];
  72. char    user_newauth[BUFSIZ];
  73. uid_t    user_id;
  74. uid_t    user_newid;
  75. gid_t    user_gid;
  76. gid_t    user_newgid;
  77. char    user_comment[BUFSIZ];
  78. char    user_home[BUFSIZ];
  79. char    user_newhome[BUFSIZ];
  80. char    user_shell[BUFSIZ];
  81. #ifdef    SHADOWPWD
  82. long    user_expire;
  83. long    user_inactive;
  84. #endif
  85. int    user_ngroups = -1;
  86. gid_t    user_groups[NGROUPS_MAX];
  87. struct    passwd    user_pwd;
  88. #ifdef    SHADOWPWD
  89. struct    spwd    user_spwd;
  90. #endif
  91.  
  92. char    *Prog;
  93. char    *auth_arg;
  94.  
  95. int    Aflg;    /* specify user defined authentication method                 */
  96. int    uflg;    /* specify user ID for new account                            */
  97. int    oflg;    /* permit non-unique user ID to be specified with -u          */
  98. int    gflg;    /* primary group ID  for new account                          */
  99. int    Gflg;    /* secondary group set for new account                        */
  100. int    dflg;    /* home directory for new account                             */
  101. int    sflg;    /* shell program for new account                              */
  102. int    cflg;    /* comment (GECOS) field for new account                      */
  103. int    mflg;    /* create user's home directory if it doesn't exist           */
  104. int    fflg;    /* days until account with expired password is locked         */
  105. int    eflg;    /* days after password changed before it becomes expired      */
  106. int    lflg;    /* new user name for user                                     */
  107.  
  108. #ifdef    NDBM
  109. extern    int    pw_dbm_mode;
  110. #ifdef    SHADOWPWD
  111. extern    int    sp_dbm_mode;
  112. #endif
  113. extern    int    gr_dbm_mode;
  114. #ifdef    SHADOWGRP
  115. extern    int    sg_dbm_mode;
  116. #endif
  117. #endif
  118. extern    FILE    *fopen();
  119. extern    int    fclose();
  120. extern    char    *malloc();
  121. extern    char    *mktemp();
  122.  
  123. extern    struct    group    *getgrnam();
  124. extern    struct    group    *getgrgid();
  125. extern    struct    group    *gr_next();
  126. extern    struct    group    *gr_locate();
  127. extern    int    gr_lock();
  128. extern    int    gr_unlock();
  129. extern    int    gr_rewind();
  130. extern    int    gr_open();
  131.  
  132. #ifdef    SHADOWGRP
  133. extern    struct    sgrp    *sgr_next();
  134. extern    int    sgr_lock();
  135. extern    int    sgr_unlock();
  136. extern    int    sgr_rewind();
  137. extern    int    sgr_open();
  138. #endif
  139.  
  140. extern    struct    passwd    *getpwnam();
  141. extern    struct    passwd    *pw_next();
  142. extern    struct    passwd    *pw_locate();
  143. extern    int    pw_lock();
  144. extern    int    pw_unlock();
  145. extern    int    pw_rewind();
  146. extern    int    pw_open();
  147.  
  148. #ifdef    SHADOWPWD
  149. extern    int    spw_lock();
  150. extern    int    spw_unlock();
  151. extern    int    spw_open();
  152. extern    struct    spwd    *spw_locate();
  153. #endif
  154.  
  155. #define    DAY    (24L*3600L)
  156. #define    WEEK    (7*DAY)
  157.  
  158. #ifdef    ITI_AGING
  159. #define    SCALE    (1)
  160. #else
  161. #define    SCALE    (DAY)
  162. #endif
  163.  
  164. /*
  165.  * days and juldays are used to compute the number of days in the
  166.  * current month, and the cummulative number of days in the preceding
  167.  * months.  they are declared so that january is 1, not 0.
  168.  */
  169.  
  170. static    short    days[13] = { 0,
  171.     31,    28,    31,    30,    31,    30,    /* JAN - JUN */
  172.     31,    31,    30,    31,    30,    31 };    /* JUL - DEC */
  173.  
  174. static    short    juldays[13] = { 0,
  175.     0,    31,    59,    90,    120,    151,    /* JAN - JUN */
  176.     181,    212,    243,    273,    304,    334 };    /* JUL - DEC */
  177.  
  178. #ifdef    NEED_RENAME
  179. /*
  180.  * rename - rename a file to another name
  181.  *
  182.  *    rename is provided for systems which do not include the rename()
  183.  *    system call.
  184.  */
  185.  
  186. int
  187. rename (begin, end)
  188. char    *begin;
  189. char    *end;
  190. {
  191.     struct    stat    s1, s2;
  192.     extern    int    errno;
  193.     int    orig_err = errno;
  194.  
  195.     if (stat (begin, &s1))
  196.         return -1;
  197.  
  198.     if (stat (end, &s2)) {
  199.         errno = orig_err;
  200.     } else {
  201.  
  202.         /*
  203.          * See if this is a cross-device link.  We do this to
  204.          * insure that the link below has a chance of working.
  205.          */
  206.  
  207.         if (s1.st_dev != s2.st_dev) {
  208.             errno = EXDEV;
  209.             return -1;
  210.         }
  211.  
  212.         /*
  213.          * See if we can unlink the existing destination
  214.          * file.  If the unlink works the directory is writable,
  215.          * so there is no need here to figure that out.
  216.          */
  217.  
  218.         if (unlink (end))
  219.             return -1;
  220.     }
  221.  
  222.     /*
  223.      * Now just link the original name to the final name.  If there
  224.      * was no file previously, this link will fail if the target
  225.      * directory isn't writable.  The unlink will fail if the source
  226.      * directory isn't writable, but life stinks ...
  227.      */
  228.  
  229.     if (link (begin, end) || unlink (begin))
  230.         return -1;
  231.  
  232.     return 0;
  233. }
  234. #endif
  235.  
  236. /*
  237.  * strtoday - compute the number of days since 1970.
  238.  *
  239.  * the total number of days prior to the current date is
  240.  * computed.  january 1, 1970 is used as the origin with
  241.  * it having a day number of 0.
  242.  */
  243.  
  244. long
  245. strtoday (str)
  246. char    *str;
  247. {
  248.     char    slop[2];
  249.     int    month;
  250.     int    day;
  251.     int    year;
  252.     long    total;
  253.  
  254.     /*
  255.      * start by separating the month, day and year.  the order
  256.      * is compiled in ...
  257.      */
  258.  
  259. #ifdef    MDY_DATE
  260.     if (sscanf (str, "%d/%d/%d%c", &month, &day, &year, slop) != 3)
  261.         return -1;
  262. #endif
  263. #ifdef    DMY_DATE
  264.     if (sscanf (str, "%d/%d/%d%c", &day, &month, &year, slop) != 3)
  265.         return -1;
  266. #endif
  267. #ifdef    YMD_DATE
  268.     if (sscanf (str, "%d/%d/%d%c", &year, &month, &day, slop) != 3)
  269.         return -1;
  270. #endif
  271.  
  272.     /*
  273.      * the month, day of the month, and year are checked for
  274.      * correctness and the year adjusted so it falls between
  275.      * 1970 and 2069.
  276.      */
  277.  
  278.     if (month < 1 || month > 12)
  279.         return -1;
  280.  
  281.     if (day < 1)
  282.         return -1;
  283.  
  284.     if ((month != 2 || (year % 4) != 0) && day > days[month])
  285.         return -1;
  286.     else if ((month == 2 && (year % 4) == 0) && day > 29)
  287.         return -1;
  288.  
  289.     if (year < 0)
  290.         return -1;
  291.     else if (year < 69)
  292.         year += 2000;
  293.     else if (year < 99)
  294.         year += 1900;
  295.  
  296.     if (year < 1970 || year > 2069)
  297.         return -1;
  298.  
  299.     /*
  300.      * the total number of days is the total number of days in all
  301.      * the whole years, plus the number of leap days, plus the
  302.      * number of days in the whole months preceding, plus the number
  303.      * of days so far in the month.
  304.      */
  305.  
  306.     total = (long) ((year - 1970) * 365L) + (((year + 1) - 1970) / 4);
  307.     total += (long) juldays[month] + (month > 2 && (year % 4) == 0 ? 1:0);
  308.     total += (long) day - 1;
  309.  
  310.     return total;
  311. }
  312.  
  313. /*
  314.  * add_list - add a member to a list of group members
  315.  *
  316.  *    the array of member names is searched for the new member
  317.  *    name, and if not present it is added to a freshly allocated
  318.  *    list of users.
  319.  */
  320.  
  321. char **
  322. add_list (list, member)
  323. char    **list;
  324. char    *member;
  325. {
  326.     int    i;
  327.     char    **tmp;
  328.  
  329.     /*
  330.      * Scan the list for the new name.  Return the original list
  331.      * pointer if it is present.
  332.      */
  333.  
  334.     for (i = 0;list[i] != (char *) 0;i++)
  335.         if (strcmp (list[i], member) == 0)
  336.             return list;
  337.  
  338.     /*
  339.      * Allocate a new list pointer large enough to hold all the
  340.      * old entries, and the new entries as well.
  341.      */
  342.  
  343.     if (! (tmp = (char **) malloc ((i + 2) * sizeof member)))
  344.         return 0;
  345.  
  346.     /*
  347.      * Copy the original list to the new list, then append the
  348.      * new member and NULL terminate the result.  This new list
  349.      * is returned to the invoker.
  350.      */
  351.  
  352.     for (i = 0;list[i] != (char *) 0;i++)
  353.         tmp[i] = list[i];
  354.  
  355.     tmp[i++] = strdup (member);
  356.     tmp[i] = (char *) 0;
  357.  
  358.     return tmp;
  359. }
  360.  
  361. /*
  362.  * del_list - delete a member from a list of group members
  363.  *
  364.  *    the array of member names is searched for the old member
  365.  *    name, and if present it is deleted from a freshly allocated
  366.  *    list of users.
  367.  */
  368.  
  369. char **
  370. del_list (list, member)
  371. char    **list;
  372. char    *member;
  373. {
  374.     int    i, j;
  375.     char    **tmp;
  376.  
  377.     /*
  378.      * Scan the list for the new name.  Return the original list
  379.      * pointer if it is present.
  380.      */
  381.  
  382.     for (i = j = 0;list[i] != (char *) 0;i++)
  383.         if (strcmp (list[i], member))
  384.             j++;
  385.  
  386.     if (j == i)
  387.         return list;
  388.  
  389.     /*
  390.      * Allocate a new list pointer large enough to hold all the
  391.      * old entries, and the new entries as well.
  392.      */
  393.  
  394.     if (! (tmp = (char **) malloc ((j + 2) * sizeof member)))
  395.         return 0;
  396.  
  397.     /*
  398.      * Copy the original list to the new list, then append the
  399.      * new member and NULL terminate the result.  This new list
  400.      * is returned to the invoker.
  401.      */
  402.  
  403.     for (i = j = 0;list[i] != (char *) 0;i++)
  404.         if (strcmp (list[i], member))
  405.             tmp[j++] = list[i];
  406.  
  407.     tmp[j] = (char *) 0;
  408.  
  409.     return tmp;
  410. }
  411.  
  412. /*
  413.  * get_groups - convert a list of group names to an array of group IDs
  414.  *
  415.  *    get_groups() takes a comma-separated list of group names and
  416.  *    converts it to an array of group ID values.  Any unknown group
  417.  *    names are reported as errors.
  418.  */
  419.  
  420. int
  421. get_groups (list)
  422. char    *list;
  423. {
  424.     char    *cp;
  425.     struct    group    *grp;
  426.     int    errors = 0;
  427.  
  428.     /*
  429.      * Initialize the list to be empty
  430.      */
  431.  
  432.     user_ngroups = 0;
  433.  
  434.     if (! *list)
  435.         return 0;
  436.  
  437.     /*
  438.      * So long as there is some data to be converted, strip off
  439.      * each name and look it up.  A mix of numerical and string
  440.      * values for group identifiers is permitted.
  441.      */
  442.  
  443.     do {
  444.         /*
  445.          * Strip off a single name from the list
  446.          */
  447.  
  448.         if (cp = strchr (list, ','))
  449.             *cp++ = '\0';
  450.  
  451.         /*
  452.          * Names starting with digits are treated as numerical
  453.          * GID values, otherwise the string is looked up as is.
  454.          */
  455.  
  456.         if (isdigit (*list))
  457.             grp = getgrgid (atoi (list));
  458.         else
  459.             grp = getgrnam (list);
  460.  
  461.         /*
  462.          * There must be a match, either by GID value or by
  463.          * string name.
  464.          */
  465.  
  466.         if (! grp) {
  467.             fprintf (stderr, "%s: unknown group %s\n", Prog, list);
  468.             errors++;
  469.         }
  470.  
  471.         /*
  472.          * Add the GID value from the group file to the user's
  473.          * list of groups.
  474.          */
  475.  
  476.         user_groups[user_ngroups++] = grp->gr_gid;
  477.  
  478.         list = cp;
  479.     } while (list);
  480.  
  481.     /*
  482.      * Any errors in finding group names are fatal
  483.      */
  484.  
  485.     if (errors)
  486.         return -1;
  487.  
  488.     return 0;
  489. }
  490.  
  491. /*
  492.  * usage - display usage message and exit
  493.  */
  494.  
  495. usage ()
  496. {
  497.     fprintf (stderr,
  498.         "usage: %s [-u uid [-o]] [-g group] [-G group,...] \n", Prog);
  499. #ifdef    SHADOWPWD
  500.     fprintf (stderr,
  501.         "\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
  502. #ifdef    MDY_DATE
  503.     fprintf (stderr,
  504.         "\t\t[-f inactive ] [-e expire mm/dd/yy ] name\n");
  505. #endif
  506. #ifdef    DMY_DATE
  507.     fprintf (stderr,
  508.         "\t\t[-f inactive ] [-e expire dd/mm/yy ] name\n");
  509. #endif
  510. #ifdef    YMD_DATE
  511.     fprintf (stderr,
  512.         "\t\t[-f inactive ] [-e expire yy/mm/dd ] name\n");
  513. #endif
  514. #else    /* !SHADOWPWD */
  515.     fprintf (stderr,
  516.         "\t\t[-d home [-m]] [-s shell] [-c comment] [-l new_name]\n");
  517.     fprintf (stderr,
  518.         "\t\t[ -A {DEFAULT|program},... ] name\n");
  519. #endif    /* SHADOWPWD */
  520.     exit (2);
  521. }
  522.  
  523. /*
  524.  * new_pwent - initialize the values in a password file entry
  525.  *
  526.  *    new_pwent() takes all of the values that have been entered and
  527.  *    fills in a (struct passwd) with them.
  528.  */
  529.  
  530. void
  531. new_pwent (pwent)
  532. struct    passwd    *pwent;
  533. {
  534.     if (lflg) {
  535. #ifdef    USE_SYSLOG
  536.         syslog (LOG_INFO, "change user name `%s' to `%s'\n",
  537.             pwent->pw_name, user_newname);
  538. #endif
  539.         pwent->pw_name = strdup (user_newname);
  540.     }
  541.     if (uflg) {
  542. #ifdef    USE_SYSLOG
  543.         syslog (LOG_INFO, "change user `%s' UID from `%d' to `%d'\n",
  544.             pwent->pw_name, pwent->pw_uid, user_newid);
  545. #endif
  546.         pwent->pw_uid = user_newid;
  547.     }
  548.     if (gflg) {
  549. #ifdef    USE_SYSLOG
  550.         syslog (LOG_INFO, "change user `%s' GID from `%d' to `%d'\n",
  551.             pwent->pw_name, pwent->pw_gid, user_newgid);
  552. #endif
  553.         pwent->pw_gid = user_newgid;
  554.     }
  555.     if (cflg)
  556.         pwent->pw_gecos = strdup (user_comment);
  557.  
  558.     if (dflg) {
  559. #ifdef    USE_SYSLOG
  560.         syslog (LOG_INFO, "change user `%s' home from `%s' to `%s'\n",
  561.             pwent->pw_name, pwent->pw_dir, user_newhome);
  562. #endif
  563.         pwent->pw_dir = strdup (user_newhome);
  564.     }
  565.     if (sflg) {
  566. #ifdef    USE_SYSLOG
  567.         syslog (LOG_INFO, "change user `%s' shell from `%s' to `%s'\n",
  568.             pwent->pw_name, pwent->pw_shell, user_shell);
  569. #endif
  570.         pwent->pw_shell = strdup (user_shell);
  571.     }
  572. }
  573.  
  574. #ifdef    SHADOWPWD
  575. /*
  576.  * new_spent - initialize the values in a shadow password file entry
  577.  *
  578.  *    new_spent() takes all of the values that have been entered and
  579.  *    fills in a (struct spwd) with them.
  580.  */
  581.  
  582. void
  583. new_spent (spent)
  584. struct    spwd    *spent;
  585. {
  586.     if (lflg)
  587.         spent->sp_namp = strdup (user_newname);
  588.  
  589.     if (fflg) {
  590. #ifdef    USE_SYSLOG
  591.         syslog (LOG_INFO, "change user `%s' inactive from `%d' to `%d'\n",
  592.             spent->sp_namp, spent->sp_inact, user_inactive);
  593. #endif
  594.         spent->sp_inact = user_inactive;
  595.     }
  596.     if (eflg) {
  597. #ifdef    USE_SYSLOG
  598.         syslog (LOG_INFO, "change user `%s' expiration from `%d' to `%d'\n",
  599.             spent->sp_namp, spent->sp_expire, user_expire);
  600. #endif
  601.         spent->sp_expire = user_expire;
  602.     }
  603. }
  604. #endif    /* SHADOWPWD */
  605.  
  606. /*
  607.  * grp_update - add user to secondary group set
  608.  *
  609.  *    grp_update() takes the secondary group set given in user_groups
  610.  *    and adds the user to each group given by that set.
  611.  */
  612.  
  613. void
  614. grp_update ()
  615. {
  616.     int    i;
  617.     int    is_member;
  618.     int    was_member;
  619.     struct    group    *grp;
  620. #ifdef    SHADOWGRP
  621.     int    was_admin;
  622.     struct    sgrp    *sgrp;
  623. #endif
  624.  
  625.     /*
  626.      * Lock and open the group file.  This will load all of the group
  627.      * entries.
  628.      */
  629.  
  630.     if (! gr_lock ()) {
  631.         fprintf (stderr, "%s: error locking group file\n", Prog);
  632. #ifdef    USE_SYSLOG
  633.         syslog (LOG_ERR, "error locking group file");
  634. #endif
  635.         exit (1);
  636.     }
  637.     if (! gr_open (O_RDWR)) {
  638.         fprintf (stderr, "%s: error opening group file\n", Prog);
  639. #ifdef    USE_SYSLOG
  640.         syslog (LOG_ERR, "error opening group file");
  641. #endif
  642.         fail_exit (1);
  643.     }
  644. #ifdef    SHADOWGRP
  645.     if (! sgr_lock ()) {
  646.         fprintf (stderr, "%s: error locking shadow group file\n", Prog);
  647. #ifdef    USE_SYSLOG
  648.         syslog (LOG_ERR, "error locking shadow group file");
  649. #endif
  650.         fail_exit (1);
  651.     }
  652.     if (! sgr_open (O_RDWR)) {
  653.         fprintf (stderr, "%s: error opening shadow group file\n", Prog);
  654. #ifdef    USE_SYSLOG
  655.         syslog (LOG_ERR, "error opening shadow group file");
  656. #endif
  657.         fail_exit (1);
  658.     }
  659. #endif    /* SHADOWGRP */
  660.  
  661.     /*
  662.      * Scan through the entire group file looking for the groups that
  663.      * the user is a member of.
  664.      */
  665.  
  666.     for (gr_rewind (), grp = gr_next ();grp;grp = gr_next ()) {
  667.  
  668.         /*
  669.          * See if the user specified this group as one of their
  670.          * concurrent groups.
  671.          */
  672.  
  673.         for (i = 0;i < user_ngroups;i++)
  674.             if (grp->gr_gid == user_groups[i])
  675.                 break;
  676.  
  677.         is_member = i == user_ngroups ? -1:i;
  678.  
  679.         for (i = 0;grp->gr_mem[i];i++)
  680.             if (strcmp (user_name, grp->gr_mem[i]) == 0)
  681.                 break;
  682.  
  683.         was_member = grp->gr_mem[i] ? i:-1;
  684.  
  685.         if (is_member == -1 && was_member == -1)
  686.             continue;
  687.  
  688.         if (was_member >= 0 && (! Gflg || is_member >= 0)) {
  689.             if (lflg) {
  690.                 grp->gr_mem = del_list (grp->gr_mem,
  691.                     user_name);
  692.                 grp->gr_mem = add_list (grp->gr_mem,
  693.                     user_newname);
  694. #ifdef    USE_SYSLOG
  695.                 syslog (LOG_INFO,
  696.                     "change `%s' to `%s' in group `%s'\n",
  697.                     user_name, user_newname, grp->gr_name);
  698. #endif
  699.             }
  700.         } else if (was_member >= 0 && Gflg && is_member < 0) {
  701.             grp->gr_mem = del_list (grp->gr_mem, user_name);
  702. #ifdef    USE_SYSLOG
  703.             syslog (LOG_INFO, "delete `%s' from group `%s'\n",
  704.                 user_name, grp->gr_name);
  705. #endif
  706.         } else if (was_member < 0 && Gflg && is_member >= 0) {
  707.             grp->gr_mem = add_list (grp->gr_mem,
  708.                 lflg ? user_newname:user_name);
  709. #ifdef    USE_SYSLOG
  710.             syslog (LOG_INFO, "add `%s' to group `%s'\n",
  711.                 lflg ? user_newname:user_name, grp->gr_name);
  712. #endif
  713.         } else
  714.             continue;
  715.  
  716.         if (! gr_update (grp)) {
  717.             fprintf (stderr, "%s: error adding new group entry\n",
  718.                 Prog);
  719. #ifdef    USE_SYSLOG
  720.             syslog (LOG_ERR, "error adding group entry");
  721. #endif
  722.             fail_exit (1);
  723.         }
  724. #ifdef    NDBM
  725.         /*
  726.          * Update the DBM group file with the new entry as well.
  727.          */
  728.  
  729.         if (! gr_dbm_update (grp)) {
  730.             fprintf (stderr, "%s: cannot add new dbm group entry\n",
  731.                 Prog);
  732. #ifdef    USE_SYSLOG
  733.             syslog (LOG_ERR, "error adding dbm group entry");
  734. #endif
  735.             fail_exit (1);
  736.         }
  737. #endif    /* NDBM */
  738.     }
  739. #ifdef NDBM
  740.     endgrent ();
  741. #endif
  742.  
  743. #ifdef    SHADOWGRP
  744.     /*
  745.      * Scan through the entire shadow group file looking for the groups
  746.      * that the user is a member of.
  747.      */
  748.  
  749.     for (sgr_rewind (), sgrp = sgr_next ();sgrp;sgrp = sgr_next ()) {
  750.  
  751.         /*
  752.          * See if the user was a member of this group
  753.          */
  754.  
  755.         for (i = 0;sgrp->sg_mem[i];i++)
  756.             if (strcmp (sgrp->sg_mem[i], user_name) == 0)
  757.                 break;
  758.  
  759.         was_member = sgrp->sg_mem[i] ? i:-1;
  760.  
  761.         /*
  762.          * See if the user was an administrator of this group
  763.          */
  764.  
  765.         for (i = 0;sgrp->sg_adm[i];i++)
  766.             if (strcmp (sgrp->sg_adm[i], user_name) == 0)
  767.                 break;
  768.  
  769.         was_admin = sgrp->sg_adm[i] ? i:-1;
  770.  
  771.         /*
  772.          * See if the user specified this group as one of their
  773.          * concurrent groups.
  774.          */
  775.  
  776.         for (i = 0;i < user_ngroups;i++) {
  777.             if (! (grp = gr_locate (sgrp->sg_name)))
  778.                 continue;
  779.  
  780.             if (grp->gr_gid == user_groups[i])
  781.                 break;
  782.         }
  783.         is_member = i == user_ngroups ? -1:i;
  784.  
  785.         if (is_member == -1 && was_admin == -1 && was_member == -1)
  786.             continue;
  787.  
  788.         if (was_admin >= 0 && lflg) {
  789.             sgrp->sg_adm = del_list (sgrp->sg_adm, user_name);
  790.             sgrp->sg_adm = add_list (sgrp->sg_adm, user_newname);
  791. #ifdef    USE_SYSLOG
  792.             syslog (LOG_INFO, "change admin `%s' to `%s' in shadow group `%s'\n",
  793.                 user_name, user_newname, sgrp->sg_name);
  794. #endif
  795.         }
  796.         if (was_member >= 0 && (! Gflg || is_member >= 0)) {
  797.             if (lflg) {
  798.                 sgrp->sg_mem = del_list (sgrp->sg_mem,
  799.                     user_name);
  800.                 sgrp->sg_mem = add_list (sgrp->sg_mem,
  801.                     user_newname);
  802. #ifdef    USE_SYSLOG
  803.                 syslog (LOG_INFO, "change `%s' to `%s' in shadow group `%s'\n",
  804.                     user_name, user_newname, sgrp->sg_name);
  805. #endif
  806.             }
  807.         } else if (was_member >= 0 && Gflg && is_member < 0) {
  808.             sgrp->sg_mem = del_list (sgrp->sg_mem, user_name);
  809. #ifdef    USE_SYSLOG
  810.             syslog (LOG_INFO,
  811.                 "delete `%s' from shadow group `%s'\n",
  812.                 user_name, sgrp->sg_name);
  813. #endif
  814.         } else if (was_member < 0 && Gflg && is_member >= 0) {
  815.             sgrp->sg_mem = add_list (sgrp->sg_mem,
  816.                 lflg ? user_newname:user_name);
  817. #ifdef    USE_SYSLOG
  818.             syslog (LOG_INFO, "add `%s' to shadow group `%s'\n",
  819.                 lflg ? user_newname:user_name, grp->gr_name);
  820. #endif
  821.         } else
  822.             continue;
  823.  
  824.         /* 
  825.          * Update the group entry to reflect the changes.
  826.          */
  827.  
  828.         if (! sgr_update (sgrp)) {
  829.             fprintf (stderr, "%s: error adding new group entry\n",
  830.                 Prog);
  831. #ifdef    USE_SYSLOG
  832.             syslog (LOG_ERR, "error adding shadow group entry\n");
  833. #endif
  834.             fail_exit (1);
  835.         }
  836. #ifdef    NDBM
  837.         /*
  838.          * Update the DBM group file with the new entry as well.
  839.          */
  840.  
  841.         if (! sg_dbm_update (sgrp)) {
  842.             fprintf (stderr, "%s: cannot add new dbm group entry\n",
  843.                 Prog);
  844. #ifdef    USE_SYSLOG
  845.             syslog (LOG_ERR,
  846.                 "error adding dbm shadow group entry\n");
  847. #endif
  848.             fail_exit (1);
  849.         }
  850. #endif    /* NDBM */
  851.     }
  852. #ifdef NDBM
  853.     endsgent ();
  854. #endif    /* NDBM */
  855. #endif    /* SHADOWGRP */
  856. }
  857.  
  858. /*
  859.  * check_new_id - verify the new UID for uniqueness
  860.  *
  861.  *    check_new_id() insures that the new UID does not exist already.
  862.  *    It does this by checking that the UID has changed and that there
  863.  *    is no current entry for this user ID.
  864.  */
  865.  
  866. int
  867. check_new_id ()
  868. {
  869.     /*
  870.      * First, the easy stuff.  If the ID can be duplicated, or if
  871.      * the ID didn't really change, just return.  If the ID didn't
  872.      * change, turn off those flags.  No sense doing needless work.
  873.      */
  874.  
  875.     if (oflg)
  876.         return 0;
  877.  
  878.     if (user_id == user_newid) {
  879.         uflg = lflg = 0;
  880.         return 0;
  881.     }
  882.     if (getpwuid (user_newid))
  883.         return -1;
  884.  
  885.     return 0;
  886. }
  887.  
  888. /*
  889.  * get_password - locate encrypted password in authentication list
  890.  */
  891.  
  892. char *
  893. get_password (list)
  894. char    *list;
  895. {
  896.     char    *cp, *end;
  897.     static    char    buf[257];
  898.  
  899.     strcpy (buf, list);
  900.     for (cp = buf;cp;cp = end) {
  901.         if (end = strchr (cp, ';'))
  902.             *end++ = 0;
  903.  
  904.         if (cp[0] == '@')
  905.             continue;
  906.  
  907.         return cp;
  908.     }
  909.     return (char *) 0;
  910. }
  911.  
  912. /*
  913.  * split_auths - break up comma list into (char *) array
  914.  */
  915.  
  916. split_auths (list, array)
  917. char    *list;
  918. char    **array;
  919. {
  920.     char    *cp, *end;
  921.     int    i = 0;
  922.  
  923.     for (cp = list;cp;cp = end) {
  924.         if (end = strchr (cp, ';'))
  925.             *end++ = '\0';
  926.  
  927.         array[i++] = cp;
  928.     }
  929.     array[i] = 0;
  930. }
  931.  
  932. /*
  933.  * update_auths - find list of methods to update
  934.  */
  935.  
  936. update_auths (old, new, update)
  937. char    *old;
  938. char    *new;
  939. char    *update;
  940. {
  941.     char    oldbuf[257], newbuf[257];
  942.     char    *oldv[32], *newv[32], *updatev[32];
  943.     int    i, j, k;
  944.  
  945.     strcpy (oldbuf, old);
  946.     split_auths (oldbuf, oldv);
  947.  
  948.     strcpy (newbuf, new);
  949.     split_auths (newbuf, newv);
  950.  
  951.     for (i = j = k = 0;oldv[i];i++) {
  952.         for (j = 0;newv[j];j++)
  953.             if (strcmp (oldv[i], newv[j]) != 0)
  954.                 break;
  955.  
  956.         if (newv[j] != (char *) 0)
  957.             updatev[k++] = oldv[i];
  958.     }
  959.     updatev[k] = 0;
  960.  
  961.     update[0] = '\0';
  962.     for (i = 0;updatev[i];i++) {
  963.         if (i)
  964.             strcat (update, ";");
  965.  
  966.         strcat (update, updatev[i]);
  967.     }
  968. }
  969.  
  970. /*
  971.  * add_auths - find list of methods to add
  972.  */
  973.  
  974. add_auths (old, new, add)
  975. char    *old;
  976. char    *new;
  977. char    *add;
  978. {
  979.     char    oldbuf[257], newbuf[257];
  980.     char    *oldv[32], *newv[32], *addv[32];
  981.     int    i, j, k;
  982.  
  983.     strcpy (oldbuf, old);
  984.     split_auths (oldbuf, oldv);
  985.  
  986.     strcpy (newbuf, new);
  987.     split_auths (newbuf, newv);
  988.  
  989.     for (i = j = k = 0;newv[i];i++) {
  990.         for (j = 0;oldv[j];j++)
  991.             if (strcmp (oldv[i], newv[j]) == 0)
  992.                 break;
  993.  
  994.         if (oldv[j] == (char *) 0)
  995.             addv[k++] = newv[i];
  996.     }
  997.     addv[k] = 0;
  998.  
  999.     add[0] = '\0';
  1000.     for (i = 0;addv[i];i++) {
  1001.         if (i)
  1002.             strcat (add, ";");
  1003.  
  1004.         strcat (add, addv[i]);
  1005.     }
  1006. }
  1007.  
  1008. /*
  1009.  * delete_auths - find list of methods to delete
  1010.  */
  1011.  
  1012. delete_auths (old, new, remove)
  1013. char    *old;
  1014. char    *new;
  1015. char    *remove;
  1016. {
  1017.     char    oldbuf[257], newbuf[257];
  1018.     char    *oldv[32], *newv[32], *removev[32];
  1019.     int    i, j, k;
  1020.  
  1021.     strcpy (oldbuf, old);
  1022.     split_auths (oldbuf, oldv);
  1023.  
  1024.     strcpy (newbuf, new);
  1025.     split_auths (newbuf, newv);
  1026.  
  1027.     for (i = j = k = 0;oldv[i];i++) {
  1028.         for (j = 0;newv[j];j++)
  1029.             if (strcmp (oldv[i], newv[j]) == 0)
  1030.                 break;
  1031.  
  1032.         if (newv[j] == (char *) 0)
  1033.             removev[k++] = oldv[i];
  1034.     }
  1035.     removev[k] = 0;
  1036.  
  1037.     remove[0] = '\0';
  1038.     for (i = 0;removev[i];i++) {
  1039.         if (i)
  1040.             strcat (remove, ";");
  1041.  
  1042.         strcat (remove, removev[i]);
  1043.     }
  1044. }
  1045.  
  1046. /*
  1047.  * convert_auth - convert the argument list to a authentication list
  1048.  */
  1049.  
  1050. convert_auth (auths, oldauths, list)
  1051. char    *auths;
  1052. char    *oldauths;
  1053. char    *list;
  1054. {
  1055.     char    *cp, *end;
  1056.     char    *old;
  1057.     char    buf[257];
  1058.  
  1059.     /*
  1060.      * Copy each method.  DEFAULT is replaced by an encrypted string
  1061.      * if one can be found in the current authentication list.
  1062.      */
  1063.  
  1064.     strcpy (buf, list);
  1065.     auths[0] = '\0';
  1066.     for (cp = buf;cp;cp = end) {
  1067.         if (auths[0])
  1068.             strcat (auths, ";");
  1069.  
  1070.         if (end = strchr (cp, ','))
  1071.             *end++ = '\0';
  1072.  
  1073.         if (strcmp (cp, "DEFAULT") == 0) {
  1074.             if (old = get_password (oldauths))
  1075.                 strcat (auths, old);
  1076.             else
  1077.                 strcat (auths, "!");
  1078.         } else {
  1079.             strcat (auths, "@");
  1080.             strcat (auths, cp);
  1081.         }
  1082.     }
  1083. }
  1084.  
  1085. /*
  1086.  * valid_auth - check authentication list for validity
  1087.  */
  1088.  
  1089. valid_auth (methods)
  1090. char    *methods;
  1091. {
  1092.     char    *cp, *end;
  1093.     char    buf[257];
  1094.     int    default_cnt = 0;
  1095.  
  1096.     /*
  1097.      * Cursory checks, length and illegal characters
  1098.      */
  1099.  
  1100.     if (strlen (methods) > 256)
  1101.         return 0;
  1102.  
  1103.     if (! VALID (methods))
  1104.         return 0;
  1105.  
  1106.     /*
  1107.      * Pick each method apart and check it.
  1108.      */
  1109.  
  1110.     strcpy (buf, methods);
  1111.     for (cp = buf;cp;cp = end) {
  1112.         if (end = strchr (cp, ','))
  1113.             *end++ = '\0';
  1114.  
  1115.         if (strcmp (cp, "DEFAULT") == 0) {
  1116.             if (default_cnt++ > 0)
  1117.                 return 0;
  1118.         } else if (cp[0] != '/')
  1119.             return 0;
  1120.     }
  1121.     return 1;
  1122. }
  1123.  
  1124. /*
  1125.  * process_flags - perform command line argument setting
  1126.  *
  1127.  *    process_flags() interprets the command line arguments and sets
  1128.  *    the values that the user will be created with accordingly.  The
  1129.  *    values are checked for sanity.
  1130.  */
  1131.  
  1132. void
  1133. process_flags (argc, argv)
  1134. int    argc;
  1135. char    **argv;
  1136. {
  1137.     extern    int    optind;
  1138.     extern    char    *optarg;
  1139.     struct    group    *grp;
  1140.     struct    passwd    *pwd;
  1141.     struct    spwd    *spwd;
  1142.     long    l;
  1143.     int    anyflag = 0;
  1144.     int    arg;
  1145.  
  1146.     if (argc == 1 || argv[argc - 1][0] == '-')
  1147.         usage ();
  1148.  
  1149.     if (! (pwd = getpwnam (argv[argc - 1]))) {
  1150.         fprintf (stderr, "%s: user %s does not exist\n",
  1151.             Prog, argv[argc - 1]);
  1152.         exit (6);
  1153.     }
  1154.     strcpy (user_name, pwd->pw_name);
  1155.     user_id = pwd->pw_uid;
  1156.     user_gid = pwd->pw_gid;
  1157.     strcpy (user_comment, pwd->pw_gecos);
  1158.     strcpy (user_home, pwd->pw_dir);
  1159.     strcpy (user_shell, pwd->pw_shell);
  1160.  
  1161. #ifdef    SHADOWPWD
  1162.     if (spwd = getspnam (user_name)) {
  1163.         user_expire = spwd->sp_expire;
  1164.         user_inactive = spwd->sp_inact;
  1165.     }
  1166. #endif
  1167. #ifdef    SHADOWPWD
  1168.     while ((arg = getopt (argc, argv, "A:u:og:G:d:s:c:mf:e:l:")) != EOF)
  1169. #else
  1170.     while ((arg = getopt (argc, argv, "A:u:og:G:d:s:c:ml:")) != EOF)
  1171. #endif
  1172.     {
  1173.         switch (arg) {
  1174.             case 'A':
  1175.                 if (! valid_auth (optarg)) {
  1176.                     fprintf (stderr,
  1177.                         "%s: invalid field `%s'\n",
  1178.                         Prog, optarg);
  1179.                     exit (3);
  1180.                 }
  1181.                 auth_arg = optarg;
  1182.                 Aflg++;
  1183.                 break;
  1184.             case 'c':
  1185.                 if (! VALID (optarg)) {
  1186.                     fprintf (stderr,
  1187.                         "%s: invalid field `%s'\n",
  1188.                         Prog, optarg);
  1189.                     exit (3);
  1190.                 }
  1191.                 strncpy (user_comment, optarg, BUFSIZ);
  1192.                 cflg++;
  1193.                 break;
  1194.             case 'd':
  1195.                 if (! VALID (optarg)) {
  1196.                     fprintf (stderr,
  1197.                         "%s: invalid field `%s'\n",
  1198.                         Prog, optarg);
  1199.                     exit (3);
  1200.                 }
  1201.                 dflg++;
  1202.                 strncpy (user_newhome, optarg, BUFSIZ);
  1203.                 break;
  1204. #ifdef    SHADOWPWD
  1205.             case 'e':
  1206.                 l = strtoday (optarg);
  1207. #ifdef    ITI_AGING
  1208.                 l *= DAY;
  1209. #endif
  1210.                 user_expire = l;
  1211.                 eflg++;
  1212.                 break;
  1213.             case 'f':
  1214.                 user_inactive = atoi (optarg);
  1215.                 fflg++;
  1216.                 break;
  1217. #endif    /* SHADOWPWD */
  1218.             case 'g':
  1219.                 if (isdigit (optarg[0]))
  1220.                     grp = getgrgid (atoi (optarg));
  1221.                 else
  1222.                     grp = getgrnam (optarg);
  1223.  
  1224.                 if (! grp) {
  1225.                     fprintf (stderr,
  1226.                         "%s: unknown group %s\n",
  1227.                         Prog, optarg);
  1228.                     exit (1);
  1229.                 }
  1230.                 user_newgid = grp->gr_gid;
  1231.                 gflg++;
  1232.                 break;
  1233.             case 'G':
  1234.                 Gflg++;
  1235.                 if (get_groups (optarg))
  1236.                     exit (1);
  1237.  
  1238.                 break;
  1239.             case 'l':
  1240.                 if (! VALID (optarg)) {
  1241.                     fprintf (stderr,
  1242.                         "%s: invalid field `%s'\n",
  1243.                         Prog, optarg);
  1244.                     exit (3);
  1245.                 }
  1246.  
  1247.                 /*
  1248.                  * If the name does not really change, we
  1249.                  * mustn't set the flag as this will cause
  1250.                  * rather serious problems later!
  1251.                  */
  1252.  
  1253.                 if (strcmp (user_newname, optarg)) {
  1254.                     strcpy (user_newname, optarg);
  1255.                     lflg++;
  1256.                 }
  1257.                 break;
  1258.             case 'm':
  1259.                 if (! dflg)
  1260.                     usage ();
  1261.  
  1262.                 mflg++;
  1263.                 break;
  1264.             case 'o':
  1265.                 if (! uflg)
  1266.                     usage ();
  1267.  
  1268.                 oflg++;
  1269.                 break;
  1270.             case 's':
  1271.                 if (! VALID (optarg)) {
  1272.                     fprintf (stderr,
  1273.                         "%s: invalid field `%s'\n",
  1274.                         Prog, optarg);
  1275.                     exit (3);
  1276.                 }
  1277.                 strncpy (user_shell, optarg, BUFSIZ);
  1278.                 sflg++;
  1279.                 break;
  1280.             case 'u':
  1281.                 uflg++;
  1282.                 user_newid = atoi (optarg);
  1283.                 break;
  1284.             default:
  1285.                 usage ();
  1286.         }
  1287.         anyflag++;
  1288.     }
  1289.     if (anyflag == 0) {
  1290.         fprintf (stderr, "%s: no flags given\n", Prog);
  1291.         exit (1);
  1292.     }
  1293.     if (optind != argc - 1)
  1294.         usage ();
  1295.  
  1296.     if (dflg && strcmp (user_home, user_newhome) == 0)
  1297.         dflg = mflg = 0;
  1298.  
  1299.     if (uflg && user_id == user_newid)
  1300.         uflg = oflg = 0;
  1301.  
  1302.     if (lflg && getpwnam (user_newname)) {
  1303.         fprintf (stderr, "%s: user %s exists\n", Prog, user_newname);
  1304.         exit (9);
  1305.     }
  1306. }
  1307.  
  1308. /*
  1309.  * close_files - close all of the files that were opened
  1310.  *
  1311.  *    close_files() closes all of the files that were opened for this
  1312.  *    new user.  This causes any modified entries to be written out.
  1313.  */
  1314.  
  1315. close_files ()
  1316. {
  1317.     if (! pw_close ()) {
  1318.         fprintf (stderr, "%s: cannot rewrite password file\n", Prog);
  1319.         fail_exit (1);
  1320.     }
  1321. #ifdef    SHADOWPWD
  1322.     if (! spw_close ()) {
  1323.         fprintf (stderr, "%s: cannot rewrite shadow password file\n",    
  1324.             Prog);
  1325.         fail_exit (1);
  1326.     }
  1327. #endif
  1328.     if (user_ngroups >= 0) {
  1329.         if (! gr_close ()) {
  1330.             fprintf (stderr, "%s: cannot rewrite group file\n",
  1331.                 Prog);
  1332.             fail_exit (1);
  1333.         }
  1334.     }
  1335.     (void) gr_unlock ();
  1336. #ifdef    SHADOWGRP
  1337.     if (user_ngroups >= 0) {
  1338.         if (! sgr_close ()) {
  1339.             fprintf (stderr, "%s: cannot rewrite shadow group file\n",
  1340.                 Prog);
  1341.             fail_exit (1);
  1342.         }
  1343.     }
  1344.     (void) sgr_unlock ();
  1345. #endif
  1346.     (void) spw_unlock ();
  1347.     (void) pw_unlock ();
  1348.  
  1349.     /*
  1350.      * Close the DBM and/or flat files
  1351.      */
  1352.  
  1353.     endpwent ();
  1354. #ifdef    SHADOWPWD
  1355.     endspent ();
  1356. #endif
  1357.     endgrent ();
  1358. #ifdef    SHADOWGRP
  1359.     endsgent ();
  1360. #endif
  1361. }
  1362.  
  1363. /*
  1364.  * open_files - lock and open the password files
  1365.  *
  1366.  *    open_files() opens the two password files.
  1367.  */
  1368.  
  1369. open_files ()
  1370. {
  1371.     if (! pw_lock ()) {
  1372.         fprintf (stderr, "%s: unable to lock password file\n", Prog);
  1373.         exit (1);
  1374.     }
  1375.     if (! pw_open (O_RDWR)) {
  1376.         fprintf (stderr, "%s: unable to open password file\n", Prog);
  1377.         fail_exit (1);
  1378.     }
  1379. #ifdef    SHADOWPWD
  1380.     if (! spw_lock ()) {
  1381.         fprintf (stderr, "%s: cannot lock shadow password file\n",
  1382.             Prog);
  1383.         fail_exit (1);
  1384.     }
  1385.     if (! spw_open (O_RDWR)) {
  1386.         fprintf (stderr, "%s: cannot open shadow password file\n",
  1387.             Prog);
  1388.         fail_exit (1);
  1389.     }
  1390. #endif
  1391. }
  1392.  
  1393. /*
  1394.  * usr_update - create the user entries
  1395.  *
  1396.  *    usr_update() creates the password file entries for this user
  1397.  *    and will update the group entries if required.
  1398.  */
  1399.  
  1400. usr_update ()
  1401. {
  1402.     struct    passwd    pwent;
  1403.     struct    passwd    *pwd;
  1404. #ifdef    SHADOWPWD
  1405.     struct    spwd    spent;
  1406.     struct    spwd    *spwd;
  1407. #endif
  1408.     char    old_auth[BUFSIZ];
  1409.     char    auth_buf[BUFSIZ];
  1410.  
  1411.     old_auth[0] = '\0';
  1412.  
  1413.     /*
  1414.      * Locate the entry in /etc/passwd, which MUST exist.
  1415.      */
  1416.  
  1417.     pwd = pw_locate (user_name);
  1418.     pwent = *pwd;
  1419.     new_pwent (&pwent);
  1420.  
  1421. #ifdef    SHADOWPWD
  1422.  
  1423.     /* 
  1424.      * Locate the entry in /etc/shadow.  It doesn't have to
  1425.      * exist, and won't be created if it doesn't.
  1426.      */
  1427.  
  1428.     if (spwd = spw_locate (user_name)) {
  1429.         spent = *spwd;
  1430.         new_spent (&spent);
  1431.         strcpy (old_auth, spent.sp_pwdp);
  1432.     } else {
  1433.         strcpy (old_auth, pwent.pw_passwd);
  1434.     }
  1435. #else
  1436.     strcpy (old_auth, pwent.pw_passwd);
  1437. #endif
  1438.     if (lflg || (Aflg && strcmp (old_auth, user_auth) != 0)) {
  1439.         convert_auth (user_auth, old_auth, auth_arg);
  1440.         delete_auths (old_auth, user_auth, auth_buf);
  1441.         if (auth_buf[0] && pw_auth (auth_buf, user_name,
  1442.                 PW_DELETE, 0)) {
  1443.             fprintf (stderr,
  1444.                 "%s: error deleting authentication method\n",
  1445.                 Prog);
  1446. #ifdef    USE_SYSLOG
  1447.             syslog (LOG_ERR, "error deleting auth for `%s'\n",
  1448.                 user_name);
  1449. #endif
  1450.             fail_exit (1);
  1451.         }
  1452.         add_auths (old_auth, user_auth, auth_buf);
  1453.         if (auth_buf[0] == '@' && pw_auth (auth_buf,
  1454.                 lflg ? user_newname:user_name, PW_ADD, 0)) {
  1455.             fprintf (stderr,
  1456.                 "%s: error adding authentication method\n",
  1457.                 Prog);
  1458. #ifdef    USE_SYSLOG
  1459.             syslog (LOG_ERR, "error adding auth for `%s'\n",
  1460.                 lflg ? user_newname:user_name);
  1461. #endif
  1462.             fail_exit (1);
  1463.         }
  1464.         update_auths (old_auth, user_auth, auth_buf);
  1465.         if (lflg && auth_buf[0] == '@' && pw_auth (auth_buf,
  1466.                 user_newname, PW_CHANGE, user_name)) {
  1467.             fprintf (stderr,
  1468.                 "%s: error changing authentication method\n",
  1469.                 Prog);
  1470. #ifdef    USE_SYSLOG
  1471.             syslog (LOG_ERR, "error changing auth for `%s'\n",
  1472.                 lflg ? user_newname:user_name);
  1473. #endif
  1474.             fail_exit (1);
  1475.         }
  1476. #ifdef    SHADOWPWD
  1477.         spent.sp_pwdp = user_auth;
  1478. #else
  1479.         pwent.pw_passwd = user_auth;
  1480. #endif
  1481.     }
  1482.     if (lflg || uflg || gflg || cflg || dflg || sflg || Aflg) {
  1483.         if (! pw_update (&pwent)) {
  1484.             fprintf (stderr, "%s: error changing password entry\n",
  1485.                 Prog);
  1486.             fail_exit (1);
  1487.         }
  1488.         if (lflg && ! pw_remove (user_name)) {
  1489.             fprintf (stderr, "%s: error removing password entry\n",
  1490.                 Prog);
  1491.             fail_exit (1);
  1492.         }
  1493. #if defined(DBM) || defined(NDBM)
  1494.         if (access ("/etc/passwd.pag", 0) == 0) {
  1495.             if (! pw_dbm_update (&pwent)) {
  1496.                 fprintf (stderr,
  1497.                     "%s: error adding password dbm entry\n",
  1498.                     Prog);
  1499.                 fail_exit (1);
  1500.             }
  1501.             if (lflg && (pwd = getpwnam (user_name)) &&
  1502.                     ! pw_dbm_remove (pwd)) {
  1503.                 fprintf (stderr,
  1504.                     "%s: error removing passwd dbm entry\n",
  1505.                     Prog);
  1506.                 fail_exit (1);
  1507.             }
  1508.         }
  1509. #endif
  1510.     }
  1511. #ifdef    SHADOWPWD
  1512.     if (spwd && (lflg || eflg || fflg || Aflg)) {
  1513.         if (! spw_update (&spent)) {
  1514.             fprintf (stderr,
  1515.                 "%s: error adding new shadow password entry\n",
  1516.                 Prog);
  1517.             fail_exit (1);
  1518.         }
  1519.         if (lflg && ! spw_remove (user_name)) {
  1520.             fprintf (stderr,
  1521.                 "%s: error removing shadow password entry\n",
  1522.                 Prog);
  1523.             fail_exit (1);
  1524.         }
  1525.     }
  1526. #ifdef    NDBM
  1527.     if (spwd && access ("/etc/shadow.pag", 0) == 0) {
  1528.         if (! sp_dbm_update (&spent)) {
  1529.             fprintf (stderr,
  1530.                 "%s: error updating shadow passwd dbm entry\n",
  1531.                 Prog);
  1532.             fail_exit (1);
  1533.         }
  1534.         if (lflg && ! sp_dbm_remove (user_name)) {
  1535.             fprintf (stderr,
  1536.                 "%s: error removing shadow passwd db entry\n",
  1537.                 Prog);
  1538.             fail_exit (1);
  1539.         }
  1540.     }
  1541. #endif    /* NDBM */
  1542. #endif    /* SHADOWPWD */
  1543.     if (Gflg || lflg)
  1544.         grp_update ();
  1545. }
  1546.  
  1547. /*
  1548.  * move_home - move the user's home directory
  1549.  *
  1550.  *    move_home() moves the user's home directory to a new location.
  1551.  *    The files will be copied if the directory cannot simply be
  1552.  *    renamed.
  1553.  */
  1554.  
  1555. move_home ()
  1556. {
  1557.     struct    stat    sb;
  1558.  
  1559.     if (mflg && stat (user_home, &sb) == 0) {
  1560.         if (access (user_newhome, 0) == 0) {
  1561.             fprintf (stderr, "%s: directory %s exists\n",
  1562.                 Prog, user_newhome);
  1563.             fail_exit (12);
  1564.         } else if (rename (user_home, user_newhome)) {
  1565.             if (errno == EXDEV) {
  1566.                 if (mkdir (user_newhome, sb.st_mode & 0777)) {
  1567.                     fprintf (stderr,
  1568.                         "%s: can't create %s\n",
  1569.                         Prog, user_newhome);
  1570.                 }
  1571.                 if (chown (user_newhome,
  1572.                         sb.st_uid, sb.st_gid)) {
  1573.                     fprintf (stderr, "%s: can't chown %s\n",
  1574.                         Prog, user_newhome);
  1575.                     rmdir (user_newhome);
  1576.                     fail_exit (12);
  1577.                 }
  1578. #ifdef    DIR_ANY
  1579.                 if (copy_tree (user_home, user_newhome,
  1580.                         uflg ? user_newid:-1,
  1581.                         gflg ? user_newgid:-1,
  1582.                         user_id, user_gid) == 0 &&
  1583.                     remove_tree (user_home) == 0 &&
  1584.                         rmdir (user_home) == 0)
  1585.                     return;
  1586.  
  1587.                 (void) remove_tree (user_newhome);
  1588.                 (void) rmdir (user_newhome);
  1589. #else
  1590.                 return;
  1591. #endif
  1592.             }
  1593.             fprintf (stderr,
  1594.                 "%s: cannot rename directory %s to %s\n",
  1595.                 Prog, user_home, user_newhome);
  1596.             fail_exit (12);
  1597.         }
  1598.     }
  1599.     if (uflg || gflg)
  1600.         chown (dflg ? user_newhome:user_home,
  1601.             uflg ? user_newid:user_id,
  1602.             gflg ? user_newgid:user_gid);
  1603. }
  1604.  
  1605. /*
  1606.  * update_files - update the lastlog and faillog files
  1607.  */
  1608.  
  1609. void
  1610. update_files ()
  1611. {
  1612.     struct    lastlog    ll;
  1613.     struct    faillog    fl;
  1614.     int    fd;
  1615.  
  1616.     /*
  1617.      * Relocate the "lastlog" entries for the user.  The old entry
  1618.      * is left alone in case the UID was shared.  It doesn't hurt
  1619.      * anything to just leave it be.
  1620.      */
  1621.  
  1622.     if ((fd = open ("/usr/adm/lastlog", O_RDWR)) != -1) {
  1623.         lseek (fd, (long) user_id * sizeof ll, 0);
  1624.         if (read (fd, &ll, sizeof ll) == sizeof ll) {
  1625.             lseek (fd, (long) user_newid * sizeof ll, 0);
  1626.             write (fd, &ll, sizeof ll);
  1627.         }
  1628.         close (fd);
  1629.     }
  1630.  
  1631.     /*
  1632.      * Relocate the "faillog" entries in the same manner.
  1633.      */
  1634.  
  1635.     if ((fd = open (FAILFILE, O_RDWR)) != -1) {
  1636.         lseek (fd, (long) user_id * sizeof fl, 0);
  1637.         if (read (fd, &fl, sizeof fl) == sizeof fl) {
  1638.             lseek (fd, (long) user_newid * sizeof ll, 0);
  1639.             write (fd, &fl, sizeof fl);
  1640.         }
  1641.         close (fd);
  1642.     }
  1643. }
  1644.  
  1645. /*
  1646.  * fail_exit - exit with an error code after unlocking files
  1647.  */
  1648.  
  1649. fail_exit (code)
  1650. int    code;
  1651. {
  1652.     (void) gr_unlock ();
  1653. #ifdef    SHADOWGRP
  1654.     (void) sgr_unlock ();
  1655. #endif
  1656. #ifdef    SHADOWPWD
  1657.     (void) spw_unlock ();
  1658. #endif
  1659.     (void) pw_unlock ();
  1660.     exit (code);
  1661. }
  1662.  
  1663. /*
  1664.  * main - usermod command
  1665.  */
  1666.  
  1667. main (argc, argv)
  1668. int    argc;
  1669. char    **argv;
  1670. {
  1671.     /*
  1672.      * Get my name so that I can use it to report errors.
  1673.      */
  1674.  
  1675.     if (Prog = strrchr (argv[0], '/'))
  1676.         Prog++;
  1677.     else
  1678.         Prog = argv[0];
  1679.  
  1680. #ifdef    USE_SYSLOG
  1681.     openlog (Prog, LOG_PID|LOG_CONS|LOG_NOWAIT, LOG_AUTH);
  1682. #endif
  1683.  
  1684.     /*
  1685.      * The open routines for the NDBM files don't use read-write
  1686.      * as the mode, so we have to clue them in.
  1687.      */
  1688.  
  1689. #ifdef    NDBM
  1690.     pw_dbm_mode = O_RDWR;
  1691. #ifdef    SHADOWPWD
  1692.     sp_dbm_mode = O_RDWR;
  1693. #endif
  1694.     gr_dbm_mode = O_RDWR;
  1695. #ifdef    SHADOWGRP
  1696.     sg_dbm_mode = O_RDWR;
  1697. #endif
  1698. #endif    /* NDBM */
  1699.     process_flags (argc, argv);
  1700.  
  1701.     /*
  1702.      * Do the hard stuff - open the files, change the user entries,
  1703.      * change the home directory, then close and update the files.
  1704.      */
  1705.  
  1706.     open_files ();
  1707.  
  1708.     usr_update ();
  1709.  
  1710.     close_files ();
  1711.  
  1712.     if (mflg)
  1713.         move_home ();
  1714.  
  1715.     if (uflg) {
  1716.         update_files ();
  1717.  
  1718.         /*
  1719.          * Change the UID on all of the files owned by `user_id'
  1720.          * to `user_newid' in the user's home directory.
  1721.          */
  1722.  
  1723.         chown_tree (dflg ? user_newhome:user_home,
  1724.             user_id, user_newid,
  1725.             user_gid, gflg ? user_newgid:user_gid);
  1726.     }
  1727.     exit (0);
  1728.     /*NOTREACHED*/
  1729. }
  1730.