su.c: implement --exec

It's now possible to run commands as other users without shell
interpolation by using "--exec":

Read /etc/shadow as root without specifying user:
```
su --exec /bin/cat -- /etc/shadow
```

Or specify user:
```
su --exec /bin/cat root -- /etc/shadow
```
This commit is contained in:
Vito Caputo 2020-05-09 18:01:22 -07:00 committed by Serge Hallyn
parent 6f38f43fdd
commit 4047d1fe8e

View File

@ -94,6 +94,7 @@ static bool do_interactive_shell = false;
static bool fakelogin = false; static bool fakelogin = false;
static /*@observer@*/const char *shellstr; static /*@observer@*/const char *shellstr;
static /*@null@*/char *command = NULL; static /*@null@*/char *command = NULL;
static /*@null@*/char *exec_command = NULL;
static int optidx; static int optidx;
@ -440,12 +441,14 @@ static void usage (int status)
"\n" "\n"
"Options:\n" "Options:\n"
" -c, --command COMMAND pass COMMAND to the invoked shell\n" " -c, --command COMMAND pass COMMAND to the invoked shell\n"
" -e, --exec PATH run PATH without shell, follow -- with args\n"
" -h, --help display this help message and exit\n" " -h, --help display this help message and exit\n"
" -, -l, --login make the shell a login shell\n" " -, -l, --login make the shell a login shell\n"
" -m, -p,\n" " -m, -p,\n"
" --preserve-environment do not reset environment variables, and\n" " --preserve-environment do not reset environment variables, and\n"
" keep the same shell\n" " keep the same shell\n"
" -s, --shell SHELL use SHELL instead of the default in passwd\n" " -s, --shell SHELL use SHELL instead of the default in passwd\n"
" -- pass all subsequent arguments on as-is\n"
"\n" "\n"
"If no username is given, assume root.\n"), (E_SUCCESS != status) ? stderr : stdout); "If no username is given, assume root.\n"), (E_SUCCESS != status) ? stderr : stdout);
exit (status); exit (status);
@ -820,6 +823,12 @@ static void process_flags (int argc, char **argv)
} }
command = argv[++optidx]; command = argv[++optidx];
} else if (flags_match (arg, "--exec", "-e", NULL)) {
if (optidx == argc - 1) {
flag_arg_required (arg);
}
exec_command = argv[++optidx];
} else if (flags_match (arg, "--help", "-h", NULL)) { } else if (flags_match (arg, "--help", "-h", NULL)) {
usage (E_SUCCESS); usage (E_SUCCESS);
} else if (flags_match (arg, "--login", "-l", "-")) { } else if (flags_match (arg, "--login", "-l", "-")) {
@ -843,6 +852,17 @@ static void process_flags (int argc, char **argv)
} }
} }
if (NULL != exec_command && NULL != command) {
fprintf (stderr,
_("%s: COMMAND and PATH are mutually exclusive\n"),
argv[0]);
usage (E_USAGE);
}
if (NULL != exec_command) {
command = exec_command;
}
/* if next arg is not "--", treat as USER */ /* if next arg is not "--", treat as USER */
if (optidx < argc && strcmp (argv[optidx], "--")) { if (optidx < argc && strcmp (argv[optidx], "--")) {
STRFCPY (name, argv[optidx++]); /* use this login id */ STRFCPY (name, argv[optidx++]); /* use this login id */
@ -1226,10 +1246,18 @@ int main (int argc, char **argv)
* with the rest of the command line included. * with the rest of the command line included.
*/ */
argv[-1] = cp; argv[-1] = cp;
execve_shell (shellstr, &argv[-1], environ);
err = errno; if (NULL != exec_command) {
(void) fprintf (stderr, (void) execve (command, &argv[1], environ);
_("Cannot execute %s\n"), shellstr); err = errno;
(void) fprintf (stderr,
_("Cannot execute \'%s\'\n"), command);
} else {
execve_shell (shellstr, &argv[-1], environ);
err = errno;
(void) fprintf (stderr,
_("Cannot execute \'%s\'\n"), shellstr);
}
errno = err; errno = err;
} else { } else {
(void) shell (shellstr, cp, environ); (void) shell (shellstr, cp, environ);