Chromium Code Reviews| 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 <limits.h> | 8 #include <limits.h> |
| 9 #include <signal.h> | 9 #include <signal.h> |
| 10 #include <stddef.h> | 10 #include <stddef.h> |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 143 case Credentials::Capability::SYS_CHROOT: | 143 case Credentials::Capability::SYS_CHROOT: |
| 144 return CAP_SYS_CHROOT; | 144 return CAP_SYS_CHROOT; |
| 145 case Credentials::Capability::SYS_ADMIN: | 145 case Credentials::Capability::SYS_ADMIN: |
| 146 return CAP_SYS_ADMIN; | 146 return CAP_SYS_ADMIN; |
| 147 } | 147 } |
| 148 | 148 |
| 149 LOG(FATAL) << "Invalid Capability: " << static_cast<int>(cap); | 149 LOG(FATAL) << "Invalid Capability: " << static_cast<int>(cap); |
| 150 return 0; | 150 return 0; |
| 151 } | 151 } |
| 152 | 152 |
| 153 void SetGidAndUidMaps(gid_t gid, uid_t uid) { | |
| 154 if (NamespaceUtils::KernelSupportsDenySetgroups()) { | |
| 155 PCHECK(NamespaceUtils::DenySetgroups()); | |
| 156 } | |
| 157 DCHECK(GetRESIds(NULL, NULL)); | |
| 158 const char kGidMapFile[] = "/proc/self/gid_map"; | |
| 159 const char kUidMapFile[] = "/proc/self/uid_map"; | |
| 160 PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid)); | |
| 161 PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid)); | |
| 162 DCHECK(GetRESIds(NULL, NULL)); | |
| 163 } | |
| 164 | |
| 153 } // namespace. | 165 } // namespace. |
| 154 | 166 |
| 155 // static | 167 // static |
| 156 bool Credentials::DropAllCapabilities(int proc_fd) { | 168 bool Credentials::DropAllCapabilities(int proc_fd) { |
| 157 if (!SetCapabilities(proc_fd, std::vector<Capability>())) { | 169 if (!SetCapabilities(proc_fd, std::vector<Capability>())) { |
| 158 return false; | 170 return false; |
| 159 } | 171 } |
| 160 | 172 |
| 161 CHECK(!HasAnyCapability()); | 173 CHECK(!HasAnyCapability()); |
| 162 return true; | 174 return true; |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 246 if (IsRunningOnValgrind()) { | 258 if (IsRunningOnValgrind()) { |
| 247 return false; | 259 return false; |
| 248 } | 260 } |
| 249 | 261 |
| 250 #if defined(THREAD_SANITIZER) | 262 #if defined(THREAD_SANITIZER) |
| 251 // With TSAN, processes will always have threads running and can never | 263 // With TSAN, processes will always have threads running and can never |
| 252 // enter a new user namespace with MoveToNewUserNS(). | 264 // enter a new user namespace with MoveToNewUserNS(). |
| 253 return false; | 265 return false; |
| 254 #endif | 266 #endif |
| 255 | 267 |
| 268 uid_t uid; | |
| 269 gid_t gid; | |
| 270 if (!GetRESIds(&uid, &gid)) { | |
| 271 return false; | |
| 272 } | |
| 273 | |
| 256 // This is roughly a fork(). | 274 // This is roughly a fork(). |
| 257 const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0); | 275 const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0); |
| 258 | 276 |
| 259 if (pid == -1) { | 277 if (pid == -1) { |
| 260 CheckCloneNewUserErrno(errno); | 278 CheckCloneNewUserErrno(errno); |
| 261 return false; | 279 return false; |
| 262 } | 280 } |
| 263 | 281 |
| 264 // The parent process could have had threads. In the child, these threads | 282 // The parent process could have had threads. In the child, these threads |
| 265 // have disappeared. Make sure to not do anything in the child, as this is a | 283 // have disappeared. Make sure to not do anything in the child, as this is a |
| 266 // fragile execution environment. | 284 // fragile execution environment. |
| 267 if (pid == 0) { | 285 if (pid == 0) { |
| 268 _exit(kExitSuccess); | 286 // unshare() requires the effective uid and gid to have a mapping in the |
| 287 // parent namespace. | |
| 288 SetGidAndUidMaps(gid, uid); | |
| 289 | |
| 290 // Make sure we drop CAP_SYS_ADMIN. | |
| 291 auto proc_fd = sandbox::ProcUtil::OpenProc(); | |
| 292 CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd.get())); | |
| 293 | |
| 294 // Ensure we have unprivileged use of CLONE_NEWUSER. Debian | |
| 295 // Jessie explicitly forbids this case. See: | |
| 296 // add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch | |
| 297 int ret = sys_unshare(CLONE_NEWUSER); | |
|
mdempsky
2016/12/14 05:11:11
I would probably do:
PCHECK(sys_unshare(CLONE
Tom (Use chromium acct)
2016/12/14 21:10:26
Done. But when I run this as root on Debian, I ge
| |
| 298 _exit(!!ret); | |
| 269 } | 299 } |
| 270 | 300 |
| 271 // Always reap the child. | 301 // Always reap the child. |
| 272 int status = -1; | 302 int status = -1; |
| 273 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid); | 303 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid); |
| 274 CHECK(WIFEXITED(status)); | 304 CHECK(WIFEXITED(status)); |
| 275 CHECK_EQ(kExitSuccess, WEXITSTATUS(status)); | |
| 276 | 305 |
| 277 // clone(2) succeeded, we can use CLONE_NEWUSER. | 306 // clone(2) succeeded. Now return true only if the system grants |
| 278 return true; | 307 // unprivileged use of CLONE_NEWUSER as well. |
| 308 return !status; | |
| 279 } | 309 } |
| 280 | 310 |
| 281 bool Credentials::MoveToNewUserNS() { | 311 bool Credentials::MoveToNewUserNS() { |
| 282 uid_t uid; | 312 uid_t uid; |
| 283 gid_t gid; | 313 gid_t gid; |
| 284 if (!GetRESIds(&uid, &gid)) { | 314 if (!GetRESIds(&uid, &gid)) { |
| 285 // If all the uids (or gids) are not equal to each other, the security | 315 // If all the uids (or gids) are not equal to each other, the security |
| 286 // model will most likely confuse the caller, abort. | 316 // model will most likely confuse the caller, abort. |
| 287 DVLOG(1) << "uids or gids differ!"; | 317 DVLOG(1) << "uids or gids differ!"; |
| 288 return false; | 318 return false; |
| 289 } | 319 } |
| 290 int ret = sys_unshare(CLONE_NEWUSER); | 320 int ret = sys_unshare(CLONE_NEWUSER); |
| 291 if (ret) { | 321 if (ret) { |
| 292 const int unshare_errno = errno; | 322 const int unshare_errno = errno; |
| 293 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " | 323 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " |
| 294 << "on this kernel."; | 324 << "on this kernel."; |
| 295 CheckCloneNewUserErrno(unshare_errno); | 325 CheckCloneNewUserErrno(unshare_errno); |
| 296 return false; | 326 return false; |
| 297 } | 327 } |
| 298 | 328 |
| 299 if (NamespaceUtils::KernelSupportsDenySetgroups()) { | |
| 300 PCHECK(NamespaceUtils::DenySetgroups()); | |
| 301 } | |
| 302 | |
| 303 // The current {r,e,s}{u,g}id is now an overflow id (c.f. | 329 // The current {r,e,s}{u,g}id is now an overflow id (c.f. |
| 304 // /proc/sys/kernel/overflowuid). Setup the uid and gid maps. | 330 // /proc/sys/kernel/overflowuid). Setup the uid and gid maps. |
| 305 DCHECK(GetRESIds(NULL, NULL)); | 331 SetGidAndUidMaps(gid, uid); |
| 306 const char kGidMapFile[] = "/proc/self/gid_map"; | |
| 307 const char kUidMapFile[] = "/proc/self/uid_map"; | |
| 308 PCHECK(NamespaceUtils::WriteToIdMapFile(kGidMapFile, gid)); | |
| 309 PCHECK(NamespaceUtils::WriteToIdMapFile(kUidMapFile, uid)); | |
| 310 DCHECK(GetRESIds(NULL, NULL)); | |
| 311 return true; | 332 return true; |
| 312 } | 333 } |
| 313 | 334 |
| 314 bool Credentials::DropFileSystemAccess(int proc_fd) { | 335 bool Credentials::DropFileSystemAccess(int proc_fd) { |
| 315 CHECK_LE(0, proc_fd); | 336 CHECK_LE(0, proc_fd); |
| 316 | 337 |
| 317 CHECK(ChrootToSafeEmptyDir()); | 338 CHECK(ChrootToSafeEmptyDir()); |
| 318 CHECK(!HasFileSystemAccess()); | 339 CHECK(!HasFileSystemAccess()); |
| 319 CHECK(!ProcUtil::HasOpenDirectory(proc_fd)); | 340 CHECK(!ProcUtil::HasOpenDirectory(proc_fd)); |
| 320 // We never let this function fail. | 341 // We never let this function fail. |
| 321 return true; | 342 return true; |
| 322 } | 343 } |
| 323 | 344 |
| 324 bool Credentials::HasFileSystemAccess() { | 345 bool Credentials::HasFileSystemAccess() { |
| 325 return base::DirectoryExists(base::FilePath("/proc")); | 346 return base::DirectoryExists(base::FilePath("/proc")); |
| 326 } | 347 } |
| 327 | 348 |
| 328 pid_t Credentials::ForkAndDropCapabilitiesInChild() { | 349 pid_t Credentials::ForkAndDropCapabilitiesInChild() { |
| 329 pid_t pid = fork(); | 350 pid_t pid = fork(); |
| 330 if (pid != 0) { | 351 if (pid != 0) { |
| 331 return pid; | 352 return pid; |
| 332 } | 353 } |
| 333 | 354 |
| 334 // Since we just forked, we are single threaded. | 355 // Since we just forked, we are single threaded. |
| 335 PCHECK(DropAllCapabilitiesOnCurrentThread()); | 356 PCHECK(DropAllCapabilitiesOnCurrentThread()); |
| 336 return 0; | 357 return 0; |
| 337 } | 358 } |
| 338 | 359 |
| 339 } // namespace sandbox. | 360 } // namespace sandbox. |
| OLD | NEW |