| 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.
|
|
|