diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index ae3001418a..f54caf44ce 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -5722,7 +5722,12 @@ int exec_invoke( } #endif - if (uid_is_valid(uid)) { + /* Only adjust ownership for TTYs we acquired via StandardInput=tty*. If stdin is passed + * as an fd, its ownership is managed by the provider of the fd, see exec_context_revert_tty(). */ + if (uid_is_valid(uid) && + params->stdin_fd < 0 && + exec_input_is_terminal(context->std_input) && + exec_context_tty_path(context)) { r = chown_terminal(STDIN_FILENO, uid); if (r < 0) { *exit_status = EXIT_STDIN; diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh index 1c0efdf08f..53abc55efa 100755 --- a/test/units/TEST-74-AUX-UTILS.run.sh +++ b/test/units/TEST-74-AUX-UTILS.run.sh @@ -339,6 +339,26 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the assert_eq "$(run0 --pipe echo -n foo)" "foo" assert_eq "$(run0 --pipe --pty echo -n foo)" "foo" + # When run0 is used in a pipeline, the caller's TTY is passed through as an fd. Its + # ownership/access mode must not be changed to the target user. + script -qec 'bash -euxc '"'"' + set -o pipefail + tty_path="$(tty)" + tty_stat="$(stat -Lc "%u:%g:%a" "$tty_path")" + run0 --user=testuser echo -n foo | cat >/dev/null + [[ "$(stat -Lc "%u:%g:%a" "$tty_path")" == "$tty_stat" ]] + '"'" /dev/null + + # --pty also passes a service PTY as an fd. It must still provide a usable TTY to + # the target user without changing the caller's TTY ownership/access mode. + script -qec 'bash -euxc '"'"' + tty_path="$(tty)" + tty_stat="$(stat -Lc "%u:%g:%a" "$tty_path")" + run0 --pty --user=testuser bash -xec \ + "test \"\$(id -un)\" = testuser; test -t 0; test -t 1; test -t 2; tty >/dev/null" + [[ "$(stat -Lc "%u:%g:%a" "$tty_path")" == "$tty_stat" ]] + '"'" /dev/null + # Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not assert_neq "$(run0 --pty tty < /dev/null)" "not a tty" assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty"