| 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 "common/sandbox.h" | 7 #include "sandbox/linux/suid/common/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 <sys/wait.h> |
| 30 #include <unistd.h> | 30 #include <unistd.h> |
| 31 | 31 |
| 32 #include "linux_util.h" | 32 #include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h" |
| 33 #include "process_util.h" | 33 #include "sandbox/linux/suid/linux_util.h" |
| 34 #include "common/suid_unsafe_environment_variables.h" | 34 #include "sandbox/linux/suid/process_util.h" |
| 35 | 35 |
| 36 #if !defined(CLONE_NEWPID) | 36 #if !defined(CLONE_NEWPID) |
| 37 #define CLONE_NEWPID 0x20000000 | 37 #define CLONE_NEWPID 0x20000000 |
| 38 #endif | 38 #endif |
| 39 #if !defined(CLONE_NEWNET) | 39 #if !defined(CLONE_NEWNET) |
| 40 #define CLONE_NEWNET 0x40000000 | 40 #define CLONE_NEWNET 0x40000000 |
| 41 #endif | 41 #endif |
| 42 | 42 |
| 43 static bool DropRoot(); | 43 static bool DropRoot(); |
| 44 | 44 |
| 45 #define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x) | 45 #define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x) |
| 46 | 46 |
| 47 static void FatalError(const char *msg, ...) | 47 static void FatalError(const char* msg, ...) |
| 48 __attribute__((noreturn, format(printf, 1, 2))); | 48 __attribute__((noreturn, format(printf, 1, 2))); |
| 49 | 49 |
| 50 static void FatalError(const char *msg, ...) { | 50 static void FatalError(const char* msg, ...) { |
| 51 va_list ap; | 51 va_list ap; |
| 52 va_start(ap, msg); | 52 va_start(ap, msg); |
| 53 | 53 |
| 54 vfprintf(stderr, msg, ap); | 54 vfprintf(stderr, msg, ap); |
| 55 fprintf(stderr, ": %s\n", strerror(errno)); | 55 fprintf(stderr, ": %s\n", strerror(errno)); |
| 56 fflush(stderr); | 56 fflush(stderr); |
| 57 va_end(ap); | 57 va_end(ap); |
| 58 _exit(1); | 58 _exit(1); |
| 59 } | 59 } |
| 60 | 60 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 78 #define SAFE_DIR "/proc/self/fdinfo" | 78 #define SAFE_DIR "/proc/self/fdinfo" |
| 79 #define SAFE_DIR2 "/proc/self/fd" | 79 #define SAFE_DIR2 "/proc/self/fd" |
| 80 | 80 |
| 81 static bool SpawnChrootHelper() { | 81 static bool SpawnChrootHelper() { |
| 82 int sv[2]; | 82 int sv[2]; |
| 83 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { | 83 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) { |
| 84 perror("socketpair"); | 84 perror("socketpair"); |
| 85 return false; | 85 return false; |
| 86 } | 86 } |
| 87 | 87 |
| 88 char *safedir = NULL; | 88 char* safedir = NULL; |
| 89 struct stat sdir_stat; | 89 struct stat sdir_stat; |
| 90 if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) | 90 if (!stat(SAFE_DIR, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) { |
| 91 safedir = SAFE_DIR; | 91 safedir = SAFE_DIR; |
| 92 else | 92 } else if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) { |
| 93 if (!stat(SAFE_DIR2, &sdir_stat) && S_ISDIR(sdir_stat.st_mode)) | 93 safedir = SAFE_DIR2; |
| 94 safedir = SAFE_DIR2; | 94 } else { |
| 95 else { | 95 fprintf(stderr, "Could not find %s\n", SAFE_DIR2); |
| 96 fprintf(stderr, "Could not find %s\n", SAFE_DIR2); | 96 return false; |
| 97 return false; | 97 } |
| 98 } | |
| 99 | 98 |
| 100 const pid_t pid = syscall( | 99 const pid_t pid = syscall(__NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); |
| 101 __NR_clone, CLONE_FS | SIGCHLD, 0, 0, 0); | |
| 102 | 100 |
| 103 if (pid == -1) { | 101 if (pid == -1) { |
| 104 perror("clone"); | 102 perror("clone"); |
| 105 close(sv[0]); | 103 close(sv[0]); |
| 106 close(sv[1]); | 104 close(sv[1]); |
| 107 return false; | 105 return false; |
| 108 } | 106 } |
| 109 | 107 |
| 110 if (pid == 0) { | 108 if (pid == 0) { |
| 111 // We share our files structure with an untrusted process. As a security in | 109 // We share our files structure with an untrusted process. As a security in |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 207 // Don't "Core" on SIGABRT. SIGABRT is sent by the Chrome OS session manager | 205 // Don't "Core" on SIGABRT. SIGABRT is sent by the Chrome OS session manager |
| 208 // when things are hanging. | 206 // when things are hanging. |
| 209 // Here, the current process is going to waitid() and _exit(), so there is no | 207 // Here, the current process is going to waitid() and _exit(), so there is no |
| 210 // point in generating a crash report. The child process is the one | 208 // point in generating a crash report. The child process is the one |
| 211 // blocking us. | 209 // blocking us. |
| 212 if (signal(SIGABRT, ExitWithErrorSignalHandler) == SIG_ERR) { | 210 if (signal(SIGABRT, ExitWithErrorSignalHandler) == SIG_ERR) { |
| 213 FatalError("Failed to change signal handler"); | 211 FatalError("Failed to change signal handler"); |
| 214 } | 212 } |
| 215 | 213 |
| 216 int wait_ret = | 214 int wait_ret = |
| 217 HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED)); | 215 HANDLE_EINTR(waitid(P_PID, child_pid, &reaped_child_info, WEXITED)); |
| 218 | 216 |
| 219 if (!wait_ret && reaped_child_info.si_pid == child_pid) { | 217 if (!wait_ret && reaped_child_info.si_pid == child_pid) { |
| 220 if (reaped_child_info.si_code == CLD_EXITED) { | 218 if (reaped_child_info.si_code == CLD_EXITED) { |
| 221 exit_code = reaped_child_info.si_status; | 219 exit_code = reaped_child_info.si_status; |
| 222 } else { | 220 } else { |
| 223 // Exit with code 0 if the child got signaled. | 221 // Exit with code 0 if the child got signaled. |
| 224 exit_code = 0; | 222 exit_code = 0; |
| 225 } | 223 } |
| 226 } | 224 } |
| 227 _exit(exit_code); | 225 _exit(exit_code); |
| 228 } | 226 } |
| 229 | 227 |
| 230 static bool MoveToNewNamespaces() { | 228 static bool MoveToNewNamespaces() { |
| 231 // These are the sets of flags which we'll try, in order. | 229 // These are the sets of flags which we'll try, in order. |
| 232 const int kCloneExtraFlags[] = { | 230 const int kCloneExtraFlags[] = {CLONE_NEWPID | CLONE_NEWNET, CLONE_NEWPID, }; |
| 233 CLONE_NEWPID | CLONE_NEWNET, | |
| 234 CLONE_NEWPID, | |
| 235 }; | |
| 236 | 231 |
| 237 // We need to close kZygoteIdFd before the child can continue. We use this | 232 // We need to close kZygoteIdFd before the child can continue. We use this |
| 238 // socketpair to tell the child when to continue; | 233 // socketpair to tell the child when to continue; |
| 239 int sync_fds[2]; | 234 int sync_fds[2]; |
| 240 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) { | 235 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_fds)) { |
| 241 FatalError("Failed to create a socketpair"); | 236 FatalError("Failed to create a socketpair"); |
| 242 } | 237 } |
| 243 | 238 |
| 244 for (size_t i = 0; | 239 for (size_t i = 0; i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]); |
| 245 i < sizeof(kCloneExtraFlags) / sizeof(kCloneExtraFlags[0]); | |
| 246 i++) { | 240 i++) { |
| 247 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0); | 241 pid_t pid = syscall(__NR_clone, SIGCHLD | kCloneExtraFlags[i], 0, 0, 0); |
| 248 const int clone_errno = errno; | 242 const int clone_errno = errno; |
| 249 | 243 |
| 250 if (pid > 0) { | 244 if (pid > 0) { |
| 251 if (!DropRoot()) { | 245 if (!DropRoot()) { |
| 252 FatalError("Could not drop privileges"); | 246 FatalError("Could not drop privileges"); |
| 253 } else { | 247 } else { |
| 254 if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD)) | 248 if (close(sync_fds[0]) || shutdown(sync_fds[1], SHUT_RD)) |
| 255 FatalError("Could not close socketpair"); | 249 FatalError("Could not close socketpair"); |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 free(saved_envvar); | 373 free(saved_envvar); |
| 380 } | 374 } |
| 381 | 375 |
| 382 return true; | 376 return true; |
| 383 } | 377 } |
| 384 | 378 |
| 385 bool CheckAndExportApiVersion() { | 379 bool CheckAndExportApiVersion() { |
| 386 // Check the environment to see if a specific API version was requested. | 380 // Check the environment to see if a specific API version was requested. |
| 387 // assume version 0 if none. | 381 // assume version 0 if none. |
| 388 long api_number = -1; | 382 long api_number = -1; |
| 389 char *api_string = getenv(kSandboxEnvironmentApiRequest); | 383 char* api_string = getenv(kSandboxEnvironmentApiRequest); |
| 390 if (!api_string) { | 384 if (!api_string) { |
| 391 api_number = 0; | 385 api_number = 0; |
| 392 } else { | 386 } else { |
| 393 errno = 0; | 387 errno = 0; |
| 394 char* endptr = NULL; | 388 char* endptr = NULL; |
| 395 api_number = strtol(api_string, &endptr, 10); | 389 api_number = strtol(api_string, &endptr, 10); |
| 396 if (!endptr || *endptr || errno != 0) | 390 if (!endptr || *endptr || errno != 0) |
| 397 return false; | 391 return false; |
| 398 } | 392 } |
| 399 | 393 |
| 400 // Warn only for now. | 394 // Warn only for now. |
| 401 if (api_number != kSUIDSandboxApiNumber) { | 395 if (api_number != kSUIDSandboxApiNumber) { |
| 402 fprintf(stderr, "The setuid sandbox provides API version %ld, " | 396 fprintf( |
| 403 "but you need %ld\n" | 397 stderr, |
| 404 "Please read " | 398 "The setuid sandbox provides API version %ld, " |
| 405 "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment." | 399 "but you need %ld\n" |
| 406 "\n\n", | 400 "Please read " |
| 407 kSUIDSandboxApiNumber, | 401 "https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment." |
| 408 api_number); | 402 "\n\n", |
| 403 kSUIDSandboxApiNumber, |
| 404 api_number); |
| 409 } | 405 } |
| 410 | 406 |
| 411 // Export our version so that the sandboxed process can verify it did not | 407 // Export our version so that the sandboxed process can verify it did not |
| 412 // use an old sandbox. | 408 // use an old sandbox. |
| 413 char version_string[64]; | 409 char version_string[64]; |
| 414 snprintf(version_string, sizeof(version_string), "%ld", | 410 snprintf( |
| 415 kSUIDSandboxApiNumber); | 411 version_string, sizeof(version_string), "%ld", kSUIDSandboxApiNumber); |
| 416 if (setenv(kSandboxEnvironmentApiProvides, version_string, 1)) { | 412 if (setenv(kSandboxEnvironmentApiProvides, version_string, 1)) { |
| 417 perror("setenv"); | 413 perror("setenv"); |
| 418 return false; | 414 return false; |
| 419 } | 415 } |
| 420 | 416 |
| 421 return true; | 417 return true; |
| 422 } | 418 } |
| 423 | 419 |
| 424 int main(int argc, char **argv) { | 420 int main(int argc, char** argv) { |
| 425 if (argc <= 1) { | 421 if (argc <= 1) { |
| 426 if (argc <= 0) { | 422 if (argc <= 0) { |
| 427 return 1; | 423 return 1; |
| 428 } | 424 } |
| 429 | 425 |
| 430 fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]); | 426 fprintf(stderr, "Usage: %s <renderer process> <args...>\n", argv[0]); |
| 431 return 1; | 427 return 1; |
| 432 } | 428 } |
| 433 | 429 |
| 434 // Allow someone to query our API version | 430 // Allow someone to query our API version |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 char* endptr = NULL; | 465 char* endptr = NULL; |
| 470 long score; | 466 long score; |
| 471 errno = 0; | 467 errno = 0; |
| 472 unsigned long pid_ul = strtoul(argv[2], &endptr, 10); | 468 unsigned long pid_ul = strtoul(argv[2], &endptr, 10); |
| 473 if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0) | 469 if (pid_ul == ULONG_MAX || !endptr || *endptr || errno != 0) |
| 474 return 1; | 470 return 1; |
| 475 pid_t pid = pid_ul; | 471 pid_t pid = pid_ul; |
| 476 endptr = NULL; | 472 endptr = NULL; |
| 477 errno = 0; | 473 errno = 0; |
| 478 score = strtol(argv[3], &endptr, 10); | 474 score = strtol(argv[3], &endptr, 10); |
| 479 if (score == LONG_MAX || score == LONG_MIN || | 475 if (score == LONG_MAX || score == LONG_MIN || !endptr || *endptr || |
| 480 !endptr || *endptr || errno != 0) | 476 errno != 0) { |
| 481 return 1; | 477 return 1; |
| 478 } |
| 482 return AdjustOOMScore(pid, score); | 479 return AdjustOOMScore(pid, score); |
| 483 } | 480 } |
| 484 | 481 |
| 485 // Protect the core setuid sandbox functionality with an API version | 482 // Protect the core setuid sandbox functionality with an API version |
| 486 if (!CheckAndExportApiVersion()) { | 483 if (!CheckAndExportApiVersion()) { |
| 487 return 1; | 484 return 1; |
| 488 } | 485 } |
| 489 | 486 |
| 490 if (geteuid() != 0) { | 487 if (geteuid() != 0) { |
| 491 fprintf(stderr, | 488 fprintf(stderr, |
| 492 "The setuid sandbox is not running as root. Common causes:\n" | 489 "The setuid sandbox is not running as root. Common causes:\n" |
| 493 " * An unprivileged process using ptrace on it, like a debugger.\n" | 490 " * An unprivileged process using ptrace on it, like a debugger.\n" |
| 494 " * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)\n"); | 491 " * A parent process set prctl(PR_SET_NO_NEW_PRIVS, ...)\n"); |
| 495 } | 492 } |
| 496 | 493 |
| 497 if (!MoveToNewNamespaces()) | 494 if (!MoveToNewNamespaces()) |
| 498 return 1; | 495 return 1; |
| 499 if (!SpawnChrootHelper()) | 496 if (!SpawnChrootHelper()) |
| 500 return 1; | 497 return 1; |
| 501 if (!DropRoot()) | 498 if (!DropRoot()) |
| 502 return 1; | 499 return 1; |
| 503 if (!SetupChildEnvironment()) | 500 if (!SetupChildEnvironment()) |
| 504 return 1; | 501 return 1; |
| 505 | 502 |
| 506 execv(argv[1], &argv[1]); | 503 execv(argv[1], &argv[1]); |
| 507 FatalError("execv failed"); | 504 FatalError("execv failed"); |
| 508 | 505 |
| 509 return 1; | 506 return 1; |
| 510 } | 507 } |
| OLD | NEW |