home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD2.bin / bbs / gnu / sh-utils-1.12-src.lha / sh-utils-1.12 / src / su.c < prev    next >
C/C++ Source or Header  |  1994-11-12  |  15KB  |  592 lines

  1. /* su for GNU.  Run a shell with substitute user and group IDs.
  2.    Copyright (C) 92, 93, 1994 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Run a shell with the real and effective UID and GID and groups
  19.    of USER, default `root'.
  20.  
  21.    The shell run is taken from USER's password entry, /bin/sh if
  22.    none is specified there.  If the account has a password, su
  23.    prompts for a password unless run by a user with real UID 0.
  24.  
  25.    Does not change the current directory.
  26.    Sets `HOME' and `SHELL' from the password entry for USER, and if
  27.    USER is not root, sets `USER' and `LOGNAME' to USER.
  28.    The subshell is not a login shell.
  29.  
  30.    If one or more ARGs are given, they are passed as additional
  31.    arguments to the subshell.
  32.  
  33.    Does not handle /bin/sh or other shells specially
  34.    (setting argv[0] to "-su", passing -c only to certain shells, etc.).
  35.    I don't see the point in doing that, and it's ugly.
  36.  
  37.    This program intentionally does not support a "wheel group" that
  38.    restricts who can su to UID 0 accounts.  RMS considers that to
  39.    be fascist.
  40.  
  41.    Options:
  42.    -, -l, --login    Make the subshell a login shell.
  43.             Unset all environment variables except
  44.             TERM, HOME and SHELL (set as above), and USER
  45.             and LOGNAME (set unconditionally as above), and
  46.             set PATH to a default value.
  47.             Change to USER's home directory.
  48.             Prepend "-" to the shell's name.
  49.    -c, --commmand=COMMAND
  50.             Pass COMMAND to the subshell with a -c option
  51.             instead of starting an interactive shell.
  52.    -f, --fast        Pass the -f option to the subshell.
  53.    -m, -p, --preserve-environment
  54.             Do not change HOME, USER, LOGNAME, SHELL.
  55.             Run $SHELL instead of USER's shell from /etc/passwd
  56.             unless not the superuser and USER's shell is
  57.             restricted.
  58.             Overridden by --login and --shell.
  59.    -s, --shell=shell    Run SHELL instead of USER's shell from /etc/passwd
  60.             unless not the superuser and USER's shell is
  61.             restricted.
  62.  
  63.    Compile-time options:
  64.    -DSYSLOG_SUCCESS    Log successful su's (by default, to root) with syslog.
  65.    -DSYSLOG_FAILURE    Log failed su's (by default, to root) with syslog.
  66.  
  67.    -DSYSLOG_NON_ROOT    Log all su's, not just those to root (UID 0).
  68.    Never logs attempted su's to nonexistent accounts.
  69.  
  70.    Written by David MacKenzie <djm@gnu.ai.mit.edu>.  */
  71.  
  72. #include <config.h>
  73. #include <stdio.h>
  74. #include <getopt.h>
  75. #include <sys/types.h>
  76. #include <pwd.h>
  77. #include <grp.h>
  78. #include "system.h"
  79.  
  80. #if defined(HAVE_SYSLOG_H) && defined(HAVE_SYSLOG)
  81. #include <syslog.h>
  82. static void log_su ();
  83. #else /* !HAVE_SYSLOG_H */
  84. #ifdef SYSLOG_SUCCESS
  85. #undef SYSLOG_SUCCESS
  86. #endif
  87. #ifdef SYSLOG_FAILURE
  88. #undef SYSLOG_FAILURE
  89. #endif
  90. #ifdef SYSLOG_NON_ROOT
  91. #undef SYSLOG_NON_ROOT
  92. #endif
  93. #endif /* !HAVE_SYSLOG_H */
  94.  
  95. #ifdef _POSIX_VERSION
  96. #include <limits.h>
  97. #ifdef NGROUPS_MAX
  98. #undef NGROUPS_MAX
  99. #endif
  100. #define NGROUPS_MAX sysconf (_SC_NGROUPS_MAX)
  101. #else /* not _POSIX_VERSION */
  102. struct passwd *getpwuid ();
  103. struct group *getgrgid ();
  104. uid_t getuid ();
  105. #include <sys/param.h>
  106. #if !defined(NGROUPS_MAX) && defined(NGROUPS)
  107. #define NGROUPS_MAX NGROUPS
  108. #endif
  109. #endif /* not _POSIX_VERSION */
  110.  
  111. #ifndef HAVE_ENDGRENT
  112. #define endgrent()
  113. #endif
  114.  
  115. #ifndef HAVE_ENDPWENT
  116. #define endpwent()
  117. #endif
  118.  
  119. #ifdef HAVE_SHADOW_H
  120. #include <shadow.h>
  121. #endif
  122.  
  123. #include "version.h"
  124.  
  125. /* The default PATH for simulated logins to non-superuser accounts.  */
  126. #define DEFAULT_LOGIN_PATH ":/bin"
  127.  
  128. /* The default PATH for simulated logins to superuser accounts.  */
  129. #define DEFAULT_ROOT_LOGIN_PATH "/gnu"
  130.  
  131. /* The shell to run if none is given in the user's passwd entry.  */
  132. #define DEFAULT_SHELL "/bin/sh"
  133.  
  134. /* The user to become if none is specified.  */
  135. #define DEFAULT_USER "root"
  136.  
  137. char *crypt ();
  138. char *getpass ();
  139. char *getusershell ();
  140. void endusershell ();
  141. void setusershell ();
  142.  
  143. char *basename ();
  144. char *xmalloc ();
  145. char *xrealloc ();
  146. char *xstrdup ();
  147. void error ();
  148.  
  149. static char *concat ();
  150. static int correct_password ();
  151. static int elements ();
  152. static int restricted_shell ();
  153. static void change_identity ();
  154. static void modify_environment ();
  155. static void run_shell ();
  156. static void usage ();
  157. static void xputenv ();
  158.  
  159. extern char **environ;
  160.  
  161. /* The name this program was run with.  */
  162. char *program_name;
  163.  
  164. /* If non-zero, display usage information and exit.  */
  165. static int show_help;
  166.  
  167. /* If non-zero, print the version on standard output and exit.  */
  168. static int show_version;
  169.  
  170. /* If nonzero, pass the `-f' option to the subshell.  */
  171. static int fast_startup;
  172.  
  173. /* If nonzero, simulate a login instead of just starting a shell.  */
  174. static int simulate_login;
  175.  
  176. /* If nonzero, change some environment vars to indicate the user su'd to.  */
  177. static int change_environment;
  178.  
  179. static struct option const longopts[] =
  180. {
  181.   {"command", required_argument, 0, 'c'},
  182.   {"fast", no_argument, &fast_startup, 1},
  183.   {"help", no_argument, &show_help, 1},
  184.   {"login", no_argument, &simulate_login, 1},
  185.   {"preserve-environment", no_argument, &change_environment, 0},
  186.   {"shell", required_argument, 0, 's'},
  187.   {"version", no_argument, &show_version, 1},
  188.   {0, 0, 0, 0}
  189. };
  190.  
  191. main (argc, argv)
  192.      int argc;
  193.      char **argv;
  194. {
  195.   int optc;
  196.   const char *new_user = DEFAULT_USER;
  197.   char *command = 0;
  198.   char **additional_args = 0;
  199.   char *shell = 0;
  200.   struct passwd *pw;
  201.   struct passwd pw_copy;
  202.  
  203.   program_name = argv[0];
  204.   fast_startup = 0;
  205.   simulate_login = 0;
  206.   change_environment = 1;
  207.  
  208.   while ((optc = getopt_long (argc, argv, "c:flmps:", longopts, (int *) 0))
  209.      != EOF)
  210.     {
  211.       switch (optc)
  212.     {
  213.     case 0:
  214.       break;
  215.  
  216.     case 'c':
  217.       command = optarg;
  218.       break;
  219.  
  220.     case 'f':
  221.       fast_startup = 1;
  222.       break;
  223.  
  224.     case 'l':
  225.       simulate_login = 1;
  226.       break;
  227.  
  228.     case 'm':
  229.     case 'p':
  230.       change_environment = 0;
  231.       break;
  232.  
  233.     case 's':
  234.       shell = optarg;
  235.       break;
  236.  
  237.     default:
  238.       usage (1);
  239.     }
  240.     }
  241.  
  242.   if (show_version)
  243.     {
  244.       printf ("su - %s\n", version_string);
  245.       exit (0);
  246.     }
  247.  
  248.   if (show_help)
  249.     usage (0);
  250.  
  251.   if (optind < argc && !strcmp (argv[optind], "-"))
  252.     {
  253.       simulate_login = 1;
  254.       ++optind;
  255.     }
  256.   if (optind < argc)
  257.     new_user = argv[optind++];
  258.   if (optind < argc)
  259.     additional_args = argv + optind;
  260.  
  261.   pw = getpwnam (new_user);
  262.   if (pw == 0)
  263.     error (1, 0, "user %s does not exist", new_user);
  264.   endpwent ();
  265.  
  266.   /* Make a copy of the password information and point pw at the local
  267.      copy instead.  Otherwise, some systems (e.g. Linux) would clobber
  268.      the static data through the getlogin call from log_su.  */
  269.   pw_copy = *pw;
  270.   pw = &pw_copy;
  271.   pw->pw_name = xstrdup (pw->pw_name);
  272.   pw->pw_dir = xstrdup (pw->pw_dir);
  273.   pw->pw_shell = xstrdup (pw->pw_shell);
  274.  
  275.   if (!correct_password (pw))
  276.     {
  277. #ifdef SYSLOG_FAILURE
  278.       log_su (pw, 0);
  279. #endif
  280.       error (1, 0, "incorrect password");
  281.     }
  282. #ifdef SYSLOG_SUCCESS
  283.   else
  284.     {
  285.       log_su (pw, 1);
  286.     }
  287. #endif
  288.  
  289.   if (pw->pw_shell == 0 || pw->pw_shell[0] == 0)
  290.     pw->pw_shell = (char *) DEFAULT_SHELL;
  291.   if (shell == 0 && change_environment == 0)
  292.     shell = getenv ("SHELL");
  293.   if (shell != 0 && getuid () && restricted_shell (pw->pw_shell))
  294.     {
  295.       /* The user being su'd to has a nonstandard shell, and so is
  296.      probably a uucp account or has restricted access.  Don't
  297.      compromise the account by allowing access with a standard
  298.      shell.  */
  299.       error (0, 0, "using restricted shell %s", pw->pw_shell);
  300.       shell = 0;
  301.     }
  302.   if (shell == 0)
  303.     {
  304.       shell = xstrdup (pw->pw_shell);
  305.     }
  306.   modify_environment (pw, shell);
  307.  
  308.   change_identity (pw);
  309.   if (simulate_login && chdir (pw->pw_dir))
  310.     error (0, errno, "warning: cannot change directory to %s", pw->pw_dir