]> granicus.if.org Git - shadow/blobdiff - src/su.c
* src/su.c: Fix indentation.
[shadow] / src / su.c
index 0a4aac3e8120a15af4f2af35c9d6b37200a46738..f6d55c10990376549c4a0e8373c2824eaedd52b2 100644 (file)
--- a/src/su.c
+++ b/src/su.c
@@ -2,7 +2,7 @@
  * Copyright (c) 1989 - 1994, Julianne Frances Haugh
  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
  * Copyright (c) 2000 - 2006, Tomasz Kłoczko
- * Copyright (c) 2007 - 2008, Nicolas François
+ * Copyright (c) 2007 - 2009, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
 #include <sys/types.h>
 #include "prototypes.h"
 #include "defines.h"
-#include "exitcodes.h"
 #include "pwauth.h"
 #include "getdef.h"
 #ifdef USE_PAM
 #include "pam_defs.h"
-#endif
+#endif                         /* USE_PAM */
+/*@-exitarg@*/
+#include "exitcodes.h"
+
 /*
  * Assorted #defines to control su's behavior
  */
@@ -76,6 +78,8 @@
  * Global variables
  */
 char *Prog;
+/* PID of the child, in case it needs to be killed */
+static pid_t pid_child = 0;
 
 /* not needed by sulog.c anymore */
 static char name[BUFSIZ];
@@ -101,11 +105,16 @@ extern size_t newenvc;
 
 /* local function prototypes */
 
+static void execve_shell (const char *shellstr,
+                          char *args[],
+                          char *const envp[]);
+static RETSIGTYPE kill_child (int s);
 #ifndef USE_PAM
-
 static RETSIGTYPE die (int);
 static int iswheel (const char *);
+#endif                         /* !USE_PAM */
 
+#ifndef USE_PAM
 /*
  * die - set or reset termio modes.
  *
@@ -124,7 +133,7 @@ static RETSIGTYPE die (int killed)
 
        if (killed) {
                closelog ();
-               exit (killed);
+               exit (128+killed);
        }
 }
 
@@ -141,6 +150,18 @@ static int iswheel (const char *username)
 }
 #endif                         /* !USE_PAM */
 
