]> granicus.if.org Git - shadow/blobdiff - src/su.c
* src/gpasswd.c: Remove log_gpasswd_success_gshadow(). Writing in
[shadow] / src / su.c
index 67c3e6a0ec4d771eac39c28749875ac47c2bbdb9..c1914d32ff9f76b2e73514a5d523c6a79988e078 100644 (file)
--- a/src/su.c
+++ b/src/su.c
  * Global variables
  */
 const char *Prog;
-static const char *caller_tty = NULL;  /* Name of tty SU is run from */
+static /*@observer@*/const char *caller_tty = NULL;    /* Name of tty SU is run from */
 static bool caller_is_root = false;
 static uid_t caller_uid;
 #ifndef USE_PAM
-static int caller_on_console = 0;
+static bool caller_on_console = false;
 #ifdef SU_ACCESS
-static char *caller_pass;
+static /*@only@*/char *caller_pass;
 #endif
 #endif                         /* !USE_PAM */
 static bool doshell = false;
 static bool fakelogin = false;
-static char *shellstr = NULL;
-static char *command = NULL;
+static /*@observer@*/const char *shellstr;
+static /*@null@*/char *command = NULL;
 
 
 /* not needed by sulog.c anymore */
@@ -115,13 +115,12 @@ static pid_t pid_child = 0;
  * External identifiers
  */
 
-extern char **newenvp;
-extern char **environ;
-extern size_t newenvc;
+extern char **newenvp; /* libmisc/env.c */
+extern size_t newenvc; /* libmisc/env.c */
 
 /* local function prototypes */
 
