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