• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory>
19 #include <vector>
20 
21 #include "base/files/file_path.h"
22 #include "base/files/file_util.h"
23 #include "base/files/scoped_file.h"
24 #include "base/logging.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 {
operator ()sandbox::__anon5c4d18650111::CapFreeDeleter36   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 std::unique_ptr<typeof(*((cap_t)0)), CapFreeDeleter> ScopedCap;
44 
WorkingDirectoryIsRoot()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 
SANDBOX_TEST(Credentials,DropAllCaps)64 SANDBOX_TEST(Credentials, DropAllCaps) {
65   CHECK(Credentials::DropAllCapabilities());
66   CHECK(!Credentials::HasAnyCapability());
67 }
68 
SANDBOX_TEST(Credentials,MoveToNewUserNS)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 
SANDBOX_TEST(Credentials,CanCreateProcessInNewUserNS)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 
SANDBOX_TEST(Credentials,UidIsPreserved)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 
NewUserNSCycle()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 
SANDBOX_TEST(Credentials,NestedUserNS)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.
SANDBOX_TEST(Credentials,CanDetectRoot)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.
SANDBOX_TEST(Credentials,DISABLE_ON_ASAN (DropFileSystemAccessIsSafe))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.
SANDBOX_TEST(Credentials,DISABLE_ON_ASAN (CannotRegainPrivileges))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 
SANDBOX_TEST(Credentials,SetCapabilities)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 
SANDBOX_TEST(Credentials,SetCapabilitiesAndChroot)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 
SANDBOX_TEST(Credentials,SetCapabilitiesMatchesLibCap2)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;
SignalHandler(int sig)244 void SignalHandler(int sig) {
245   signal_handler_called = 1;
246 }
247 
248 // Disabled on ASAN because of crbug.com/451603.
SANDBOX_TEST(Credentials,DISABLE_ON_ASAN (DropFileSystemAccessPreservesTLS))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