| Index: sandbox/linux/services/credentials_unittest.cc
|
| diff --git a/sandbox/linux/services/credentials_unittest.cc b/sandbox/linux/services/credentials_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..28199fb30a5294526a622ee4a7bb58a845b4f1cf
|
| --- /dev/null
|
| +++ b/sandbox/linux/services/credentials_unittest.cc
|
| @@ -0,0 +1,236 @@
|
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "sandbox/linux/services/credentials.h"
|
| +
|
| +#include <errno.h>
|
| +#include <fcntl.h>
|
| +#include <stdio.h>
|
| +#include <sys/stat.h>
|
| +#include <sys/types.h>
|
| +#include <unistd.h>
|
| +
|
| +#include "base/files/file_util.h"
|
| +#include "base/files/scoped_file.h"
|
| +#include "base/logging.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "sandbox/linux/tests/unit_tests.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +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) {
|
| + {
|
| + Credentials cred1;
|
| + (void) cred1;
|
| + }
|
| + scoped_ptr<Credentials> cred2(new Credentials);
|
| +}
|
| +
|
| +TEST(Credentials, CountOpenFds) {
|
| + base::ScopedFD proc_fd(open("/proc", O_RDONLY | O_DIRECTORY));
|
| + ASSERT_TRUE(proc_fd.is_valid());
|
| + Credentials creds;
|
| + int fd_count = creds.CountOpenFds(proc_fd.get());
|
| + int fd = open("/dev/null", O_RDONLY);
|
| + ASSERT_LE(0, fd);
|
| + EXPECT_EQ(fd_count + 1, creds.CountOpenFds(proc_fd.get()));
|
| + ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
|
| + EXPECT_EQ(fd_count, creds.CountOpenFds(proc_fd.get()));
|
| +}
|
| +
|
| +TEST(Credentials, HasOpenDirectory) {
|
| + Credentials creds;
|
| + // No open directory should exist at startup.
|
| + EXPECT_FALSE(creds.HasOpenDirectory(-1));
|
| + {
|
| + // Have a "/dev" file descriptor around.
|
| + int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
|
| + base::ScopedFD dev_fd_closer(dev_fd);
|
| + EXPECT_TRUE(creds.HasOpenDirectory(-1));
|
| + }
|
| + EXPECT_FALSE(creds.HasOpenDirectory(-1));
|
| +}
|
| +
|
| +TEST(Credentials, HasOpenDirectoryWithFD) {
|
| + Credentials creds;
|
| +
|
| + int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
|
| + base::ScopedFD proc_fd_closer(proc_fd);
|
| + ASSERT_LE(0, proc_fd);
|
| +
|
| + // Don't pass |proc_fd|, an open directory (proc_fd) should
|
| + // be detected.
|
| + EXPECT_TRUE(creds.HasOpenDirectory(-1));
|
| + // Pass |proc_fd| and no open directory should be detected.
|
| + EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
|
| +
|
| + {
|
| + // Have a "/dev" file descriptor around.
|
| + int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
|
| + base::ScopedFD dev_fd_closer(dev_fd);
|
| + EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
|
| + }
|
| +
|
| + // The "/dev" file descriptor should now be closed, |proc_fd| is the only
|
| + // directory file descriptor open.
|
| + EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
|
| +}
|
| +
|
| +SANDBOX_TEST(Credentials, DropAllCaps) {
|
| + Credentials creds;
|
| + CHECK(creds.DropAllCapabilities());
|
| + CHECK(!creds.HasAnyCapability());
|
| +}
|
| +
|
| +SANDBOX_TEST(Credentials, GetCurrentCapString) {
|
| + Credentials creds;
|
| + CHECK(creds.DropAllCapabilities());
|
| + const char kNoCapabilityText[] = "=";
|
| + CHECK(*creds.GetCurrentCapString() == kNoCapabilityText);
|
| +}
|
| +
|
| +SANDBOX_TEST(Credentials, MoveToNewUserNS) {
|
| + Credentials creds;
|
| + creds.DropAllCapabilities();
|
| + bool moved_to_new_ns = creds.MoveToNewUserNS();
|
| + fprintf(stdout,
|
| + "Unprivileged CLONE_NEWUSER supported: %s\n",
|
| + moved_to_new_ns ? "true." : "false.");
|
| + fflush(stdout);
|
| + if (!moved_to_new_ns) {
|
| + fprintf(stdout, "This kernel does not support unprivileged namespaces. "
|
| + "USERNS tests will succeed without running.\n");
|
| + fflush(stdout);
|
| + return;
|
| + }
|
| + CHECK(creds.HasAnyCapability());
|
| + creds.DropAllCapabilities();
|
| + CHECK(!creds.HasAnyCapability());
|
| +}
|
| +
|
| +SANDBOX_TEST(Credentials, SupportsUserNS) {
|
| + Credentials creds;
|
| + creds.DropAllCapabilities();
|
| + bool user_ns_supported = Credentials::SupportsNewUserNS();
|
| + bool moved_to_new_ns = creds.MoveToNewUserNS();
|
| + CHECK_EQ(user_ns_supported, moved_to_new_ns);
|
| +}
|
| +
|
| +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, DISABLE_ON_LSAN(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, DISABLE_ON_LSAN(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(!Credentials::SupportsNewUserNS());
|
| + CHECK(!creds.MoveToNewUserNS());
|
| +}
|
| +
|
| +} // namespace.
|
| +
|
| +} // namespace sandbox.
|
|
|