+static RETSIGTYPE kill_child (int unused(s))
+{
+       if (0 != pid_child) {
+               (void) kill (pid_child, SIGKILL);
+               (void) fputs (_(" ...killed.\n"), stderr);
+       } else {
+               (void) fputs (_(" ...waiting for child to terminate.\n"),
+                             stderr);
+       }
+       exit (255);
+}
+
 /* borrowed from GNU sh-utils' "su.c" */
 static bool restricted_shell (const char *shellstr)
 {
@@ -159,7 +180,7 @@ static bool restricted_shell (const char *shellstr)
 
 static void su_failure (const char *tty)
 {
-       sulog (tty, 0, oldname, name);  /* log failed attempt */
+       sulog (tty, false, oldname, name);      /* log failed attempt */
 #ifdef USE_SYSLOG
        if (getdef_bool ("SYSLOG_SU_ENAB")) {
                SYSLOG (((0 != pwent.pw_uid) ? LOG_INFO : LOG_NOTICE,
@@ -172,6 +193,43 @@ static void su_failure (const char *tty)
        exit (1);
 }
 
+/*
+ * execve_shell - Execute a shell with execve, or interpret it with
+ * /bin/sh
+ */
+static void execve_shell (const char *shellstr,
+                          char *args[],
+                          char *const envp[])
+{
+       int err;
+       (void) execve (shellstr, (char **) args, envp);
+       err = errno;
+
+       if (access (shellstr, R_OK|X_OK) == 0) {
+               /*
+                * Assume this is a shell script (with no shebang).
+                * Interpret it with /bin/sh
+                */
+               size_t n_args = 0;
+               char **targs;
+               while (NULL != args[n_args]) {
+                       n_args++;
+               }
+               targs = (char **) xmalloc ((n_args + 3) * sizeof (args[0]));
+               targs[0] = "sh";
+               targs[1] = "-";
+               targs[2] = xstrdup (shellstr);
+               targs[n_args+2] = NULL;
+               while (1 != n_args) {
+                       targs[n_args+1] = args[n_args - 1];
+                       n_args--;
+               }
+
+               (void) execve (SHELL, targs, envp);
+       } else {
+               errno = err;
+       }
+}
 
 #ifdef USE_PAM
 /* Signal handler for parent process later */
@@ -185,7 +243,7 @@ static void catch_signals (unused int sig)
  * su.c from shadow.
  */
 static void run_shell (const char *shellstr, char *args[], bool doshell,
-                      char *const envp[])
+                       char *const envp[])
 {
        pid_t child;
        sigset_t ourset;
@@ -204,19 +262,25 @@ static void run_shell (const char *shellstr, char *args[], bool doshell,
                if (doshell) {
                        (void) shell (shellstr, (char *) args[0], envp);
                } else {
-                       (void) execve (shellstr, (char **) args, envp);
+                       execve_shell (shellstr, (char **) args, envp);
                }
+
                exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
        } else if ((pid_t)-1 == child) {
-               (void) fprintf (stderr, "%s: Cannot fork user shell\n", Prog);
+               (void) fprintf (stderr,
+                               _("%s: Cannot fork user shell\n"),
+                               Prog);
                SYSLOG ((LOG_WARN, "Cannot execute %s", shellstr));
                closelog ();
                exit (1);
        }
        /* parent only */
+       pid_child = child;
        sigfillset (&ourset);
        if (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0) {
-               (void) fprintf (stderr, "%s: signal malfunction\n", Prog);
+               (void) fprintf (stderr,
+                               _("%s: signal malfunction\n"),
+                               Prog);
                caught = true;
        }
        if (!caught) {
@@ -233,7 +297,8 @@ static void run_shell (const char *shellstr, char *args[], bool doshell,
                    || (sigprocmask (SIG_UNBLOCK, &ourset, NULL) != 0)
                    ) {
                        fprintf (stderr,
-                                "%s: signal masking malfunction\n", Prog);
+                                _("%s: signal masking malfunction\n"),
+                                Prog);
                        caught = true;
                }
        }
@@ -255,8 +320,10 @@ static void run_shell (const char *shellstr, char *args[], bool doshell,
        }
 
        if (caught) {
-               fprintf (stderr, "\nSession terminated, killing shell...");
-               kill (child, SIGTERM);
+               (void) fputs ("\n", stderr);
+               (void) fputs (_("Session terminated, terminating shell..."),
+                             stderr);
+               (void) kill (child, SIGTERM);
        }
 
        ret = pam_close_session (pamh, 0);
@@ -271,10 +338,11 @@ static void run_shell (const char *shellstr, char *args[], bool doshell,
        ret = pam_end (pamh, PAM_SUCCESS);
 
        if (caught) {
-               sleep (2);
-               kill (child, SIGKILL);
-               fprintf (stderr, " ...killed.\n");
-               exit (-1);
+               (void) signal (SIGALRM, kill_child);
+               (void) alarm (2);
+
+               (void) wait (&status);
+               (void) fputs (_(" ...terminated.\n"), stderr);
        }
 
        exit ((0 != WIFEXITED (status)) ? WEXITSTATUS (status)
@@ -285,7 +353,7 @@ static void run_shell (const char *shellstr, char *args[], bool doshell,
 /*
  * usage - print command line syntax and exit
   */
-static void usage (void)
+static void usage (int status)
 {
        fputs (_("Usage: su [options] [LOGIN]\n"
                 "\n"
@@ -297,15 +365,15 @@ static void usage (void)
                 "  --preserve-environment        do not reset environment variables, and\n"
                 "                                keep the same shell\n"
                 "  -s, --shell SHELL             use SHELL instead of the default in passwd\n"
-                "\n"), stderr);
-       exit (E_USAGE);
+                "\n"), (E_SUCCESS != status) ? stderr : stdout);
+       exit (status);
 }
 
 /*
  * su - switch user id
  *
  *     su changes the user's ids to the values for the specified user.  if
- *     no new user name is specified, "root" is used by default.
+ *     no new user name is specified, "root" or UID 0 is used by default.
  *
  *     Any additional arguments are passed to the user's shell. In
  *     particular, the argument "-c" will cause the next argument to be
@@ -313,7 +381,7 @@ static void usage (void)
  */
 int main (int argc, char **argv)
 {
-       char *cp;
+       const char *cp;
        const char *tty = NULL; /* Name of tty SU is run from        */
        bool doshell = false;
        bool fakelogin = false;
@@ -383,7 +451,7 @@ int main (int argc, char **argv)
                                command = optarg;
                                break;
                        case 'h':
-                               usage ();
+                               usage (E_SUCCESS);
                                break;
                        case 'l':
                                fakelogin = true;
@@ -400,7 +468,7 @@ int main (int argc, char **argv)
                                shellstr = optarg;
                                break;
                        default:
-                               usage ();       /* NOT REACHED */
+                               usage (E_USAGE);        /* NOT REACHED */
                        }
                }
 
@@ -423,13 +491,8 @@ int main (int argc, char **argv)
         * Get the tty name. Entries will be logged indicating that the user
         * tried to change to the named new user from the current terminal.
         */
-       cp = ttyname (0);
-       if ((isatty (0) != 0) && (NULL != cp)) {
-               if (strncmp (cp, "/dev/", 5) == 0) {
-                       tty = cp + 5;
-               } else {
-                       tty = cp;
-               }
+       tty = ttyname (0);
+       if ((isatty (0) != 0) && (NULL != tty)) {
 #ifndef USE_PAM
                is_console = console (tty);
 #endif
@@ -439,7 +502,8 @@ int main (int argc, char **argv)
                 */
                if (!amroot) {
                        fprintf (stderr,
-                                _("%s: must be run from a terminal\n"), Prog);
+                                _("%s: must be run from a terminal\n"),
+                                Prog);
                        exit (1);
                }
                tty = "???";
@@ -457,8 +521,18 @@ int main (int argc, char **argv)
                        optind++;
                }
        }
-       if ('\0' == name[0]) {          /* use default user ID */
-               (void) strcpy (name, "root");
+       if ('\0' == name[0]) {          /* use default user */
+               struct passwd *root_pw = getpwnam ("root");
+               if ((NULL != root_pw) && (0 == root_pw->pw_uid)) {
+                       (void) strcpy (name, "root");
+               } else {
+                       root_pw = getpwuid (0);
+                       if (NULL == root_pw) {
+                               SYSLOG ((LOG_CRIT, "There is no UID 0 user."));
+                               su_failure (tty);
+                       }
+                       (void) strcpy (name, root_pw->pw_name);
+               }
        }
 
        doshell = (argc == optind);     /* any arguments remaining? */
@@ -472,7 +546,8 @@ int main (int argc, char **argv)
         */
        pw = get_my_pwent ();
        if (NULL == pw) {
-               fprintf (stderr, _("%s: Cannot determine your user name.\n"),
+               fprintf (stderr,
+                        _("%s: Cannot determine your user name.\n"),
                         Prog);
                SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
                         (unsigned long) my_uid));
@@ -497,8 +572,9 @@ int main (int argc, char **argv)
        ret = pam_start ("su", name, &conv, &pamh);
        if (PAM_SUCCESS != ret) {
                SYSLOG ((LOG_ERR, "pam_start: error %d", ret);
-                       fprintf (stderr, _("%s: pam_start: error %d\n"),
-                                Prog, ret));
+                       fprintf (stderr,
+                                _("%s: pam_start: error %d\n"),
+                                Prog, ret));
                exit (1);
        }
 
@@ -552,7 +628,7 @@ int main (int argc, char **argv)
         * (note: in the case of a subsystem, the shell will be restricted,
         *        and this won't be executed on the first pass)
         */
-       if (fakelogin && change_environment) {
+       if (change_environment && fakelogin) {
                /*
                 * The terminal type will be left alone if it is present in
                 * the environment already.
@@ -561,6 +637,20 @@ int main (int argc, char **argv)
                if (NULL != cp) {
                        addenv ("TERM", cp);
                }
+
+               /*
+                * For some terminals COLORTERM seems to be the only way
+                * for checking for that specific terminal. For instance,
+                * gnome-terminal sets its TERM as "xterm" but its
+                * COLORTERM as "gnome-terminal". The COLORTERM variable
+                * is also of use when running GNU screen since it sets
+                * TERM to "screen" but doesn't touch COLORTERM.
+                */
+               cp = getenv ("COLORTERM");
+               if (NULL != cp) {
+                       addenv ("COLORTERM", cp);
+               }
+
 #ifndef USE_PAM
                cp = getdef_str ("ENV_TZ");
                if (NULL != cp) {
@@ -574,6 +664,7 @@ int main (int argc, char **argv)
                if (NULL != cp) {
                        addenv (cp, NULL);      /* set the default $HZ, if one */
                }
+#endif                         /* !USE_PAM */
 
                /*
                 * Also leave DISPLAY and XAUTHORITY if present, else
@@ -587,7 +678,6 @@ int main (int argc, char **argv)
                if (NULL != cp) {
                        addenv ("XAUTHORITY", cp);
                }
-#endif                         /* !USE_PAM */
        } else {
                while (NULL != *envp) {
                        addenv (*envp, NULL);
@@ -619,7 +709,8 @@ int main (int argc, char **argv)
                    && getdef_bool ("SU_WHEEL_ONLY")
                    && !iswheel (oldname)) {
                        fprintf (stderr,
-                                _("You are not authorized to su %s\n"), name);
+                                _("You are not authorized to su %s\n"),
+                                name);
                        exit (1);
                }
 #ifdef SU_ACCESS
@@ -635,7 +726,8 @@ int main (int argc, char **argv)
                        break;
                default:        /* access denied (-1) or unexpected value */
                        fprintf (stderr,
-                                _("You are not authorized to su %s\n"), name);
+                                _("You are not authorized to su %s\n"),
+                                name);
                        exit (1);
                }
 #endif                         /* SU_ACCESS */
@@ -668,7 +760,7 @@ int main (int argc, char **argv)
         * Set the default shell.
         */
        if ((NULL == shellstr) || ('\0' == shellstr[0])) {
-               shellstr = "/bin/sh";
+               shellstr = SHELL;
        }
 
        (void) signal (SIGINT, SIG_IGN);
@@ -686,23 +778,26 @@ int main (int argc, char **argv)
        ret = pam_acct_mgmt (pamh, 0);
        if (PAM_SUCCESS != ret) {
                if (amroot) {
-                       fprintf (stderr, _("%s: %s\n(Ignored)\n"), Prog,
-                                pam_strerror (pamh, ret));
+                       fprintf (stderr,
+                                _("%s: %s\n(Ignored)\n"),
+                                Prog, pam_strerror (pamh, ret));
                } else if (PAM_NEW_AUTHTOK_REQD == ret) {
                        ret = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
                        if (PAM_SUCCESS != ret) {
                                SYSLOG ((LOG_ERR, "pam_chauthtok: %s",
                                         pam_strerror (pamh, ret)));
-                               fprintf (stderr, _("%s: %s\n"), Prog,
-                                        pam_strerror (pamh, ret));
+                               fprintf (stderr,
+                                        _("%s: %s\n"),
+                                        Prog, pam_strerror (pamh, ret));
                                (void) pam_end (pamh, ret);
                                su_failure (tty);
                        }
                } else {
                        SYSLOG ((LOG_ERR, "pam_acct_mgmt: %s",
                                 pam_strerror (pamh, ret)));
-                       fprintf (stderr, _("%s: %s\n"), Prog,
-                                pam_strerror (pamh, ret));
+                       fprintf (stderr,
+                                _("%s: %s\n"),
+                                Prog, pam_strerror (pamh, ret));
                        (void) pam_end (pamh, ret);
                        su_failure (tty);
                }
@@ -760,8 +855,9 @@ int main (int argc, char **argv)
                        SYSLOG (((0 != pwent.pw_uid) ? LOG_WARN : LOG_CRIT,
                                 "SU by %s to restricted account %s",
                                 oldname, name));
-                       fprintf(stderr,
-                               _("%s: You are not authorized to su at that time\n"), Prog);
+                       fprintf (stderr,
+                                _("%s: You are not authorized to su at that time\n"),
+                                Prog);
                        su_failure (tty);
                }
        }
@@ -928,7 +1024,7 @@ int main (int argc, char **argv)
                 */
                argv[-1] = shellstr;
 #ifndef USE_PAM
-               (void) execve (shellstr, &argv[-1], environ);
+               execve_shell (shellstr, &argv[-1], environ);
                err = errno;
                (void) fputs (_("No shell\n"), stderr);
                SYSLOG ((LOG_WARN, "Cannot execute %s", shellstr));