From 16f934474f8b10fcd26cd707b643aaa03dddd8f8 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 19 Dec 2022 19:32:31 +0100 Subject: [PATCH] Kernel+Tests: Allow deleting someone else's file in my sticky directory This should be allowed according to Dr. POSIX. :^) --- Kernel/FileSystem/VirtualFileSystem.cpp | 7 +++- Tests/LibC/TestIo.cpp | 44 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/Kernel/FileSystem/VirtualFileSystem.cpp b/Kernel/FileSystem/VirtualFileSystem.cpp index fe0b827db2..98621c85ec 100644 --- a/Kernel/FileSystem/VirtualFileSystem.cpp +++ b/Kernel/FileSystem/VirtualFileSystem.cpp @@ -868,8 +868,13 @@ ErrorOr VirtualFileSystem::rmdir(Credentials const& credentials, StringVie return EACCES; if (parent_metadata.is_sticky()) { - if (!credentials.is_superuser() && inode.metadata().uid != credentials.euid()) + // [EACCES] The S_ISVTX flag is set on the directory containing the file referred to by the path argument + // and the process does not satisfy the criteria specified in XBD Directory Protection. + if (!credentials.is_superuser() + && inode.metadata().uid != credentials.euid() + && parent_metadata.uid != credentials.euid()) { return EACCES; + } } size_t child_count = 0; diff --git a/Tests/LibC/TestIo.cpp b/Tests/LibC/TestIo.cpp index 5b1bb1b6db..e121876d73 100644 --- a/Tests/LibC/TestIo.cpp +++ b/Tests/LibC/TestIo.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #define EXPECT_ERROR_2(err, syscall, arg1, arg2) \ @@ -356,6 +357,49 @@ TEST_CASE(rmdir_dot_dot) EXPECT_EQ(rc, 0); } +TEST_CASE(rmdir_someone_elses_directory_in_my_sticky_directory) +{ + // NOTE: This test only works when run as root, since it has to chown a directory to someone else. + if (getuid() != 0) + return; + + // Create /tmp/sticky-dir a sticky directory owned by 12345:12345 + // Then, create /tmp/sticky-dir/notmine, a normal directory owned by 23456:23456 + // Then, fork and seteuid to 12345, and try to rmdir the "notmine" directory. This should succeed. + // In the parent, waitpid on the child, and finally rmdir /tmp/sticky-dir + + int rc = mkdir("/tmp/sticky-dir", 01777); + EXPECT_EQ(rc, 0); + + rc = chown("/tmp/sticky-dir", 12345, 12345); + EXPECT_EQ(rc, 0); + + rc = mkdir("/tmp/sticky-dir/notmine", 0700); + EXPECT_EQ(rc, 0); + + rc = chown("/tmp/sticky-dir/notmine", 23456, 23456); + EXPECT_EQ(rc, 0); + + int pid = fork(); + EXPECT(pid >= 0); + + if (pid == 0) { + // We are in the child. + rc = seteuid(12345); + EXPECT_EQ(rc, 0); + + rc = rmdir("/tmp/sticky-dir/notmine"); + EXPECT_EQ(rc, 0); + _exit(0); + } + + int status = 0; + waitpid(pid, &status, 0); + + rc = rmdir("/tmp/sticky-dir"); + EXPECT_EQ(rc, 0); +} + TEST_CASE(rmdir_while_inside_dir) { int rc = mkdir("/home/anon/testdir", 0700);