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 #include <time.h> | 5 #include <time.h> |
6 | 6 |
7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | 7 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" |
8 #include "sandbox/linux/seccomp-bpf/verifier.h" | 8 #include "sandbox/linux/seccomp-bpf/verifier.h" |
9 | 9 |
| 10 namespace { |
| 11 |
| 12 void WriteFailedStderrSetupMessage(int out_fd) { |
| 13 const char* error_string = strerror(errno); |
| 14 static const char msg[] = "Failed to set up stderr: "; |
| 15 if (HANDLE_EINTR(write(out_fd, msg, sizeof(msg)-1)) > 0 && error_string && |
| 16 HANDLE_EINTR(write(out_fd, error_string, strlen(error_string))) > 0 && |
| 17 HANDLE_EINTR(write(out_fd, "\n", 1))) { |
| 18 } |
| 19 } |
| 20 |
| 21 } // namespace |
| 22 |
10 // The kernel gives us a sandbox, we turn it into a playground :-) | 23 // The kernel gives us a sandbox, we turn it into a playground :-) |
11 // This is version 2 of the playground; version 1 was built on top of | 24 // This is version 2 of the playground; version 1 was built on top of |
12 // pre-BPF seccomp mode. | 25 // pre-BPF seccomp mode. |
13 namespace playground2 { | 26 namespace playground2 { |
14 | 27 |
| 28 const int kExpectedExitCode = 100; |
| 29 |
15 // We define a really simple sandbox policy. It is just good enough for us | 30 // We define a really simple sandbox policy. It is just good enough for us |
16 // to tell that the sandbox has actually been activated. | 31 // to tell that the sandbox has actually been activated. |
17 ErrorCode Sandbox::probeEvaluator(int signo) { | 32 ErrorCode Sandbox::probeEvaluator(int signo) { |
18 switch (signo) { | 33 switch (signo) { |
19 case __NR_getpid: | 34 case __NR_getpid: |
20 // Return EPERM so that we can check that the filter actually ran. | 35 // Return EPERM so that we can check that the filter actually ran. |
21 return ErrorCode(EPERM); | 36 return ErrorCode(EPERM); |
22 case __NR_exit_group: | 37 case __NR_exit_group: |
23 // Allow exit() with a non-default return code. | 38 // Allow exit() with a non-default return code. |
24 return ErrorCode(ErrorCode::ERR_ALLOWED); | 39 return ErrorCode(ErrorCode::ERR_ALLOWED); |
25 default: | 40 default: |
26 // Make everything else fail in an easily recognizable way. | 41 // Make everything else fail in an easily recognizable way. |
27 return ErrorCode(EINVAL); | 42 return ErrorCode(EINVAL); |
28 } | 43 } |
29 } | 44 } |
30 | 45 |
31 void Sandbox::probeProcess(void) { | 46 void Sandbox::probeProcess(void) { |
32 if (syscall(__NR_getpid) < 0 && errno == EPERM) { | 47 if (syscall(__NR_getpid) < 0 && errno == EPERM) { |
33 syscall(__NR_exit_group, (intptr_t)100); | 48 syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode)); |
34 } | 49 } |
35 } | 50 } |
36 | 51 |
37 ErrorCode Sandbox::allowAllEvaluator(int signo) { | 52 ErrorCode Sandbox::allowAllEvaluator(int signo) { |
38 if (signo < static_cast<int>(MIN_SYSCALL) || | 53 if (signo < static_cast<int>(MIN_SYSCALL) || |
39 signo > static_cast<int>(MAX_SYSCALL)) { | 54 signo > static_cast<int>(MAX_SYSCALL)) { |
40 return ErrorCode(ENOSYS); | 55 return ErrorCode(ENOSYS); |
41 } | 56 } |
42 return ErrorCode(ErrorCode::ERR_ALLOWED); | 57 return ErrorCode(ErrorCode::ERR_ALLOWED); |
43 } | 58 } |
44 | 59 |
45 void Sandbox::tryVsyscallProcess(void) { | 60 void Sandbox::tryVsyscallProcess(void) { |
46 time_t current_time; | 61 time_t current_time; |
47 // time() is implemented as a vsyscall. With an older glibc, with | 62 // time() is implemented as a vsyscall. With an older glibc, with |
48 // vsyscall=emulate and some versions of the seccomp BPF patch | 63 // vsyscall=emulate and some versions of the seccomp BPF patch |
49 // we may get SIGKILL-ed. Detect this! | 64 // we may get SIGKILL-ed. Detect this! |
50 if (time(¤t_time) != static_cast<time_t>(-1)) { | 65 if (time(¤t_time) != static_cast<time_t>(-1)) { |
51 syscall(__NR_exit_group, (intptr_t)100); | 66 syscall(__NR_exit_group, static_cast<intptr_t>(kExpectedExitCode)); |
52 } | 67 } |
53 } | 68 } |
54 | 69 |
55 bool Sandbox::RunFunctionInPolicy(void (*CodeInSandbox)(), | 70 bool Sandbox::RunFunctionInPolicy(void (*CodeInSandbox)(), |
56 EvaluateSyscall syscallEvaluator, | 71 EvaluateSyscall syscallEvaluator, |
57 int proc_fd) { | 72 int proc_fd) { |
58 // Block all signals before forking a child process. This prevents an | 73 // Block all signals before forking a child process. This prevents an |
59 // attacker from manipulating our test by sending us an unexpected signal. | 74 // attacker from manipulating our test by sending us an unexpected signal. |
60 sigset_t oldMask, newMask; | 75 sigset_t oldMask, newMask; |
61 if (sigfillset(&newMask) || | 76 if (sigfillset(&newMask) || |
62 sigprocmask(SIG_BLOCK, &newMask, &oldMask)) { | 77 sigprocmask(SIG_BLOCK, &newMask, &oldMask)) { |
63 SANDBOX_DIE("sigprocmask() failed"); | 78 SANDBOX_DIE("sigprocmask() failed"); |
64 } | 79 } |
65 int fds[2]; | 80 int fds[2]; |
66 if (pipe2(fds, O_NONBLOCK|O_CLOEXEC)) { | 81 if (pipe2(fds, O_NONBLOCK|O_CLOEXEC)) { |
67 SANDBOX_DIE("pipe() failed"); | 82 SANDBOX_DIE("pipe() failed"); |
68 } | 83 } |
69 | 84 |
| 85 if (fds[0] <= 2 || fds[1] <= 2) { |
| 86 SANDBOX_DIE("Process started without standard file descriptors"); |
| 87 } |
| 88 |
70 pid_t pid = fork(); | 89 pid_t pid = fork(); |
71 if (pid < 0) { | 90 if (pid < 0) { |
72 // Die if we cannot fork(). We would probably fail a little later | 91 // Die if we cannot fork(). We would probably fail a little later |
73 // anyway, as the machine is likely very close to running out of | 92 // anyway, as the machine is likely very close to running out of |
74 // memory. | 93 // memory. |
75 // But what we don't want to do is return "false", as a crafty | 94 // But what we don't want to do is return "false", as a crafty |
76 // attacker might cause fork() to fail at will and could trick us | 95 // attacker might cause fork() to fail at will and could trick us |
77 // into running without a sandbox. | 96 // into running without a sandbox. |
78 sigprocmask(SIG_SETMASK, &oldMask, NULL); // OK, if it fails | 97 sigprocmask(SIG_SETMASK, &oldMask, NULL); // OK, if it fails |
79 SANDBOX_DIE("fork() failed unexpectedly"); | 98 SANDBOX_DIE("fork() failed unexpectedly"); |
80 } | 99 } |
81 | 100 |
82 // In the child process | 101 // In the child process |
83 if (!pid) { | 102 if (!pid) { |
84 // Test a very simple sandbox policy to verify that we can | 103 // Test a very simple sandbox policy to verify that we can |
85 // successfully turn on sandboxing. | 104 // successfully turn on sandboxing. |
86 Die::EnableSimpleExit(); | 105 Die::EnableSimpleExit(); |
87 errno = 0; | |
88 if (HANDLE_EINTR(close(fds[0])) || | |
89 HANDLE_EINTR(dup2(fds[1], 2)) != 2 || | |
90 HANDLE_EINTR(close(fds[1]))) { | |
91 const char* error_string = strerror(errno); | |
92 static const char msg[] = "Failed to set up stderr: "; | |
93 if (HANDLE_EINTR(write(fds[1], msg, sizeof(msg)-1)) > 0 && error_string && | |
94 HANDLE_EINTR(write(fds[1], error_string, strlen(error_string))) > 0 && | |
95 HANDLE_EINTR(write(fds[1], "\n", 1))) { | |
96 } | |
97 } else { | |
98 evaluators_.clear(); | |
99 setSandboxPolicy(syscallEvaluator, NULL); | |
100 setProcFd(proc_fd); | |
101 | 106 |
102 // By passing "quiet=true" to "startSandboxInternal()" we suppress | 107 if (HANDLE_EINTR(close(fds[0]))) { |
103 // messages for expected and benign failures (e.g. if the current | 108 WriteFailedStderrSetupMessage(fds[1]); |
104 // kernel lacks support for BPF filters). | 109 SANDBOX_DIE(NULL); |
105 startSandboxInternal(true); | 110 } |
| 111 if (HANDLE_EINTR(dup2(fds[1], 2)) != 2) { |
| 112 // Stderr could very well be a file descriptor to .xsession-errors, or |
| 113 // another file, which could be backed by a file system that could cause |
| 114 // dup2 to fail while trying to close stderr. It's important that we do |
| 115 // not fail on trying to close stderr. |
| 116 // If dup2 fails here, we will continue normally, this means that our |
| 117 // parent won't cause a fatal failure if something writes to stderr in |
| 118 // this child. |
| 119 } |
| 120 if (HANDLE_EINTR(close(fds[1]))) { |
| 121 WriteFailedStderrSetupMessage(fds[1]); |
| 122 SANDBOX_DIE(NULL); |
| 123 } |
106 | 124 |
107 // Run our code in the sandbox | 125 evaluators_.clear(); |
108 CodeInSandbox(); | 126 setSandboxPolicy(syscallEvaluator, NULL); |
109 } | 127 setProcFd(proc_fd); |
| 128 |
| 129 // By passing "quiet=true" to "startSandboxInternal()" we suppress |
| 130 // messages for expected and benign failures (e.g. if the current |
| 131 // kernel lacks support for BPF filters). |
| 132 startSandboxInternal(true); |
| 133 |
| 134 // Run our code in the sandbox. |
| 135 CodeInSandbox(); |
| 136 |
| 137 // CodeInSandbox() is not supposed to return here. |
110 SANDBOX_DIE(NULL); | 138 SANDBOX_DIE(NULL); |
111 } | 139 } |
112 | 140 |
113 // In the parent process. | 141 // In the parent process. |
114 if (HANDLE_EINTR(close(fds[1]))) { | 142 if (HANDLE_EINTR(close(fds[1]))) { |
115 SANDBOX_DIE("close() failed"); | 143 SANDBOX_DIE("close() failed"); |
116 } | 144 } |
117 if (sigprocmask(SIG_SETMASK, &oldMask, NULL)) { | 145 if (sigprocmask(SIG_SETMASK, &oldMask, NULL)) { |
118 SANDBOX_DIE("sigprocmask() failed"); | 146 SANDBOX_DIE("sigprocmask() failed"); |
119 } | 147 } |
120 int status; | 148 int status; |
121 if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) { | 149 if (HANDLE_EINTR(waitpid(pid, &status, 0)) != pid) { |
122 SANDBOX_DIE("waitpid() failed unexpectedly"); | 150 SANDBOX_DIE("waitpid() failed unexpectedly"); |
123 } | 151 } |
124 bool rc = WIFEXITED(status) && WEXITSTATUS(status) == 100; | 152 bool rc = WIFEXITED(status) && WEXITSTATUS(status) == kExpectedExitCode; |
125 | 153 |
126 // If we fail to support sandboxing, there might be an additional | 154 // If we fail to support sandboxing, there might be an additional |
127 // error message. If so, this was an entirely unexpected and fatal | 155 // error message. If so, this was an entirely unexpected and fatal |
128 // failure. We should report the failure and somebody must fix | 156 // failure. We should report the failure and somebody must fix |
129 // things. This is probably a security-critical bug in the sandboxing | 157 // things. This is probably a security-critical bug in the sandboxing |
130 // code. | 158 // code. |
131 if (!rc) { | 159 if (!rc) { |
132 char buf[4096]; | 160 char buf[4096]; |
133 ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1)); | 161 ssize_t len = HANDLE_EINTR(read(fds[0], buf, sizeof(buf) - 1)); |
134 if (len > 0) { | 162 if (len > 0) { |
(...skipping 586 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
721 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; | 749 Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; |
722 int Sandbox::proc_fd_ = -1; | 750 int Sandbox::proc_fd_ = -1; |
723 Sandbox::Evaluators Sandbox::evaluators_; | 751 Sandbox::Evaluators Sandbox::evaluators_; |
724 Sandbox::ErrMap Sandbox::errMap_; | 752 Sandbox::ErrMap Sandbox::errMap_; |
725 Sandbox::Traps *Sandbox::traps_ = NULL; | 753 Sandbox::Traps *Sandbox::traps_ = NULL; |
726 Sandbox::TrapIds Sandbox::trapIds_; | 754 Sandbox::TrapIds Sandbox::trapIds_; |
727 ErrorCode *Sandbox::trapArray_ = NULL; | 755 ErrorCode *Sandbox::trapArray_ = NULL; |
728 size_t Sandbox::trapArraySize_ = 0; | 756 size_t Sandbox::trapArraySize_ = 0; |
729 | 757 |
730 } // namespace | 758 } // namespace |
OLD | NEW |