Index: sandbox/linux/services/credentials_unittest.cc |
diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc |
index 7c705a41043d294bbeb1e6b670b14798585d3b9a..355d4ab5e4a61043750247f103e9c3d46ecd1f93 100644 |
--- a/sandbox/linux/services/credentials_unittest.cc |
+++ b/sandbox/linux/services/credentials_unittest.cc |
@@ -4,6 +4,9 @@ |
#include "sandbox/linux/services/credentials.h" |
+#include <errno.h> |
+#include <unistd.h> |
+ |
#include "base/logging.h" |
#include "base/memory/scoped_ptr.h" |
#include "sandbox/linux/tests/unit_tests.h" |
@@ -11,6 +14,34 @@ |
namespace sandbox { |
+namespace { |
+ |
+bool DirectoryExists(const char* path) { |
+ struct stat dir; |
+ errno = 0; |
+ int ret = stat(path, &dir); |
+ return -1 != ret || ENOENT != errno; |
+} |
+ |
+bool WorkingDirectoryIsRoot() { |
+ char current_dir[PATH_MAX]; |
+ char* cwd = getcwd(current_dir, sizeof(current_dir)); |
+ PCHECK(cwd); |
+ if (strcmp("/", cwd)) return false; |
+ |
+ // The current directory is the root. Add a few paranoid checks. |
+ struct stat current; |
+ CHECK_EQ(0, stat(".", ¤t)); |
+ struct stat parrent; |
+ CHECK_EQ(0, stat("..", &parrent)); |
+ CHECK_EQ(current.st_dev, parrent.st_dev); |
+ CHECK_EQ(current.st_ino, parrent.st_ino); |
+ CHECK_EQ(current.st_mode, parrent.st_mode); |
+ CHECK_EQ(current.st_uid, parrent.st_uid); |
+ CHECK_EQ(current.st_gid, parrent.st_gid); |
+ return true; |
+} |
+ |
// Give dynamic tools a simple thing to test. |
TEST(Credentials, CreateAndDestroy) { |
{ |
@@ -22,15 +53,116 @@ TEST(Credentials, CreateAndDestroy) { |
SANDBOX_TEST(Credentials, DropAllCaps) { |
Credentials creds; |
- creds.DropAllCapabilities(); |
- SANDBOX_ASSERT(!creds.HasAnyCapability()); |
+ CHECK(creds.DropAllCapabilities()); |
+ CHECK(!creds.HasAnyCapability()); |
} |
SANDBOX_TEST(Credentials, GetCurrentCapString) { |
Credentials creds; |
- creds.DropAllCapabilities(); |
+ CHECK(creds.DropAllCapabilities()); |
const char kNoCapabilityText[] = "="; |
- SANDBOX_ASSERT(*creds.GetCurrentCapString() == kNoCapabilityText); |
+ CHECK(*creds.GetCurrentCapString() == kNoCapabilityText); |
+} |
+ |
+SANDBOX_TEST(Credentials, MoveToNewUserNS) { |
+ Credentials creds; |
+ creds.DropAllCapabilities(); |
+ bool userns_supported = creds.MoveToNewUserNS(); |
+ printf("Unprivileged CLONE_NEWUSER supported: %s\n", |
+ userns_supported ? "true." : "false."); |
+ if (!userns_supported) { |
+ printf("This kernel does not support unprivileged namespaces. " |
+ "USERNS tests will all pass.\n"); |
+ return; |
+ } |
+ CHECK(creds.HasAnyCapability()); |
+ creds.DropAllCapabilities(); |
+ CHECK(!creds.HasAnyCapability()); |
+} |
+ |
+SANDBOX_TEST(Credentials, UidIsPreserved) { |
+ Credentials creds; |
+ creds.DropAllCapabilities(); |
+ uid_t old_ruid, old_euid, old_suid; |
+ gid_t old_rgid, old_egid, old_sgid; |
+ PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid)); |
+ PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid)); |
+ // Probably missing kernel support. |
+ if (!creds.MoveToNewUserNS()) return; |
+ uid_t new_ruid, new_euid, new_suid; |
+ PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid)); |
+ CHECK(old_ruid == new_ruid); |
+ CHECK(old_euid == new_euid); |
+ CHECK(old_suid == new_suid); |
+ |
+ gid_t new_rgid, new_egid, new_sgid; |
+ PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid)); |
+ CHECK(old_rgid == new_rgid); |
+ CHECK(old_egid == new_egid); |
+ CHECK(old_sgid == new_sgid); |
+} |
+ |
+bool NewUserNSCycle(Credentials* creds) { |
+ DCHECK(creds); |
+ if (!creds->MoveToNewUserNS() || |
+ !creds->HasAnyCapability() || |
+ !creds->DropAllCapabilities() || |
+ creds->HasAnyCapability()) { |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+SANDBOX_TEST(Credentials, NestedUserNS) { |
+ Credentials creds; |
+ CHECK(creds.DropAllCapabilities()); |
+ // Probably missing kernel support. |
+ if (!creds.MoveToNewUserNS()) return; |
+ creds.DropAllCapabilities(); |
+ // As of 3.12, the kernel has a limit of 32. See create_user_ns(). |
+ const int kNestLevel = 10; |
+ for (int i = 0; i < kNestLevel; ++i) { |
+ CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration " |
+ << i << "."; |
+ } |
} |
+// Test the WorkingDirectoryIsRoot() helper. |
+TEST(Credentials, CanDetectRoot) { |
+ ASSERT_EQ(0, chdir("/proc/")); |
+ ASSERT_FALSE(WorkingDirectoryIsRoot()); |
+ ASSERT_EQ(0, chdir("/")); |
+ ASSERT_TRUE(WorkingDirectoryIsRoot()); |
+} |
+ |
+SANDBOX_TEST(Credentials, DropFileSystemAccessIsSafe) { |
+ Credentials creds; |
+ CHECK(creds.DropAllCapabilities()); |
+ // Probably missing kernel support. |
+ if (!creds.MoveToNewUserNS()) return; |
+ CHECK(creds.DropFileSystemAccess()); |
+ CHECK(!DirectoryExists("/proc")); |
+ CHECK(WorkingDirectoryIsRoot()); |
+ // We want the chroot to never have a subdirectory. A subdirectory |
+ // could allow a chroot escape. |
+ CHECK_NE(0, mkdir("/test", 0700)); |
+} |
+ |
+// Check that after dropping filesystem access and dropping privileges |
+// it is not possible to regain capabilities. |
+SANDBOX_TEST(Credentials, CannotRegainPrivileges) { |
+ Credentials creds; |
+ CHECK(creds.DropAllCapabilities()); |
+ // Probably missing kernel support. |
+ if (!creds.MoveToNewUserNS()) return; |
+ CHECK(creds.DropFileSystemAccess()); |
+ CHECK(creds.DropAllCapabilities()); |
+ |
+ // The kernel should now prevent us from regaining capabilities because we |
+ // are in a chroot. |
+ CHECK(!creds.MoveToNewUserNS()); |
+} |
+ |
+} // namespace. |
+ |
} // namespace sandbox. |