home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2287 / chsh.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  8.8 KB  |  460 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[] = "@(#)chsh.c    2.2    12:54:39    10/22/90";
  32. #endif
  33.  
  34. char    *myname;
  35. int    amroot;
  36.  
  37. char    user[BUFSIZ];
  38. char    loginsh[BUFSIZ];
  39. int    sflg;
  40.  
  41. struct    passwd    pwent;
  42.  
  43. extern    int    errno;
  44.  
  45. char    Usage[] =
  46. "Usage: %s [ -s login_shell ] [ user_name ]\n";
  47.  
  48. /*
  49.  * usage - print command line syntax and exit
  50.  */
  51.  
  52. void
  53. usage ()
  54. {
  55.     fprintf (stderr, Usage, myname);
  56.     exit (1);
  57. }
  58.  
  59. /*
  60.  * valid_field - insure that a field contains all legal characters
  61.  *
  62.  * The supplied field is scanned for non-printing and other illegal
  63.  * characters.  If any illegal characters are found, valid_field
  64.  * prints a message and exits.
  65.  */
  66.  
  67. void
  68. valid_field (field, illegal)
  69. char    *field;
  70. char    *illegal;
  71. {
  72.     char    *cp;
  73.  
  74.     for (cp = field;*cp && isprint (*cp) && ! strchr (illegal, *cp);cp++)
  75.         ;
  76.  
  77.     if (*cp) {
  78.         fprintf (stderr, "%s: invalid field: %s\n", myname, field);
  79.         exit (1);
  80.     }
  81. }
  82.  
  83. /*
  84.  * change_field - change a single field if a new value is given.
  85.  *
  86.  * prompt the user with the name of the field being changed and the
  87.  * current value.
  88.  */
  89.  
  90. void
  91. change_field (buf, prompt)
  92. char    *buf;
  93. char    *prompt;
  94. {
  95.     char    new[BUFSIZ];
  96.     char    *cp;
  97.  
  98.     printf ("\t%s [%s]: ", prompt, buf);
  99.     fgets (new, BUFSIZ, stdin);
  100.  
  101.     if (cp = strchr (new, '\n'))
  102.         *cp = '\0';
  103.     else
  104.         return;
  105.  
  106.     if (new[0])
  107.         strcpy (buf, new);
  108. }
  109.  
  110. /*
  111.  * new_fields - change the user's login shell information interactively
  112.  *
  113.  * prompt the user for the login shell and change it according to the
  114.  * response, or leave it alone if nothing was entered.
  115.  */
  116.  
  117. new_fields ()
  118. {
  119.     printf ("Enter the new value, or press return for the default\n\n");
  120.  
  121.     change_field (loginsh, "Login Shell");
  122. }
  123.  
  124. /*
  125.  * check_shell - see if the user's login shell is listed in /etc/shells
  126.  *
  127.  * The /etc/shells file is read for valid names of login shells.  If the
  128.  * /etc/shells file does not exist the user cannot set any shell unless
  129.  * they are root.
  130.  */
  131.  
  132. check_shell (shell)
  133. char    *shell;
  134. {
  135.     char    buf[BUFSIZ];
  136.     char    *cp;
  137.     int    found = 0;
  138.     FILE    *fp;
  139.  
  140.     if (amroot)
  141.         return 1;
  142.  
  143.     if ((fp = fopen ("/etc/shells", "r")) == (FILE *) 0)
  144.         return 0;
  145.  
  146.     while (fgets (buf, BUFSIZ, fp) && ! found) {
  147.         if (cp = strrchr (buf, '\n'))
  148.             *cp = '\0';
  149.  
  150.         if (strcmp (buf, shell) == 0)
  151.             found = 1;
  152.     }
  153.     fclose (fp);
  154.  
  155.     return found;
  156. }
  157.  
  158. /*
  159.  * restricted_shell - return true if the named shell begins with 'r' or 'R'
  160.  *
  161.  * If the first letter of the filename is 'r' or 'R', the shell is
  162.  * considered to be restricted.
  163.  */
  164.  
  165. int
  166. restricted_shell (shell)
  167. char    *shell;
  168. {
  169.     char    *cp;
  170.  
  171.     if (cp = strrchr (shell, '/'))
  172.         cp++;
  173.     else
  174.         cp = shell;
  175.  
  176.     return *cp == 'r' || *cp == 'R';
  177. }
  178.  
  179. #ifdef    DBM
  180. /*
  181.  * update_dbm
  182.  *
  183.  * Updates the DBM password files, if they exist.
  184.  */
  185.  
  186. update_dbm (pw)
  187. struct    passwd    *pw;
  188. {
  189.     datum    key;
  190.     datum    content;
  191.     char    data[BUFSIZ];
  192.     int    len;
  193.  
  194.     strcpy (data, PWDFILE);
  195.     strcat (data, ".pag");
  196.     if (access (data, 0))
  197.         return;
  198.  
  199.     len = pw_pack (pw, data);
  200.     content.dsize = len;
  201.     content.dptr = data;
  202.  
  203.     key.dsize = strlen (pw->pw_name);
  204.     key.dptr = pw->pw_name;
  205.     store (key, content);
  206.  
  207.     key.dsize = sizeof pw->pw_uid;
  208.     key.dptr = (char *) &pw->pw_uid;
  209.     store (key, content);
  210. }
  211. #endif
  212.  
  213. int
  214. main (argc, argv)
  215. int    argc;
  216. char    **argv;
  217. {
  218.     extern    int    optind;
  219.     extern    char    *optarg;
  220.     void    die ();
  221.     char    *cp;
  222.     char    *getlogin ();
  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, "s:")) != EOF) {
  241.         switch (flag) {
  242.             case 's':
  243.                 sflg++;
  244.                 strcpy (loginsh, optarg);
  245.                 break;
  246.             default:
  247.                 usage ();
  248.         }
  249.     }
  250.     if (argc > optind) {
  251.         if (argc > optind + 1)
  252.             usage ();
  253.  
  254.         if (! (pw = getpwnam (argv[optind]))) {
  255.             fprintf (stderr, "%s: unknown user: %s\n",
  256.                 myname, argv[optind]);
  257.             exit (1);
  258.         }
  259.     } else {
  260.         if (cp = getlogin ()) {
  261.             if (! (pw = getpwnam (cp))) {
  262.                 fprintf (stderr, "%s: unknown user: %s\n",
  263.                     myname, cp);
  264.                 exit (1);
  265.             }
  266.         } else if (! (pw = getpwuid (getuid ()))) {
  267.             fprintf (stderr, "%s: who are you?\n",
  268.                 myname);
  269.             exit (1);
  270.         }
  271.     }
  272.     if (! amroot && pw->pw_uid != getuid ()) {
  273.         fprintf (stderr, "%s: permission denied\n",
  274.             myname);
  275.         exit (1);
  276.     }
  277.     if (! amroot && restricted_shell (pw->pw_shell)) {
  278.         fprintf (stderr, "%s: permission denied\n",
  279.             myname);
  280.         exit (1);
  281.     }
  282.     strcpy (user, pw->pw_name);
  283.  
  284.     pwent = *pw;
  285.     pwent.pw_name = strdup (pw->pw_name);
  286.     pwent.pw_passwd = strdup (pw->pw_passwd);
  287.     pwent.pw_dir = strdup (pw->pw_dir);
  288.     pwent.pw_gecos = strdup (pw->pw_gecos);
  289.  
  290.     /*
  291.      * Now get the login shell.  Either get it from the password
  292.      * file, or use the value from the command line.
  293.      */
  294.  
  295.     if (! sflg)
  296.         strcpy (loginsh, pw->pw_shell);
  297.  
  298.     /*
  299.      * If the login shell was not set on the command line,
  300.      * let the user interactively change it.
  301.      */
  302.  
  303.     if (! sflg) {
  304.         printf ("Changing the login shell for %s\n", user);
  305.         new_fields ();
  306.     }
  307.  
  308.     /*
  309.      * Check all of the fields for valid information
  310.      */
  311.  
  312.     valid_field (loginsh, ":,=");
  313.     if (! check_shell (loginsh)) {
  314.         fprintf (stderr, "%s: %s is an invalid shell\n",
  315.             myname, loginsh);
  316.         exit (1);
  317.     }
  318.     pwent.pw_shell = loginsh;
  319.  
  320.     /*
  321.      * Now we get to race the bad guy.  I don't think he can get us.
  322.      *
  323.      * Ignore most reasonable signals.
  324.      *    Maybe we should ignore more?  He can't hurt us until the end.
  325.      *
  326.      * Get a lock file.
  327.      *
  328.      * Copy first part of password file to new file.
  329.      *    Illegal lines are copied verbatim.
  330.      *    File permissions are r--r--r--, owner root, group root.
  331.      *
  332.      * Output the new entry.
  333.      *    Only fields in struct passwd are output.
  334.      *
  335.      * Copy the rest of the file verbatim.
  336.      *
  337.      * Rename (link, unlink) password file to backup.
  338.      *    Kill me now and nothing changes or no one gets in.
  339.      *
  340.      * Rename (link, unlink) temporary file to password file.
  341.      *    Kill me now and no one gets in or lock is left.
  342.      *
  343.      * Remove locking file.
  344.      *
  345.      * That's all folks ...
  346.      */
  347.  
  348.     signal (SIGHUP, SIG_IGN);
  349.     signal (SIGINT, SIG_IGN);
  350.     signal (SIGQUIT, SIG_IGN);
  351.     signal (SIGTERM, SIG_IGN);
  352.  
  353.     ulimit (30000);            /* prevent any funny business */
  354.     umask (0);            /* get new files modes correct */
  355. #ifndef    NDEBUG
  356.     if ((lockfd = open (".pwdlock", O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
  357. #else
  358.     if ((lockfd = open (PWDLOCK, O_RDONLY|O_CREAT|O_EXCL), 0444) == -1)
  359. #endif    /* NDEBUG */
  360.     {
  361.         puts ("Can't get lock");
  362.         exit (1);
  363.     }
  364.     umask (077);            /* close security holes to come ... */
  365.  
  366. #ifdef    DBM
  367.     update_dbm (&pwent);
  368. #endif
  369.     if (access (NPWDFILE, 0) == 0 && unlink (NPWDFILE) == -1) {
  370.         perror (NPWDFILE);
  371.         exit (1);
  372.     }
  373. #ifndef    NDEBUG
  374.     if ((npwd = fopen ("npasswd", "w")) == (FILE *) 0)
  375. #else
  376.     umask (077);        /* no permissions for non-roots */
  377.  
  378.     if ((npwd = fopen (NPWDFILE, "w")) == (FILE *) 0)
  379. #endif    /* NDEBUG */
  380.     {
  381.         perror (NPWDFILE);
  382.         exit (1);
  383.     }
  384. #ifndef    NDEBUG
  385.     chmod (NPWDFILE, 0444);        /* lets have some security here ... */
  386.     chown (NPWDFILE, 0, 0);        /* ... and keep the bad guy away */
  387. #endif    /* NDEBUG */
  388.     if ((pwd = fopen (PWDFILE, "r")) == (FILE *) 0) {
  389.         perror (NPWDFILE);
  390.         exit (1);
  391.     }
  392.     while (fgets (buf, BUFSIZ, pwd) != (char *) 0) {
  393.         if (buf[0] == '#' || ! (pw = sgetpwent (buf))) {
  394.             fputs (buf, npwd);
  395.         } else if (strcmp (pw->pw_name, pwent.pw_name) != 0)
  396.             fputs (buf, npwd);
  397.         else
  398.             break;
  399.     }
  400.     (void) fprintf (npwd, "%s:", pw->pw_name);
  401.     if (pwent.pw_age && pwent.pw_age[0])
  402.         (void) fprintf (npwd, "%s,%s:", pwent.pw_passwd, pwent.pw_age);
  403.     else
  404.         (void) fprintf (npwd, "%s:", pwent.pw_passwd);
  405.  
  406.     (void) fprintf (npwd, "%d:%d:%s:%s:%s\n",
  407.         pwent.pw_uid, pwent.pw_gid, pwent.pw_gecos, pwent.pw_dir,
  408.         pwent.pw_shell);
  409.  
  410.     while (fgets (buf, BUFSIZ, pwd) != (char *) 0)
  411.         fputs (buf, npwd);
  412.  
  413.     if (ferror (npwd)) {
  414.         perror (NPWDFILE);
  415.         if (unlink (NPWDFILE) || unlink (PWDLOCK))
  416.             fputs ("Help!\n", stderr);
  417.  
  418.         exit (1);
  419.     }
  420.     fflush (npwd);
  421.     fclose (npwd);
  422. #ifdef    NDEBUG
  423.     chmod (NPWDFILE, 0644);
  424.     if (unlink (OPWDFILE) == -1) {
  425.         if (errno != ENOENT) {
  426.             puts ("Can't unlink backup file");
  427.             goto unlock;
  428.         }
  429.     }
  430.     if (link (PWDFILE, OPWDFILE) || unlink (PWDFILE)) {
  431.         puts ("Can't save backup file");
  432.         goto unlock;
  433.     }
  434. #ifndef    BSD
  435.     if (link (NPWDFILE, PWDFILE) || unlink (NPWDFILE))
  436. #else
  437.     if (rename (NPWDFILE, PWDFILE))
  438. #endif
  439.     {
  440.         puts ("Can't rename new file");
  441.         goto unlock;
  442.     }
  443. #endif    /* NDEBUG */
  444. #ifndef    NDEBUG
  445.     (void) unlink (".pwdlock");
  446. #else
  447.     (void) unlink (PWDLOCK);
  448. #endif
  449.     exit (0);
  450.     /*NOTREACHED*/
  451.  
  452. unlock:
  453.     if (lockfd >= 0)
  454.         (void) unlink (PWDLOCK);
  455.  
  456.     (void) unlink (NPWDFILE);
  457.     exit (1);
  458.     /*NOTREACHED*/
  459. }
  460.