| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sandbox/linux/services/credentials.h" | 5 #include "sandbox/linux/services/credentials.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <signal.h> | 8 #include <signal.h> |
| 9 #include <stdint.h> |
| 9 #include <stdio.h> | 10 #include <stdio.h> |
| 10 #include <sys/syscall.h> | 11 #include <sys/syscall.h> |
| 11 #include <sys/types.h> | 12 #include <sys/types.h> |
| 12 #include <sys/wait.h> | 13 #include <sys/wait.h> |
| 13 #include <unistd.h> | 14 #include <unistd.h> |
| 14 | 15 |
| 15 #include "base/basictypes.h" | |
| 16 #include "base/bind.h" | 16 #include "base/bind.h" |
| 17 #include "base/files/file_path.h" | 17 #include "base/files/file_path.h" |
| 18 #include "base/files/file_util.h" | 18 #include "base/files/file_util.h" |
| 19 #include "base/logging.h" | 19 #include "base/logging.h" |
| 20 #include "base/posix/eintr_wrapper.h" | 20 #include "base/posix/eintr_wrapper.h" |
| 21 #include "base/process/launch.h" | 21 #include "base/process/launch.h" |
| 22 #include "base/template_util.h" | 22 #include "base/template_util.h" |
| 23 #include "base/third_party/valgrind/valgrind.h" | 23 #include "base/third_party/valgrind/valgrind.h" |
| 24 #include "build/build_config.h" | 24 #include "build/build_config.h" |
| 25 #include "sandbox/linux/services/namespace_utils.h" | 25 #include "sandbox/linux/services/namespace_utils.h" |
| 26 #include "sandbox/linux/services/proc_util.h" | 26 #include "sandbox/linux/services/proc_util.h" |
| 27 #include "sandbox/linux/services/syscall_wrappers.h" | 27 #include "sandbox/linux/services/syscall_wrappers.h" |
| 28 #include "sandbox/linux/services/thread_helpers.h" | 28 #include "sandbox/linux/services/thread_helpers.h" |
| 29 #include "sandbox/linux/system_headers/capability.h" | 29 #include "sandbox/linux/system_headers/capability.h" |
| 30 #include "sandbox/linux/system_headers/linux_signal.h" |
| 30 | 31 |
| 31 namespace sandbox { | 32 namespace sandbox { |
| 32 | 33 |
| 33 namespace { | 34 namespace { |
| 34 | 35 |
| 35 bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; } | 36 bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; } |
| 36 | 37 |
| 37 // Checks that the set of RES-uids and the set of RES-gids have | 38 // Checks that the set of RES-uids and the set of RES-gids have |
| 38 // one element each and return that element in |resuid| and |resgid| | 39 // one element each and return that element in |resuid| and |resgid| |
| 39 // respectively. It's ok to pass NULL as one or both of the ids. | 40 // respectively. It's ok to pass NULL as one or both of the ids. |
| 40 bool GetRESIds(uid_t* resuid, gid_t* resgid) { | 41 bool GetRESIds(uid_t* resuid, gid_t* resgid) { |
| 41 uid_t ruid, euid, suid; | 42 uid_t ruid, euid, suid; |
| 42 gid_t rgid, egid, sgid; | 43 gid_t rgid, egid, sgid; |
| 43 PCHECK(getresuid(&ruid, &euid, &suid) == 0); | 44 PCHECK(sys_getresuid(&ruid, &euid, &suid) == 0); |
| 44 PCHECK(getresgid(&rgid, &egid, &sgid) == 0); | 45 PCHECK(sys_getresgid(&rgid, &egid, &sgid) == 0); |
| 45 const bool uids_are_equal = (ruid == euid) && (ruid == suid); | 46 const bool uids_are_equal = (ruid == euid) && (ruid == suid); |
| 46 const bool gids_are_equal = (rgid == egid) && (rgid == sgid); | 47 const bool gids_are_equal = (rgid == egid) && (rgid == sgid); |
| 47 if (!uids_are_equal || !gids_are_equal) return false; | 48 if (!uids_are_equal || !gids_are_equal) return false; |
| 48 if (resuid) *resuid = euid; | 49 if (resuid) *resuid = euid; |
| 49 if (resgid) *resgid = egid; | 50 if (resgid) *resgid = egid; |
| 50 return true; | 51 return true; |
| 51 } | 52 } |
| 52 | 53 |
| 53 const int kExitSuccess = 0; | 54 const int kExitSuccess = 0; |
| 54 | 55 |
| 55 int ChrootToSelfFdinfo(void*) { | 56 int ChrootToSelfFdinfo(void*) { |
| 56 RAW_CHECK(chroot("/proc/self/fdinfo/") == 0); | 57 RAW_CHECK(sys_chroot("/proc/self/fdinfo/") == 0); |
| 57 | 58 |
| 58 // CWD is essentially an implicit file descriptor, so be careful to not | 59 // CWD is essentially an implicit file descriptor, so be careful to not |
| 59 // leave it behind. | 60 // leave it behind. |
| 60 RAW_CHECK(chdir("/") == 0); | 61 RAW_CHECK(chdir("/") == 0); |
| 61 _exit(kExitSuccess); | 62 _exit(kExitSuccess); |
| 62 } | 63 } |
| 63 | 64 |
| 64 // chroot() to an empty dir that is "safe". To be safe, it must not contain | 65 // chroot() to an empty dir that is "safe". To be safe, it must not contain |
| 65 // any subdirectory (chroot-ing there would allow a chroot escape) and it must | 66 // any subdirectory (chroot-ing there would allow a chroot escape) and it must |
| 66 // be impossible to create an empty directory there. | 67 // be impossible to create an empty directory there. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 82 // PID namespace). With a process, we can just use /proc/self. | 83 // PID namespace). With a process, we can just use /proc/self. |
| 83 pid_t pid = -1; | 84 pid_t pid = -1; |
| 84 char stack_buf[PTHREAD_STACK_MIN]; | 85 char stack_buf[PTHREAD_STACK_MIN]; |
| 85 #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \ | 86 #if defined(ARCH_CPU_X86_FAMILY) || defined(ARCH_CPU_ARM_FAMILY) || \ |
| 86 defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY) | 87 defined(ARCH_CPU_MIPS64_FAMILY) || defined(ARCH_CPU_MIPS_FAMILY) |
| 87 // The stack grows downward. | 88 // The stack grows downward. |
| 88 void* stack = stack_buf + sizeof(stack_buf); | 89 void* stack = stack_buf + sizeof(stack_buf); |
| 89 #else | 90 #else |
| 90 #error "Unsupported architecture" | 91 #error "Unsupported architecture" |
| 91 #endif | 92 #endif |
| 93 |
| 92 pid = clone(ChrootToSelfFdinfo, stack, | 94 pid = clone(ChrootToSelfFdinfo, stack, |
| 93 CLONE_VM | CLONE_VFORK | CLONE_FS | SIGCHLD, nullptr, nullptr, | 95 CLONE_VM | CLONE_VFORK | CLONE_FS | LINUX_SIGCHLD, nullptr, |
| 94 nullptr, nullptr); | 96 nullptr, nullptr, nullptr); |
| 95 PCHECK(pid != -1); | 97 PCHECK(pid != -1); |
| 96 | 98 |
| 97 int status = -1; | 99 int status = -1; |
| 98 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid); | 100 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid); |
| 99 | 101 |
| 100 return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess; | 102 return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess; |
| 101 } | 103 } |
| 102 | 104 |
| 103 // CHECK() that an attempt to move to a new user namespace raised an expected | 105 // CHECK() that an attempt to move to a new user namespace raised an expected |
| 104 // errno. | 106 // errno. |
| 105 void CheckCloneNewUserErrno(int error) { | 107 void CheckCloneNewUserErrno(int error) { |
| 106 // EPERM can happen if already in a chroot. EUSERS if too many nested | 108 // EPERM can happen if already in a chroot. EUSERS if too many nested |
| 107 // namespaces are used. EINVAL for kernels that don't support the feature. | 109 // namespaces are used. EINVAL for kernels that don't support the feature. |
| 108 // Valgrind will ENOSYS unshare(). | 110 // Valgrind will ENOSYS unshare(). |
| 109 PCHECK(error == EPERM || error == EUSERS || error == EINVAL || | 111 PCHECK(error == EPERM || error == EUSERS || error == EINVAL || |
| 110 error == ENOSYS); | 112 error == ENOSYS); |
| 111 } | 113 } |
| 112 | 114 |
| 113 // Converts a LinuxCapability to the corresponding Linux CAP_XXX value. | 115 // Converts a Capability to the corresponding Linux CAP_XXX value. |
| 114 int LinuxCapabilityToKernelValue(LinuxCapability cap) { | 116 int CapabilityToKernelValue(Credentials::Capability cap) { |
| 115 switch (cap) { | 117 switch (cap) { |
| 116 case LinuxCapability::kCapSysChroot: | 118 case Credentials::Capability::SYS_CHROOT: |
| 117 return CAP_SYS_CHROOT; | 119 return CAP_SYS_CHROOT; |
| 118 case LinuxCapability::kCapSysAdmin: | 120 case Credentials::Capability::SYS_ADMIN: |
| 119 return CAP_SYS_ADMIN; | 121 return CAP_SYS_ADMIN; |
| 120 } | 122 } |
| 121 | 123 |
| 122 LOG(FATAL) << "Invalid LinuxCapability: " << static_cast<int>(cap); | 124 LOG(FATAL) << "Invalid Capability: " << static_cast<int>(cap); |
| 123 return 0; | 125 return 0; |
| 124 } | 126 } |
| 125 | 127 |
| 126 } // namespace. | 128 } // namespace. |
| 127 | 129 |
| 130 // static |
| 128 bool Credentials::DropAllCapabilities(int proc_fd) { | 131 bool Credentials::DropAllCapabilities(int proc_fd) { |
| 129 if (!SetCapabilities(proc_fd, std::vector<LinuxCapability>())) { | 132 if (!SetCapabilities(proc_fd, std::vector<Capability>())) { |
| 130 return false; | 133 return false; |
| 131 } | 134 } |
| 132 | 135 |
| 133 CHECK(!HasAnyCapability()); | 136 CHECK(!HasAnyCapability()); |
| 134 return true; | 137 return true; |
| 135 } | 138 } |
| 136 | 139 |
| 140 // static |
| 137 bool Credentials::DropAllCapabilities() { | 141 bool Credentials::DropAllCapabilities() { |
| 138 base::ScopedFD proc_fd(ProcUtil::OpenProc()); | 142 base::ScopedFD proc_fd(ProcUtil::OpenProc()); |
| 139 return Credentials::DropAllCapabilities(proc_fd.get()); | 143 return Credentials::DropAllCapabilities(proc_fd.get()); |
| 140 } | 144 } |
| 141 | 145 |
| 142 // static | 146 // static |
| 143 bool Credentials::SetCapabilities(int proc_fd, | 147 bool Credentials::DropAllCapabilitiesOnCurrentThread() { |
| 144 const std::vector<LinuxCapability>& caps) { | 148 return SetCapabilitiesOnCurrentThread(std::vector<Capability>()); |
| 145 DCHECK_LE(0, proc_fd); | 149 } |
| 146 | 150 |
| 147 #if !defined(THREAD_SANITIZER) | 151 // static |
| 148 // With TSAN, accept to break the security model as it is a testing | 152 bool Credentials::SetCapabilitiesOnCurrentThread( |
| 149 // configuration. | 153 const std::vector<Capability>& caps) { |
| 150 CHECK(ThreadHelpers::IsSingleThreaded(proc_fd)); | |
| 151 #endif | |
| 152 | |
| 153 struct cap_hdr hdr = {}; | 154 struct cap_hdr hdr = {}; |
| 154 hdr.version = _LINUX_CAPABILITY_VERSION_3; | 155 hdr.version = _LINUX_CAPABILITY_VERSION_3; |
| 155 struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}}; | 156 struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}}; |
| 156 | 157 |
| 157 // Initially, cap has no capability flags set. Enable the effective and | 158 // Initially, cap has no capability flags set. Enable the effective and |
| 158 // permitted flags only for the requested capabilities. | 159 // permitted flags only for the requested capabilities. |
| 159 for (const LinuxCapability cap : caps) { | 160 for (const Capability cap : caps) { |
| 160 const int cap_num = LinuxCapabilityToKernelValue(cap); | 161 const int cap_num = CapabilityToKernelValue(cap); |
| 161 const size_t index = CAP_TO_INDEX(cap_num); | 162 const size_t index = CAP_TO_INDEX(cap_num); |
| 162 const uint32_t mask = CAP_TO_MASK(cap_num); | 163 const uint32_t mask = CAP_TO_MASK(cap_num); |
| 163 data[index].effective |= mask; | 164 data[index].effective |= mask; |
| 164 data[index].permitted |= mask; | 165 data[index].permitted |= mask; |
| 165 } | 166 } |
| 166 | 167 |
| 167 return sys_capset(&hdr, data) == 0; | 168 return sys_capset(&hdr, data) == 0; |
| 168 } | 169 } |
| 169 | 170 |
| 171 // static |
| 172 bool Credentials::SetCapabilities(int proc_fd, |
| 173 const std::vector<Capability>& caps) { |
| 174 DCHECK_LE(0, proc_fd); |
| 175 |
| 176 #if !defined(THREAD_SANITIZER) |
| 177 // With TSAN, accept to break the security model as it is a testing |
| 178 // configuration. |
| 179 CHECK(ThreadHelpers::IsSingleThreaded(proc_fd)); |
| 180 #endif |
| 181 |
| 182 return SetCapabilitiesOnCurrentThread(caps); |
| 183 } |
| 184 |
| 170 bool Credentials::HasAnyCapability() { | 185 bool Credentials::HasAnyCapability() { |
| 171 struct cap_hdr hdr = {}; | 186 struct cap_hdr hdr = {}; |
| 172 hdr.version = _LINUX_CAPABILITY_VERSION_3; | 187 hdr.version = _LINUX_CAPABILITY_VERSION_3; |
| 173 struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}}; | 188 struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}}; |
| 174 | 189 |
| 175 PCHECK(sys_capget(&hdr, data) == 0); | 190 PCHECK(sys_capget(&hdr, data) == 0); |
| 176 | 191 |
| 177 for (size_t i = 0; i < arraysize(data); ++i) { | 192 for (size_t i = 0; i < arraysize(data); ++i) { |
| 178 if (data[i].effective || data[i].permitted || data[i].inheritable) { | 193 if (data[i].effective || data[i].permitted || data[i].inheritable) { |
| 179 return true; | 194 return true; |
| 180 } | 195 } |
| 181 } | 196 } |
| 182 | 197 |
| 183 return false; | 198 return false; |
| 184 } | 199 } |
| 185 | 200 |
| 186 bool Credentials::HasCapability(LinuxCapability cap) { | 201 bool Credentials::HasCapability(Capability cap) { |
| 187 struct cap_hdr hdr = {}; | 202 struct cap_hdr hdr = {}; |
| 188 hdr.version = _LINUX_CAPABILITY_VERSION_3; | 203 hdr.version = _LINUX_CAPABILITY_VERSION_3; |
| 189 struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}}; | 204 struct cap_data data[_LINUX_CAPABILITY_U32S_3] = {{}}; |
| 190 | 205 |
| 191 PCHECK(sys_capget(&hdr, data) == 0); | 206 PCHECK(sys_capget(&hdr, data) == 0); |
| 192 | 207 |
| 193 const int cap_num = LinuxCapabilityToKernelValue(cap); | 208 const int cap_num = CapabilityToKernelValue(cap); |
| 194 const size_t index = CAP_TO_INDEX(cap_num); | 209 const size_t index = CAP_TO_INDEX(cap_num); |
| 195 const uint32_t mask = CAP_TO_MASK(cap_num); | 210 const uint32_t mask = CAP_TO_MASK(cap_num); |
| 196 | 211 |
| 197 return (data[index].effective | data[index].permitted | | 212 return (data[index].effective | data[index].permitted | |
| 198 data[index].inheritable) & | 213 data[index].inheritable) & |
| 199 mask; | 214 mask; |
| 200 } | 215 } |
| 201 | 216 |
| 202 // static | 217 // static |
| 203 bool Credentials::CanCreateProcessInNewUserNS() { | 218 bool Credentials::CanCreateProcessInNewUserNS() { |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 240 | 255 |
| 241 bool Credentials::MoveToNewUserNS() { | 256 bool Credentials::MoveToNewUserNS() { |
| 242 uid_t uid; | 257 uid_t uid; |
| 243 gid_t gid; | 258 gid_t gid; |
| 244 if (!GetRESIds(&uid, &gid)) { | 259 if (!GetRESIds(&uid, &gid)) { |
| 245 // If all the uids (or gids) are not equal to each other, the security | 260 // If all the uids (or gids) are not equal to each other, the security |
| 246 // model will most likely confuse the caller, abort. | 261 // model will most likely confuse the caller, abort. |
| 247 DVLOG(1) << "uids or gids differ!"; | 262 DVLOG(1) << "uids or gids differ!"; |
| 248 return false; | 263 return false; |
| 249 } | 264 } |
| 250 int ret = unshare(CLONE_NEWUSER); | 265 int ret = sys_unshare(CLONE_NEWUSER); |
| 251 if (ret) { | 266 if (ret) { |
| 252 const int unshare_errno = errno; | 267 const int unshare_errno = errno; |
| 253 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " | 268 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " |
| 254 << "on this kernel."; | 269 << "on this kernel."; |
| 255 CheckCloneNewUserErrno(unshare_errno); | 270 CheckCloneNewUserErrno(unshare_errno); |
| 256 return false; | 271 return false; |
| 257 } | 272 } |
| 258 | 273 |
| 259 if (NamespaceUtils::KernelSupportsDenySetgroups()) { | 274 if (NamespaceUtils::KernelSupportsDenySetgroups()) { |
| 260 PCHECK(NamespaceUtils::DenySetgroups()); | 275 PCHECK(NamespaceUtils::DenySetgroups()); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 275 CHECK_LE(0, proc_fd); | 290 CHECK_LE(0, proc_fd); |
| 276 | 291 |
| 277 CHECK(ChrootToSafeEmptyDir()); | 292 CHECK(ChrootToSafeEmptyDir()); |
| 278 CHECK(!base::DirectoryExists(base::FilePath("/proc"))); | 293 CHECK(!base::DirectoryExists(base::FilePath("/proc"))); |
| 279 CHECK(!ProcUtil::HasOpenDirectory(proc_fd)); | 294 CHECK(!ProcUtil::HasOpenDirectory(proc_fd)); |
| 280 // We never let this function fail. | 295 // We never let this function fail. |
| 281 return true; | 296 return true; |
| 282 } | 297 } |
| 283 | 298 |
| 284 } // namespace sandbox. | 299 } // namespace sandbox. |
| OLD | NEW |