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 |
256 // This is roughly a fork(). | 268 uid_t uid; |
257 const pid_t pid = sys_clone(CLONE_NEWUSER | SIGCHLD, 0, 0, 0, 0); | 269 gid_t gid; |
270 if (!GetRESIds(&uid, &gid)) { | |
271 return false; | |
272 } | |
273 | |
274 const pid_t pid = | |
275 base::ForkWithFlags(CLONE_NEWUSER | SIGCHLD, nullptr, nullptr); | |
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. |
266 // fragile execution environment. | |
267 if (pid == 0) { | 284 if (pid == 0) { |
285 // unshare() requires the effective uid and gid to have a mapping in the | |
286 // parent namespace. | |
287 SetGidAndUidMaps(gid, uid); | |
288 | |
289 // Make sure we drop CAP_SYS_ADMIN. | |
290 auto proc_fd = sandbox::ProcUtil::OpenProc(); | |
291 CHECK(sandbox::Credentials::DropAllCapabilities(proc_fd.get())); | |
mdempsky
2017/01/10 22:48:23
nit: You can just use DropAllCapabilities(), which
Tom (Use chromium acct)
2017/01/10 23:32:03
Done.
| |
292 | |
293 // Ensure we have unprivileged use of CLONE_NEWUSER. Debian | |
294 // Jessie explicitly forbids this case. See: | |
295 // add-sysctl-to-disallow-unprivileged-CLONE_NEWUSER-by-default.patch | |
296 PCHECK(0 == sys_unshare(CLONE_NEWUSER)); | |
268 _exit(kExitSuccess); | 297 _exit(kExitSuccess); |
269 } | 298 } |
270 | 299 |
271 // Always reap the child. | 300 // Always reap the child. |
272 int status = -1; | 301 int status = -1; |
273 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid); | 302 PCHECK(HANDLE_EINTR(waitpid(pid, &status, 0)) == pid); |
274 CHECK(WIFEXITED(status)); | |
275 CHECK_EQ(kExitSuccess, WEXITSTATUS(status)); | |
276 | 303 |
277 // clone(2) succeeded, we can use CLONE_NEWUSER. | 304 // clone(2) succeeded. Now return true only if the system grants |
278 return true; | 305 // unprivileged use of CLONE_NEWUSER as well. |
306 return WIFEXITED(status) && WEXITSTATUS(status) == kExitSuccess; | |
279 } | 307 } |
280 | 308 |
281 bool Credentials::MoveToNewUserNS() { | 309 bool Credentials::MoveToNewUserNS() { |
282 uid_t uid; | 310 uid_t uid; |
283 gid_t gid; | 311 gid_t gid; |
284 if (!GetRESIds(&uid, &gid)) { | 312 if (!GetRESIds(&uid, &gid)) { |
285 // If all the uids (or gids) are not equal to each other, the security | 313 // If all the uids (or gids) are not equal to each other, the security |
286 // model will most likely confuse the caller, abort. | 314 // model will most likely confuse the caller, abort. |
287 DVLOG(1) << "uids or gids differ!"; | 315 DVLOG(1) << "uids or gids differ!"; |
288 return false; | 316 return false; |
289 } | 317 } |
290 int ret = sys_unshare(CLONE_NEWUSER); | 318 int ret = sys_unshare(CLONE_NEWUSER); |
291 if (ret) { | 319 if (ret) { |
292 const int unshare_errno = errno; | 320 const int unshare_errno = errno; |
293 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " | 321 VLOG(1) << "Looks like unprivileged CLONE_NEWUSER may not be available " |
294 << "on this kernel."; | 322 << "on this kernel."; |
295 CheckCloneNewUserErrno(unshare_errno); | 323 CheckCloneNewUserErrno(unshare_errno); |
296 return false; | 324 return false; |
297 } | 325 } |
298 | 326 |
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. | 327 // 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. | 328 // /proc/sys/kernel/overflowuid). Setup the uid and gid maps. |
305 DCHECK(GetRESIds(NULL, NULL)); | 329 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; | 330 return true; |
312 } | 331 } |
313 | 332 |
314 bool Credentials::DropFileSystemAccess(int proc_fd) { | 333 bool Credentials::DropFileSystemAccess(int proc_fd) { |
315 CHECK_LE(0, proc_fd); | 334 CHECK_LE(0, proc_fd); |
316 | 335 |
317 CHECK(ChrootToSafeEmptyDir()); | 336 CHECK(ChrootToSafeEmptyDir()); |
318 CHECK(!HasFileSystemAccess()); | 337 CHECK(!HasFileSystemAccess()); |
319 CHECK(!ProcUtil::HasOpenDirectory(proc_fd)); | 338 CHECK(!ProcUtil::HasOpenDirectory(proc_fd)); |
320 // We never let this function fail. | 339 // We never let this function fail. |
321 return true; | 340 return true; |
322 } | 341 } |
323 | 342 |
324 bool Credentials::HasFileSystemAccess() { | 343 bool Credentials::HasFileSystemAccess() { |
325 return base::DirectoryExists(base::FilePath("/proc")); | 344 return base::DirectoryExists(base::FilePath("/proc")); |
326 } | 345 } |
327 | 346 |
328 pid_t Credentials::ForkAndDropCapabilitiesInChild() { | 347 pid_t Credentials::ForkAndDropCapabilitiesInChild() { |
329 pid_t pid = fork(); | 348 pid_t pid = fork(); |
330 if (pid != 0) { | 349 if (pid != 0) { |
331 return pid; | 350 return pid; |
332 } | 351 } |
333 | 352 |
334 // Since we just forked, we are single threaded. | 353 // Since we just forked, we are single threaded. |
335 PCHECK(DropAllCapabilitiesOnCurrentThread()); | 354 PCHECK(DropAllCapabilitiesOnCurrentThread()); |
336 return 0; | 355 return 0; |
337 } | 356 } |
338 | 357 |
339 } // namespace sandbox. | 358 } // namespace sandbox. |
OLD | NEW |