diff --git a/src/syscall/proc-identity.c b/src/syscall/proc-identity.c index ea187565..31e34af6 100644 --- a/src/syscall/proc-identity.c +++ b/src/syscall/proc-identity.c @@ -107,11 +107,15 @@ uint32_t proc_get_sgid(void) static bool uid_is_permitted(uint32_t val) { + if (emu_euid == 0 || proc_fakeroot_enabled()) + return true; return val == emu_uid || val == emu_euid || val == emu_suid; } static bool gid_is_permitted(uint32_t val) { + if (emu_euid == 0 || proc_fakeroot_enabled()) + return true; return val == emu_gid || val == emu_egid || val == emu_sgid; } @@ -119,7 +123,12 @@ int64_t proc_sys_setuid(uint32_t uid) { if (!uid_is_permitted(uid)) return -LINUX_EPERM; - emu_euid = uid; + if (emu_euid == 0 || proc_fakeroot_enabled()) { + emu_uid = uid; + emu_euid = uid; + emu_suid = uid; + } else + emu_euid = uid; return 0; } @@ -127,7 +136,12 @@ int64_t proc_sys_setgid(uint32_t gid) { if (!gid_is_permitted(gid)) return -LINUX_EPERM; - emu_egid = gid; + if (emu_euid == 0 || proc_fakeroot_enabled()) { + emu_gid = gid; + emu_egid = gid; + emu_sgid = gid; + } else + emu_egid = gid; return 0; } @@ -135,7 +149,8 @@ int64_t proc_sys_setgid(uint32_t gid) int64_t proc_sys_setre##suffix(uint32_t r, uint32_t e) \ { \ uint32_t old_real = real; \ - if (r != (uint32_t) -1 && r != real && r != eff) \ + if (r != (uint32_t) -1 && r != real && r != eff && \ + !(emu_euid == 0 || proc_fakeroot_enabled())) \ return -LINUX_EPERM; \ if (e != (uint32_t) -1 && !perm_fn(e)) \ return -LINUX_EPERM; \ diff --git a/tests/test-credentials.c b/tests/test-credentials.c index ca4c1663..76c2a8f6 100644 --- a/tests/test-credentials.c +++ b/tests/test-credentials.c @@ -10,6 +10,7 @@ */ #include /* gid_t */ +#include /* waitpid */ #include /* getgroups */ #include "test-harness.h" @@ -67,14 +68,22 @@ int main(void) TEST("setuid(expected) succeeds"); EXPECT_TRUE(raw_syscall1(__NR_setuid, expected_id) == 0, "setuid failed"); - /* setuid: setting to arbitrary value must fail with -EPERM */ - TEST("setuid(other) returns -EPERM"); + /* setuid: setting to arbitrary value must fail with -EPERM for non-root */ + TEST("setuid(other) returns expected status"); { - long rc = raw_syscall1(__NR_setuid, expected_id == 0 ? 1000 : 0); - if (rc == -1) /* -EPERM */ - PASS(); + pid_t pid = fork(); + if (pid == 0) { + long rc = raw_syscall1(__NR_setuid, expected_id == 0 ? 1000 : 0); + _exit(rc == 0 ? 0 : 1); + } + int status; + waitpid(pid, &status, 0); + if (expected_id == 0) + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "setuid(1000) failed for root"); else - FAIL("expected -EPERM"); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 1, + "expected -EPERM"); } /* setresuid: swap euid (no-op but must succeed) */ @@ -86,15 +95,23 @@ int main(void) } /* setresuid: set euid to a value not in {ruid, euid, suid} */ - TEST("setresuid(-1,other,-1) returns -EPERM"); + TEST("setresuid(-1,other,-1) returns expected status"); { - long rc = - raw_syscall3(__NR_setresuid, (long) (unsigned) -1, - expected_id == 0 ? 1000 : 42, (long) (unsigned) -1); - if (rc == -1) /* -EPERM */ - PASS(); + pid_t pid = fork(); + if (pid == 0) { + long rc = raw_syscall3(__NR_setresuid, (long) (unsigned) -1, + expected_id == 0 ? 1000 : 42, + (long) (unsigned) -1); + _exit(rc == 0 ? 0 : 1); + } + int status; + waitpid(pid, &status, 0); + if (expected_id == 0) + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "setresuid failed for root"); else - FAIL("expected -EPERM"); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 1, + "expected -EPERM"); } /* setreuid: ruid can only be set to current ruid or euid */ @@ -105,14 +122,22 @@ int main(void) EXPECT_TRUE(rc == 0, "setreuid(ruid,-1) failed"); } - TEST("setreuid(other,-1) returns -EPERM"); + TEST("setreuid(other,-1) returns expected status"); { - long rc = raw_syscall2(__NR_setreuid, expected_id == 0 ? 1000 : 42, - (long) (unsigned) -1); - if (rc == -1) /* -EPERM */ - PASS(); + pid_t pid = fork(); + if (pid == 0) { + long rc = raw_syscall2(__NR_setreuid, expected_id == 0 ? 1000 : 42, + (long) (unsigned) -1); + _exit(rc == 0 ? 0 : 1); + } + int status; + waitpid(pid, &status, 0); + if (expected_id == 0) + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "setreuid failed for root"); else - FAIL("expected -EPERM"); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 1, + "expected -EPERM"); } /* getresgid */ @@ -130,9 +155,22 @@ int main(void) TEST("setgid(expected) succeeds"); EXPECT_TRUE(raw_syscall1(__NR_setgid, expected_id) == 0, "setgid failed"); - TEST("setgid(other) returns -EPERM"); - EXPECT_TRUE(raw_syscall1(__NR_setgid, expected_id == 0 ? 1000 : 0) == -1, - "expected -EPERM"); + TEST("setgid(other) returns expected status"); + { + pid_t pid = fork(); + if (pid == 0) { + long rc = raw_syscall1(__NR_setgid, expected_id == 0 ? 1000 : 0); + _exit(rc == 0 ? 0 : 1); + } + int status; + waitpid(pid, &status, 0); + if (expected_id == 0) + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "setgid(1000) failed for root"); + else + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 1, + "expected -EPERM"); + } /* setfsuid / setfsgid: Linux contract is to return the previous fsuid / * fsgid. @@ -238,6 +276,42 @@ int main(void) EXPECT_TRUE(rc > 0 && (mask & 1), "CPU0 not in mask"); } + if (expected_id == 0) { + TEST("privileged setreuid(1000, -1) sets real ID"); + { + pid_t pid = fork(); + if (pid == 0) { + long rc = + raw_syscall2(__NR_setreuid, 1000, (long) (unsigned) -1); + _exit(rc == 0 ? 0 : 1); + } + int status; + waitpid(pid, &status, 0); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "setreuid(1000, -1) failed"); + } + + TEST("privileged setuid(1000) resets ruid/euid/suid"); + { + pid_t pid = fork(); + if (pid == 0) { + long rc = raw_syscall1(__NR_setuid, 1000); + if (rc != 0) + _exit(1); + unsigned int ruid = 0, euid = 0, suid = 0; + long grc = raw_syscall3(__NR_getresuid, (long) &ruid, + (long) &euid, (long) &suid); + if (grc == 0 && ruid == 1000 && euid == 1000 && suid == 1000) + _exit(0); + _exit(2); + } + int status; + waitpid(pid, &status, 0); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0, + "uid triple not fully updated"); + } + } + SUMMARY("test-credentials"); return fails ? 1 : 0; } diff --git a/tests/test-matrix.sh b/tests/test-matrix.sh index 18300bb8..3cb3877b 100755 --- a/tests/test-matrix.sh +++ b/tests/test-matrix.sh @@ -274,6 +274,7 @@ QEMU_SKIP=" test-mmap-hint test-msync test-credentials + test-credentials-fakeroot test-sched-policy test-rseq test-tier-a @@ -748,6 +749,7 @@ run_unit_tests() printf "\nCredential/identity emulation\n" test_rc "$runner" "test-credentials" 0 "$bindir/test-credentials" + test_rc "$runner" "test-credentials-fakeroot" 0 --fakeroot "$bindir/test-credentials" printf "\nScheduler policy stub\n" test_rc "$runner" "test-sched-policy" 0 "$bindir/test-sched-policy"