| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox | 5 // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox |
| 6 | 6 |
| 7 #include "sandbox.h" | 7 #include "sandbox.h" |
| 8 | 8 |
| 9 #define _GNU_SOURCE | 9 #define _GNU_SOURCE |
| 10 #include <asm/unistd.h> | 10 #include <asm/unistd.h> |
| 11 #include <errno.h> | 11 #include <errno.h> |
| 12 #include <fcntl.h> | 12 #include <fcntl.h> |
| 13 #include <limits.h> | 13 #include <limits.h> |
| 14 #include <sched.h> | 14 #include <sched.h> |
| 15 #include <signal.h> | 15 #include <signal.h> |
| 16 #include <stdarg.h> | 16 #include <stdarg.h> |
| 17 #include <stdbool.h> | 17 #include <stdbool.h> |
| 18 #include <stdint.h> | 18 #include <stdint.h> |
| 19 #include <stdio.h> | 19 #include <stdio.h> |
| 20 #include <stdlib.h> | 20 #include <stdlib.h> |
| 21 #include <string.h> | 21 #include <string.h> |
| 22 #include <sys/prctl.h> | 22 #include <sys/prctl.h> |
| 23 #include <sys/resource.h> | 23 #include <sys/resource.h> |
| 24 #include <sys/socket.h> | 24 #include <sys/socket.h> |
| 25 #include <sys/stat.h> | 25 #include <sys/stat.h> |
| 26 #include <sys/time.h> | 26 #include <sys/time.h> |
| 27 #include <sys/types.h> | 27 #include <sys/types.h> |
| 28 #include <sys/vfs.h> | 28 #include <sys/vfs.h> |
| 29 #include <sys/wait.h> |
| 29 #include <unistd.h> | 30 #include <unistd.h> |
| 30 | 31 |
| 31 #include "linux_util.h" | 32 #include "linux_util.h" |
| 32 #include "process_util.h" | 33 #include "process_util.h" |
| 33 #include "suid_unsafe_environment_variables.h" | 34 #include "suid_unsafe_environment_variables.h" |
| 34 | 35 |
| 35 #if !defined(CLONE_NEWPID) | 36 #if !defined(CLONE_NEWPID) |
| 36 #define CLONE_NEWPID 0x20000000 | 37 #define CLONE_NEWPID 0x20000000 |
| 37 #endif | 38 #endif |
| 38 #if !defined(CLONE_NEWNET) | 39 #if !defined(CLONE_NEWNET) |
| 39 #define CLONE_NEWNET 0x40000000 | 40 #define CLONE_NEWNET 0x40000000 |
| 40 #endif | 41 #endif |
| 41 | 42 |
| 42 static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; | 43 static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; |
| 43 static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID"; | 44 static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID"; |
| 45 // This number must be kept in sync with common/zygote_commands_linux.h |
| 46 static const int kZygoteIdFd = 7; |
| 44 | 47 |
| 45 // These are the magic byte values which the sandboxed process uses to request | 48 // These are the magic byte values which the sandboxed process uses to request |
| 46 // that it be chrooted. | 49 // that it be chrooted. |
| 47 static const char kMsgChrootMe = 'C'; | 50 static const char kMsgChrootMe = 'C'; |
| 48 static const char kMsgChrootSuccessful = 'O'; | 51 static const char kMsgChrootSuccessful = 'O'; |
| 49 | 52 |
| 53 static bool DropRoot(); |
| 54 |
| 55 #define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x) |
| 56 |
| 50 static void FatalError(const char *msg, ...) | 57 static void FatalError(const char *msg, ...) |
| 51 __attribute__((noreturn, format(printf, 1, 2))); | 58 __attribute__((noreturn, format(printf, 1, 2))); |
| 52 | 59 |
| 53 static void FatalError(const char *msg, ...) { | 60 static void FatalError(const char *msg, ...) { |
| 54 va_list ap; | 61 va_list ap; |
| 55 va_start(ap, msg); | 62 va_start(ap, msg); |
| 56 | 63 |
| 57 vfprintf(stderr, msg, ap); | 64 vfprintf(stderr, msg, ap); |
| 58 fprintf(stderr, ": %s\n", strerror(errno)); | 65 fprintf(stderr, ": %s\n", strerror(errno)); |
| 59 fflush(stderr); | 66 fflush(stderr); |
| 67 va_end(ap); |
| 60 _exit(1); | 68 _exit(1); |
| 61 } | 69 } |
| 62 | 70 |
| 63 // We will chroot() to the helper's /proc/self directory. Anything there will | 71 // We will chroot() to the helper's /proc/self directory. Anything there will |
| 64 // not exist anymore if we make sure to wait() for the helper. | 72 // not exist anymore if we make sure to wait() for the helper. |
| 65 // | 73 // |
| 66 // /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty | 74 // /proc/self/fdinfo or /proc/self/fd are especially safe and will be empty |
| 67 // even if the helper survives as a zombie. | 75 // even if the helper survives as a zombie. |
| 68 // | 76 // |
| 69 // There is very little reason to use fdinfo/ instead of fd/ but we are | 77 // There is very little reason to use fdinfo/ instead of fd/ but we are |
| (...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 | 193 |
| 186 if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) { | 194 if (setenv(kSandboxHelperPidEnvironmentVarName, helper_pid_str, 1)) { |
| 187 perror("setenv"); | 195 perror("setenv"); |
| 188 close(sv[1]); | 196 close(sv[1]); |
| 189 return false; | 197 return false; |
| 190 } | 198 } |
| 191 | 199 |
| 192 return true; | 200 return true; |
| 193 } | 201 } |
| 194 | 202 |
| 203 // Block until child_pid exits, then exit. Try to preserve the exit code. |
| 204 static void WaitForChildAndExit(pid_t child_pid) { |
| 205 int exit_code = -1; |
| 206 siginfo_t reaped_child_info; |
| 207 |
| 208 int wait_ret = |
| 209 HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED)); |
| 210 |
| 211 if (!wait_ret && reaped_child_info.si_pid == child_pid) { |
| 212 if (reaped_child_info.si_code == CLD_EXITED) { |
| 213 exit_code = reaped_child_info.si_status; |
| 214 } else { |
| 215 // Exit with code 0 if the child got signaled. |
| 216 exit_code = 0; |
| 217 } |
| 218 } |
| 219 _exit(exit_code); |
| 220 } |
| 221 |
| 195 static bool MoveToNewNamespaces() { | 222 static bool MoveToNewNamespaces() { |
| 196 // These are the sets of flags which we'll try, in order. | 223 // These are the sets of flags which we'll try, in order. |
| 197 const int kCloneExtraFlags[] = { | 224 const int kCloneExtraFlags[] = { |
| 198 CLONE_NEWPID | CLONE_NEWNET, | 225 CLONE_NEWPID | CLONE_NEWNET, |
| 199 CLONE_NEWPID, | 226 CLONE_NEWPID, |
| 200 }; | 227 }; |
| 201 | 228 |
| 229 // We need to close kZygoteIdFd before the child can continue. We use this |
| 230 // socketpair to tell the child when to continue; |
| 231 int sync_fds[2]; |
| 232 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) { |
| 233 FatalError("Failed to create a socketpair"); |
| 234 } |
| 235 |
| 202 for (size_t i = 0; | 236 for (size_t i = 0; |
| 203 i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]); | 237 i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]); |
| 204 i++) { | 238 i++) { |
| 205 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0); | 239 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0); |
| 206 | 240 |
| 207 if (pid > 0) | 241 if (pid > 0) { |
| 208 _exit(0); | 242 if (!DropRoot()) { |
| 243 FatalError("Could not drop privileges"); |
| 244 } else { |
| 245 if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD)) |
| 246 FatalError("Could not close socketpair"); |
| 247 // The kZygoteIdFd needs to be closed in the parent before |
| 248 // Zygote gets started. |
| 249 if (close(kZygoteIdFd)) |
| 250 FatalError("close"); |
| 251 // Tell our child to continue |
| 252 if (HANDLE_EINTR(send(sync_fds[1], "C", 1, MSG_NOSIGNAL)) != 1) |
| 253 FatalError("send"); |
| 254 if (close(sync_fds[1])) |
| 255 FatalError("close"); |
| 256 // We want to keep a full process tree and we don't want our childs to |
| 257 // be reparented to (the outer PID namespace) init. So we wait for it. |
| 258 WaitForChildAndExit(pid); |
| 259 } |
| 260 // NOTREACHED |
| 261 FatalError("Not reached"); |
| 262 } |
| 209 | 263 |
| 210 if (pid == 0) { | 264 if (pid == 0) { |
| 265 if (close(sync_fds[1]) || shutdown(sync_fds[0], SHUT_WR)) |
| 266 FatalError("Could not close socketpair"); |
| 267 |
| 268 // Wait for the parent to confirm it closed kZygoteIdFd before we |
| 269 // continue |
| 270 char should_continue; |
| 271 if (HANDLE_EINTR(read(sync_fds[0], &should_continue, 1)) != 1) |
| 272 FatalError("Read on socketpair"); |
| 273 if (close(sync_fds[0])) |
| 274 FatalError("close"); |
| 275 |
| 211 if (kCloneExtraFlags[i] & CLONE_NEWPID) { | 276 if (kCloneExtraFlags[i] & CLONE_NEWPID) { |
| 212 setenv("SBX_PID_NS", "", 1 /* overwrite */); | 277 setenv("SBX_PID_NS", "", 1 /* overwrite */); |
| 213 } else { | 278 } else { |
| 214 unsetenv("SBX_PID_NS"); | 279 unsetenv("SBX_PID_NS"); |
| 215 } | 280 } |
| 216 | 281 |
| 217 if (kCloneExtraFlags[i] & CLONE_NEWNET) { | 282 if (kCloneExtraFlags[i] & CLONE_NEWNET) { |
| 218 setenv("SBX_NET_NS", "", 1 /* overwrite */); | 283 setenv("SBX_NET_NS", "", 1 /* overwrite */); |
| 219 } else { | 284 } else { |
| 220 unsetenv("SBX_NET_NS"); | 285 unsetenv("SBX_NET_NS"); |
| (...skipping 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 368 if (!DropRoot()) | 433 if (!DropRoot()) |
| 369 return 1; | 434 return 1; |
| 370 if (!SetupChildEnvironment()) | 435 if (!SetupChildEnvironment()) |
| 371 return 1; | 436 return 1; |
| 372 | 437 |
| 373 execv(argv[1], &argv[1]); | 438 execv(argv[1], &argv[1]); |
| 374 FatalError("execv failed"); | 439 FatalError("execv failed"); |
| 375 | 440 |
| 376 return 1; | 441 return 1; |
| 377 } | 442 } |
| OLD | NEW |