home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / security / shadow-3.1.4 / newusers.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-28  |  12.8 KB  |  594 lines

  1. /*
  2.  * Copyright 1990, 1991, John F. Haugh II
  3.  * All rights reserved.
  4.  *
  5.  * Permission is granted to copy and create derivative works for any
  6.  * non-commercial purpose, provided this copyright notice is preserved
  7.  * in all copies of source code, or included in human readable form
  8.  * and conspicuously displayed on all copies of object code or
  9.  * distribution media.
  10.  *
  11.  *    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 "config.h"
  19. #include <stdio.h>
  20. #include "pwd.h"
  21. #include <grp.h>
  22. #include <fcntl.h>
  23. #include <string.h>
  24. #ifdef    SHADOWPWD
  25. #include "shadow.h"
  26. #endif
  27.  
  28. #ifndef    lint
  29. static    char    sccsid[] = "@(#)newusers.c    3.6    12:04:31    12/28/91";
  30. #endif
  31.  
  32. char    *Prog;
  33.  
  34. extern    char    *pw_encrypt();
  35. extern    char    *malloc();
  36.  
  37. int    pw_lock(), gr_lock();
  38. int    pw_open(), gr_open();
  39. struct    passwd    *pw_locate(), *pw_next();
  40. struct    group    *gr_locate(), *gr_next();
  41. int    pw_update(), gr_update();
  42. int    pw_close(), gr_close();
  43. int    pw_unlock(), gr_unlock();
  44. extern    int    getdef_num();
  45.  
  46. #ifdef    SHADOWPWD
  47. int    spw_lock(), spw_open(), spw_update(), spw_close(), spw_unlock();
  48. struct    spwd    *spw_locate(), *spw_next();
  49. #endif
  50.  
  51. #ifndef    MKDIR
  52.  
  53. /*
  54.  * mkdir - for those of us with no mkdir() system call.
  55.  */
  56.  
  57. mkdir (dir, mode)
  58. char    *dir;
  59. int    mode;
  60. {
  61.     int    mask;
  62.     int    status;
  63.     int    pid;
  64.     int    i;
  65.  
  66.     mode = (~mode & 0777);
  67.     mask = umask (mode);
  68.     if ((pid = fork ()) == 0) {
  69.         execl ("/bin/mkdir", "mkdir", dir, (char *) 0);
  70.         perror ("/bin/mkdir");
  71.         _exit (1);
  72.     } else {
  73.         while ((i = wait (&status)) != pid && i != -1)
  74.             ;
  75.     }
  76.     umask (mask);
  77.     return status;
  78. }
  79. #endif
  80.  
  81. /*
  82.  * usage - display usage message and exit
  83.  */
  84.  
  85. usage ()
  86. {
  87.     fprintf (stderr, "Usage: %s [ input ]\n", Prog);
  88.     exit (1);
  89. }
  90.  
  91. /*
  92.  * add_group - create a new group or add a user to an existing group
  93.  */
  94.  
  95. int
  96. add_group (name, gid, ngid)
  97. char    *name;
  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.     } else 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.     static    char    newage[5];
  274.     extern    char    *l64a();
  275.  
  276.     /*
  277.      * In the case of regular password files, this is real
  278.      * easy - pwd points to the entry in the password file.
  279.      * Shadow files are harder since there are zillions of
  280.      * things to do ...
  281.      */
  282.  
  283. #ifndef    SHADOWPWD
  284.     pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
  285. #ifdef    ATT_AGE
  286.     if (strlen (pwd->pw_age) == 4) {
  287.         strcpy (newage, pwd->pw_age);
  288.         strcpy (newage + 2,
  289.             l64a (time ((long *) 0) / (7L*24L*3600L)));
  290.         pwd->pw_age = newage;
  291.     }
  292. #endif    /* ATT_AGE */
  293.     return 0;
  294. #else
  295.  
  296.     /*
  297.      * Do the first and easiest shadow file case.  The user
  298.      * already exists in the shadow password file.
  299.      */
  300.  
  301.     if (sp = spw_locate (pwd->pw_name)) {
  302.         spent = *sp;
  303.         spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
  304.         return ! spw_update (sp);
  305.     }
  306.  
  307.     /*
  308.      * Pick the next easiest case - the user has an encrypted
  309.      * password which isn't equal to "!".  The password was set
  310.      * to "!" earlier when the entry was created, so this user
  311.      * would have to have had the password set someplace else.
  312.      */
  313.  
  314.     if (strcmp (pwd->pw_passwd, "!") != 0) {
  315.         pwd->pw_passwd = pw_encrypt (passwd, (char *) 0);
  316. #ifdef    ATT_AGE
  317.         if (strlen (pwd->pw_age) == 4) {
  318.             strcpy (newage, pwd->pw_age);
  319.             strcpy (newage + 2,
  320.                 l64a (time ((long *) 0) / (7L*24L*3600L)));
  321.             pwd->pw_age = newage;
  322.         }
  323. #endif    /* ATT_AGE */
  324.         return 0;
  325.     }
  326.  
  327.     /*
  328.      * Now the really hard case - I need to create an entirely
  329.      * shadow password file entry.
  330.      */
  331.  
  332.     spent.sp_namp = pwd->pw_name;
  333.     spent.sp_pwdp = pw_encrypt (passwd, (char *) 0);
  334.     spent.sp_lstchg = time ((long *) 0) / (24L*3600L);
  335.     spent.sp_min = getdef_num("PASS_MIN_DAYS", 0);
  336.                     /* 10000 is infinity this week */
  337.     spent.sp_max = getdef_num("PASS_MAX_DAYS", 10000);
  338.     spent.sp_warn = getdef_num("PASS_WARN_AGE", -1);
  339.     spent.sp_inact = -1;
  340.     spent.sp_expire = -1;
  341.     spent.sp_flag = -1;
  342.  
  343.     return ! spw_update (&spent);
  344. #endif
  345. }
  346.  
  347. main (argc, argv)
  348. int    argc;
  349. char    **argv;
  350. {
  351.     char    buf[BUFSIZ];
  352.     char    *fields[8];
  353.     int    nfields;
  354.     char    *cp;
  355. #ifdef    SHADOWPWD
  356.     struct    spwd    *spw_locate();
  357. #endif
  358.     struct    passwd    *pw;
  359.     struct    passwd    newpw;
  360.     struct    passwd    *pw_locate();
  361.     int    errors = 0;
  362.     int    line = 0;
  363.     int    uid;
  364.     int    gid;
  365.     int    i;
  366.  
  367.     if (Prog = strrchr (argv[0], '/'))
  368.         Prog++;
  369.     else
  370.         Prog = argv[0];
  371.  
  372.     if (argc > 1 && argv[1][0] == '-')
  373.         usage ();
  374.  
  375.     if (argc == 2) {
  376.         if (! freopen (argv[1], "r", stdin)) {
  377.             sprintf (buf, "%s: %s", Prog, argv[1]);
  378.             perror (buf);
  379.             exit (1);
  380.         }
  381.     }
  382.  
  383.     /*
  384.      * Lock the password files and open them for update.  This will
  385.      * bring all of the entries into memory where they may be
  386.      * searched for an modified, or new entries added.  The password
  387.      * file is the key - if it gets locked, assume the others can
  388.      * be locked right away.
  389.      */
  390.  
  391.     for (i = 0;i < 30;i++) {
  392.         if (pw_lock ())
  393.             break;
  394.     }
  395.     if (i == 30) {
  396.         fprintf (stderr, "%s: can't lock /etc/passwd.\n", Prog);
  397.         exit (1);
  398.     }
  399. #ifdef    SHADOWPWD
  400.     if (! spw_lock () || ! gr_lock ())
  401. #else
  402.     if (! gr_lock ())
  403. #endif
  404.     {
  405.         fprintf (stderr, "%s: can't lock files, try again later\n",
  406.             Prog);
  407.         (void) pw_unlock ();
  408. #ifdef    SHADOWPWD
  409.         (void) spw_unlock ();
  410. #endif
  411.         exit (1);
  412.     }
  413. #ifdef    SHADOWPWD
  414.     if (! pw_open (O_RDWR) || ! spw_open (O_RDWR) || ! gr_open (O_RDWR))
  415. #else
  416.     if (! pw_open (O_RDWR) || ! gr_open (O_RDWR))
  417. #endif
  418.     {
  419.         fprintf (stderr, "%s: can't open files\n", Prog);
  420.         (void) pw_unlock ();
  421. #ifdef    SHADOWPWD
  422.         (void) spw_unlock ();
  423. #endif
  424.         (void) gr_unlock ();
  425.         exit (1);
  426.     }
  427.  
  428.     /*
  429.      * Read each line.  The line has the same format as a password
  430.      * file entry, except that certain fields are not contrained to
  431.      * be numerical values.  If a group ID is entered which does
  432.      * not already exist, an attempt is made to allocate the same
  433.      * group ID as the numerical user ID.  Should that fail, the
  434.      * next available group ID over 100 is allocated.  The pw_gid
  435.      * field will be updated with that value.
  436.      */
  437.  
  438.     while (fgets (buf, sizeof buf, stdin) != (char *) 0) {
  439.         line++;
  440.         if (cp = strrchr (buf, '\n')) {
  441.             *cp = '\0';
  442.         } else {
  443.             fprintf (stderr, "%s: line %d: line too long\n",
  444.                 Prog, line);
  445.             errors++;
  446.             continue;
  447.         }
  448.  
  449.         /*
  450.          * Break the string into fields and screw around with
  451.          * them.  There MUST be 7 colon separated fields,
  452.          * although the values aren't that particular.
  453.          */
  454.  
  455.         for (cp = buf, nfields = 0;nfields < 7;nfields++) {
  456.             fields[nfields] = cp;
  457.             if (cp = strchr (cp, ':'))
  458.                 *cp++ = '\0';
  459.             else
  460.                 break;
  461.         }
  462.         if (nfields != 6) {
  463.             fprintf (stderr, "%s: line %d: invalid line\n",
  464.                 Prog, line);
  465.             continue;
  466.         }
  467.  
  468.         /*
  469.          * Now the fields are processed one by one.  The first
  470.          * field to be processed is the group name.  A new
  471.          * group will be created if the group name is non-numeric
  472.          * and does not already exist.  The named user will be
  473.          * the only member.  If there is no named group to be a
  474.          * member of, the UID will be figured out and that value
  475.          * will be a candidate for a new group, if that group ID
  476.          * exists, a whole new group ID will be made up.
  477.          */
  478.         
  479.         if (! (pw = pw_locate (fields[0])) &&
  480.             add_group (fields[0], fields[3], &gid)) {
  481.             fprintf (stderr, "%s: %d: can't create GID\n",
  482.                 Prog, line);
  483.             errors++;
  484.             continue;
  485.         }
  486.  
  487.         /*
  488.          * Now we work on the user ID.  It has to be specified
  489.          * either as a numerical value, or left blank.  If it
  490.          * is a numerical value, that value will be used, otherwise
  491.          * the next available user ID is computed and used.  After
  492.          * this there will at least be a (struct passwd) for the
  493.          * user.
  494.          */
  495.  
  496.         if (! pw && add_user (fields[0], fields[2], &uid, gid)) {
  497.             fprintf (stderr, "%s: line %d: can't create UID\n",
  498.                 Prog, line);
  499.             errors++;
  500.             continue;
  501.         }
  502.  
  503.         /*
  504.          * The password, gecos field, directory, and shell fields
  505.          * all come next.
  506.          */
  507.  
  508.         if (! (pw = pw_locate (fields[0]))) {
  509.             fprintf (stderr, "%s: line %d: cannot find user %s\n",
  510.                 Prog, line, fields[0]);
  511.             errors++;
  512.             continue;
  513.         }
  514.         newpw = *pw;
  515.  
  516.         if (add_passwd (&newpw, fields[1])) {
  517.             fprintf (stderr, "%s: line %d: can't update password\n",
  518.                 Prog, line);
  519.             errors++;
  520.             continue;
  521.         }
  522.         if (fields[4][0])
  523.             newpw.pw_gecos = fields[4];
  524.  
  525.         if (fields[5][0])
  526.             newpw.pw_dir = fields[5];
  527.  
  528.         if (fields[6][0])
  529.             newpw.pw_shell = fields[6];
  530.  
  531.         if (newpw.pw_dir[0] && access (newpw.pw_dir, 0)) {
  532.             if (mkdir (newpw.pw_dir,
  533.                     0777 & ~getdef_num("UMASK", 0)))
  534.                 fprintf (stderr, "%s: line %d: mkdir failed\n",
  535.                     Prog, line);
  536.             else if (chown (newpw.pw_dir,
  537.                     newpw.pw_uid, newpw.pw_gid))
  538.                 fprintf (stderr, "%s: line %d: chown failed\n",
  539.                     Prog, line);
  540.         }
  541.  
  542.         /*
  543.          * Update the password entry with the new changes made.
  544.          */
  545.  
  546.         if (! pw_update (&newpw)) {
  547.             fprintf (stderr, "%s: line %d: can't update entry\n",
  548.                 Prog, line);
  549.             errors++;
  550.             continue;
  551.         }
  552.     }
  553.  
  554.     /*
  555.      * Any detected errors will cause the entire set of changes
  556.      * to be aborted.  Unlocking the password file will cause
  557.      * all of the changes to be ignored.  Otherwise the file is
  558.      * closed, causing the changes to be written out all at
  559.      * once, and then unlocked afterwards.
  560.      */
  561.  
  562.     if (errors) {
  563.         fprintf (stderr, "%s: error detected, changes ignored\n", Prog);
  564.         (void) gr_unlock ();
  565. #ifdef    SHADOWPWD
  566.         (void) spw_unlock ();
  567. #endif
  568.         (void) pw_unlock ();
  569.         exit (1);
  570.     }
  571. #ifdef    SHADOWPWD
  572.     if (! pw_close () || ! spw_close () || ! gr_close ())
  573. #else
  574.     if (! pw_close () || ! gr_close ())
  575. #endif
  576.     {
  577.         fprintf (stderr, "%s: error updating files\n", Prog);
  578.         (void) gr_unlock ();
  579. #ifdef    SHADOWPWD
  580.         (void) spw_unlock ();
  581. #endif
  582.         (void) pw_unlock ();
  583.         exit (1);
  584.     }
  585.     (void) gr_unlock ();
  586. #ifdef    SHADOWPWD
  587.     (void) spw_unlock ();
  588. #endif
  589.     (void) pw_unlock ();
  590.  
  591.     exit (0);
  592.     /*NOTREACHED*/
  593. }
  594.