diff --git a/Dockerfile b/Dockerfile index 3fe3c13..42a0a94 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ COPY src ./src RUN cargo build --release --target x86_64-unknown-linux-musl FROM python:latest -RUN pip install pytest pyyaml +RUN pip install pytest RUN useradd -m hacker COPY --from=builder --chmod=6755 /usr/src/exec-suid/target/x86_64-unknown-linux-musl/release/exec-suid /usr/bin/exec-suid COPY tests /tests diff --git a/tests/programs/test_bash b/tests/programs/test_bash index d27be39..df52dd6 100755 --- a/tests/programs/test_bash +++ b/tests/programs/test_bash @@ -1,6 +1,3 @@ #!/usr/bin/exec-suid -- /bin/bash -p -if [ "$(id -u)" -ne 0 ]; then - echo "Expected euid to be 0, got $(id -u)" - exit 1 -fi +id -u diff --git a/tests/programs/test_opt_real b/tests/programs/test_opt_real new file mode 100755 index 0000000..8c1940f --- /dev/null +++ b/tests/programs/test_opt_real @@ -0,0 +1,12 @@ +#!/usr/bin/exec-suid --real -- /usr/bin/python3 -I + +import json +import os +import sys + +json.dump(dict( + argv=sys.argv, + env=os.environ, + uid=os.getresuid(), + gid=os.getresgid(), +), sys.stdout) diff --git a/tests/programs/test_python b/tests/programs/test_python new file mode 100755 index 0000000..42d1a33 --- /dev/null +++ b/tests/programs/test_python @@ -0,0 +1,12 @@ +#!/usr/bin/exec-suid -- /usr/bin/python3 -I + +import json +import os +import sys + +json.dump(dict( + argv=sys.argv, + env=os.environ, + uid=os.getresuid(), + gid=os.getresgid(), +), sys.stdout) diff --git a/tests/programs/test_python_argv0_absolute_path b/tests/programs/test_python_argv0_absolute_path deleted file mode 100755 index fcb269e..0000000 --- a/tests/programs/test_python_argv0_absolute_path +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/exec-suid -- /usr/bin/python3 -I - -import sys -import pathlib - -path = pathlib.Path(__file__).resolve() -assert sys.argv[0] == str(path), f"Expected {sys.argv[0]} to be {path}" diff --git a/tests/programs/test_python_argv0_relative_path b/tests/programs/test_python_argv0_relative_path deleted file mode 100755 index 0913b96..0000000 --- a/tests/programs/test_python_argv0_relative_path +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/exec-suid -- /usr/bin/python3 -I - -import sys -import pathlib - -path = pathlib.Path(__file__) -assert sys.argv[0] == f"./{path.name}", f"Expected {sys.argv[0]} to be ./{path.name}" diff --git a/tests/programs/test_python_empty_env b/tests/programs/test_python_empty_env deleted file mode 100755 index 40a4248..0000000 --- a/tests/programs/test_python_empty_env +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/exec-suid -- /usr/bin/python3 -I - -import os - -os.environ.pop("LC_CTYPE") # This environment variable may be automatically set by Python -assert os.environ == {}, f"Expected {os.environ} to be {{}}" diff --git a/tests/programs/test_python_euid b/tests/programs/test_python_euid deleted file mode 100755 index 0b0c155..0000000 --- a/tests/programs/test_python_euid +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/exec-suid -- /usr/bin/python3 -I - -import os - -ruid, euid, suid = os.getresuid() -assert euid == 0, f"Expected euid to be 0, got {euid}" -assert ruid == 1000, f"Expected ruid to be 1000, got {ruid}" -assert suid == 0, f"Expected suid to be 0, got {suid}" - -rgid, egid, sgid = os.getresgid() -assert egid == 1000, f"Expected egid to be 1000, got {egid}" -assert rgid == 1000, f"Expected rgid to be 1000, got {rgid}" -assert sgid == 1000, f"Expected sgid to be 1000, got {sgid}" diff --git a/tests/programs/test_python_opt_real b/tests/programs/test_python_opt_real deleted file mode 100755 index e0a5c34..0000000 --- a/tests/programs/test_python_opt_real +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/exec-suid --real -- /usr/bin/python3 -I - -import os - -ruid, euid, suid = os.getresuid() -assert euid == 0, f"Expected euid to be 0, got {euid}" -assert ruid == 0, f"Expected ruid to be 0, got {ruid}" -assert suid == 0, f"Expected suid to be 0, got {suid}" - -rgid, egid, sgid = os.getresgid() -assert egid == 1000, f"Expected egid to be 1000, got {egid}" -assert rgid == 1000, f"Expected rgid to be 1000, got {rgid}" -assert sgid == 1000, f"Expected sgid to be 1000, got {sgid}" diff --git a/tests/programs/test_sh b/tests/programs/test_sh index 429ca2b..64854b8 100755 --- a/tests/programs/test_sh +++ b/tests/programs/test_sh @@ -1,6 +1,3 @@ #!/usr/bin/exec-suid -- /bin/sh -p -if [ "$(id -u)" -ne 0 ]; then - echo "Expected euid to be 0, got $(id -u)" - exit 1 -fi +id -u diff --git a/tests/test_programs.py b/tests/test_programs.py index 52a3d08..016bfcb 100644 --- a/tests/test_programs.py +++ b/tests/test_programs.py @@ -1,38 +1,38 @@ +import json import subprocess import os -import pytest -import yaml +def preexec_fn(uid=1000, gid=1000, cwd="/"): + os.setgid(gid) + os.setuid(uid) + os.chdir(cwd) -test_configs = yaml.safe_load(open("/tests/test_programs.yml")) +def test_python(): + result = json.loads(subprocess.check_output("/tests/programs/test_python", preexec_fn=preexec_fn)) + result["env"].pop("LC_CTYPE") # This environment variable may be automatically set by Python" + assert result == dict(argv=["/tests/programs/test_python"], + env={}, + uid=[0, 1000, 0], + gid=[1000, 1000, 1000]) -@pytest.mark.parametrize("config", test_configs, ids=[test_config["name"] for test_config in test_configs]) -def test_program(config): - name = config["name"] - config.setdefault("path", f"/tests/programs/{name}") - config.setdefault("argv", [config["path"]]) - config.setdefault("cwd", "/") +def test_python_relative(): + result = json.loads(subprocess.check_output("./test_python", preexec_fn=lambda: preexec_fn(cwd="/tests/programs"))) + assert result["argv"] == ["./test_python"] - config.setdefault("permissions", {}) - config["permissions"].setdefault("user", 0) - config["permissions"].setdefault("group", 1000) - config["permissions"].setdefault("mode", 0o4755) - config.setdefault("run_as", {}) - config["run_as"].setdefault("user", 1000) - config["run_as"].setdefault("group", 1000) +def test_sh(): + result = int(subprocess.check_output("/tests/programs/test_sh", preexec_fn=preexec_fn)) + assert result == 0 - os.chown(config["path"], config["permissions"]["user"], config["permissions"]["group"]) - os.chmod(config["path"], config["permissions"]["mode"]) - def preexec_fn(): - os.setgid(config["run_as"]["group"]) - os.setuid(config["run_as"]["user"]) - os.chdir(config["cwd"]) +def test_bash(): + result = int(subprocess.check_output("/tests/programs/test_bash", preexec_fn=preexec_fn)) + assert result == 0 - subprocess.run(["ls", "-l", config["path"]]) - subprocess.run(*config["argv"], preexec_fn=preexec_fn, check=True) +def test_opt_real(): + result = json.loads(subprocess.check_output("/tests/programs/test_opt_real", preexec_fn=preexec_fn)) + assert result["uid"] == [0, 0, 0] diff --git a/tests/test_programs.yml b/tests/test_programs.yml deleted file mode 100644 index 41d8f0b..0000000 --- a/tests/test_programs.yml +++ /dev/null @@ -1,9 +0,0 @@ -- name: test_python_euid -- name: test_python_argv0_absolute_path -- name: test_python_argv0_relative_path - argv: [./test_python_argv0_relative_path] - cwd: /tests/programs -- name: test_python_empty_env -- name: test_sh -- name: test_bash -- name: test_python_opt_real