OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 <dlfcn.h> | 5 #include <dlfcn.h> |
| 6 #include <unistd.h> |
6 #include <sys/epoll.h> | 7 #include <sys/epoll.h> |
| 8 #include <sys/types.h> |
| 9 #include <sys/socket.h> |
| 10 #include <sys/signal.h> |
7 #include <sys/prctl.h> | 11 #include <sys/prctl.h> |
8 #include <sys/signal.h> | |
9 #include <sys/socket.h> | |
10 #include <sys/types.h> | |
11 #include <sys/wait.h> | 12 #include <sys/wait.h> |
12 #include <unistd.h> | |
13 | |
14 #if defined(CHROMIUM_SELINUX) | |
15 #include <selinux/selinux.h> | |
16 #include <selinux/context.h> | |
17 #endif | |
18 | 13 |
19 #include "base/basictypes.h" | 14 #include "base/basictypes.h" |
20 #include "base/command_line.h" | 15 #include "base/command_line.h" |
21 #include "base/eintr_wrapper.h" | 16 #include "base/eintr_wrapper.h" |
22 #include "base/global_descriptors_posix.h" | 17 #include "base/global_descriptors_posix.h" |
23 #include "base/hash_tables.h" | |
24 #include "base/linux_util.h" | |
25 #include "base/path_service.h" | 18 #include "base/path_service.h" |
26 #include "base/pickle.h" | 19 #include "base/pickle.h" |
27 #include "base/rand_util.h" | 20 #include "base/rand_util.h" |
28 #include "base/scoped_ptr.h" | 21 #include "base/scoped_ptr.h" |
29 #include "base/sys_info.h" | 22 #include "base/sys_info.h" |
30 #include "base/unix_domain_socket_posix.h" | 23 #include "base/unix_domain_socket_posix.h" |
31 | 24 |
32 #include "chrome/browser/zygote_host_linux.h" | 25 #include "chrome/browser/zygote_host_linux.h" |
33 #include "chrome/common/chrome_descriptors.h" | 26 #include "chrome/common/chrome_descriptors.h" |
34 #include "chrome/common/chrome_switches.h" | 27 #include "chrome/common/chrome_switches.h" |
35 #include "chrome/common/main_function_params.h" | 28 #include "chrome/common/main_function_params.h" |
36 #include "chrome/common/process_watcher.h" | 29 #include "chrome/common/process_watcher.h" |
37 #include "chrome/common/sandbox_methods_linux.h" | 30 #include "chrome/common/sandbox_methods_linux.h" |
38 | 31 |
39 #include "media/base/media.h" | 32 #include "media/base/media.h" |
40 | 33 |
41 #include "skia/ext/SkFontHost_fontconfig_control.h" | 34 #include "skia/ext/SkFontHost_fontconfig_control.h" |
42 | 35 |
| 36 #if defined(CHROMIUM_SELINUX) |
| 37 #include <selinux/selinux.h> |
| 38 #include <selinux/context.h> |
| 39 #endif |
| 40 |
43 #include "unicode/timezone.h" | 41 #include "unicode/timezone.h" |
44 | 42 |
45 // http://code.google.com/p/chromium/wiki/LinuxZygote | 43 // http://code.google.com/p/chromium/wiki/LinuxZygote |
46 | 44 |
47 static const int kBrowserDescriptor = 3; | |
48 static const int kMagicSandboxIPCDescriptor = 5; | 45 static const int kMagicSandboxIPCDescriptor = 5; |
49 static const int kZygoteIdDescriptor = 7; | |
50 static bool g_suid_sandbox_active = false; | |
51 | 46 |
52 // This is the object which implements the zygote. The ZygoteMain function, | 47 // This is the object which implements the zygote. The ZygoteMain function, |
53 // which is called from ChromeMain, at the the bottom and simple constructs one | 48 // which is called from ChromeMain, at the the bottom and simple constructs one |
54 // of these objects and runs it. | 49 // of these objects and runs it. |
55 class Zygote { | 50 class Zygote { |
56 public: | 51 public: |
57 bool ProcessRequests() { | 52 bool ProcessRequests() { |
58 // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the | 53 // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the |
59 // browser on it. | 54 // browser on it. |
60 // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel. | 55 // A SOCK_DGRAM is installed in fd 4. This is the sandbox IPC channel. |
61 // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC | 56 // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC |
62 | 57 |
63 // We need to accept SIGCHLD, even though our handler is a no-op because | 58 // We need to accept SIGCHLD, even though our handler is a no-op because |
64 // otherwise we cannot wait on children. (According to POSIX 2001.) | 59 // otherwise we cannot wait on children. (According to POSIX 2001.) |
65 struct sigaction action; | 60 struct sigaction action; |
66 memset(&action, 0, sizeof(action)); | 61 memset(&action, 0, sizeof(action)); |
67 action.sa_handler = SIGCHLDHandler; | 62 action.sa_handler = SIGCHLDHandler; |
68 CHECK(sigaction(SIGCHLD, &action, NULL) == 0); | 63 CHECK(sigaction(SIGCHLD, &action, NULL) == 0); |
69 | 64 |
70 if (g_suid_sandbox_active) { | |
71 // Let the ZygoteHost know we are ready to go. | |
72 // The receiving code is in chrome/browser/zygote_host_linux.cc. | |
73 std::vector<int> empty; | |
74 bool r = base::SendMsg(kBrowserDescriptor, kZygoteMagic, | |
75 sizeof(kZygoteMagic), empty); | |
76 CHECK(r) << "Sending zygote magic failed"; | |
77 } | |
78 | |
79 for (;;) { | 65 for (;;) { |
80 if (HandleRequestFromBrowser(kBrowserDescriptor)) | 66 if (HandleRequestFromBrowser(3)) |
81 return true; | 67 return true; |
82 } | 68 } |
83 } | 69 } |
84 | 70 |
85 private: | 71 private: |
86 // See comment below, where sigaction is called. | 72 // See comment below, where sigaction is called. |
87 static void SIGCHLDHandler(int signal) { } | 73 static void SIGCHLDHandler(int signal) { } |
88 | 74 |
89 // --------------------------------------------------------------------------- | 75 // --------------------------------------------------------------------------- |
90 // Requests from the browser... | 76 // Requests from the browser... |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
129 } | 115 } |
130 } | 116 } |
131 | 117 |
132 LOG(WARNING) << "Error parsing message from browser"; | 118 LOG(WARNING) << "Error parsing message from browser"; |
133 for (std::vector<int>::const_iterator | 119 for (std::vector<int>::const_iterator |
134 i = fds.begin(); i != fds.end(); ++i) | 120 i = fds.begin(); i != fds.end(); ++i) |
135 close(*i); | 121 close(*i); |
136 return false; | 122 return false; |
137 } | 123 } |
138 | 124 |
139 bool HandleReapRequest(int fd, const Pickle& pickle, void* iter) { | 125 bool HandleReapRequest(int fd, Pickle& pickle, void* iter) { |
140 base::ProcessId child; | 126 pid_t child; |
141 base::ProcessId actual_child; | |
142 | 127 |
143 if (!pickle.ReadInt(&iter, &child)) { | 128 if (!pickle.ReadInt(&iter, &child)) { |
144 LOG(WARNING) << "Error parsing reap request from browser"; | 129 LOG(WARNING) << "Error parsing reap request from browser"; |
145 return false; | 130 return false; |
146 } | 131 } |
147 | 132 |
148 if (g_suid_sandbox_active) { | 133 ProcessWatcher::EnsureProcessTerminated(child); |
149 actual_child = real_pids_to_sandbox_pids[child]; | |
150 if (!actual_child) | |
151 return false; | |
152 real_pids_to_sandbox_pids.erase(child); | |
153 } else { | |
154 actual_child = child; | |
155 } | |
156 | |
157 ProcessWatcher::EnsureProcessTerminated(actual_child); | |
158 | 134 |
159 return false; | 135 return false; |
160 } | 136 } |
161 | 137 |
162 bool HandleDidProcessCrash(int fd, const Pickle& pickle, void* iter) { | 138 bool HandleDidProcessCrash(int fd, Pickle& pickle, void* iter) { |
163 base::ProcessHandle child; | 139 base::ProcessHandle child; |
164 | 140 |
165 if (!pickle.ReadInt(&iter, &child)) { | 141 if (!pickle.ReadInt(&iter, &child)) { |
166 LOG(WARNING) << "Error parsing DidProcessCrash request from browser"; | 142 LOG(WARNING) << "Error parsing DidProcessCrash request from browser"; |
167 return false; | 143 return false; |
168 } | 144 } |
169 | 145 |
170 bool child_exited; | 146 bool child_exited; |
171 bool did_crash; | 147 bool did_crash = base::DidProcessCrash(&child_exited, child); |
172 if (g_suid_sandbox_active) | |
173 child = real_pids_to_sandbox_pids[child]; | |
174 if (child) | |
175 did_crash = base::DidProcessCrash(&child_exited, child); | |
176 else | |
177 did_crash = child_exited = false; | |
178 | 148 |
179 Pickle write_pickle; | 149 Pickle write_pickle; |
180 write_pickle.WriteBool(did_crash); | 150 write_pickle.WriteBool(did_crash); |
181 write_pickle.WriteBool(child_exited); | 151 write_pickle.WriteBool(child_exited); |
182 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); | 152 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); |
183 | 153 |
184 return false; | 154 return false; |
185 } | 155 } |
186 | 156 |
187 // Handle a 'fork' request from the browser: this means that the browser | 157 // Handle a 'fork' request from the browser: this means that the browser |
188 // wishes to start a new renderer. | 158 // wishes to start a new renderer. |
189 bool HandleForkRequest(int fd, const Pickle& pickle, void* iter, | 159 bool HandleForkRequest(int fd, Pickle& pickle, void* iter, |
190 std::vector<int>& fds) { | 160 std::vector<int>& fds) { |
191 std::vector<std::string> args; | 161 std::vector<std::string> args; |
192 int argc, numfds; | 162 int argc, numfds; |
193 base::GlobalDescriptors::Mapping mapping; | 163 base::GlobalDescriptors::Mapping mapping; |
194 base::ProcessId child; | 164 pid_t child; |
195 uint64_t dummy_inode = 0; | |
196 int dummy_fd = -1; | |
197 | 165 |
198 if (!pickle.ReadInt(&iter, &argc)) | 166 if (!pickle.ReadInt(&iter, &argc)) |
199 goto error; | 167 goto error; |
200 | 168 |
201 for (int i = 0; i < argc; ++i) { | 169 for (int i = 0; i < argc; ++i) { |
202 std::string arg; | 170 std::string arg; |
203 if (!pickle.ReadString(&iter, &arg)) | 171 if (!pickle.ReadString(&iter, &arg)) |
204 goto error; | 172 goto error; |
205 args.push_back(arg); | 173 args.push_back(arg); |
206 } | 174 } |
207 | 175 |
208 if (!pickle.ReadInt(&iter, &numfds)) | 176 if (!pickle.ReadInt(&iter, &numfds)) |
209 goto error; | 177 goto error; |
210 if (numfds != static_cast<int>(fds.size())) | 178 if (numfds != static_cast<int>(fds.size())) |
211 goto error; | 179 goto error; |
212 | 180 |
213 for (int i = 0; i < numfds; ++i) { | 181 for (int i = 0; i < numfds; ++i) { |
214 base::GlobalDescriptors::Key key; | 182 base::GlobalDescriptors::Key key; |
215 if (!pickle.ReadUInt32(&iter, &key)) | 183 if (!pickle.ReadUInt32(&iter, &key)) |
216 goto error; | 184 goto error; |
217 mapping.push_back(std::make_pair(key, fds[i])); | 185 mapping.push_back(std::make_pair(key, fds[i])); |
218 } | 186 } |
219 | 187 |
220 mapping.push_back(std::make_pair( | 188 mapping.push_back(std::make_pair( |
221 static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor)); | 189 static_cast<uint32_t>(kSandboxIPCChannel), 5)); |
222 | |
223 if (g_suid_sandbox_active) { | |
224 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); | |
225 if (dummy_fd < 0) | |
226 goto error; | |
227 | |
228 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) | |
229 goto error; | |
230 } | |
231 | 190 |
232 child = fork(); | 191 child = fork(); |
233 | 192 |
234 if (!child) { | 193 if (!child) { |
235 close(kBrowserDescriptor); // our socket from the browser | 194 close(3); // our socket from the browser is in fd 3 |
236 close(kZygoteIdDescriptor); // another socket from the browser | |
237 Singleton<base::GlobalDescriptors>()->Reset(mapping); | 195 Singleton<base::GlobalDescriptors>()->Reset(mapping); |
238 | 196 |
239 // Reset the process-wide command line to our new command line. | 197 // Reset the process-wide command line to our new command line. |
240 CommandLine::Reset(); | 198 CommandLine::Reset(); |
241 CommandLine::Init(0, NULL); | 199 CommandLine::Init(0, NULL); |
242 CommandLine::ForCurrentProcess()->InitFromArgv(args); | 200 CommandLine::ForCurrentProcess()->InitFromArgv(args); |
243 CommandLine::SetProcTitle(); | 201 CommandLine::SetProcTitle(); |
244 return true; | 202 return true; |
245 } else if (child < 0) { | |
246 LOG(ERROR) << "Zygote could not fork"; | |
247 goto error; | |
248 } | 203 } |
249 | 204 |
250 { | |
251 base::ProcessId proc_id; | |
252 if (g_suid_sandbox_active) { | |
253 close(dummy_fd); | |
254 dummy_fd = -1; | |
255 uint8_t reply_buf[512]; | |
256 Pickle request; | |
257 request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); | |
258 request.WriteUInt64(dummy_inode); | |
259 | |
260 const ssize_t r = base::SendRecvMsg(kMagicSandboxIPCDescriptor, | |
261 reply_buf, sizeof(reply_buf), | |
262 NULL, request); | |
263 if (r == -1) | |
264 goto error; | |
265 | |
266 Pickle reply(reinterpret_cast<char*>(reply_buf), r); | |
267 void* iter2 = NULL; | |
268 if (!reply.ReadInt(&iter2, &proc_id)) | |
269 goto error; | |
270 real_pids_to_sandbox_pids[proc_id] = child; | |
271 } else { | |
272 proc_id = child; | |
273 } | |
274 | |
275 for (std::vector<int>::const_iterator | |
276 i = fds.begin(); i != fds.end(); ++i) | |
277 close(*i); | |
278 | |
279 HANDLE_EINTR(write(fd, &proc_id, sizeof(proc_id))); | |
280 return false; | |
281 } | |
282 | |
283 error: | |
284 LOG(ERROR) << "Error parsing fork request from browser"; | |
285 for (std::vector<int>::const_iterator | 205 for (std::vector<int>::const_iterator |
286 i = fds.begin(); i != fds.end(); ++i) | 206 i = fds.begin(); i != fds.end(); ++i) |
287 close(*i); | 207 close(*i); |
288 if (dummy_fd >= 0) | 208 |
289 close(dummy_fd); | 209 HANDLE_EINTR(write(fd, &child, sizeof(child))); |
| 210 return false; |
| 211 |
| 212 error: |
| 213 LOG(WARNING) << "Error parsing fork request from browser"; |
| 214 for (std::vector<int>::const_iterator |
| 215 i = fds.begin(); i != fds.end(); ++i) |
| 216 close(*i); |
290 return false; | 217 return false; |
291 } | 218 } |
292 | |
293 // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs | |
294 // fork() returns are not the real PIDs, so we need to map the Real PIDS | |
295 // into the sandbox PID namespace. | |
296 typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap; | |
297 ProcessMap real_pids_to_sandbox_pids; | |
298 }; | 219 }; |
299 | 220 |
300 // With SELinux we can carve out a precise sandbox, so we don't have to play | 221 // With SELinux we can carve out a precise sandbox, so we don't have to play |
301 // with intercepting libc calls. | 222 // with intercepting libc calls. |
302 #if !defined(CHROMIUM_SELINUX) | 223 #if !defined(CHROMIUM_SELINUX) |
303 | 224 |
304 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, | 225 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, |
305 char* timezone_out, | 226 char* timezone_out, |
306 size_t timezone_out_len) { | 227 size_t timezone_out_len) { |
307 Pickle request; | 228 Pickle request; |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 } | 395 } |
475 | 396 |
476 #if !defined(CHROMIUM_SELINUX) | 397 #if !defined(CHROMIUM_SELINUX) |
477 static bool EnterSandbox() { | 398 static bool EnterSandbox() { |
478 const char* const sandbox_fd_string = getenv("SBX_D"); | 399 const char* const sandbox_fd_string = getenv("SBX_D"); |
479 if (sandbox_fd_string) { | 400 if (sandbox_fd_string) { |
480 // The SUID sandbox sets this environment variable to a file descriptor | 401 // The SUID sandbox sets this environment variable to a file descriptor |
481 // over which we can signal that we have completed our startup and can be | 402 // over which we can signal that we have completed our startup and can be |
482 // chrooted. | 403 // chrooted. |
483 | 404 |
484 g_suid_sandbox_active = true; | |
485 | |
486 char* endptr; | 405 char* endptr; |
487 const long fd_long = strtol(sandbox_fd_string, &endptr, 10); | 406 const long fd_long = strtol(sandbox_fd_string, &endptr, 10); |
488 if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) | 407 if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) |
489 return false; | 408 return false; |
490 const int fd = fd_long; | 409 const int fd = fd_long; |
491 | 410 |
492 PreSandboxInit(); | 411 PreSandboxInit(); |
493 | 412 |
494 static const char kChrootMe = 'C'; | 413 static const char kChrootMe = 'C'; |
495 static const char kChrootMeSuccess = 'O'; | 414 static const char kChrootMeSuccess = 'O'; |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
583 | 502 |
584 if (!EnterSandbox()) { | 503 if (!EnterSandbox()) { |
585 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " | 504 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " |
586 << errno << ")"; | 505 << errno << ")"; |
587 return false; | 506 return false; |
588 } | 507 } |
589 | 508 |
590 Zygote zygote; | 509 Zygote zygote; |
591 return zygote.ProcessRequests(); | 510 return zygote.ProcessRequests(); |
592 } | 511 } |
OLD | NEW |