home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2287 / chfn.c next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  10.3 KB  |  524 lines

  1. /*
  2.  * Copyright 1989, 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.  
  9. #include <sys/types.h>
  10. #include <stdio.h>
  11. #include <pwd.h>
  12. #include <fcntl.h>
  13. #include <signal.h>
  14. #include <errno.h>
  15. #include <ctype.h>
  16. #ifndef    BSD
  17. #include <string.h>
  18. #include <memory.h>
  19. #else
  20. #include <strings.h>
  21. #define    strchr    index
  22. #define    strrchr    rindex
  23. #endif
  24. #include "config.h"
  25.  
  26. #ifdef    DBM
  27. #include <dbm.h>
  28. #endif
  29.  
  30. #ifndef    lint
  31. static    char    _sccsid[] = "@(#)chfn.c    2.2    19:17:28    8/4/90";
  32. #endif
  33.  
  34. char    *myname;
  35.  
  36. char    user[BUFSIZ];
  37. char    fullnm[BUFSIZ];
  38. char    roomno[BUFSIZ];
  39. char    workph[BUFSIZ];
  40. char    homeph[BUFSIZ];
  41. char    slop[BUFSIZ];
  42. int    fflg;
  43. int    rflg;
  44. int    wflg;
  45. int    hflg;
  46. int    oflg;
  47.  
  48. struct    passwd    pwent;
  49.  
  50. extern    int    errno;
  51.  
  52. char    Usage[] =
  53. "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ] [ -h home_ph ]\n";
  54.  
  55. char    Usage_root[] =
  56. "Usage: %s [ -f full_name ] [ -r room_no ] [ -w work_ph ]\n\
  57.        [ -h home_ph ] [ -o other ] [ user ]\n";
  58.  
  59. /*
  60.  * usage - print command line syntax and exit
  61.  */
  62.  
  63. void
  64. usage ()
  65. {
  66.     fprintf (stderr, getuid () == 0 ? Usage_root:Usage, myname);
  67.     exit (1);
  68. }
  69.  
  70. /*
  71.  * valid_field - insure that a field contains all legal characters
  72.  *
  73.  * The supplied field is scanned for non-printing and other illegal
  74.  * characters.  If any illegal characters are found, valid_field
  75.  * prints a message and exits.
  76.  */
  77.  
  78. void
  79. valid_field (field, illegal)
  80. char    *field;
  81. char    *illegal;
  82. {
  83.     char    *cp;
  84.  
  85.     for (cp = field;*cp && isprint (*cp) && ! strchr (illegal, *cp);cp++)
  86.         ;
  87.  
  88.     if (*cp) {
  89.         fprintf (stderr, "%s: invalid field: %s\n", myname, field);
  90.         exit (1);
  91.     }
  92. }
  93.  
  94. /*
  95.  * change_field - change a single field if a new value is given.
  96.  *
  97.  * prompt the user with the name of the field being changed and the
  98.  * current value.
  99.  */
  100.  
  101. void
  102. change_field (buf, prompt)
  103. char    *buf;
  104. char    *prompt;
  105. {
  106.     char    new[BUFSIZ];
  107.     char    *cp;
  108.  
  109.     printf ("\t%s [%s]: ", prompt, buf);
  110.     fgets (new, BUFSIZ, stdin);
  111.  
  112.     if (cp = strchr (new, '\n'))
  113.         *cp = '\0';
  114.     else
  115.         return;
  116.  
  117.     if (new[0])
  118.         strcpy (buf, new);
  119. }
  120.  
  121. /*
  122.  * new_fields - change the user's GECOS information interactively
  123.  *
  124.  * prompt the user for each of the four fields and fill in the fields
  125.  * from the user's response, or leave alone if nothing was entered.
  126.  */
  127.  
  128. new_fields ()
  129. {
  130.     printf ("Enter the new value, or press return for the default\n\n");
  131.  
  132.     change_field (fullnm, "Full Name");
  133.     change_field (roomno, "Room Number");
  134.     change_field (workph, "Work Phone");
  135.     change_field (homeph, "Home Phone");
  136.  
  137.     if (getuid () == 0)
  138.         change_field (slop, "Other");
  139. }
  140.  
  141. /*
  142.  * copy_field - get the next field from the gecos field
  143.  *
  144.  * copy_field copies the next field from the gecos field, returning a
  145.  * pointer to the field which follows, or NULL if there are no more
  146.  * fields.
  147.  */
  148.  
  149. char *
  150. copy_field (in, out, extra)
  151. char    *in;            /* the current GECOS field */
  152. char    *out;            /* where to copy the field to */
  153. char    *extra;            /* fields with '=' get copied here */
  154. {
  155.     char    *cp;
  156.  
  157.     while (in) {
  158.         if (cp = strchr (in, ','))
  159.             *cp++ = '\0';
  160.  
  161.         if (! strchr (in, '='))
  162.             break;
  163.  
  164.         if (extra) {
  165.             if (extra[0])
  166.                 strcat (extra, ",");
  167.  
  168.             strcat (extra, in);
  169.         }
  170.         in = cp;
  171.     }
  172.     if (in && out)
  173.         strcpy (out, in);
  174.  
  175.     return cp;
  176. }
  177.  
  178. #ifdef    DBM
  179. /*
  180.  * update_dbm
  181.  *
  182.  * Updates the DBM password files, if they exist.
  183.  */
  184.  
  185. update_dbm (pw)
  186. struct    passwd    *pw;
  187. {
  188.     datum    key;
  189.     datum    content;
  190.     char    data[BUFSIZ];
  191.     int    len;
  192.  
  193.     strcpy (data, PWDFILE);
  194.     strcat (data, ".pag");
  195.     if (access (data, 0))
  196.         return;
  197.  
  198.     len = pw_pack (pw, data);
  199.     content.dsize = len;
  200.     content.dptr = data;
  201.  
  202.     key.dsize = strlen (pw->pw_name);
  203.     key.dptr = pw->pw_name;
  204.     store (key, content);
  205.  
  206.     key.dsize = sizeof pw->pw_uid;
  207.     key.dptr = (char *) &pw->pw_uid;
  208.     store (key, content);
  209. }
  210. #endif
  211.  
  212. int
  213. main (argc, argv)
  214. int    argc;
  215. char    **argv;
  216. {
  217.     extern    int    optind;
  218.     extern    char    *optarg;
  219.     void    die ();
  220.     char    *cp;
  221.     char    *getlogin ();
  222.     int    amroot;
  223.     int    lockfd = -1;
  224.     int    flag;
  225.     struct    passwd    *pw;
  226.     struct    passwd    *getpwuid ();
  227.     struct    passwd    *getpwnam ();
  228.     struct    passwd    *sgetpwent ();
  229.     FILE    *npwd;
  230.     FILE    *pwd;
  231.     char    buf[BUFSIZ];
  232.     char    tmp[BUFSIZ];
  233.  
  234.     amroot = getuid () == 0;
  235.     if (myname = strchr (argv[0], '/'))
  236.         myname++;
  237.     else
  238.         myname = argv[0];
  239.  
  240.     while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
  241.         switch (flag) {
  242.             case 'f':
  243.                 fflg++;
  244.                 strcpy (fullnm, optarg);
  245.                 break;
  246.             case 'r':
  247.                 rflg++;
  248.                 strcpy (roomno, optarg);
  249.                 break;
  250.             case 'w':
  251.                 wflg++;
  252.                 strcpy (workph, optarg);
  253.                 break;
  254.             case 'h':
  255.                 hflg++;
  256.                 strcpy (homeph, optarg);
  257.                 break;
  258.             case 'o':
  259.                 if (getuid () == 0) {
  260.                     oflg++;
  261.                     strcpy (slop, optarg);
  262.                     break;
  263.                 }
  264.                 fprintf (stderr, "%s: permission denied\n",
  265.                     myname);
  266.                 exit (1);
  267.             default:
  268.                 usage ();
  269.         }
  270.     }
  271.     if (argc > optind) {
  272.         if (argc > optind + 1)
  273.             usage ();
  274.  
  275.         if (! (pw = getpwnam (argv[optind]))) {
  276.             fprintf (stderr, "%s: unknown user: %s\n",
  277.                 myname, argv[optind]);
  278.             exit (1);
  279.         }
  280.     } else {
  281.         if (cp = getlogin ()) {
  282.             if (! (pw = getpwnam (cp))) {
  283.                 fprintf (stderr, "%s: unknown user: %s\n",
  284.                     myname, cp);
  285.                 exit (1);
  286.             }
  287.         } else if (! (pw = getpwuid (getuid ()))) {
  288.             fprintf (stderr, "%s: who are you?\n",
  289.                 myname);
  290.             exit (1);
  291.         }
  292.     }
  293.     if (! amroot && pw->pw_uid != getuid ()) {
  294.         fprintf (stderr, "%s: permission denied\n",
  295.             myname);
  296.         exit (1);
  297.     }
  298.     strcpy (user, pw->pw_name);
  299.  
  300.     pwent = *pw;
  301.     pwent.pw_name = strdup (pw->pw_name);
  302.     pwent.pw_passwd = strdup (pw->pw_passwd);
  303.     pwent.pw_dir = strdup (pw->pw_dir);
  304.     pwent.pw_shell = strdup (pw->pw_shell);
  305.  
  306.     /*
  307.      * Now get the full name.  It is the first comma separated field
  308.      * in the GECOS field.
  309.      */
  310.  
  311.     strcpy (buf, pw->pw_gecos);
  312.     cp = copy_field (buf, fflg ? (char *) 0:fullnm, slop);
  313.  
  314.     /*
  315.      * Now get the room number.  It is the next comma separated field,
  316.      * if there is indeed one.
  317.      */
  318.  
  319.     if (cp)
  320.         cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
  321.  
  322.     /*
  323.      * Now get the work phone number.  It is the third field.
  324.      */
  325.  
  326.     if (cp)
  327.         cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
  328.  
  329.     /*
  330.      * Now get the home phone number.  It is the fourth field.
  331.      */
  332.  
  333.     if (cp)
  334.         cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
  335.  
  336.     /*
  337.      * Anything left over is "slop".
  338.      */
  339.  
  340.     if (cp) {
  341.         if (slop[0])
  342.             strcat (slop, ",");
  343.  
  344.         strcat (slop, cp);
  345.     }
  346.  
  347.     /*
  348.      * If none of the fields were changed from the command line,
  349.      * let the user interactively change them.
  350.      */
  351.  
  352.     if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg) {
  353.         printf ("Changing the user information for %s\n", user);
  354.         new_fields ();
  355.     }
  356.  
  357.     /*
  358.      * Check all of the fields for valid information
  359.      */
  360.  
  361.     valid_field (fullnm, ":,=");
  362.     valid_field (roomno, ":,=");
  363.     valid_field (workph, ":,=");
  364.     valid_field (homeph, ":,=");
  365.     valid_field (slop, ":");
  366.  
  367.     /*
  368.      * Build the new GECOS field by plastering all the pieces together,
  369.      * if they will fit ...
  370.      */
  371.  
  372.     if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
  373.             strlen (homeph) + strlen (slop) > 80) {
  374.         fprintf (stderr, "%s: fields too long\n", myname);
  375.         exit (1);
  376.     }
  377.     sprintf (tmp, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
  378.     if (slop[0]) {
  379.         strcat (tmp, ",");
  380.         strcat (tmp, slop);
  381.     }
  382.     pwent.pw_gecos = tmp;
  383.  
  384.     /*
  385.      * Now we get to race the bad guy.  I don't think he can get us.
  386.      *
  387.      * Ignore most reasonable signals.
  388.      *    Maybe we should ignore more?  He can't hurt us until the end.
  389.      *
  390.      * Get a lock file.
  391.      *
  392.      * Copy first part of password file to new file.
  393.      *    Illegal lines are copied verbatim.
  394.      *    File permissions are r--r--r--, owner root, group root.
  395.      *
  396.      * Output the new entry.
  397.      *    Only fields in struct passwd are output.
  398.      *
  399.      * Copy the rest of the file verbatim.
  400.      *
  401.      * Rename (link, unlink) password file to backup.
  402.      *    Kill me now and nothing changes or no one gets in.
  403.      *
  404.      * Rename (link, unlink) temporary file to password file.
  405.      *    Kill me now and no one gets in or lock is left.
  406.      *
  407.      * Remove locking file.
  408.      *
  409.      * That's all folks ...
  410.      */
  411.  
  412.     signal (SIGHUP, SIG_IGN);
  413.     signal (SIGINT, SIG_IGN);
  414.     signal (SIGQUIT, SIG_IGN);
  415.     signal (SIGTERM, SIG_IGN);
  416.  
  417.     ulimit (30000);            /* prevent any funny business */
  418.     umask (0);            /* get new files modes correct */
  419. #ifndef    NDEBUG
  420.     if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
  421. #else
  422.     if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
  423. #endif    /* NDEBUG */
  424.     {
  425.         puts ("Can't get lock");
  426.         exit (1);
  427.     }
  428.     umask (077);            /* close security holes to come ... */
  429.  
  430. #ifdef    DBM
  431.     update_dbm (&pwent);
  432. #endif
  433.     if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1) {
  434.         perror (NPWDFILE);
  435.         exit (1);
  436.     }
  437. #ifndef    NDEBUG
  438.     if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0)
  439. #else
  440.     umask (077);        /* no permissions for non-roots */
  441.  
  442.     if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0)
  443. #endif    /* NDEBUG */
  444.     {
  445.         perror (NPWDFILE);
  446.         exit (1);
  447.     }
  448. #ifndef    NDEBUG
  449.     chmod (NPWDFILE, 0444);        /* lets have some security here ... */
  450.     chown (NPWDFILE, 0, 0);        /* ... and keep the bad guy away */
  451. #endif    /* NDEBUG */
  452.     if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) {
  453.         perror (NPWDFILE);
  454.         exit (1);
  455.     }
  456.     while (fgets (buf, BUFSIZ, pwd) != (char *) 0) {
  457.         if (buf[0] == '#' || ! (pw = sgetpwent (buf))) {
  458.             fputs (buf, npwd);
  459.         } else if (strcmp (pw->pw_name, pwent.pw_name) != 0)
  460.             fputs (buf, npwd);
  461.         else
  462.             break;
  463.     }
  464.     (void) fprintf (npwd, "%s:", pw->pw_name);
  465.     if (pwent.pw_age)
  466.         (void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age);
  467.     else
  468.         (void) fprintf (npwd, "%s:", pwent.pw_passwd);
  469.  
  470.     (void) fprintf (npwd, "%d:%d:%s:%s:%s\n",
  471.         pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir,
  472.         pwent.pw_shell ? pwent.pw_shell:"");
  473.  
  474.     while (fgets (buf, BUFSIZ, pwd) != (char *) 0)
  475.         fputs (buf, npwd);
  476.  
  477.     if (ferror (npwd)) {
  478.         perror (NPWDFILE);
  479.         if (unlink (NPWDFILE) || unlink (PWDLOCK))
  480.             fputs ("Help!\n", stderr);
  481.  
  482.         exit (1);
  483.     }
  484.     fflush (npwd);
  485.     fclose (npwd);
  486. #ifdef    NDEBUG
  487.     chmod (NPWDFILE, 0644);
  488.     if (unlink (OPWDFILE) == -1) {
  489.         if (errno != ENOENT) {
  490.             puts ("Can't unlink backup file");
  491.             goto unlock;
  492.         }
  493.     }
  494.     if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) {
  495.         puts ("Can't save backup file");
  496.         goto unlock;
  497.     }
  498. #ifndef    BSD
  499.     if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE))
  500. #else
  501.     if (rename (NPWDFILE, PWDFILE))
  502. #endif
  503.     {
  504.         puts ("Can't rename new file");
  505.         goto unlock;
  506.     }
  507. #endif    /* NDEBUG */
  508. #ifndef    NDEBUG
  509.     (void) unlink (".pwdlock");
  510. #else
  511.     (void) unlink (PWDLOCK);
  512. #endif
  513.     exit (0);
  514.     /*NOTREACHED*/
  515.  
  516. unlock:
  517.     if (lockfd >= 0)
  518.         (void) unlink (PWDLOCK);
  519.  
  520.     (void) unlink (NPWDFILE);
  521.     exit (1);
  522.     /*NOTREACHED*/
  523. }
  524.