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

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