* 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 */
* 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
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
/*
#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;
}
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
* 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
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];
*/
static void usage (int status)
{
+ (void)
fputs (_("Usage: su [options] [LOGIN]\n"
"\n"
"Options:\n"
}
#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);
}
}
#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) {
#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;
}
}
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,
* 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);
* 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);
subsystem (pw); /* change to the subsystem root */
endpwent (); /* close the old password databases */
endspent ();
+ pw_free (pw);
return check_perms (); /* authenticate in the subsystem */
}
*/
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.
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);
* 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);
}
/*
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);
* in /etc/passwd.
*/
if (NULL == shellstr) {
- shellstr = (char *) strdup (pw->pw_shell);
+ shellstr = pw->pw_shell;
}
/*
(void) shell (shellstr, cp, environ);
}
+ pw_free (pw);
+
return (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC);
}