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(".", ¤t)); 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