-static void execve_shell (const char *shellstr,
+static void execve_shell (const char *shellname,
                           char *args[],
                           char *const envp[]);
 #ifdef USE_PAM
@@ -131,14 +130,17 @@ static void prepare_pam_close_session (void);
 static RETSIGTYPE die (int);
 static bool iswheel (const char *);
 #endif                         /* !USE_PAM */
-static struct passwd * check_perms (void);
+static bool restricted_shell (const char *shellname);
+static /*@noreturn@*/void su_failure (const char *tty, bool su_to_root);
+static /*@only@*/struct passwd * check_perms (void);
 #ifdef USE_PAM
-static void check_perms_pam (struct passwd *pw);
+static void check_perms_pam (const struct passwd *pw);
 #else                          /* !USE_PAM */
-static void check_perms_nopam (struct passwd *pw);
+static void check_perms_nopam (const struct passwd *pw);
 #endif                         /* !USE_PAM */
 static void save_caller_context (char **argv);
 static void process_flags (int argc, char **argv);
+static void set_environment (struct passwd *pw);
 
 #ifndef USE_PAM
 /*
@@ -190,13 +192,13 @@ static RETSIGTYPE kill_child (int unused(s))
 #endif                         /* USE_PAM */
 
 /* borrowed from GNU sh-utils' "su.c" */
-static bool restricted_shell (const char *shellstr)
+static bool restricted_shell (const char *shellname)
 {
-       char *line;
+       /*@observer@*/const char *line;
 
        setusershell ();
        while ((line = getusershell ()) != NULL) {
-               if (('#' != *line) && (strcmp (line, shellstr) == 0)) {
+               if (('#' != *line) && (strcmp (line, shellname) == 0)) {
                        endusershell ();
                        return false;
                }
@@ -205,7 +207,7 @@ static bool restricted_shell (const char *shellstr)
        return true;
 }
 
-static void su_failure (const char *tty, bool su_to_root)
+static /*@noreturn@*/void su_failure (const char *tty, bool su_to_root)
 {
        sulog (tty, false, caller_name, name);  /* log failed attempt */
 #ifdef USE_SYSLOG
@@ -224,15 +226,15 @@ static void su_failure (const char *tty, bool su_to_root)
  * execve_shell - Execute a shell with execve, or interpret it with
  * /bin/sh
  */
-static void execve_shell (const char *shellstr,
+static void execve_shell (const char *shellname,
                           char *args[],
                           char *const envp[])
 {
        int err;
-       (void) execve (shellstr, (char **) args, envp);
+       (void) execve (shellname, (char **) args, envp);
        err = errno;
 
-       if (access (shellstr, R_OK|X_OK) == 0) {
+       if (access (shellname, R_OK|X_OK) == 0) {
                /*
                 * Assume this is a shell script (with no shebang).
                 * Interpret it with /bin/sh
@@ -245,7 +247,7 @@ static void execve_shell (const char *shellstr,
                targs = (char **) xmalloc ((n_args + 3) * sizeof (args[0]));
                targs[0] = "sh";
                targs[1] = "-";
-               targs[2] = xstrdup (shellstr);
+               targs[2] = xstrdup (shellname);
                targs[n_args+2] = NULL;
                while (1 != n_args) {
                        targs[n_args+1] = args[n_args - 1];
@@ -400,6 +402,7 @@ static void prepare_pam_close_session (void)
  */
 static void usage (int status)
 {
+       (void)
        fputs (_("Usage: su [options] [LOGIN]\n"
                 "\n"
                 "Options:\n"
@@ -415,7 +418,7 @@ static void usage (int status)
 }
 
 #ifdef USE_PAM
-static void check_perms_pam (struct passwd *pw)
+static void check_perms_pam (const struct passwd *pw)
 {
        int ret;
        ret = pam_authenticate (pamh, 0);
@@ -456,9 +459,10 @@ static void check_perms_pam (struct passwd *pw)
        }
 }
 #else                          /* !USE_PAM */
-static void check_perms_nopam (struct passwd *pw)
+static void check_perms_nopam (const struct passwd *pw)
 {
-       struct spwd *spwd = NULL;
+       /*@observer@*/const struct spwd *spwd = NULL;
+       /*@observer@*/const char *password = pw->pw_passwd;
        RETSIGTYPE (*oldsig) (int);
 
        if (caller_is_root) {
@@ -495,7 +499,7 @@ static void check_perms_nopam (struct passwd *pw)
 #ifdef SU_ACCESS
        if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
                if (NULL != spwd) {
-                       pw->pw_passwd = spwd->sp_pwdp;
+                       password = spwd->sp_pwdp;
                }
        }
 
@@ -503,11 +507,11 @@ static void check_perms_nopam (struct passwd *pw)
        case 0: /* normal su, require target user's password */
                break;
        case 1: /* require no password */
-               pw->pw_passwd = "";     /* XXX warning: const */
+               password = "";  /* XXX warning: const */
                break;
        case 2: /* require own password */
-               puts (_("(Enter your own password)"));
-               pw->pw_passwd = caller_pass;
+               (void) puts (_("(Enter your own password)"));
+               password = caller_pass;
                break;
        default:        /* access denied (-1) or unexpected value */
                fprintf (stderr,
@@ -527,7 +531,7 @@ static void check_perms_nopam (struct passwd *pw)
         * The first character of an administrator defined method is an '@'
         * character.
         */
-       if (pw_auth (pw->pw_passwd, name, PW_SU, (char *) 0) != 0) {
+       if (pw_auth (password, name, PW_SU, (char *) 0) != 0) {
                SYSLOG (((pw->pw_uid != 0)? LOG_NOTICE : LOG_WARN,
                         "Authentication failed for %s", name));
                fprintf(stderr, _("%s: Authentication failure\n"), Prog);
@@ -568,26 +572,59 @@ static void check_perms_nopam (struct passwd *pw)
  *     In case of subsystem login, the user is first authenticated in the
  *     caller's root subsystem, and then in the user's target subsystem.
  */
-static struct passwd * check_perms (void)
+static /*@only@*/struct passwd * check_perms (void)
 {
+#ifdef USE_PAM
+       const char *tmp_name;
+       int ret;
+#endif                         /* !USE_PAM */
        /*
         * The password file entries for the user is gotten and the account
         * validated.
         */
        struct passwd *pw = xgetpwnam (name);
        if (NULL == pw) {
-               (void) fprintf (stderr, _("Unknown id: %s\n"), name);
-               closelog ();
-               exit (1);
+               (void) fprintf (stderr,
+                               _("No passwd entry for user '%s'\n"), name);
+               SYSLOG ((LOG_ERR, "No passwd entry for user '%s'", name));
+               su_failure (caller_tty, true);
        }
 
        (void) signal (SIGINT, SIG_IGN);
        (void) signal (SIGQUIT, SIG_IGN);
+
 #ifdef USE_PAM
        check_perms_pam (pw);
+       /* PAM authentication can request a change of account */
+       ret = pam_get_item(pamh, PAM_USER, (const void **) &tmp_name);
+       if (ret != PAM_SUCCESS) {
+               SYSLOG((LOG_ERR, "pam_get_item: internal PAM error\n"));
+               (void) fprintf (stderr,
+                               "%s: Internal PAM error retrieving username\n",
+                               Prog);
+               (void) pam_end (pamh, ret);
+               su_failure (caller_tty, 0 == pw->pw_uid);
+       }
+       if (strcmp (name, tmp_name) != 0) {
+               SYSLOG ((LOG_INFO,
+                        "Change user from '%s' to '%s' as requested by PAM",
+                        name, tmp_name));
+               strncpy (name, tmp_name, sizeof(name) - 1);
+               name[sizeof(name) - 1] = '\0';
+               pw = xgetpwnam (name);
+               if (NULL == pw) {
+                       (void) fprintf (stderr,
+                                       _("No passwd entry for user '%s'\n"),
+                                       name);
+                       SYSLOG ((LOG_ERR,
+                                "No passwd entry for user '%s'", name));
+                       su_failure (caller_tty, true);
+               }
+       }
 #else                          /* !USE_PAM */
        check_perms_nopam (pw);
 #endif                         /* !USE_PAM */
+
        (void) signal (SIGINT, SIG_DFL);
        (void) signal (SIGQUIT, SIG_DFL);
 
@@ -600,6 +637,7 @@ static struct passwd * check_perms (void)
                subsystem (pw); /* change to the subsystem root */
                endpwent ();            /* close the old password databases */
                endspent ();
+               pw_free (pw);
                return check_perms ();  /* authenticate in the subsystem */
        }
 
@@ -616,7 +654,8 @@ static struct passwd * check_perms (void)
  */
 static void save_caller_context (char **argv)
 {
-       struct passwd *pw = NULL;
+       const struct passwd *pw = NULL;
+       const char *password = NULL;
        /*
         * Get the program name. The program name is used as a prefix to
         * most error messages.
@@ -659,7 +698,7 @@ static void save_caller_context (char **argv)
                         Prog);
                SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)",
                         (unsigned long) caller_uid));
-               su_failure (caller_tty, true); // FIXME: at this time I do not know the target UID
+               su_failure (caller_tty, true); /* unknown target UID*/
        }
        STRFCPY (caller_name, pw->pw_name);
 
@@ -669,15 +708,18 @@ static void save_caller_context (char **argv)
         * Sort out the password of user calling su, in case needed later
         * -- chris
         */
+       password = pw->pw_passwd;
        if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
-               struct spwd *spwd = getspnam (caller_name);
+               const struct spwd *spwd = getspnam (caller_name);
                if (NULL != spwd) {
-                       pw->pw_passwd = spwd->sp_pwdp;
+                       password = spwd->sp_pwdp;
                }
        }
-       caller_pass = xstrdup (pw->pw_passwd);
+       free (caller_pass);
+       caller_pass = xstrdup (password);
 #endif                         /* SU_ACCESS */
 #endif                         /* !USE_PAM */
+       pw_free (pw);
 }
 
 /*
@@ -871,7 +913,10 @@ static void set_environment (struct passwd *pw)
 
        if (change_environment) {
                if (fakelogin) {
-                       pw->pw_shell = shellstr;
+                       if (shellstr != pw->pw_shell) {
+                               free (pw->pw_shell);
+                               pw->pw_shell = xstrdup (shellstr);
+                       }
                        setup_env (pw);
                } else {
                        addenv ("HOME", pw->pw_dir);
@@ -961,7 +1006,7 @@ int main (int argc, char **argv)
         * in /etc/passwd.
         */
        if (NULL == shellstr) {
-               shellstr = (char *) strdup (pw->pw_shell);
+               shellstr = pw->pw_shell;
        }
 
        /*
@@ -1116,6 +1161,8 @@ int main (int argc, char **argv)
                (void) shell (shellstr, cp, environ);
        }
 
+       pw_free (pw);
+
        return (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
 }