home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2083 / newusers.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  12.7 KB  |  599 lines

  1. /*
  2.  * Copyright 1990, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Use, duplication, and disclosure prohibited without
  6.  * the express written permission of the author.
  7.  *
  8.  *    newusers - create users from a batch file
  9.  *
  10.  *    newusers creates a collection of entries in /etc/passwd
  11.  *    and related files by reading a passwd-format file and
  12.  *    adding entries in the related directories.
  13.  */
  14.  
  15. #include <stdio.h>
  16. #include <pwd.h>
  17. #include <grp.h>
  18. #include <fcntl.h>
  19. #include <string.h>
  20. #include "config.h"
  21. #ifdef    SHADOWPWD
  22. #include "shadow.h"
  23. #endif
  24.  
  25. #ifndef    lint
  26. static    char    sccsid[] = "@(#)newusers.c    3.1 22:33:23 11/16/90";
  27. #endif
  28.  
  29. char    *Prog;
  30.  
  31. extern    char    *pw_encrypt();
  32.  
  33. int    pw_lock(), gr_lock();
  34. int    pw_open(), gr_open();
  35. struct    passwd    *pw_locate(), *pw_next();
  36. struct    group    *gr_locate(), *gr_next();
  37. int    pw_update(), gr_update();
  38. int    pw_close(), gr_close();
  39. int    pw_unlock(), gr_unlock();
  40.  
  41. #ifdef    SHADOWPWD
  42. int    spw_lock(), spw_open(), spw_update(), spw_close(), spw_unlock();
  43. struct    spwd    *spw_locate(), *spw_next();
  44. #endif
  45.  
  46. #ifndef    MKDIR
  47.  
  48. /*
  49.  * mkdir - for those of us with no mkdir() system call.
  50.  */
  51.  
  52. mkdir (dir, mode)
  53. char    *dir;
  54. int    mode;
  55. {
  56.     int    mask;
  57.     int    status;
  58.     int    pid;
  59.     int    i;
  60.     char    buf[BUFSIZ];
  61.  
  62.     mode = (~mode & 0777);
  63.     mask = umask (mode);
  64.     if ((pid = fork ()) == 0) {
  65.         execl ("/bin/mkdir", "mkdir", dir, (char *) 0);
  66.         perror ("/bin/mkdir");
  67.         _exit (1);
  68.     } else {
  69.         while ((i = wait (&status)) != pid && i != -1)
  70.             ;
  71.     }
  72.     umask (mask);
  73.     return status;
  74. }
  75. #endif
  76.  
  77. /*
  78.  * usage - display usage message and exit
  79.  */
  80.  
  81. usage ()
  82. {
  83.     fprintf (stderr, "Usage: %s [ input ]\n", Prog);
  84.     exit (1);
  85. }
  86.  
  87. /*
  88.  * add_group - create a new group or add a user to an existing group
  89.  */
  90.  
  91. int
  92. add_group (name, uid, gid, ngid)
  93. char    *name;
  94. char    *uid;
  95. char    *gid;
  96. int    *ngid;
  97. {
  98.     struct    passwd    *pwd;
  99.     struct    group    *grp;
  100.     struct    group    grent;
  101.     char    *members[2];
  102.     int    i;
  103.  
  104.     /*
  105.      * Start by seeing if the named group already exists.  This
  106.      * will be very easy to deal with if it does.
  107.      */
  108.  
  109.     if (grp = gr_locate (gid)) {
  110. add_member:
  111.         grent = *grp;
  112.         *ngid = grent.gr_gid;
  113.         for (i = 0;grent.gr_mem[i] != (char *) 0;i++)
  114.             if (strcmp (grent.gr_mem[i], name) == 0)
  115.                 return 0;
  116.  
  117.         if (! (grent.gr_mem = (char **)
  118.                 malloc (sizeof (char *) * (i + 2)))) {
  119.             fprintf (stderr, "%s: Out of Memory\n", Prog);
  120.             return -1;
  121.         }
  122.         memcpy (grent.gr_mem, grp->gr_mem, sizeof (char *) * (i + 2));
  123.         grent.gr_mem[i] = strdup (name);
  124.         grent.gr_mem[i + 1] = (char *) 0;
  125.  
  126.         return ! gr_update (&grent);
  127.     }
  128.  
  129.     /*
  130.      * The group did not exist, so I try to figure out what the
  131.      * GID is going to be.  The gid parameter is probably "", meaning
  132.      * I figure out the GID from the password file.  I want the UID
  133.      * and GID to match, unless the GID is already used.
  134.      */
  135.  
  136.     if (gid[0] == '\0') {
  137.         i = 100;
  138.         for (pw_rewind ();pwd = pw_next ();) {
  139.             if (pwd->pw_uid >= i)
  140.                 i = pwd->pw_uid + 1;
  141.         }
  142.         for (gr_rewind ();grp = gr_next ();) {
  143.             if (grp->gr_gid == i) {
  144.                 i = -1;
  145.                 break;
  146.             }
  147.         }
  148.     } else if (gid[0] >= '0' && gid[0] <= '9') {
  149.  
  150.     /*
  151.      * The GID is a number, which means either this is a brand new
  152.      * group, or an existing group.  For existing groups I just add
  153.      * myself as a member, just like I did earlier.
  154.      */
  155.  
  156.         i = atoi (gid);
  157.         for (gr_rewind ();grp = gr_next ();)
  158.             if (grp->gr_gid == i)
  159.                 goto add_member;
  160.     } else
  161.  
  162.     /*
  163.      * The last alternative is that the GID is a name which is not
  164.      * already the name of an existing group, and I need to figure
  165.      * out what group ID that group name is going to have.
  166.      */
  167.  
  168.         i = -1;
  169.  
  170.     /*
  171.      * If I don't have a group ID by now, I'll go get the
  172.      * next one.
  173.      */
  174.  
  175.     if (i == -1) {
  176.         for (i = 100, gr_rewind;grp = gr_next ();)
  177.             if (grp->gr_gid >= i)
  178.                 i = grp->gr_gid + 1;
  179.     }
  180.  
  181.     /*
  182.      * Now I have all of the fields required to create the new
  183.      * group.
  184.      */
  185.  
  186.     if (gid[0] && (gid[0] <= '0' || gid[0] >= '9'))
  187.         grent.gr_name = gid;
  188.     else
  189.         grent.gr_name = name;
  190.  
  191.     grent.gr_passwd = "!";
  192.     grent.gr_gid = i;
  193.     members[0] = name;
  194.     members[1] = (char *) 0;
  195.     grent.gr_mem = members;
  196.  
  197.     *ngid = grent.gr_gid;
  198.     return ! gr_update (&grent);
  199. }
  200.  
  201. /*
  202.  * add_user - create a new user ID
  203.  */
  204.  
  205. add_user (name, uid, nuid, gid)
  206. char    *name;
  207. char    *uid;
  208. int    *nuid;
  209. int    gid;
  210. {
  211.     struct    passwd    *pwd;
  212.     struct    passwd    pwent;
  213.     int    i;
  214.  
  215.     /*
  216.      * The first guess for the UID is either the numerical UID
  217.      * that the caller provided, or the next available UID.
  218.      */
  219.  
  220.     if (uid[0] >= '0' && uid[0] <= '9') {
  221.         i = atoi (uid);
  222.     } if (uid[0] && (pwd = pw_locate (uid))) {
  223.         i = pwd->pw_uid;
  224.     } else {
  225.         i = 100;
  226.         for (pw_rewind ();pwd = pw_next ();)
  227.             if (pwd->pw_uid >= i)
  228.                 i = pwd->pw_uid + 1;
  229.     }
  230.  
  231.     /*
  232.      * I don't want to fill in the entire password structure
  233.      * members JUST YET, since there is still more data to be
  234.      * added.  So, I fill in the parts that I have.
  235.      */
  236.  
  237.     pwent.pw_name = name;
  238.     pwent.pw_passwd = "!";
  239.     pwent.pw_age = "";
  240.     pwent.pw_uid = i;
  241.     pwent.pw_gid = gid;
  242.     pwent.pw_gecos = "";
  243.     pwent.pw_dir = "";
  244.     pwent.pw_shell = "";
  245.  
  246.     *nuid = i;
  247.     return ! pw_update (&pwent);
  248. }
  249.  
  250. /*
  251.  * add_passwd - add or update the encrypted password
  252.  */
  253.  
  254. add_passwd (pwd, passwd)
  255. struct    passwd    *pwd;
  256. char    *passwd;
  257. {
  258. #ifdef    SHADOWPWD
  259.     struct    spwd    *sp;
  260.     struct    spwd    spent;
  261. #endif
  262.     struct    passwd    *pw;
  263.     struct    passwd    pwent;
  264.     static    char    newage[5];
  265.  
  266.     /*
  267.      * In the case of regular password files, this is real
  268.      * easy - pwd points to the entry in the password file.
  269.      * Shadow files are harder since there are zillions of
  270.      * things to do ...
  271.      */
  272.  
  273. #ifndef    SHADOWPWD
  274.     pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
  275.     if (strlen (pwd->pw_age) == 4) {
  276.         strcpy (newage, pwd->pw_age);
  277.         strcpy (newage + 2,
  278.             l64a (time ((long *) 0) / (7L*24L*3600L)));
  279.         pwd->pw_age = newage;
  280.     }
  281.     return 0;
  282. #else
  283.  
  284.     /*
  285.      * Do the first and easiest shadow file case.  The user
  286.      * already exists in the shadow password file.
  287.      */
  288.  
  289.     if (sp = spw_locate (pwd->pw_name)) {
  290.         spent = *sp;
  291.         spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
  292.         return ! spw_update (sp);
  293.     }
  294.  
  295.     /*
  296.      * Pick the next easiest case - the user has an encrypted
  297.      * password which isn't equal to "!".  The password was set
  298.      * to "!" earlier when the entry was created, so this user
  299.      * would have to have had the password set someplace else.
  300.      */
  301.  
  302.     if (strcmp (pwd->pw_passwd, "!") != 0) {
  303.         pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
  304.         if (strlen (pwd->pw_age) == 4) {
  305.             strcpy (newage, pwd->pw_age);
  306.             strcpy (newage + 2,
  307.                 l64a (time ((long *) 0) / (7L*24L*3600L)));
  308.             pwd->pw_age = newage;
  309.         }
  310.         return 0;
  311.     }
  312.  
  313.     /*
  314.      * Now the really hard case - I need to create an entirely
  315.      * shadow password file entry.
  316.      */
  317.  
  318.     spent.sp_namp = pwd->pw_name;
  319.     spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
  320.     spent.sp_lstchg = time ((long *) 0) / (24L*3600L);
  321. #ifdef    MINDAYS
  322.     spent.sp_min = MINDAYS;
  323. #else
  324.     spent.sp_min = 0;
  325. #endif
  326. #ifdef    MAXDAYS
  327.     spent.sp_max = MAXDAYS;
  328. #else
  329.     spent.sp_max = 10000;        /* 10000 is infinity this week */
  330. #endif
  331. #ifdef    WARNAGE
  332.     spent.sp_warn = WARNAGE;
  333. #else
  334.     spent.sp_warn = -1;
  335. #endif
  336.     spent.sp_inact = -1;
  337.     spent.sp_expire = -1;
  338.     spent.sp_flag = -1;
  339.  
  340.     return ! spw_update (&spent);
  341. #endif
  342. }
  343.  
  344. main (argc, argv)
  345. int    argc;
  346. char    **argv;
  347. {
  348.     char    buf[BUFSIZ];
  349.     char    *fields[8];
  350.     int    nfields;
  351.     char    *name;
  352.     char    *newpwd;
  353.     char    *cp;
  354. #ifdef    SHADOWPWD
  355.     struct    spwd    *sp;
  356.     struct    spwd    newsp;
  357.     struct    spwd    *spw_locate();
  358. #endif
  359.     struct    passwd    *pw;
  360.     struct    passwd    newpw;
  361.     struct    passwd    *pw_locate();
  362.     char    newage[5];
  363.     int    errors = 0;
  364.     int    line = 0;
  365.     long    now = time ((long *) 0) / (24L*3600L);
  366.     int    uid;
  367.     int    gid;
  368.     int    i;
  369.  
  370.     if (Prog = strrchr (argv[0], '/'))
  371.         Prog++;
  372.     else
  373.         Prog = argv[0];
  374.  
  375.     if (argc > 1 && argv[1][0] == '-')
  376.         usage ();
  377.  
  378.     if (argc == 2) {
  379.         if (! freopen (argv[1], "r", stdin)) {
  380.             sprintf (buf, "%s: %s", Prog, argv[1]);
  381.             perror (buf);
  382.             exit (1);
  383.         }
  384.     }
  385.  
  386.     /*
  387.      * Lock the password files and open them for update.  This will
  388.      * bring all of the entries into memory where they may be
  389.      * searched for an modified, or new entries added.  The password
  390.      * file is the key - if it gets locked, assume the others can
  391.      * be locked right away.
  392.      */
  393.  
  394.     for (i = 0;i < 30;i++) {
  395.         if (pw_lock ())
  396.             break;
  397.     }
  398.     if (i == 30) {
  399.         fprintf (stderr, "%s: can't lock /etc/passwd.\n", Prog);
  400.         exit (1);
  401.     }
  402. #ifdef    SHADOWPWD
  403.     if (! spw_lock () || ! gr_lock ())
  404. #else
  405.     if (! gr_lock ())
  406. #endif
  407.     {
  408.         fprintf (stderr, "%s: can't lock files, try again later\n",
  409.             Prog);
  410.         (void) pw_unlock ();
  411. #ifdef    SHADOWPWD
  412.         (void) spw_unlock ();
  413. #endif
  414.         exit (1);
  415.     }
  416. #ifdef    SHADOWPWD
  417.     if (! pw_open (O_RDWR) || ! spw_open (O_RDWR) || ! gr_open (O_RDWR))
  418. #else
  419.     if (! pw_open (O_RDWR) || ! gr_open (O_RDWR))
  420. #endif
  421.     {
  422.         fprintf (stderr, "%s: can't open files\n", Prog);
  423.         (void) pw_unlock ();
  424. #ifdef    SHADOWPWD
  425.         (void) spw_unlock ();
  426. #endif
  427.         (void) gr_unlock ();
  428.         exit (1);
  429.     }
  430.  
  431.     /*
  432.      * Read each line.  The line has the same format as a password
  433.      * file entry, except that certain fields are not contrained to
  434.      * be numerical values.  If a group ID is entered which does
  435.      * not already exist, an attempt is made to allocate the same
  436.      * group ID as the numerical user ID.  Should that fail, the
  437.      * next available group ID over 100 is allocated.  The pw_gid
  438.      * field will be updated with that value.
  439.      */
  440.  
  441.     while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
  442.         line++;
  443.         if (cp = strrchr (buf, '\n')) {
  444.             *cp = '\0';
  445.         } else {
  446.             fprintf (stderr, "%s: line %d: line too long\n",
  447.                 Prog, line);
  448.             errors++;
  449.             continue;
  450.         }
  451.  
  452.         /*
  453.          * Break the string into fields and screw around with
  454.          * them.  There MUST be 7 colon separated fields,
  455.          * although the values aren't that particular.
  456.          */
  457.  
  458.         for (cp = buf, nfields = 0;nfields < 7;nfields++) {
  459.             fields[nfields] = cp;
  460.             if (cp = strchr (cp, ':'))
  461.                 *cp++ = '\0';
  462.             else
  463.                 break;
  464.         }
  465.         if (*cp || nfields != 6) {
  466.             fprintf (stderr, "%s: line %d: invalid line\n",
  467.                 Prog, line);
  468.             continue;
  469.         }
  470.  
  471.         /*
  472.          * Now the fields are processed one by one.  The first
  473.          * field to be processed is the group name.  A new
  474.          * group will be created if the group name is non-numeric
  475.          * and does not already exist.  The named user will be
  476.          * the only member.  If there is no named group to be a
  477.          * member of, the UID will be figured out and that value
  478.          * will be a candidate for a new group, if that group ID
  479.          * exists, a whole new group ID will be made up.
  480.          */
  481.         
  482.         if (! (pw = pw_locate (fields[0])) &&
  483.             add_group (fields[0], fields[2], fields[3], &gid)) {
  484.             fprintf (stderr, "%s: %d: can't create GID\n",
  485.                 Prog, line);
  486.             errors++;
  487.             continue;
  488.         }
  489.  
  490.         /*
  491.          * Now we work on the user ID.  It has to be specified
  492.          * either as a numerical value, or left blank.  If it
  493.          * is a numerical value, that value will be used, otherwise
  494.          * the next available user ID is computed and used.  After
  495.          * this there will at least be a (struct passwd) for the
  496.          * user.
  497.          */
  498.  
  499.         if (! pw && add_user (fields[0], fields[2], &uid, gid)) {
  500.             fprintf (stderr, "%s: line %d: can't create UID\n",
  501.                 Prog, line);
  502.             errors++;
  503.             continue;
  504.         }
  505.  
  506.         /*
  507.          * The password, gecos field, directory, and shell fields
  508.          * all come next.
  509.          */
  510.  
  511.         if (! (pw = pw_locate (fields[0]))) {
  512.             fprintf (stderr, "%s: line %d: cannot find user %s\n",
  513.                 Prog, line, fields[0]);
  514.             errors++;
  515.             continue;
  516.         }
  517.         newpw = *pw;
  518.  
  519.         if (add_passwd (&newpw, fields[1])) {
  520.             fprintf (stderr, "%s: line %d: can't update password\n",
  521.                 Prog, line);
  522.             errors++;
  523.             continue;
  524.         }
  525.         if (fields[4][0])
  526.             newpw.pw_gecos = fields[4];
  527.  
  528.         if (fields[5][0])
  529.             newpw.pw_dir = fields[5];
  530.  
  531.         if (fields[6][0])
  532.             newpw.pw_shell = fields[6];
  533.  
  534.         if (newpw.pw_dir[0] && access (newpw.pw_dir, 0)) {
  535. #ifdef    UMASK
  536.             if (mkdir (newpw.pw_dir, 0777 & (~UMASK)))
  537. #else
  538.             if (mkdir (newpw.pw_dir, 0777))
  539. #endif
  540.                 fprintf (stderr, "%s: line %d: mkdir failed\n",
  541.                     Prog, line);
  542.             else if (chown (newpw.pw_dir,
  543.                     newpw.pw_uid, newpw.pw_gid))
  544.                 fprintf (stderr, "%s: line %d: chown failed\n",
  545.                     Prog, line);
  546.         }
  547.  
  548.         /*
  549.          * Update the password entry with the new changes made.
  550.          */
  551.  
  552.         if (! pw_update (&newpw)) {
  553.             fprintf (stderr, "%s: line %d: can't update entry\n",
  554.                 Prog, line);
  555.             errors++;
  556.             continue;
  557.         }
  558.     }
  559.  
  560.     /*
  561.      * Any detected errors will cause the entire set of changes
  562.      * to be aborted.  Unlocking the password file will cause
  563.      * all of the changes to be ignored.  Otherwise the file is
  564.      * closed, causing the changes to be written out all at
  565.      * once, and then unlocked afterwards.
  566.      */
  567.  
  568.     if (errors) {
  569.         fprintf ("%s: error detected, changes ignored\n", Prog);
  570.         (void) gr_unlock ();
  571. #ifdef    SHADOWPWD
  572.         (void) spw_unlock ();
  573. #endif
  574.         (void) pw_unlock ();
  575.         exit (1);
  576.     }
  577. #ifdef    SHADOWPWD
  578.     if (! pw_close () || ! spw_close () || ! gr_close ())
  579. #else
  580.     if (! pw_close () || ! gr_close ())
  581. #endif
  582.     {
  583.         fprintf ("%s: error updating files\n", Prog);
  584.         (void) gr_unlock ();
  585. #ifdef    SHADOWPWD
  586.         (void) spw_unlock ();
  587. #endif
  588.         (void) pw_unlock ();
  589.         exit (1);
  590.     }
  591.     (void) gr_unlock ();
  592. #ifdef    SHADOWPWD
  593.     (void) spw_unlock ();
  594. #endif
  595.     (void) pw_unlock ();
  596.  
  597.     exit (0);
  598. }
  599.