]> granicus.if.org Git - shadow/blobdiff - src/su.c
* src/su.c: Fix indentation.
[shadow] / src / su.c
index a75ede00225ceed5a3333be7478871311d0246d7..f6d55c10990376549c4a0e8373c2824eaedd52b2 100644 (file)
--- a/src/su.c
+++ b/src/su.c
@@ -1,5 +1,8 @@
 /*
- * Copyright 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1989 - 1994, Julianne Frances Haugh
+ * Copyright (c) 1996 - 2000, Marek Michałkiewicz
+ * Copyright (c) 2000 - 2006, Tomasz Kłoczko
+ * Copyright (c) 2007 - 2009, Nicolas François
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
+ * 3. The name of the copyright holders or contributors may not be used to
+ *    endorse or promote products derived from this software without
+ *    specific prior written permission.
  *
- * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+/* Some parts substantially derived from an ancestor of:
+   su for GNU.  Run a shell with substitute user and group IDs.
+
+   Copyright (C) 1992-2003 Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+
 #include <config.h>
 
-#ident "$Id: su.c,v 1.71 2006/06/20 20:00:04 kloczek Exp $"
+#ident "$Id$"
 
 #include <getopt.h>
 #include <grp.h>
 #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
  */
 /*
  * 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];
 static char oldname[BUFSIZ];
 
 /* If nonzero, change some environment vars to indicate the user su'd to. */
-static int change_environment;
+static bool change_environment;
 
 #ifdef USE_PAM
 static pam_handle_t *pamh = NULL;
-static int caught = 0;
+static bool caught = false;
 #endif
 
