From dc732e77347461d18dd6132c429630a4c0f8ca97 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sat, 9 May 2020 16:06:27 -0700 Subject: [PATCH 1/4] su.c: replace getopt with ad-hoc flag processing In preparation for supporting --exec I was testing the robustness of "--" handling and it became apparent that things are currently a bit broken in `su`. Since "--" is currently of limited utility, as the subsequent words are simply passed to the shell after "-c","command_string", it seems to have gone unnoticed for ages. However, with --exec, it's expected that "--" would be an almost required separator with every such usage, considering the following flags must be passed verbatim to execve() and will likely begin with hyphens looking indistinguishable from any other flags in lieu of shell interpolation to worry about. For some practical context of the existing situation, this invocation doesn't work today: ``` $ su --command ls -- flags for shell No passwd entry for user 'flags' $ ``` This should just run ls as root with "flags","for","shell" forwarded to the shell after "-c","ls". The "--" should block "flags" from being treated as the user. That particular issue isn't a getopt one per-se, it's arguably just a bug in su.c's implementation. It *seemed* like an easy fix for this would be to add a check if argv[optind-1] were "--" before treating argv[optind] as USER. But testing that fix revealed getopt was rearranging things when encountering "--", the "--" would always separate the handled opts from the unhandled ones. USER would become shifted to *after* "--" even when it occurred before it! If we change the command to specify the user, it works as-is: ``` $ su --command ls root -- flags for shell Password: testfile $ ``` But what's rather surprising is how that works; the argv winds up: "su","--command","ls","--","root","flags","for","shell" with optind pointing at "root". That arrangement of argv is indistinguishable from omitting the user and having "root","flags","for","shell" as the stuff after "--". This makes it non-trivial to fix the bug of omitting user treating the first word after "--" as the user, which one could argue is a potentially serious security bug if you omit the user, expect the command to run as root, and the first word after "--" is a valid user, and what follows that something valid and potentially destructive not only running in unintended form but as whatever user happened to be the first word after "--". So, it seems like something important to fix, and getopt seems to be getting in the way of fixing it properly without being more trouble than replacing getopt. In disbelief of what I was seeing getopt doing with argv here, I took a glance at the getopt source and found the following: ``` /* The special ARGV-element '--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (d->optind != argc && !strcmp (argv[d->optind], "--")) ``` I basically never use getopt personally because ages ago it annoyed me with its terrible API for what little it brought to the table, and this brings it to a whole new level of awful. --- src/su.c | 111 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 37 deletions(-) diff --git a/src/su.c b/src/su.c index fc0e826f7..006d51d70 100644 --- a/src/su.c +++ b/src/su.c @@ -55,7 +55,6 @@ #ident "$Id$" -#include #include #include #include @@ -95,6 +94,7 @@ static bool doshell = false; static bool fakelogin = false; static /*@observer@*/const char *shellstr; static /*@null@*/char *command = NULL; +static int optidx; /* not needed by sulog.c anymore */ @@ -760,6 +760,48 @@ static void save_caller_context (char **argv) pw_free (pw); } +/* + * flags_match - test arg against flag candidates + */ +static bool flags_match(const char *arg, const char *a, const char *b, const char *c) +{ + return (a != NULL && strcmp (arg, a) == 0) || + (b != NULL && strcmp (arg, b) == 0) || + (c != NULL && strcmp (arg, c) == 0); +} + +/* is_flag_like - test if arg resembles a flag + * + * lone "--" and bare leading-hyphen-less words are not flag-like, + * everything else is considered a probable flag. + */ +static bool is_flag_like(const char *arg) +{ + if (arg[0] != '-') + return false; + + if (strcmp (arg, "--") == 0) + return false; + + return true; +} + +static void flag_arg_required(const char *arg) +{ + fprintf (stderr, + _("%s: option \'%s\' requires an argument\n"), + Prog, arg); + usage (E_USAGE); +} + +static void flag_unknown(const char *arg) +{ + fprintf (stderr, + _("%s: unrecognized option \'%s\'\n"), + Prog, arg); + usage (E_BAD_ARG); +} + /* * process_flags - Process the command line arguments * @@ -769,51 +811,41 @@ static void save_caller_context (char **argv) */ static void process_flags (int argc, char **argv) { - int c; - static struct option long_options[] = { - {"command", required_argument, NULL, 'c'}, - {"help", no_argument, NULL, 'h'}, - {"login", no_argument, NULL, 'l'}, - {"preserve-environment", no_argument, NULL, 'p'}, - {"shell", required_argument, NULL, 's'}, - {NULL, 0, NULL, '\0'} - }; - - while ((c = getopt_long (argc, argv, "c:hlmps:", - long_options, NULL)) != -1) { - switch (c) { - case 'c': - command = optarg; - break; - case 'h': + for (optidx = 1; optidx < argc; optidx++) { + const char *arg = argv[optidx]; + + if (flags_match (arg, "--command", "-c", NULL)) { + if (optidx == argc - 1) { + flag_arg_required (arg); + } + + command = argv[++optidx]; + } else if (flags_match (arg, "--help", "-h", NULL)) { usage (E_SUCCESS); - break; - case 'l': + } else if (flags_match (arg, "--login", "-l", "-")) { fakelogin = true; - break; - case 'm': - case 'p': + } else if (flags_match (arg, "--preserve-environment", "-p", "-m")) { /* This will only have an effect if the target * user do not have a restricted shell, or if * su is called by root. */ change_environment = false; + } else if (flags_match (arg, "--shell", "-s", NULL)) { + if (optidx == argc - 1) { + flag_arg_required (arg); + } + + shellstr = argv[++optidx]; + } else if (is_flag_like (arg)) { + flag_unknown (arg); + } else { break; - case 's': - shellstr = optarg; - break; - default: - usage (E_USAGE); /* NOT REACHED */ } } - if ((optind < argc) && (strcmp (argv[optind], "-") == 0)) { - fakelogin = true; - optind++; - } - - if (optind < argc) { - STRFCPY (name, argv[optind++]); /* use this login id */ + /* if next arg is not "--", treat as USER */ + if (optidx < argc && strcmp (argv[optidx], "--")) { + STRFCPY (name, argv[optidx++]); /* use this login id */ } if ('\0' == name[0]) { /* use default user */ struct passwd *root_pw = getpwnam ("root"); @@ -829,7 +861,12 @@ static void process_flags (int argc, char **argv) } } - doshell = (argc == optind); /* any arguments remaining? */ + /* if more and next arg is "--", skip it and leave rest as-is */ + if (optidx < argc && strcmp (argv[optidx], "--") == 0) { + optidx++; + } + + doshell = (argc == optidx); /* any arguments remaining? */ if (NULL != command) { doshell = false; } @@ -1178,7 +1215,7 @@ int main (int argc, char **argv) if (!doshell) { int err; /* Position argv to the remaining arguments */ - argv += optind; + argv += optidx; if (NULL != command) { argv -= 2; argv[0] = "-c"; From 6f38f43fdd745fa28b9e100f08bdf8b59b474280 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sat, 9 May 2020 17:43:14 -0700 Subject: [PATCH 2/4] su.c: s/doshell/do_interactive_shell/ Mechanical rename distinguishing this variable from intended changes supporting executing commands without using an interpretive shell (i.e. no '/bin/sh -c'). --- src/su.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/su.c b/src/su.c index 006d51d70..578361a76 100644 --- a/src/su.c +++ b/src/su.c @@ -90,7 +90,7 @@ static bool caller_on_console = false; static /*@only@*/char *caller_pass; #endif #endif /* !USE_PAM */ -static bool doshell = false; +static bool do_interactive_shell = false; static bool fakelogin = false; static /*@observer@*/const char *shellstr; static /*@null@*/char *command = NULL; @@ -327,11 +327,11 @@ static void prepare_pam_close_session (void) if ( (sigaddset (&ourset, SIGTERM) != 0) || (sigaddset (&ourset, SIGALRM) != 0) || (sigaction (SIGTERM, &action, NULL) != 0) - || ( !doshell /* handle SIGINT (Ctrl-C), SIGQUIT - * (Ctrl-\), and SIGTSTP (Ctrl-Z) - * since the child will not control - * the tty. - */ + || (!do_interactive_shell /* handle SIGINT (Ctrl-C), SIGQUIT + * (Ctrl-\), and SIGTSTP (Ctrl-Z) + * since the child will not control + * the tty. + */ && ( (sigaddset (&ourset, SIGINT) != 0) || (sigaddset (&ourset, SIGQUIT) != 0) || (sigaddset (&ourset, SIGTSTP) != 0) @@ -866,9 +866,9 @@ static void process_flags (int argc, char **argv) optidx++; } - doshell = (argc == optidx); /* any arguments remaining? */ + do_interactive_shell = (argc == optidx); /* any arguments remaining? */ if (NULL != command) { - doshell = false; + do_interactive_shell = false; } } @@ -1144,7 +1144,7 @@ int main (int argc, char **argv) set_environment (pw); - if (!doshell) { + if (!do_interactive_shell) { /* There is no need for a controlling terminal. * This avoids the callee to inject commands on * the caller's tty. */ @@ -1212,7 +1212,7 @@ int main (int argc, char **argv) cp = Basename (shellstr); } - if (!doshell) { + if (!do_interactive_shell) { int err; /* Position argv to the remaining arguments */ argv += optidx; From 4047d1fe8edbe5ae952fef17fc09a4105d0de14b Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sat, 9 May 2020 18:01:22 -0700 Subject: [PATCH 3/4] 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 ``` --- src/su.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/src/su.c b/src/su.c index 578361a76..08a28861b 100644 --- a/src/su.c +++ b/src/su.c @@ -94,6 +94,7 @@ static bool do_interactive_shell = false; static bool fakelogin = false; static /*@observer@*/const char *shellstr; static /*@null@*/char *command = NULL; +static /*@null@*/char *exec_command = NULL; static int optidx; @@ -440,12 +441,14 @@ static void usage (int status) "\n" "Options:\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" " -, -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" + " -- pass all subsequent arguments on as-is\n" "\n" "If no username is given, assume root.\n"), (E_SUCCESS != status) ? stderr : stdout); exit (status); @@ -820,6 +823,12 @@ static void process_flags (int argc, char **argv) } 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)) { usage (E_SUCCESS); } 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 (optidx < argc && strcmp (argv[optidx], "--")) { 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. */ argv[-1] = cp; - execve_shell (shellstr, &argv[-1], environ); - err = errno; - (void) fprintf (stderr, - _("Cannot execute %s\n"), shellstr); + + if (NULL != exec_command) { + (void) execve (command, &argv[1], environ); + 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; } else { (void) shell (shellstr, cp, environ); From cf8101aaae51464cbe7e8bf3fe9c03d10c82a8ac Mon Sep 17 00:00:00 2001 From: Serge Hallyn Date: Thu, 27 Aug 2020 23:09:13 -0500 Subject: [PATCH 4/4] Update su tests Some of these tests seem wrong. The assume that su -- -c command should work, whereas -- should mean pass all remaining arguments along to the command. Add some new tests based on examples in Issue 253 Signed-off-by: Serge Hallyn --- tests/run_some | 24 +++++++++------ tests/su/03/data/rootuser.out | 2 ++ tests/su/03/data/tsuser.out | 2 ++ tests/su/03/su_run_command17.test | 2 ++ tests/su/03/su_run_command18.test | 50 +++++++++++++++++++++++++++++++ tests/su/03/su_run_command19.test | 50 +++++++++++++++++++++++++++++++ tests/su/03/su_run_command20.test | 50 +++++++++++++++++++++++++++++++ tests/su/03/su_run_command21.test | 50 +++++++++++++++++++++++++++++++ tests/su/03/su_run_command22.test | 50 +++++++++++++++++++++++++++++++ tests/su/03/su_run_command23.test | 50 +++++++++++++++++++++++++++++++ 10 files changed, 321 insertions(+), 9 deletions(-) create mode 100644 tests/su/03/data/rootuser.out create mode 100644 tests/su/03/data/tsuser.out create mode 100755 tests/su/03/su_run_command18.test create mode 100755 tests/su/03/su_run_command19.test create mode 100755 tests/su/03/su_run_command20.test create mode 100755 tests/su/03/su_run_command21.test create mode 100755 tests/su/03/su_run_command22.test create mode 100755 tests/su/03/su_run_command23.test diff --git a/tests/run_some b/tests/run_some index 2d085d597..fba59bd3a 100755 --- a/tests/run_some +++ b/tests/run_some @@ -90,22 +90,28 @@ run_test ./su/02/env_special_root-options_-p_bash run_test ./su/02/env_special_root-options_ run_test ./su/02/env_special_root-options__bash run_test ./su/03/su_run_command01.test -run_test ./su/03/su_run_command02.test -run_test ./su/03/su_run_command03.test +#run_test ./su/03/su_run_command02.test +#run_test ./su/03/su_run_command03.test run_test ./su/03/su_run_command04.test -run_test ./su/03/su_run_command05.test +#run_test ./su/03/su_run_command05.test run_test ./su/03/su_run_command06.test run_test ./su/03/su_run_command07.test -run_test ./su/03/su_run_command08.test -run_test ./su/03/su_run_command09.test +#run_test ./su/03/su_run_command08.test +#run_test ./su/03/su_run_command09.test run_test ./su/03/su_run_command10.test -run_test ./su/03/su_run_command11.test -run_test ./su/03/su_run_command12.test -run_test ./su/03/su_run_command13.test +#run_test ./su/03/su_run_command11.test +#run_test ./su/03/su_run_command12.test +#run_test ./su/03/su_run_command13.test run_test ./su/03/su_run_command14.test run_test ./su/03/su_run_command15.test run_test ./su/03/su_run_command16.test -run_test ./su/03/su_run_command17.test +#run_test ./su/03/su_run_command17.test +run_test ./su/03/su_run_command18.test +run_test ./su/03/su_run_command19.test +run_test ./su/03/su_run_command20.test +run_test ./su/03/su_run_command21.test +run_test ./su/03/su_run_command22.test +run_test ./su/03/su_run_command23.test run_test ./su/04/su_wrong_user.test run_test ./su/04/su_user_wrong_passwd.test run_test ./su/04/su_user_wrong_passwd_syslog.test diff --git a/tests/su/03/data/rootuser.out b/tests/su/03/data/rootuser.out new file mode 100644 index 000000000..9da4483ec --- /dev/null +++ b/tests/su/03/data/rootuser.out @@ -0,0 +1,2 @@ +root +args: first second third diff --git a/tests/su/03/data/tsuser.out b/tests/su/03/data/tsuser.out new file mode 100644 index 000000000..4796a5486 --- /dev/null +++ b/tests/su/03/data/tsuser.out @@ -0,0 +1,2 @@ +myuser +args: first second third diff --git a/tests/su/03/su_run_command17.test b/tests/su/03/su_run_command17.test index c1f15c5eb..024d0da1b 100755 --- a/tests/su/03/su_run_command17.test +++ b/tests/su/03/su_run_command17.test @@ -7,6 +7,8 @@ cd $(dirname $0) . ../../common/config.sh . ../../common/log.sh +# FIXME +# su: ignoring --preserve-environment, it's mutually exclusive with --login log_start "$0" "Running commands (check working directory): su -c pwd - myuser -p" diff --git a/tests/su/03/su_run_command18.test b/tests/su/03/su_run_command18.test new file mode 100755 index 000000000..280c3f16b --- /dev/null +++ b/tests/su/03/su_run_command18.test @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) + +. ../../common/config.sh +. ../../common/log.sh + +cat > /tmp/shadow_test_wrap << "EOF" +#!/bin/sh + +whoami +echo args: "$@" +EOF +chmod 755 /tmp/shadow_test_wrap + +log_start "$0" "Running commands: su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' testsuite -- first second third" + +save_config + +# restore the files on exit +trap 'log_status "$0" "FAILURE"; restore_config' 0 + +change_config + +echo "/bin/su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' testsuite -- first second third > tmp/out 2> tmp/err" +/bin/su -c '/tmp/shadow_test_wrap $1 $2 $3' testsuite -- first second third > tmp/out 2> tmp/err + +echo "su reported:" +echo "=== stdout ===" +cat tmp/out +echo "=== stderr ===" +cat tmp/err +echo "==============" + +echo -n "Checking tmp/out..." +diff -au data/tsuser.out tmp/out +rm -f tmp/out +echo "OK" + +echo -n "Checking tmp/err..." +[ "$(wc -c tmp/err)" = "0 tmp/err" ] || false +rm -f tmp/err +echo "OK" + +log_status "$0" "SUCCESS" +restore_config +trap '' 0 + diff --git a/tests/su/03/su_run_command19.test b/tests/su/03/su_run_command19.test new file mode 100755 index 000000000..c4c7b0ad2 --- /dev/null +++ b/tests/su/03/su_run_command19.test @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) + +. ../../common/config.sh +. ../../common/log.sh + +cat > /tmp/shadow_test_wrap << "EOF" +#!/bin/sh + +whoami +echo args: "$@" +EOF +chmod 755 /tmp/shadow_test_wrap + +log_start "$0" "Running commands: su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' testsuite first second third" + +save_config + +# restore the files on exit +trap 'log_status "$0" "FAILURE"; restore_config' 0 + +change_config + +echo "/bin/su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' testsuite first second third > tmp/out 2> tmp/err" +/bin/su -c '/tmp/shadow_test_wrap $1 $2 $3' testsuite first second third > tmp/out 2> tmp/err + +echo "su reported:" +echo "=== stdout ===" +cat tmp/out +echo "=== stderr ===" +cat tmp/err +echo "==============" + +echo -n "Checking tmp/out..." +diff -au data/tsuser.out tmp/out +rm -f tmp/out +echo "OK" + +echo -n "Checking tmp/err..." +[ "$(wc -c tmp/err)" = "0 tmp/err" ] || false +rm -f tmp/err +echo "OK" + +log_status "$0" "SUCCESS" +restore_config +trap '' 0 + diff --git a/tests/su/03/su_run_command20.test b/tests/su/03/su_run_command20.test new file mode 100755 index 000000000..21393610b --- /dev/null +++ b/tests/su/03/su_run_command20.test @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) + +. ../../common/config.sh +. ../../common/log.sh + +cat > /tmp/shadow_test_wrap << "EOF" +#!/bin/sh + +whoami +echo args: "$@" +EOF +chmod 755 /tmp/shadow_test_wrap + +log_start "$0" "Running commands: su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' - testsuite first second third" + +save_config + +# restore the files on exit +trap 'log_status "$0" "FAILURE"; restore_config' 0 + +change_config + +echo "/bin/su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' - testsuite first second third > tmp/out 2> tmp/err" +/bin/su -c '/tmp/shadow_test_wrap $1 $2 $3' - testsuite first second third > tmp/out 2> tmp/err + +echo "su reported:" +echo "=== stdout ===" +cat tmp/out +echo "=== stderr ===" +cat tmp/err +echo "==============" + +echo -n "Checking tmp/out..." +diff -au data/tsuser.out tmp/out +rm -f tmp/out +echo "OK" + +echo -n "Checking tmp/err..." +[ "$(wc -c tmp/err)" = "0 tmp/err" ] || false +rm -f tmp/err +echo "OK" + +log_status "$0" "SUCCESS" +restore_config +trap '' 0 + diff --git a/tests/su/03/su_run_command21.test b/tests/su/03/su_run_command21.test new file mode 100755 index 000000000..36c9940aa --- /dev/null +++ b/tests/su/03/su_run_command21.test @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) + +. ../../common/config.sh +. ../../common/log.sh + +cat > /tmp/shadow_test_wrap << "EOF" +#!/bin/sh + +whoami +echo args: "$@" +EOF +chmod 755 /tmp/shadow_test_wrap + +log_start "$0" "Running commands: su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' - testsuite -- first second third" + +save_config + +# restore the files on exit +trap 'log_status "$0" "FAILURE"; restore_config' 0 + +change_config + +echo "/bin/su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' - testsuite -- first second third > tmp/out 2> tmp/err" +/bin/su -c '/tmp/shadow_test_wrap $1 $2 $3' - testsuite -- first second third > tmp/out 2> tmp/err + +echo "su reported:" +echo "=== stdout ===" +cat tmp/out +echo "=== stderr ===" +cat tmp/err +echo "==============" + +echo -n "Checking tmp/out..." +diff -au data/tsuser.out tmp/out +rm -f tmp/out +echo "OK" + +echo -n "Checking tmp/err..." +[ "$(wc -c tmp/err)" = "0 tmp/err" ] || false +rm -f tmp/err +echo "OK" + +log_status "$0" "SUCCESS" +restore_config +trap '' 0 + diff --git a/tests/su/03/su_run_command22.test b/tests/su/03/su_run_command22.test new file mode 100755 index 000000000..b80248894 --- /dev/null +++ b/tests/su/03/su_run_command22.test @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) + +. ../../common/config.sh +. ../../common/log.sh + +cat > /tmp/shadow_test_wrap << "EOF" +#!/bin/sh + +whoami +echo args: "$@" +EOF +chmod 755 /tmp/shadow_test_wrap + +log_start "$0" "Running commands: su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' - -- first second third" + +save_config + +# restore the files on exit +trap 'log_status "$0" "FAILURE"; restore_config' 0 + +change_config + +echo "/bin/su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' - -- first second third > tmp/out 2> tmp/err" +/bin/su -c '/tmp/shadow_test_wrap $1 $2 $3' - -- first second third > tmp/out 2> tmp/err + +echo "su reported:" +echo "=== stdout ===" +cat tmp/out +echo "=== stderr ===" +cat tmp/err +echo "==============" + +echo -n "Checking tmp/out..." +diff -au data/rootuser.out tmp/out +rm -f tmp/out +echo "OK" + +echo -n "Checking tmp/err..." +[ "$(wc -c tmp/err)" = "0 tmp/err" ] || false +rm -f tmp/err +echo "OK" + +log_status "$0" "SUCCESS" +restore_config +trap '' 0 + diff --git a/tests/su/03/su_run_command23.test b/tests/su/03/su_run_command23.test new file mode 100755 index 000000000..9efec1876 --- /dev/null +++ b/tests/su/03/su_run_command23.test @@ -0,0 +1,50 @@ +#!/bin/sh + +set -e + +cd $(dirname $0) + +. ../../common/config.sh +. ../../common/log.sh + +cat > /tmp/shadow_test_wrap << "EOF" +#!/bin/sh + +whoami +echo args: "$@" +EOF +chmod 755 /tmp/shadow_test_wrap + +log_start "$0" "Running commands: su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' -- first second third" + +save_config + +# restore the files on exit +trap 'log_status "$0" "FAILURE"; restore_config' 0 + +change_config + +echo "/bin/su -c '/tmp/shadow_test_wrap \$1 \$2 \$3' -- first second third > tmp/out 2> tmp/err" +/bin/su -c '/tmp/shadow_test_wrap $1 $2 $3' -- first second third > tmp/out 2> tmp/err + +echo "su reported:" +echo "=== stdout ===" +cat tmp/out +echo "=== stderr ===" +cat tmp/err +echo "==============" + +echo -n "Checking tmp/out..." +diff -au data/rootuser.out tmp/out +rm -f tmp/out +echo "OK" + +echo -n "Checking tmp/err..." +[ "$(wc -c tmp/err)" = "0 tmp/err" ] || false +rm -f tmp/err +echo "OK" + +log_status "$0" "SUCCESS" +restore_config +trap '' 0 +