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 <limits.h>
     10 #include <pthread.h>
     11 #include <signal.h>
     12 #include <stdio.h>
     13 #include <sys/capability.h>
     14 #include <sys/stat.h>
     15 #include <sys/types.h>
     16 #include <unistd.h>
     17 
     18 #include <vector>
     19 
     20 #include "base/files/file_path.h"
     21 #include "base/files/file_util.h"
     22 #include "base/files/scoped_file.h"
     23 #include "base/logging.h"
     24 #include "base/memory/scoped_ptr.h"
     25 #include "sandbox/linux/services/proc_util.h"
     26 #include "sandbox/linux/services/syscall_wrappers.h"
     27 #include "sandbox/linux/system_headers/capability.h"
     28 #include "sandbox/linux/tests/unit_tests.h"
     29 #include "testing/gtest/include/gtest/gtest.h"
     30 
     31 namespace sandbox {
     32 
     33 namespace {
     34 
     35 struct CapFreeDeleter {
     36   inline void operator()(cap_t cap) const {
     37     int ret = cap_free(cap);
     38     CHECK_EQ(0, ret);
     39   }
     40 };
     41 
     42 // Wrapper to manage libcap2's cap_t type.
     43 typedef scoped_ptr<typeof(*((cap_t)0)), CapFreeDeleter> ScopedCap;
     44 
     45 bool WorkingDirectoryIsRoot() {
     46   char current_dir[PATH_MAX];
     47   char* cwd = getcwd(current_dir, sizeof(current_dir));
     48   PCHECK(cwd);
     49   if (strcmp("/", cwd)) return false;
     50 
     51   // The current directory is the root. Add a few paranoid checks.
     52   struct stat current;
     53   CHECK_EQ(0, stat(".", &current));
     54   struct stat parrent;
     55   CHECK_EQ(0, stat("..", &parrent));
     56   CHECK_EQ(current.st_dev, parrent.st_dev);
     57   CHECK_EQ(current.st_ino, parrent.st_ino);
     58   CHECK_EQ(current.st_mode, parrent.st_mode);
     59   CHECK_EQ(current.st_uid, parrent.st_uid);
     60   CHECK_EQ(current.st_gid, parrent.st_gid);
     61   return true;
     62 }
     63 
     64 SANDBOX_TEST(Credentials, DropAllCaps) {
     65   CHECK(Credentials::DropAllCapabilities());
     66   CHECK(!Credentials::HasAnyCapability());
     67 }
     68 
     69 SANDBOX_TEST(Credentials, MoveToNewUserNS) {
     70   CHECK(Credentials::DropAllCapabilities());
     71   bool moved_to_new_ns = Credentials::MoveToNewUserNS();
     72   fprintf(stdout,
     73           "Unprivileged CLONE_NEWUSER supported: %s\n",
     74           moved_to_new_ns ? "true." : "false.");
     75   fflush(stdout);
     76   if (!moved_to_new_ns) {
     77     fprintf(stdout, "This kernel does not support unprivileged namespaces. "
     78             "USERNS tests will succeed without running.\n");
     79     fflush(stdout);
     80     return;
     81   }
     82   CHECK(Credentials::HasAnyCapability());
     83   CHECK(Credentials::DropAllCapabilities());
     84   CHECK(!Credentials::HasAnyCapability());
     85 }
     86 
     87 SANDBOX_TEST(Credentials, CanCreateProcessInNewUserNS) {
     88   CHECK(Credentials::DropAllCapabilities());
     89   bool user_ns_supported = Credentials::CanCreateProcessInNewUserNS();
     90   bool moved_to_new_ns = Credentials::MoveToNewUserNS();
     91   CHECK_EQ(user_ns_supported, moved_to_new_ns);
     92 }
     93 
     94 SANDBOX_TEST(Credentials, UidIsPreserved) {
     95   CHECK(Credentials::DropAllCapabilities());
     96   uid_t old_ruid, old_euid, old_suid;
     97   gid_t old_rgid, old_egid, old_sgid;
     98   PCHECK(0 == getresuid(&old_ruid, &old_euid, &old_suid));
     99   PCHECK(0 == getresgid(&old_rgid, &old_egid, &old_sgid));
    100   // Probably missing kernel support.
    101   if (!Credentials::MoveToNewUserNS()) return;
    102   uid_t new_ruid, new_euid, new_suid;
    103   PCHECK(0 == getresuid(&new_ruid, &new_euid, &new_suid));
    104   CHECK(old_ruid == new_ruid);
    105   CHECK(old_euid == new_euid);
    106   CHECK(old_suid == new_suid);
    107 
    108   gid_t new_rgid, new_egid, new_sgid;
    109   PCHECK(0 == getresgid(&new_rgid, &new_egid, &new_sgid));
    110   CHECK(old_rgid == new_rgid);
    111   CHECK(old_egid == new_egid);
    112   CHECK(old_sgid == new_sgid);
    113 }
    114 
    115 bool NewUserNSCycle() {
    116   if (!Credentials::MoveToNewUserNS() ||
    117       !Credentials::HasAnyCapability() ||
    118       !Credentials::DropAllCapabilities() ||
    119       Credentials::HasAnyCapability()) {
    120     return false;
    121   }
    122   return true;
    123 }
    124 
    125 SANDBOX_TEST(Credentials, NestedUserNS) {
    126   CHECK(Credentials::DropAllCapabilities());
    127   // Probably missing kernel support.
    128   if (!Credentials::MoveToNewUserNS()) return;
    129   CHECK(Credentials::DropAllCapabilities());
    130   // As of 3.12, the kernel has a limit of 32. See create_user_ns().
    131   const int kNestLevel = 10;
    132   for (int i = 0; i < kNestLevel; ++i) {
    133     CHECK(NewUserNSCycle()) << "Creating new user NS failed at iteration "
    134                                   << i << ".";
    135   }
    136 }
    137 
    138 // Test the WorkingDirectoryIsRoot() helper.
    139 SANDBOX_TEST(Credentials, CanDetectRoot) {
    140   PCHECK(0 == chdir("/proc/"));
    141   CHECK(!WorkingDirectoryIsRoot());
    142   PCHECK(0 == chdir("/"));
    143   CHECK(WorkingDirectoryIsRoot());
    144 }
    145 
    146 // Disabled on ASAN because of crbug.com/451603.
    147 SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(DropFileSystemAccessIsSafe)) {
    148   CHECK(Credentials::DropAllCapabilities());
    149   // Probably missing kernel support.
    150   if (!Credentials::MoveToNewUserNS()) return;
    151   CHECK(Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
    152   CHECK(!base::DirectoryExists(base::FilePath("/proc")));
    153   CHECK(WorkingDirectoryIsRoot());
    154   CHECK(base::IsDirectoryEmpty(base::FilePath("/")));
    155   // We want the chroot to never have a subdirectory. A subdirectory
    156   // could allow a chroot escape.
    157   CHECK_NE(0, mkdir("/test", 0700));
    158 }
    159 
    160 // Check that after dropping filesystem access and dropping privileges
    161 // it is not possible to regain capabilities.
    162 SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(CannotRegainPrivileges)) {
    163   base::ScopedFD proc_fd(ProcUtil::OpenProc());
    164   CHECK(Credentials::DropAllCapabilities(proc_fd.get()));
    165   // Probably missing kernel support.
    166   if (!Credentials::MoveToNewUserNS()) return;
    167   CHECK(Credentials::DropFileSystemAccess(proc_fd.get()));
    168   CHECK(Credentials::DropAllCapabilities(proc_fd.get()));
    169 
    170   // The kernel should now prevent us from regaining capabilities because we
    171   // are in a chroot.
    172   CHECK(!Credentials::CanCreateProcessInNewUserNS());
    173   CHECK(!Credentials::MoveToNewUserNS());
    174 }
    175 
    176 SANDBOX_TEST(Credentials, SetCapabilities) {
    177   // Probably missing kernel support.
    178   if (!Credentials::MoveToNewUserNS())
    179     return;
    180 
    181   base::ScopedFD proc_fd(ProcUtil::OpenProc());
    182 
    183   CHECK(Credentials::HasCapability(Credentials::Capability::SYS_ADMIN));
    184   CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
    185 
    186   std::vector<Credentials::Capability> caps;
    187   caps.push_back(Credentials::Capability::SYS_CHROOT);
    188   CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
    189 
    190   CHECK(!Credentials::HasCapability(Credentials::Capability::SYS_ADMIN));
    191   CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
    192 
    193   const std::vector<Credentials::Capability> no_caps;
    194   CHECK(Credentials::SetCapabilities(proc_fd.get(), no_caps));
    195   CHECK(!Credentials::HasAnyCapability());
    196 }
    197 
    198 SANDBOX_TEST(Credentials, SetCapabilitiesAndChroot) {
    199   // Probably missing kernel support.
    200   if (!Credentials::MoveToNewUserNS())
    201     return;
    202 
    203   base::ScopedFD proc_fd(ProcUtil::OpenProc());
    204 
    205   CHECK(Credentials::HasCapability(Credentials::Capability::SYS_CHROOT));
    206   PCHECK(chroot("/") == 0);
    207 
    208   std::vector<Credentials::Capability> caps;
    209   caps.push_back(Credentials::Capability::SYS_CHROOT);
    210   CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
    211   PCHECK(chroot("/") == 0);
    212 
    213   CHECK(Credentials::DropAllCapabilities());
    214   PCHECK(chroot("/") == -1 && errno == EPERM);
    215 }
    216 
    217 SANDBOX_TEST(Credentials, SetCapabilitiesMatchesLibCap2) {
    218   // Probably missing kernel support.
    219   if (!Credentials::MoveToNewUserNS())
    220     return;
    221 
    222   base::ScopedFD proc_fd(ProcUtil::OpenProc());
    223 
    224   std::vector<Credentials::Capability> caps;
    225   caps.push_back(Credentials::Capability::SYS_CHROOT);
    226   CHECK(Credentials::SetCapabilities(proc_fd.get(), caps));
    227 
    228   ScopedCap actual_cap(cap_get_proc());
    229   PCHECK(actual_cap != nullptr);
    230 
    231   ScopedCap expected_cap(cap_init());
    232   PCHECK(expected_cap != nullptr);
    233 
    234   const cap_value_t allowed_cap = CAP_SYS_CHROOT;
    235   for (const cap_flag_t flag : {CAP_EFFECTIVE, CAP_PERMITTED}) {
    236     PCHECK(cap_set_flag(expected_cap.get(), flag, 1, &allowed_cap, CAP_SET) ==
    237            0);
    238   }
    239 
    240   CHECK_EQ(0, cap_compare(expected_cap.get(), actual_cap.get()));
    241 }
    242 
    243 volatile sig_atomic_t signal_handler_called;
    244 void SignalHandler(int sig) {
    245   signal_handler_called = 1;
    246 }
    247 
    248 // Disabled on ASAN because of crbug.com/451603.
    249 SANDBOX_TEST(Credentials, DISABLE_ON_ASAN(DropFileSystemAccessPreservesTLS)) {
    250   // Probably missing kernel support.
    251   if (!Credentials::MoveToNewUserNS()) return;
    252   CHECK(Credentials::DropFileSystemAccess(ProcUtil::OpenProc().get()));
    253 
    254   // In glibc, pthread_getattr_np makes an assertion about the cached PID/TID in
    255   // TLS.
    256   pthread_attr_t attr;
    257   EXPECT_EQ(0, pthread_getattr_np(pthread_self(), &attr));
    258 
    259   // raise also uses the cached TID in glibc.
    260   struct sigaction action = {};
    261   action.sa_handler = &SignalHandler;
    262   PCHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
    263 
    264   PCHECK(raise(SIGUSR1) == 0);
    265   CHECK_EQ(1, signal_handler_called);
    266 }
    267 
    268 }  // namespace.
    269 
    270 }  // namespace sandbox.
    271