-static char *Prog;
 extern struct passwd pwent;
 
 /*
@@ -76,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.
  *
@@ -99,7 +133,7 @@ static RETSIGTYPE die (int killed)
 
        if (killed) {
                closelog ();
-               exit (killed);
+               exit (128+killed);
        }
 }
 
@@ -107,83 +141,147 @@ static int iswheel (const char *username)
 {
        struct group *grp;
 
-       grp = getgrnam ("wheel");;
-       if (!grp || !grp->gr_mem)
+       grp = getgrnam ("wheel"); /* !USE_PAM, no need for xgetgrnam */
+       if (   (NULL ==grp)
+           || (NULL == grp->gr_mem)) {
                return 0;
+       }
        return is_on_list (grp->gr_mem, 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 int restricted_shell (const char *shellstr)
+static bool restricted_shell (const char *shellstr)
 {
        char *line;
 
        setusershell ();
        while ((line = getusershell ()) != NULL) {
-               if (*line != '#' && strcmp (line, shellstr) == 0) {
+               if (('#' != *line) && (strcmp (line, shellstr) == 0)) {
                        endusershell ();
-                       return 0;
+                       return false;
                }
        }
        endusershell ();
-       return 1;
+       return true;
 }
 
 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 ((pwent.pw_uid ? LOG_INFO : LOG_NOTICE,
-                        "- %s %s:%s", tty,
-                        oldname[0] ? oldname : "???", name[0] ? name : "???"));
+       if (getdef_bool ("SYSLOG_SU_ENAB")) {
+               SYSLOG (((0 != pwent.pw_uid) ? LOG_INFO : LOG_NOTICE,
+                        "- %s %s:%s", tty,
+                        ('\0' != oldname[0]) ? oldname : "???",
+                        ('\0' != name[0]) ? name : "???"));
+       }
        closelog ();
 #endif
-       puts (_("Sorry."));
        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 */
-static void catch_signals (int sig)
+static void catch_signals (unused int sig)
 {
-       ++caught;
+       caught = true;
 }
 
 /* This I ripped out of su.c from sh-utils after the Mandrake pam patch
  * have been applied.  Some work was needed to get it integrated into
  * su.c from shadow.
  */
-static void run_shell (const char *shellstr, char *args[], int doshell,
-                      char *const envp[])
+static void run_shell (const char *shellstr, char *args[], bool doshell,
+                       char *const envp[])
 {
-       int child;
+       pid_t child;
        sigset_t ourset;
        int status;
        int ret;
 
        child = fork ();
        if (child == 0) {       /* child shell */
-               pam_end (pamh, PAM_SUCCESS);
+               /*
+                * PAM_DATA_SILENT is not supported by some modules, and
+                * there is no strong need to clean up the process space's
+                * memory since we will either call exec or exit.
+               pam_end (pamh, PAM_SUCCESS | PAM_DATA_SILENT);
+                */
 
-               if (doshell)
+               if (doshell) {
                        (void) shell (shellstr, (char *) args[0], envp);
-               else
-                       (void) execve (shellstr, (char **) args, envp);
+               } else {
+                       execve_shell (shellstr, (char **) args, envp);
+               }
+
                exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
-       } else if (child == -1) {
-               (void) fprintf (stderr, "%s: Cannot fork user shell\n", Prog);
+       } else if ((pid_t)-1 == child) {
+               (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)) {
-               (void) fprintf (stderr, "%s: signal malfunction\n", Prog);
-               caught = 1;
+       if (sigprocmask (SIG_BLOCK, &ourset, NULL) != 0) {
+               (void) fprintf (stderr,
+                               _("%s: signal malfunction\n"),
+                               Prog);
+               caught = true;
        }
        if (!caught) {
                struct sigaction action;
@@ -193,84 +291,89 @@ static void run_shell (const char *shellstr, char *args[], int doshell,
                action.sa_flags = 0;
                sigemptyset (&ourset);
 
-               if (sigaddset (&ourset, SIGTERM)
-                   || sigaddset (&ourset, SIGALRM)
-                   || sigaction (SIGTERM, &action, NULL)
-                   || sigprocmask (SIG_UNBLOCK, &ourset, NULL)
+               if (   (sigaddset (&ourset, SIGTERM) != 0)
+                   || (sigaddset (&ourset, SIGALRM) != 0)
+                   || (sigaction (SIGTERM, &action, NULL) != 0)
+                   || (sigprocmask (SIG_UNBLOCK, &ourset, NULL) != 0)
                    ) {
                        fprintf (stderr,
-                                "%s: signal masking malfunction\n", Prog);
-                       caught = 1;
+                                _("%s: signal masking malfunction\n"),
+                                Prog);
+                       caught = true;
                }
        }
 
        if (!caught) {
                do {
-                       int pid;
+                       pid_t pid;
 
                        pid = waitpid (-1, &status, WUNTRACED);
 
-                       if (WIFSTOPPED (status)) {
-                               kill (getpid (), SIGSTOP);
-                               /* once we get here, we must have resumed */
+                       if (((pid_t)-1 != pid) && (0 != WIFSTOPPED (status))) {
+                               /* The child (shell) was suspended.
+                                * Suspend su. */
+                               kill (getpid (), WSTOPSIG(status));
+                               /* wake child when resumed */
                                kill (pid, SIGCONT);
                        }
-               } while (WIFSTOPPED (status));
+               } while (0 != WIFSTOPPED (status));
        }
 
        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);
-       if (ret != PAM_SUCCESS) {
+       if (PAM_SUCCESS != ret) {
                SYSLOG ((LOG_ERR, "pam_close_session: %s",
                         pam_strerror (pamh, ret)));
                fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
-               pam_end (pamh, ret);
+               (void) pam_end (pamh, ret);
                exit (1);
        }
 
        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 (WIFEXITED (status)
-             ? WEXITSTATUS (status)
-             : WTERMSIG (status) + 128);
+       exit ((0 != WIFEXITED (status)) ? WEXITSTATUS (status)
+                                       : WTERMSIG (status) + 128);
 }
 #endif
 
 /*
  * usage - print command line syntax and exit
   */
-static void usage (void)
+static void usage (int status)
 {
-       fprintf (stderr, _("Usage: su [options] [login]\n"
-                          "\n"
-                          "Options:\n"
-                          "  -c, --command COMMAND             pass COMMAND to the invoked shell\n"
-                          "  -h, --help                        display this help message and exit\n"
-                          "  -, -l, --login            make the shell a login shell\n"
-                          "  -m, -p,\n"
-                          "  --preserve-environment    do not reset environment variables, and keep\n"
-                          "                            the same shell\n"
-                          "  -s, --shell SHELL         use SHELL instead of the default in passwd\n"
-                          "\n"));
-       exit (E_USAGE);
+       fputs (_("Usage: su [options] [LOGIN]\n"
+                "\n"
+                "Options:\n"
+                "  -c, --command COMMAND         pass COMMAND to the invoked shell\n"
+                "  -h, --help                    display this help message and exit\n"
+                "  -, -l, --login                make the shell a login shell\n"
+                "  -m, -p,\n"
+                "  --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"), (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
@@ -278,23 +381,24 @@ static void usage (void)
  */
 int main (int argc, char **argv)
 {
-       char *cp;
-       char **envcp;
-       const char *tty = 0;    /* Name of tty SU is run from        */
-       int doshell = 0;
-       int fakelogin = 0;
-       int amroot = 0;
+       const char *cp;
+       const char *tty = NULL; /* Name of tty SU is run from        */
+       bool doshell = false;
+       bool fakelogin = false;
+       bool amroot = false;
        uid_t my_uid;
-       struct passwd *pw = 0;
+       struct passwd *pw = NULL;
        char **envp = environ;
-       char *shellstr = 0, *command = 0;
+       char *shellstr = NULL;
+       char *command = NULL;
 
 #ifdef USE_PAM
+       char **envcp;
        int ret;
 #else                          /* !USE_PAM */
        int err = 0;
 
-       RETSIGTYPE (*oldsig) ();
+       RETSIGTYPE (*oldsig) (int);
        int is_console = 0;
 
        struct spwd *spwd = 0;
@@ -306,11 +410,11 @@ int main (int argc, char **argv)
 
        sanitize_env ();
 
-       setlocale (LC_ALL, "");
-       bindtextdomain (PACKAGE, LOCALEDIR);
-       textdomain (PACKAGE);
+       (void) setlocale (LC_ALL, "");
+       (void) bindtextdomain (PACKAGE, LOCALEDIR);
+       (void) textdomain (PACKAGE);
 
-       change_environment = 1;
+       change_environment = true;
 
        /*
         * Get the program name. The program name is used as a prefix to
@@ -340,27 +444,17 @@ int main (int argc, char **argv)
                };
 
                while ((c =
-                       getopt_long (argc, argv, "-c:hlmps:", long_options,
+                       getopt_long (argc, argv, "c:hlmps:", long_options,
                                     &option_index)) != -1) {
                        switch (c) {
-                       case 1:
-                               /* this is not an su option */
-                               /* The next arguments are either '-', the
-                                * target name, or arguments to be passed
-                                * to the shell.
-                                */
-                               /* rewind the (not yet handled) option */
-                               optind--;
-                               goto end_su_options;
-                               break;  /* NOT REACHED */
                        case 'c':
                                command = optarg;
                                break;
                        case 'h':
-                               usage ();
+                               usage (E_SUCCESS);
                                break;
                        case 'l':
-                               fakelogin = 1;
+                               fakelogin = true;
                                break;
                        case 'm':
                        case 'p':
@@ -368,21 +462,23 @@ int main (int argc, char **argv)
                                 * user do not have a restricted shell, or if
                                 * su is called by root.
                                 */
-                               change_environment = 0;
+                               change_environment = false;
                                break;
                        case 's':
                                shellstr = optarg;
                                break;
                        default:
-                               usage ();       /* NOT REACHED */
+                               usage (E_USAGE);        /* NOT REACHED */
                        }
                }
-             end_su_options:
-               if (optind < argc && !strcmp (argv[optind], "-")) {
-                       fakelogin = 1;
+
+               if ((optind < argc) && (strcmp (argv[optind], "-") == 0)) {
+                       fakelogin = true;
                        optind++;
-                       if (optind < argc && !strcmp (argv[optind], "--"))
+                       if (   (optind < argc)
+                           && (strcmp (argv[optind], "--") == 0)) {
                                optind++;
+                       }
                }
        }
 
@@ -395,11 +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.
         */
-       if (isatty (0) && (cp = ttyname (0))) {
-               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
@@ -409,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 = "???";
@@ -421,25 +515,42 @@ int main (int argc, char **argv)
         * doesn't start with a "-" unless you specify the new user name.
         * Any remaining arguments will be passed to the user's login shell.
         */
-       if (optind < argc && argv[optind][0] != '-') {
+       if ((optind < argc) && ('-' != argv[optind][0])) {
                STRFCPY (name, argv[optind++]); /* use this login id */
-               if (optind < argc && !strcmp (argv[optind], "--"))
+               if ((optind < argc) && (strcmp (argv[optind], "--") == 0)) {
                        optind++;
+               }
+       }
+       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);
+               }
        }
-       if (!name[0])           /* use default user ID */
-               (void) strcpy (name, "root");
 
-       doshell = argc == optind;       /* any arguments remaining? */
-       if (command)
-               doshell = 0;
+       doshell = (argc == optind);     /* any arguments remaining? */
+       if (NULL != command) {
+               doshell = false;
+       }
 
        /*
         * Get the user's real name. The current UID is used to determine
         * who has executed su. That user ID must exist.
         */
        pw = get_my_pwent ();
-       if (!pw) {
-               SYSLOG ((LOG_CRIT, "Unknown UID: %u", my_uid));
+       if (NULL == pw) {
+               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));
                su_failure (tty);
        }
        STRFCPY (oldname, pw->pw_name);
@@ -450,24 +561,28 @@ int main (int argc, char **argv)
         * Sort out the password of user calling su, in case needed later
         * -- chris
         */
-       if ((spwd = getspnam (oldname)))
+       spwd = getspnam (oldname); /* !USE_PAM, no need for xgetspnam */
+       if (NULL != spwd) {
                pw->pw_passwd = spwd->sp_pwdp;
+       }
        oldpass = xstrdup (pw->pw_passwd);
 #endif                         /* SU_ACCESS */
 
 #else                          /* USE_PAM */
        ret = pam_start ("su", name, &conv, &pamh);
-       if (ret != PAM_SUCCESS) {
+       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);
        }
 
        ret = pam_set_item (pamh, PAM_TTY, (const void *) tty);
-       if (ret == PAM_SUCCESS)
+       if (PAM_SUCCESS == ret) {
                ret = pam_set_item (pamh, PAM_RUSER, (const void *) oldname);
-       if (ret != PAM_SUCCESS) {
+       }
+       if (PAM_SUCCESS != ret) {
                SYSLOG ((LOG_ERR, "pam_set_item: %s",
                         pam_strerror (pamh, ret)));
                fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
@@ -485,16 +600,20 @@ int main (int argc, char **argv)
         * The password file entries for the user is gotten and the account
         * validated.
         */
-       if (!(pw = getpwnam (name))) {
+       pw = xgetpwnam (name);
+       if (NULL == pw) {
                (void) fprintf (stderr, _("Unknown id: %s\n"), name);
                closelog ();
                exit (1);
        }
 #ifndef USE_PAM
        spwd = NULL;
-       if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0
-           && (spwd = getspnam (name)))
-               pw->pw_passwd = spwd->sp_pwdp;
+       if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
+               spwd = getspnam (name); /* !USE_PAM, no need for xgetspnam */
+               if (NULL != spwd) {
+                       pw->pw_passwd = spwd->sp_pwdp;
+               }
+       }
 #endif                         /* !USE_PAM */
        pwent = *pw;
 
@@ -509,35 +628,61 @@ 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.
                 */
-               if ((cp = getenv ("TERM")))
+               cp = getenv ("TERM");
+               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
-               if ((cp = getdef_str ("ENV_TZ")))
-                       addenv (*cp == '/' ? tz (cp) : cp, NULL);
+               cp = getdef_str ("ENV_TZ");
+               if (NULL != cp) {
+                       addenv (('/' == *cp) ? tz (cp) : cp, NULL);
+               }
 
                /*
                 * The clock frequency will be reset to the login value if required
                 */
-               if ((cp = getdef_str ("ENV_HZ")))
+               cp = getdef_str ("ENV_HZ");
+               if (NULL != cp) {
                        addenv (cp, NULL);      /* set the default $HZ, if one */
+               }
+#endif                         /* !USE_PAM */
 
                /*
                 * Also leave DISPLAY and XAUTHORITY if present, else
                 * pam_xauth will not work.
                 */
-               if ((cp = getenv ("DISPLAY")))
+               cp = getenv ("DISPLAY");
+               if (NULL != cp) {
                        addenv ("DISPLAY", cp);
-               if ((cp = getenv ("XAUTHORITY")))
+               }
+               cp = getenv ("XAUTHORITY");
+               if (NULL != cp) {
                        addenv ("XAUTHORITY", cp);
-#endif                         /* !USE_PAM */
+               }
        } else {
-               while (*envp)
-                       addenv (*envp++, NULL);
+               while (NULL != *envp) {
+                       addenv (*envp, NULL);
+                       envp++;
+               }
        }
 
 #ifndef USE_PAM
@@ -560,10 +705,12 @@ int main (int argc, char **argv)
         */
 
        if (!amroot) {
-               if (pwent.pw_uid == 0 && getdef_bool ("SU_WHEEL_ONLY")
+               if (   (0 == pwent.pw_uid)
+                   && 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
@@ -579,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 */
@@ -590,58 +738,67 @@ int main (int argc, char **argv)
         * use the current SHELL.
         * (unless another shell is required by the command line)
         */
-       if (shellstr == NULL && change_environment == 0)
+       if ((NULL == shellstr) && !change_environment) {
                shellstr = getenv ("SHELL");
+       }
        /* For users with non null UID, if this user has a restricted
         * shell, the shell must be the one specified in /etc/passwd
         */
-       if (shellstr != NULL && !amroot && restricted_shell (pwent.pw_shell))
+       if (   (NULL != shellstr)
+           && !amroot
+           && restricted_shell (pwent.pw_shell)) {
                shellstr = NULL;
+       }
        /* If the shell is not set at this time, use the shell specified
         * in /etc/passwd.
         */
-       if (shellstr == NULL)
+       if (NULL == shellstr) {
                shellstr = (char *) strdup (pwent.pw_shell);
+       }
 
        /*
         * Set the default shell.
         */
-       if (shellstr == NULL || shellstr[0] == '\0')
-               shellstr = "/bin/sh";
+       if ((NULL == shellstr) || ('\0' == shellstr[0])) {
+               shellstr = SHELL;
+       }
 
-       signal (SIGINT, SIG_IGN);
-       signal (SIGQUIT, SIG_IGN);
+       (void) signal (SIGINT, SIG_IGN);
+       (void) signal (SIGQUIT, SIG_IGN);
 #ifdef USE_PAM
        ret = pam_authenticate (pamh, 0);
-       if (ret != PAM_SUCCESS) {
+       if (PAM_SUCCESS != ret) {
                SYSLOG ((LOG_ERR, "pam_authenticate: %s",
                         pam_strerror (pamh, ret)));
                fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
-               pam_end (pamh, ret);
+               (void) pam_end (pamh, ret);
                su_failure (tty);
        }
 
        ret = pam_acct_mgmt (pamh, 0);
-       if (ret != PAM_SUCCESS) {
+       if (PAM_SUCCESS != ret) {
                if (amroot) {
-                       fprintf (stderr, _("%s: %s\n(Ignored)\n"), Prog,
-                                pam_strerror (pamh, ret));
-               } else if (ret == PAM_NEW_AUTHTOK_REQD) {
+                       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 (ret != PAM_SUCCESS) {
+                       if (PAM_SUCCESS != ret) {
                                SYSLOG ((LOG_ERR, "pam_chauthtok: %s",
                                         pam_strerror (pamh, ret)));
-                               fprintf (stderr, _("%s: %s\n"), Prog,
-                                        pam_strerror (pamh, ret));
-                               pam_end (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));
-                       pam_end (pamh, ret);
+                       fprintf (stderr,
+                                _("%s: %s\n"),
+                                Prog, pam_strerror (pamh, ret));
+                       (void) pam_end (pamh, ret);
                        su_failure (tty);
                }
        }
@@ -659,10 +816,11 @@ int main (int argc, char **argv)
         */
        if (!amroot && pw_auth (pwent.pw_passwd, name, PW_SU, (char *) 0)) {
                SYSLOG ((pwent.pw_uid ? LOG_NOTICE : LOG_WARN,
-                        "Authentication failed for %s", name));
+                        "Authentication failed for %s", name));
+               fprintf(stderr, _("%s: Authentication failure\n"), Prog);
                su_failure (tty);
        }
-       signal (SIGQUIT, oldsig);
+       (void) signal (SIGQUIT, oldsig);
 
        /*
         * Check to see if the account is expired. root gets to ignore any
@@ -670,15 +828,19 @@ int main (int argc, char **argv)
         * expired password.
         */
        if (!amroot) {
-               if (!spwd)
+               if (NULL == spwd) {
                        spwd = pwd_to_spwd (&pwent);
+               }
 
                if (expire (&pwent, spwd)) {
+                       /* !USE_PAM, no need for xgetpwnam */
                        struct passwd *pwd = getpwnam (name);
 
+                       /* !USE_PAM, no need for xgetspnam */
                        spwd = getspnam (name);
-                       if (pwd)
+                       if (NULL != pwd) {
                                pwent = *pwd;
+                       }
                }
        }
 
@@ -690,40 +852,39 @@ int main (int argc, char **argv)
         */
        if (!amroot) {
                if (!isttytime (pwent.pw_name, "SU", time ((time_t *) 0))) {
-                       SYSLOG ((pwent.pw_uid ? LOG_WARN : LOG_CRIT,
-                                "SU by %s to restricted account %s",
-                                oldname, name));
+                       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);
                        su_failure (tty);
                }
        }
 #endif                         /* !USE_PAM */
 
-       signal (SIGINT, SIG_DFL);
-       signal (SIGQUIT, SIG_DFL);
+       (void) signal (SIGINT, SIG_DFL);
+       (void) signal (SIGQUIT, SIG_DFL);
 
        cp = getdef_str ((pwent.pw_uid == 0) ? "ENV_SUPATH" : "ENV_PATH");
-       if (!cp) {
+       if (NULL == cp) {
                addenv ("PATH=/bin:/usr/bin", NULL);
-       } else if (strchr (cp, '=')) {
+       } else if (strchr (cp, '=') != NULL) {
                addenv (cp, NULL);
        } else {
                addenv ("PATH", cp);
        }
 
-#ifndef USE_PAM
-       /* setup the environment for PAM later on, else we run into auth problems */
-       environ = newenvp;      /* make new environment active */
-#endif
-
-       if (getenv ("IFS"))     /* don't export user IFS ... */
+       if (getenv ("IFS") != NULL) {   /* don't export user IFS ... */
                addenv ("IFS= \t\n", NULL);     /* ... instead, set a safe IFS */
+       }
 
        /*
         * Even if --shell is specified, the subsystem login test is based on
         * the shell specified in /etc/passwd (not the one specified with
         * --shell, which will be the one executed in the chroot later).
         */
-       if (pwent.pw_shell[0] == '*') { /* subsystem root required */
+       if ('*' == pwent.pw_shell[0]) { /* subsystem root required */
                pwent.pw_shell++;       /* skip the '*' */
                subsystem (&pwent);     /* figure out what to execute */
                endpwent ();
@@ -731,18 +892,20 @@ int main (int argc, char **argv)
                goto top;
        }
 
-       sulog (tty, 1, oldname, name);  /* save SU information */
+       sulog (tty, true, oldname, name);       /* save SU information */
        endpwent ();
        endspent ();
 #ifdef USE_SYSLOG
-       if (getdef_bool ("SYSLOG_SU_ENAB"))
+       if (getdef_bool ("SYSLOG_SU_ENAB")) {
                SYSLOG ((LOG_INFO, "+ %s %s:%s", tty,
-                        oldname[0] ? oldname : "???", name[0] ? name : "???"));
+                        ('\0' != oldname[0]) ? oldname : "???",
+                        ('\0' != name[0]) ? name : "???"));
+       }
 #endif
 
 #ifdef USE_PAM
        /* set primary group id and supplementary groups */
-       if (setup_groups (&pwent)) {
+       if (setup_groups (&pwent) != 0) {
                pam_end (pamh, PAM_ABORT);
                exit (1);
        }
@@ -752,20 +915,20 @@ int main (int argc, char **argv)
         * and much more, depending on the configured modules
         */
        ret = pam_setcred (pamh, PAM_ESTABLISH_CRED);
-       if (ret != PAM_SUCCESS) {
+       if (PAM_SUCCESS != ret) {
                SYSLOG ((LOG_ERR, "pam_setcred: %s", pam_strerror (pamh, ret)));
                fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
-               pam_end (pamh, ret);
+               (void) pam_end (pamh, ret);
                exit (1);
        }
 
        ret = pam_open_session (pamh, 0);
-       if (ret != PAM_SUCCESS) {
+       if (PAM_SUCCESS != ret) {
                SYSLOG ((LOG_ERR, "pam_open_session: %s",
                         pam_strerror (pamh, ret)));
                fprintf (stderr, _("%s: %s\n"), Prog, pam_strerror (pamh, ret));
                pam_setcred (pamh, PAM_DELETE_CRED);
-               pam_end (pamh, ret);
+               (void) pam_end (pamh, ret);
                exit (1);
        }
 
@@ -778,8 +941,8 @@ int main (int argc, char **argv)
 
                /* update environment with all pam set variables */
                envcp = pam_getenvlist (pamh);
-               if (envcp) {
-                       while (*envcp) {
+               if (NULL != envcp) {
+                       while (NULL != *envcp) {
                                addenv (*envcp, NULL);
                                envcp++;
                        }
@@ -787,18 +950,23 @@ int main (int argc, char **argv)
        }
 
        /* become the new user */
-       if (change_uid (&pwent)) {
+       if (change_uid (&pwent) != 0) {
                pam_close_session (pamh, 0);
                pam_setcred (pamh, PAM_DELETE_CRED);
-               pam_end (pamh, PAM_ABORT);
+               (void) pam_end (pamh, PAM_ABORT);
                exit (1);
        }
 #else                          /* !USE_PAM */
-       if (!amroot)            /* no limits if su from root */
+       environ = newenvp;      /* make new environment active */
+
+       /* no limits if su from root (unless su must fake login's behavior) */
+       if (!amroot || fakelogin) {
                setup_limits (&pwent);
+       }
 
-       if (setup_uid_gid (&pwent, is_console))
+       if (setup_uid_gid (&pwent, is_console) != 0) {
                exit (1);
+       }
 #endif                         /* !USE_PAM */
 
        if (change_environment) {
@@ -830,20 +998,22 @@ int main (int argc, char **argv)
                char *arg0;
 
                cp = getdef_str ("SU_NAME");
-               if (!cp)
+               if (NULL == cp) {
                        cp = Basename (shellstr);
+               }
 
                arg0 = xmalloc (strlen (cp) + 2);
                arg0[0] = '-';
                strcpy (arg0 + 1, cp);
                cp = arg0;
-       } else
+       } else {
                cp = Basename (shellstr);
+       }
 
        if (!doshell) {
                /* Position argv to the remaining arguments */
                argv += optind;
-               if (command) {
+               if (NULL != command) {
                        argv -= 2;
                        argv[0] = "-c";
                        argv[1] = command;
@@ -854,22 +1024,23 @@ 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) fprintf (stderr, _("No shell\n"));
+               (void) fputs (_("No shell\n"), stderr);
                SYSLOG ((LOG_WARN, "Cannot execute %s", shellstr));
                closelog ();
-               exit (err == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
+               exit ((ENOENT == err) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
 #else
-               run_shell (shellstr, &argv[-1], 0, environ);    /* no return */
+               run_shell (shellstr, &argv[-1], false, environ); /* no return */
 #endif
        }
 #ifndef USE_PAM
        err = shell (shellstr, cp, environ);
-       exit (err == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
+       exit ((ENOENT == err) ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
 #else
-       run_shell (shellstr, &cp, 1, environ);
+       run_shell (shellstr, &cp, true, environ);
 #endif
        /* NOT REACHED */
        exit (1);
 }
+