fix: refuse stdin to a privileged command while a sudo password is set
CI / test (3.10) (push) Successful in 1m2s
CI / test (3.11) (push) Successful in 47s
CI / test (3.12) (push) Successful in 42s
CI / test (3.10) (pull_request) Successful in 43s
CI / test (3.11) (pull_request) Successful in 46s
CI / test (3.12) (pull_request) Successful in 51s
CI / test (3.10) (push) Successful in 1m2s
CI / test (3.11) (push) Successful in 47s
CI / test (3.12) (push) Successful in 42s
CI / test (3.10) (pull_request) Successful in 43s
CI / test (3.11) (pull_request) Successful in 46s
CI / test (3.12) (pull_request) Successful in 51s
run_command(privileged=True, stdin=data) sent `sudo -S` with stdin = <sudo password>\n + data. A sudo that does not prompt (NOPASSWD, a cached timestamp) leaves the password line on stdin, and the command reads it as the first line of the data - `cat > file` wrote it into the file (NetOrk/netork#1029). run_command now raises ValueError for that combination before opening a channel. Stdin still reaches the command under `sudo -n` (no password set), for a root login, and for unprivileged commands. Callers stage the data without privilege and have root install the file. open_stream documents the same caveat.
This commit is contained in:
1 parent
7bd005eb29
commit
647e058b8a
3 files changed
+72
-7
No files matched your search
@@ -107,21 +107,54 @@ def test_a_root_login_needs_no_sudo():
|
||||
assert transport.channels[-1].sent == b""
|
||||
|
||||
|
||||
def test_stdin_follows_the_sudo_password():
|
||||
def test_a_privileged_command_with_a_sudo_password_takes_no_stdin():
|
||||
"""sudo -S reads the password from stdin. A sudo that does not prompt --
|
||||
NOPASSWD, a cached timestamp -- leaves it there, and the command reads it as
|
||||
its first line: it wrote the password into an sshd drop-in (NetOrk/netork#1029)."""
|
||||
driver, transport = _driver(sudo_password="pw")
|
||||
|
||||
with pytest.raises(ValueError, match="stdin"):
|
||||
driver.run_command("tee /etc/x", privileged=True, stdin=b"data", timeout=5)
|
||||
|
||||
assert transport.channels == [] # nothing reached the host
|
||||
|
||||
|
||||
def test_privileged_stdin_without_a_sudo_password_goes_to_the_command():
|
||||
"""sudo -n reads nothing from stdin: the data is the command's alone."""
|
||||
driver, transport = _driver()
|
||||
|
||||
driver.run_command("tee /etc/x", privileged=True, stdin=b"data", timeout=5)
|
||||
|
||||
assert transport.channels[-1].sent == b"pw\ndata"
|
||||
channel = transport.channels[-1]
|
||||
assert channel.command == "sudo -n sh -c 'tee /etc/x'"
|
||||
assert channel.sent == b"data"
|
||||
|
||||
|
||||
def test_privileged_stdin_for_a_root_login_goes_to_the_command():
|
||||
driver, transport = _driver(root=True, sudo_password="pw")
|
||||
|
||||
driver.run_command("tee /etc/x", privileged=True, stdin=b"data", timeout=5)
|
||||
|
||||
assert transport.channels[-1].command == "tee /etc/x"
|
||||
assert transport.channels[-1].sent == b"data"
|
||||
|
||||
|
||||
def test_unprivileged_stdin_with_a_sudo_password_goes_to_the_command():
|
||||
driver, transport = _driver(sudo_password="pw")
|
||||
|
||||
driver.run_command("cat > /tmp/x", stdin=b"data", timeout=5)
|
||||
|
||||
assert transport.channels[-1].command == "cat > /tmp/x"
|
||||
assert transport.channels[-1].sent == b"data"
|
||||
|
||||
|
||||
def test_a_privileged_stream_gets_the_password_first():
|
||||
driver, transport = _driver(sudo_password="pw")
|
||||
|
||||
driver.open_stream("cat > /tmp/x", privileged=True)
|
||||
driver.open_stream("journalctl -f", privileged=True)
|
||||
|
||||
channel = transport.channels[-1]
|
||||
assert channel.command == "sudo -S -p '' sh -c 'cat > /tmp/x'"
|
||||
assert channel.command == "sudo -S -p '' sh -c 'journalctl -f'"
|
||||
assert channel.sent == b"pw\n"
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user