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 |