home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 1 / 1641 / chfn.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  10.2 KB  |  518 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    1.1    08:07:55    8/1/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.     len = pw_pack (pw, data);
  194.     content.dsize = len;
  195.     content.dptr = data;
  196.  
  197.     key.dsize = strlen (pw->pw_name);
  198.     key.dptr = pw->pw_name;
  199.     store (key, content);
  200.  
  201.     key.dsize = sizeof pw->pw_uid;
  202.     key.dptr = (char *) &pw->pw_uid;
  203.     store (key, content);
  204. }
  205. #endif
  206.  
  207. int
  208. main (argc, argv)
  209. int    argc;
  210. char    **argv;
  211. {
  212.     extern    int    optind;
  213.     extern    char    *optarg;
  214.     void    die ();
  215.     char    *cp;
  216.     char    *getlogin ();
  217.     int    amroot;
  218.     int    lockfd = -1;
  219.     int    flag;
  220.     struct    passwd    *pw;
  221.     struct    passwd    *getpwuid ();
  222.     struct    passwd    *getpwnam ();
  223.     struct    passwd    *sgetpwent ();
  224.     FILE    *npwd;
  225.     FILE    *pwd;
  226.     char    buf[BUFSIZ];
  227.     char    tmp[BUFSIZ];
  228.  
  229.     amroot = getuid () == 0;
  230.     if (myname = strchr (argv[0], '/'))
  231.         myname++;
  232.     else
  233.         myname = argv[0];
  234.  
  235.     while ((flag = getopt (argc, argv, "f:r:w:h:o:")) != EOF) {
  236.         switch (flag) {
  237.             case 'f':
  238.                 fflg++;
  239.                 strcpy (fullnm, optarg);
  240.                 break;
  241.             case 'r':
  242.                 rflg++;
  243.                 strcpy (roomno, optarg);
  244.                 break;
  245.             case 'w':
  246.                 wflg++;
  247.                 strcpy (workph, optarg);
  248.                 break;
  249.             case 'h':
  250.                 hflg++;
  251.                 strcpy (homeph, optarg);
  252.                 break;
  253.             case 'o':
  254.                 if (getuid () == 0) {
  255.                     oflg++;
  256.                     strcpy (slop, optarg);
  257.                     break;
  258.                 }
  259.                 fprintf (stderr, "%s: permission denied\n",
  260.                     myname);
  261.                 exit (1);
  262.             default:
  263.                 usage ();
  264.         }
  265.     }
  266.     if (argc > optind) {
  267.         if (argc > optind + 1)
  268.             usage ();
  269.  
  270.         if (! (pw = getpwnam (argv[optind]))) {
  271.             fprintf (stderr, "%s: unknown user: %s\n",
  272.                 myname, argv[optind]);
  273.             exit (1);
  274.         }
  275.     } else {
  276.         if (cp = getlogin ()) {
  277.             if (! (pw = getpwnam (cp))) {
  278.                 fprintf (stderr, "%s: unknown user: %s\n",
  279.                     myname, cp);
  280.                 exit (1);
  281.             }
  282.         } else if (! (pw = getpwuid (getuid ()))) {
  283.             fprintf (stderr, "%s: who are you?\n",
  284.                 myname);
  285.             exit (1);
  286.         }
  287.     }
  288.     if (! amroot && pw->pw_uid != getuid ()) {
  289.         fprintf (stderr, "%s: permission denied\n",
  290.             myname);
  291.         exit (1);
  292.     }
  293.     strcpy (user, pw->pw_name);
  294.     printf ("Changing the user information for %s\n", user);
  295.  
  296.     pwent = *pw;
  297.     pwent.pw_name = strdup (pw->pw_name);
  298.     pwent.pw_passwd = strdup (pw->pw_passwd);
  299.     pwent.pw_dir = strdup (pw->pw_dir);
  300.     pwent.pw_shell = strdup (pw->pw_shell);
  301.  
  302.     /*
  303.      * Now get the full name.  It is the first comma separated field
  304.      * in the GECOS field.
  305.      */
  306.  
  307.     strcpy (buf, pw->pw_gecos);
  308.     cp = copy_field (buf, fflg ? (char *) 0:fullnm, slop);
  309.  
  310.     /*
  311.      * Now get the room number.  It is the next comma separated field,
  312.      * if there is indeed one.
  313.      */
  314.  
  315.     if (cp)
  316.         cp = copy_field (cp, rflg ? (char *) 0:roomno, slop);
  317.  
  318.     /*
  319.      * Now get the work phone number.  It is the third field.
  320.      */
  321.  
  322.     if (cp)
  323.         cp = copy_field (cp, wflg ? (char *) 0:workph, slop);
  324.  
  325.     /*
  326.      * Now get the home phone number.  It is the fourth field.
  327.      */
  328.  
  329.     if (cp)
  330.         cp = copy_field (cp, hflg ? (char *) 0:homeph, slop);
  331.  
  332.     /*
  333.      * Anything left over is "slop".
  334.      */
  335.  
  336.     if (cp) {
  337.         if (slop[0])
  338.             strcat (slop, ",");
  339.  
  340.         strcat (slop, cp);
  341.     }
  342.  
  343.     /*
  344.      * If none of the fields were changed from the command line,
  345.      * let the user interactively change them.
  346.      */
  347.  
  348.     if (! fflg && ! rflg && ! wflg && ! hflg && ! oflg)
  349.         new_fields ();
  350.  
  351.     /*
  352.      * Check all of the fields for valid information
  353.      */
  354.  
  355.     valid_field (fullnm, ":,=");
  356.     valid_field (roomno, ":,=");
  357.     valid_field (workph, ":,=");
  358.     valid_field (homeph, ":,=");
  359.     valid_field (slop, ":");
  360.  
  361.     /*
  362.      * Build the new GECOS field by plastering all the pieces together,
  363.      * if they will fit ...
  364.      */
  365.  
  366.     if (strlen (fullnm) + strlen (roomno) + strlen (workph) +
  367.             strlen (homeph) + strlen (slop) > 80) {
  368.         fprintf (stderr, "%s: fields too long\n", myname);
  369.         exit (1);
  370.     }
  371.     sprintf (tmp, "%s,%s,%s,%s", fullnm, roomno, workph, homeph);
  372.     if (slop[0]) {
  373.         strcat (tmp, ",");
  374.         strcat (tmp, slop);
  375.     }
  376.     pwent.pw_gecos = tmp;
  377.  
  378.     /*
  379.      * Now we get to race the bad guy.  I don't think he can get us.
  380.      *
  381.      * Ignore most reasonable signals.
  382.      *    Maybe we should ignore more?  He can't hurt us until the end.
  383.      *
  384.      * Get a lock file.
  385.      *
  386.      * Copy first part of password file to new file.
  387.      *    Illegal lines are copied verbatim.
  388.      *    File permissions are r--r--r--, owner root, group root.
  389.      *
  390.      * Output the new entry.
  391.      *    Only fields in struct passwd are output.
  392.      *
  393.      * Copy the rest of the file verbatim.
  394.      *
  395.      * Rename (link, unlink) password file to backup.
  396.      *    Kill me now and nothing changes or no one gets in.
  397.      *
  398.      * Rename (link, unlink) temporary file to password file.
  399.      *    Kill me now and no one gets in or lock is left.
  400.      *
  401.      * Remove locking file.
  402.      *
  403.      * That's all folks ...
  404.      */
  405.  
  406.     signal (SIGHUP, SIG_IGN);
  407.     signal (SIGINT, SIG_IGN);
  408.     signal (SIGQUIT, SIG_IGN);
  409.     signal (SIGTERM, SIG_IGN);
  410.  
  411.     ulimit (30000);            /* prevent any funny business */
  412.     umask (0);            /* get new files modes correct */
  413. #ifndef    NDEBUG
  414.     if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
  415. #else
  416.     if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
  417. #endif    /* NDEBUG */
  418.     {
  419.         puts ("Can't get lock");
  420.         exit (1);
  421.     }
  422.     umask (077);            /* close security holes to come ... */
  423.  
  424. #ifdef    DBM
  425.     update_dbm (&pwent);
  426. #endif
  427.     if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1) {
  428.         perror (NPWDFILE);
  429.         exit (1);
  430.     }
  431. #ifndef    NDEBUG
  432.     if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0)
  433. #else
  434.     umask (077);        /* no permissions for non-roots */
  435.  
  436.     if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0)
  437. #endif    /* NDEBUG */
  438.     {
  439.         perror (NPWDFILE);
  440.         exit (1);
  441.     }
  442. #ifndef    NDEBUG
  443.     chmod (NPWDFILE, 0444);        /* lets have some security here ... */
  444.     chown (NPWDFILE, 0, 0);        /* ... and keep the bad guy away */
  445. #endif    /* NDEBUG */
  446.     if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) {
  447.         perror (NPWDFILE);
  448.         exit (1);
  449.     }
  450.     while (fgets (buf, BUFSIZ, pwd) != (char *) 0) {
  451.         if (buf[0] == '#' || ! (pw = sgetpwent (buf))) {
  452.             fputs (buf, npwd);
  453.         } else if (strcmp (pw->pw_name, pwent.pw_name) != 0)
  454.             fputs (buf, npwd);
  455.         else
  456.             break;
  457.     }
  458.     (void) fprintf (npwd, "%s:", pw->pw_name);
  459.     if (pwent.pw_age)
  460.         (void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age);
  461.     else
  462.         (void) fprintf (npwd, "%s:", pwent.pw_passwd);
  463.  
  464.     (void) fprintf (npwd, "%d:%d:%s:%s:%s\n",
  465.         pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir,
  466.         pwent.pw_shell ? pwent.pw_shell:"");
  467.  
  468.     while (fgets (buf, BUFSIZ, pwd) != (char *) 0)
  469.         fputs (buf, npwd);
  470.  
  471.     if (ferror (npwd)) {
  472.         perror (NPWDFILE);
  473.         if (unlink (NPWDFILE) || unlink (PWDLOCK))
  474.             fputs ("Help!\n", stderr);
  475.  
  476.         exit (1);
  477.     }
  478.     fflush (npwd);
  479.     fclose (npwd);
  480. #ifdef    NDEBUG
  481.     chmod (NPWDFILE, 0644);
  482.     if (unlink (OPWDFILE) == -1) {
  483.         if (errno != ENOENT) {
  484.             puts ("Can't unlink backup file");
  485.             goto unlock;
  486.         }
  487.     }
  488.     if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) {
  489.         puts ("Can't save backup file");
  490.         goto unlock;
  491.     }
  492. #ifndef    BSD
  493.     if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE))
  494. #else
  495.     if (rename (NPWDFILE, PWDFILE))
  496. #endif
  497.     {
  498.         puts ("Can't rename new file");
  499.         goto unlock;
  500.     }
  501. #endif    /* NDEBUG */
  502. #ifndef    NDEBUG
  503.     (void) unlink (".pwdlock");
  504. #else
  505.     (void) unlink (PWDLOCK);
  506. #endif
  507.     exit (0);
  508.     /*NOTREACHED*/
  509.  
  510. unlock:
  511.     if (lockfd >= 0)
  512.         (void) unlink (PWDLOCK);
  513.  
  514.     (void) unlink (NPWDFILE);
  515.     exit (1);
  516.     /*NOTREACHED*/
  517. }
  518.