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