Home | History | Annotate | Download | only in services
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "sandbox/linux/services/credentials.h"
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <stdio.h>
     10 #include <sys/stat.h>
     11 #include <sys/types.h>
     12 #include <unistd.h>
     13 
     14 #include "base/files/file_util.h"
     15 #include "base/files/scoped_file.h"
     16 #include "base/logging.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "sandbox/linux/tests/unit_tests.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 namespace sandbox {
     22 
     23 namespace {
     24 
     25 bool DirectoryExists(const char* path) {
     26   struct stat dir;
     27   errno = 0;
     28   int ret = stat(path, &dir);
     29   return -1 != ret || ENOENT != errno;
     30 }
     31 
     32 bool WorkingDirectoryIsRoot() {
     33   char current_dir[PATH_MAX];
     34   char* cwd = getcwd(current_dir, sizeof(current_dir));
     35   PCHECK(cwd);
     36   if (strcmp("/", cwd)) return false;
     37 
     38   // The current directory is the root. Add a few paranoid checks.
     39   struct stat current;
     40   CHECK_EQ(0, stat(".", &current));
     41   struct stat parrent;
     42   CHECK_EQ(0, stat("..", &parrent));
     43   CHECK_EQ(current.st_dev, parrent.st_dev);
     44   CHECK_EQ(current.st_ino, parrent.st_ino);
     45   CHECK_EQ(current.st_mode, parrent.st_mode);
     46   CHECK_EQ(current.st_uid, parrent.st_uid);
     47   CHECK_EQ(current.st_gid, parrent.st_gid);
     48   return true;
     49 }
     50 
     51 // Give dynamic tools a simple thing to test.
     52 TEST(Credentials, CreateAndDestroy) {
     53   {
     54     Credentials cred1;
     55     (void) cred1;
     56   }
     57   scoped_ptr<Credentials> cred2(new Credentials);
     58 }
     59 
     60 TEST(Credentials, CountOpenFds) {
     61   base::ScopedFD proc_fd(open("/proc", O_RDONLY | O_DIRECTORY));
     62   ASSERT_TRUE(proc_fd.is_valid());
     63   Credentials creds;
     64   int fd_count = creds.CountOpenFds(proc_fd.get());
     65   int fd = open("/dev/null", O_RDONLY);
     66   ASSERT_LE(0, fd);
     67   EXPECT_EQ(fd_count + 1, creds.CountOpenFds(proc_fd.get()));
     68   ASSERT_EQ(0, IGNORE_EINTR(close(fd)));
     69   EXPECT_EQ(fd_count, creds.CountOpenFds(proc_fd.get()));
     70 }
     71 
     72 TEST(Credentials, HasOpenDirectory) {
     73   Credentials creds;
     74   // No open directory should exist at startup.
     75   EXPECT_FALSE(creds.HasOpenDirectory(-1));
     76   {
     77     // Have a "/dev" file descriptor around.
     78     int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
     79     base::ScopedFD dev_fd_closer(dev_fd);
     80     EXPECT_TRUE(creds.HasOpenDirectory(-1));
     81   }
     82   EXPECT_FALSE(creds.HasOpenDirectory(-1));
     83 }
     84 
     85 TEST(Credentials, HasOpenDirectoryWithFD) {
     86   Credentials creds;
     87 
     88   int proc_fd = open("/proc", O_RDONLY | O_DIRECTORY);
     89   base::ScopedFD proc_fd_closer(proc_fd);
     90   ASSERT_LE(0, proc_fd);
     91 
     92   // Don't pass |proc_fd|, an open directory (proc_fd) should
     93   // be detected.
     94   EXPECT_TRUE(creds.HasOpenDirectory(-1));
     95   // Pass |proc_fd| and no open directory should be detected.
     96   EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
     97 
     98   {
     99     // Have a "/dev" file descriptor around.
    100     int dev_fd = open("/dev", O_RDONLY | O_DIRECTORY);
    101     base::ScopedFD dev_fd_closer(dev_fd);
    102     EXPECT_TRUE(creds.HasOpenDirectory(proc_fd));
    103   }
    104 
    105   // The "/dev" file descriptor should now be closed, |proc_fd| is the only
    106   // directory file descriptor open.
    107   EXPECT_FALSE(creds.HasOpenDirectory(proc_fd));
    108 }
    109 
    110 SANDBOX_TEST(Credentials, DropAllCaps) {
    111   Credentials creds;
    112   CHECK(creds.DropAllCapabilities());
    113   CHECK(!creds.HasAnyCapability());
    114 }
    115 
    116 SANDBOX_TEST(Credentials, GetCurrentCapString) {
    117   Credentials creds;
    118   CHECK(creds.DropAllCapabilities());
    119   const char kNoCapabilityText[] = "=";
    120   CHECK(*creds.GetCurrentCapString() == kNoCapabilityText);
    121 }
    122 
    123 SANDBOX_TEST(Credentials, MoveToNewUserNS) {
    124   Credentials creds;
    125   creds.DropAllCapabilities();
    126   bool moved_to_new_ns = creds.MoveToNewUserNS();
    127   fprintf(stdout,
    128           "Unprivileged CLONE_NEWUSER supported: %s\n",
    129           moved_to_new_ns ? "true." : "false.");
    130   fflush(stdout);
    131   if (!moved_to_new_ns) {
    132     fprintf(stdout, "This kernel does not support unprivileged namespaces. "
    133             "USERNS tests will succeed without running.\n");
    134     fflush(stdout);
    135     return;
    136   }
    137   CHECK(creds.HasAnyCapability());
    138   creds.DropAllCapabilities();
    139   CHECK(!creds.HasAnyCapability());
    140 }
    141 
    142 SANDBOX_TEST(Credentials, SupportsUserNS) {
    143   Credentials creds;
    144   creds.DropAllCapabilities();
    145   bool user_ns_supported = Credentials::SupportsNewUserNS();
    146   bool moved_to_new_ns = creds.MoveToNewUserNS();
    147   CHECK_EQ(user_ns_supported, moved_to_new_ns);
    148 }
    149 
    150 SANDBOX_TEST(Credentials, UidIsPreserved) {
    151   Credentials creds;
    152   creds.DropAllCapabilities();
    153   uid_t old_ruid, old_euid, old_suid;
    154   gid_t old_rgid, old_egid, old_sgid;
    155   PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
    156   PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
    157   // Probably missing kernel support.
    158   if (!creds.MoveToNewUserNS()) return;
    159   uid_t new_ruid, new_euid, new_suid;
    160   PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
    161   CHECK(old_ruid == new_ruid);
    162   CHECK(old_euid == new_euid);
    163   CHECK(old_suid == new_suid);
    164 
    165   gid_t new_rgid, new_egid, new_sgid;
    166   PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid));
    167   CHECK(old_rgid == new_rgid);
    168   CHECK(old_egid == new_egid);
    169   CHECK(old_sgid == new_sgid);
    170 }
    171 
    172 bool NewUserNSCycle(Credentials* creds) {
    173   DCHECK(creds);
    174   if (!creds->MoveToNewUserNS() ||
    175       !creds->HasAnyCapability() ||
    176       !creds->DropAllCapabilities() ||
    177       creds->HasAnyCapability()) {
    178     return false;
    179   }
    180   return true;
    181 }
    182 
    183 SANDBOX_TEST(Credentials, NestedUserNS) {
    184   Credentials creds;
    185   CHECK(creds.DropAllCapabilities());
    186   // Probably missing kernel support.
    187   if (!creds.MoveToNewUserNS()) return;
    188   creds.DropAllCapabilities();
    189   // As of 3.12, the kernel has a limit of 32. See create_user_ns().
    190   const int kNestLevel = 10;
    191   for (int i = 0; i < kNestLevel; ++i) {
    192     CHECK(NewUserNSCycle(&creds)) << "Creating new user NS failed at iteration "
    193                                   << i << ".";
    194   }
    195 }
    196 
    197 // Test the WorkingDirectoryIsRoot() helper.
    198 TEST(Credentials, CanDetectRoot) {
    199   ASSERT_EQ(0, chdir("/proc/"));
    200   ASSERT_FALSE(WorkingDirectoryIsRoot());
    201   ASSERT_EQ(0, chdir("/"));
    202   ASSERT_TRUE(WorkingDirectoryIsRoot());
    203 }
    204 
    205 SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(DropFileSystemAccessIsSafe)) {
    206   Credentials creds;
    207   CHECK(creds.DropAllCapabilities());
    208   // Probably missing kernel support.
    209   if (!creds.MoveToNewUserNS()) return;
    210   CHECK(creds.DropFileSystemAccess());
    211   CHECK(!DirectoryExists("/proc"));
    212   CHECK(WorkingDirectoryIsRoot());
    213   // We want the chroot to never have a subdirectory. A subdirectory
    214   // could allow a chroot escape.
    215   CHECK_NE(0, mkdir("/test", 0700));
    216 }
    217 
    218 // Check that after dropping filesystem access and dropping privileges
    219 // it is not possible to regain capabilities.
    220 SANDBOX_TEST(Credentials, DISABLE_ON_LSAN(CannotRegainPrivileges)) {
    221   Credentials creds;
    222   CHECK(creds.DropAllCapabilities());
    223   // Probably missing kernel support.
    224   if (!creds.MoveToNewUserNS()) return;
    225   CHECK(creds.DropFileSystemAccess());
    226   CHECK(creds.DropAllCapabilities());
    227 
    228   // The kernel should now prevent us from regaining capabilities because we
    229   // are in a chroot.
    230   CHECK(!Credentials::SupportsNewUserNS());
    231   CHECK(!creds.MoveToNewUserNS());
    232 }
    233 
    234 }  // namespace.
    235 
    236 }  // namespace sandbox.
    237