OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 <fcntl.h> | 6 #include <fcntl.h> |
7 #include <pthread.h> | 7 #include <pthread.h> |
8 #include <sys/epoll.h> | 8 #include <sys/epoll.h> |
9 #include <sys/prctl.h> | 9 #include <sys/prctl.h> |
10 #include <sys/signal.h> | 10 #include <sys/signal.h> |
(...skipping 20 matching lines...) Expand all Loading... | |
31 #include "base/memory/scoped_ptr.h" | 31 #include "base/memory/scoped_ptr.h" |
32 #include "base/path_service.h" | 32 #include "base/path_service.h" |
33 #include "base/pickle.h" | 33 #include "base/pickle.h" |
34 #include "base/process_util.h" | 34 #include "base/process_util.h" |
35 #include "base/rand_util.h" | 35 #include "base/rand_util.h" |
36 #include "base/sys_info.h" | 36 #include "base/sys_info.h" |
37 #include "build/build_config.h" | 37 #include "build/build_config.h" |
38 #include "crypto/nss_util.h" | 38 #include "crypto/nss_util.h" |
39 #include "chrome/common/chrome_paths.h" | 39 #include "chrome/common/chrome_paths.h" |
40 #include "chrome/common/chrome_switches.h" | 40 #include "chrome/common/chrome_switches.h" |
41 #include "chrome/common/nacl_helper_linux.h" | |
41 #include "content/common/chrome_descriptors.h" | 42 #include "content/common/chrome_descriptors.h" |
42 #include "content/common/font_config_ipc_linux.h" | 43 #include "content/common/font_config_ipc_linux.h" |
43 #include "content/common/main_function_params.h" | 44 #include "content/common/main_function_params.h" |
44 #include "content/common/pepper_plugin_registry.h" | 45 #include "content/common/pepper_plugin_registry.h" |
45 #include "content/common/process_watcher.h" | 46 #include "content/common/process_watcher.h" |
46 #include "content/common/result_codes.h" | 47 #include "content/common/result_codes.h" |
47 #include "content/common/sandbox_methods_linux.h" | 48 #include "content/common/sandbox_methods_linux.h" |
48 #include "content/common/set_process_title.h" | 49 #include "content/common/set_process_title.h" |
49 #include "content/common/unix_domain_socket_posix.h" | 50 #include "content/common/unix_domain_socket_posix.h" |
50 #include "media/base/media.h" | 51 #include "media/base/media.h" |
(...skipping 10 matching lines...) Expand all Loading... | |
61 | 62 |
62 // http://code.google.com/p/chromium/wiki/LinuxZygote | 63 // http://code.google.com/p/chromium/wiki/LinuxZygote |
63 | 64 |
64 static const int kBrowserDescriptor = 3; | 65 static const int kBrowserDescriptor = 3; |
65 static const int kMagicSandboxIPCDescriptor = 5; | 66 static const int kMagicSandboxIPCDescriptor = 5; |
66 static const int kZygoteIdDescriptor = 7; | 67 static const int kZygoteIdDescriptor = 7; |
67 static bool g_suid_sandbox_active = false; | 68 static bool g_suid_sandbox_active = false; |
68 #if defined(SECCOMP_SANDBOX) | 69 #if defined(SECCOMP_SANDBOX) |
69 // |g_proc_fd| is used only by the seccomp sandbox. | 70 // |g_proc_fd| is used only by the seccomp sandbox. |
70 static int g_proc_fd = -1; | 71 static int g_proc_fd = -1; |
72 static int g_nacl_helper_fd = -1; | |
73 static base::ProcessId g_naclpid = -1; | |
71 #endif | 74 #endif |
72 | 75 |
73 #if defined(CHROMIUM_SELINUX) | 76 #if defined(CHROMIUM_SELINUX) |
74 static void SELinuxTransitionToTypeOrDie(const char* type) { | 77 static void SELinuxTransitionToTypeOrDie(const char* type) { |
75 security_context_t security_context; | 78 security_context_t security_context; |
76 if (getcon(&security_context)) | 79 if (getcon(&security_context)) |
77 LOG(FATAL) << "Cannot get SELinux context"; | 80 LOG(FATAL) << "Cannot get SELinux context"; |
78 | 81 |
79 context_t context = context_new(security_context); | 82 context_t context = context_new(security_context); |
80 context_type_set(context, type); | 83 context_type_set(context, type); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
154 return false; | 157 return false; |
155 } | 158 } |
156 | 159 |
157 Pickle pickle(buf, len); | 160 Pickle pickle(buf, len); |
158 void* iter = NULL; | 161 void* iter = NULL; |
159 | 162 |
160 int kind; | 163 int kind; |
161 if (pickle.ReadInt(&iter, &kind)) { | 164 if (pickle.ReadInt(&iter, &kind)) { |
162 switch (kind) { | 165 switch (kind) { |
163 case ZygoteHost::kCmdFork: | 166 case ZygoteHost::kCmdFork: |
167 case ZygoteHost::kCmdNaClFork: | |
164 // This function call can return multiple times, once per fork(). | 168 // This function call can return multiple times, once per fork(). |
165 return HandleForkRequest(fd, pickle, iter, fds); | 169 return HandleForkRequest(fd, pickle, iter, fds, |
170 kind == ZygoteHost::kCmdNaClFork); | |
166 case ZygoteHost::kCmdReap: | 171 case ZygoteHost::kCmdReap: |
167 if (!fds.empty()) | 172 if (!fds.empty()) |
168 break; | 173 break; |
169 HandleReapRequest(fd, pickle, iter); | 174 HandleReapRequest(fd, pickle, iter); |
170 return false; | 175 return false; |
171 case ZygoteHost::kCmdGetTerminationStatus: | 176 case ZygoteHost::kCmdGetTerminationStatus: |
172 if (!fds.empty()) | 177 if (!fds.empty()) |
173 break; | 178 break; |
174 HandleGetTerminationStatus(fd, pickle, iter); | 179 HandleGetTerminationStatus(fd, pickle, iter); |
175 return false; | 180 return false; |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
234 | 239 |
235 Pickle write_pickle; | 240 Pickle write_pickle; |
236 write_pickle.WriteInt(static_cast<int>(status)); | 241 write_pickle.WriteInt(static_cast<int>(status)); |
237 write_pickle.WriteInt(exit_code); | 242 write_pickle.WriteInt(exit_code); |
238 ssize_t written = | 243 ssize_t written = |
239 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); | 244 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size())); |
240 if (written != static_cast<ssize_t>(write_pickle.size())) | 245 if (written != static_cast<ssize_t>(write_pickle.size())) |
241 PLOG(ERROR) << "write"; | 246 PLOG(ERROR) << "write"; |
242 } | 247 } |
243 | 248 |
249 base::ProcessId ForkNaClLauncher(std::vector<int>& fds) { | |
250 base::ProcessId naclchild; | |
251 VLOG(1) << "ForkNaClLauncher"; | |
252 | |
253 if (g_suid_sandbox_active) { | |
254 DCHECK(fds.size() == kNaClParentFDIndex + 1); | |
255 } else { | |
256 DCHECK(fds.size() == kNaClSandboxFDIndex + 1); | |
257 } | |
258 if (!UnixDomainSocket::SendMsg(g_nacl_helper_fd, kNaClForkRequest, | |
259 sizeof(kNaClForkRequest), fds)) { | |
260 perror("ForkNaClLauncher: send failed"); | |
261 return -1; | |
262 } | |
263 if (read(g_nacl_helper_fd, &naclchild, sizeof(naclchild)) | |
264 != sizeof(naclchild)) { | |
265 perror("ForkNaClLauncher: read failed"); | |
Mark Seaborn
2011/06/15 16:09:04
You're calling perror() even if read() didn't retu
Brad Chen
2011/06/15 18:47:47
Done.
| |
266 return -1; | |
Mark Seaborn
2011/06/15 16:09:04
If the read() fails, don't you need to abandon the
Brad Chen
2011/06/15 18:47:47
Let's talk face-to-face about what makes sense in
| |
267 } | |
268 VLOG(1) << "nacl_child is " << naclchild; | |
269 return naclchild; | |
270 } | |
271 | |
244 // This is equivalent to fork(), except that, when using the SUID | 272 // This is equivalent to fork(), except that, when using the SUID |
245 // sandbox, it returns the real PID of the child process as it | 273 // sandbox, it returns the real PID of the child process as it |
246 // appears outside the sandbox, rather than returning the PID inside | 274 // appears outside the sandbox, rather than returning the PID inside |
247 // the sandbox. | 275 // the sandbox. |
248 int ForkWithRealPid() { | 276 int ForkWithRealPid(const bool naclfork, std::vector<int>& fds) { |
249 if (!g_suid_sandbox_active) | 277 // If we didn't start a nacl helper, don't use it to fork. |
250 return fork(); | 278 // TODO(bradchen): remove this once nacl helper is debugged |
279 bool isnaclfork = naclfork && (g_naclpid != -1); | |
280 | |
281 if (!g_suid_sandbox_active) { | |
282 VLOG(1) << "suid sandbox NOT active"; | |
283 VLOG(1) << "fds.size() is " << fds.size(); | |
284 if (isnaclfork) { | |
285 return ForkNaClLauncher(fds); | |
286 } else { | |
287 return fork(); | |
288 } | |
289 } | |
251 | 290 |
252 int dummy_fd; | 291 int dummy_fd; |
253 ino_t dummy_inode; | 292 ino_t dummy_inode; |
254 int pipe_fds[2] = { -1, -1 }; | 293 int pipe_fds[2] = { -1, -1 }; |
255 base::ProcessId pid = 0; | 294 base::ProcessId pid = 0; |
256 | 295 |
257 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); | 296 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); |
258 if (dummy_fd < 0) { | 297 if (dummy_fd < 0) { |
259 LOG(ERROR) << "Failed to create dummy FD"; | 298 LOG(ERROR) << "Failed to create dummy FD"; |
260 goto error; | 299 goto error; |
261 } | 300 } |
262 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) { | 301 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) { |
263 LOG(ERROR) << "Failed to get inode for dummy FD"; | 302 LOG(ERROR) << "Failed to get inode for dummy FD"; |
264 goto error; | 303 goto error; |
265 } | 304 } |
266 if (pipe(pipe_fds) != 0) { | 305 if (pipe(pipe_fds) != 0) { |
267 LOG(ERROR) << "Failed to create pipe"; | 306 LOG(ERROR) << "Failed to create pipe"; |
268 goto error; | 307 goto error; |
269 } | 308 } |
270 | 309 |
271 pid = fork(); | 310 if (isnaclfork) { |
311 fds.push_back(dummy_fd); | |
312 fds.push_back(pipe_fds[0]); | |
313 pid = ForkNaClLauncher(fds); | |
314 } else { | |
315 pid = fork(); | |
316 } | |
272 if (pid < 0) { | 317 if (pid < 0) { |
273 goto error; | 318 goto error; |
274 } else if (pid == 0) { | 319 } else if (pid == 0) { |
275 // In the child process. | 320 // In the child process. |
276 close(pipe_fds[1]); | 321 close(pipe_fds[1]); |
277 char buffer[1]; | 322 char buffer[1]; |
278 // Wait until the parent process has discovered our PID. We | 323 // Wait until the parent process has discovered our PID. We |
279 // should not fork any child processes (which the seccomp | 324 // should not fork any child processes (which the seccomp |
280 // sandbox does) until then, because that can interfere with the | 325 // sandbox does) until then, because that can interfere with the |
281 // parent's discovery of our PID. | 326 // parent's discovery of our PID. |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
334 if (pipe_fds[0] >= 0) | 379 if (pipe_fds[0] >= 0) |
335 close(pipe_fds[0]); | 380 close(pipe_fds[0]); |
336 if (pipe_fds[1] >= 0) | 381 if (pipe_fds[1] >= 0) |
337 close(pipe_fds[1]); | 382 close(pipe_fds[1]); |
338 return -1; | 383 return -1; |
339 } | 384 } |
340 | 385 |
341 // Handle a 'fork' request from the browser: this means that the browser | 386 // Handle a 'fork' request from the browser: this means that the browser |
342 // wishes to start a new renderer. | 387 // wishes to start a new renderer. |
343 bool HandleForkRequest(int fd, const Pickle& pickle, void* iter, | 388 bool HandleForkRequest(int fd, const Pickle& pickle, void* iter, |
344 std::vector<int>& fds) { | 389 std::vector<int>& fds, const bool isnaclfork) { |
345 std::vector<std::string> args; | 390 std::vector<std::string> args; |
346 int argc, numfds; | 391 int argc, numfds; |
347 base::GlobalDescriptors::Mapping mapping; | 392 base::GlobalDescriptors::Mapping mapping; |
348 base::ProcessId child; | 393 base::ProcessId child; |
349 | 394 |
350 if (!pickle.ReadInt(&iter, &argc)) | 395 if (!pickle.ReadInt(&iter, &argc)) |
351 goto error; | 396 goto error; |
352 | 397 |
353 for (int i = 0; i < argc; ++i) { | 398 for (int i = 0; i < argc; ++i) { |
354 std::string arg; | 399 std::string arg; |
(...skipping 10 matching lines...) Expand all Loading... | |
365 for (int i = 0; i < numfds; ++i) { | 410 for (int i = 0; i < numfds; ++i) { |
366 base::GlobalDescriptors::Key key; | 411 base::GlobalDescriptors::Key key; |
367 if (!pickle.ReadUInt32(&iter, &key)) | 412 if (!pickle.ReadUInt32(&iter, &key)) |
368 goto error; | 413 goto error; |
369 mapping.push_back(std::make_pair(key, fds[i])); | 414 mapping.push_back(std::make_pair(key, fds[i])); |
370 } | 415 } |
371 | 416 |
372 mapping.push_back(std::make_pair( | 417 mapping.push_back(std::make_pair( |
373 static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor)); | 418 static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor)); |
374 | 419 |
375 child = ForkWithRealPid(); | 420 if (isnaclfork) { |
421 fds.push_back(kMagicSandboxIPCDescriptor); | |
422 } | |
423 child = ForkWithRealPid(isnaclfork, fds); | |
376 | 424 |
377 if (!child) { | 425 if (!child) { |
378 #if defined(SECCOMP_SANDBOX) | 426 #if defined(SECCOMP_SANDBOX) |
379 // Try to open /proc/self/maps as the seccomp sandbox needs access to it | 427 // Try to open /proc/self/maps as the seccomp sandbox needs access to it |
380 if (g_proc_fd >= 0) { | 428 if (g_proc_fd >= 0) { |
381 int proc_self_maps = openat(g_proc_fd, "self/maps", O_RDONLY); | 429 int proc_self_maps = openat(g_proc_fd, "self/maps", O_RDONLY); |
382 if (proc_self_maps >= 0) { | 430 if (proc_self_maps >= 0) { |
383 SeccompSandboxSetProcSelfMaps(proc_self_maps); | 431 SeccompSandboxSetProcSelfMaps(proc_self_maps); |
384 } | 432 } |
385 close(g_proc_fd); | 433 close(g_proc_fd); |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
440 | 488 |
441 // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs | 489 // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs |
442 // fork() returns are not the real PIDs, so we need to map the Real PIDS | 490 // fork() returns are not the real PIDs, so we need to map the Real PIDS |
443 // into the sandbox PID namespace. | 491 // into the sandbox PID namespace. |
444 typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap; | 492 typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap; |
445 ProcessMap real_pids_to_sandbox_pids; | 493 ProcessMap real_pids_to_sandbox_pids; |
446 | 494 |
447 const int sandbox_flags_; | 495 const int sandbox_flags_; |
448 }; | 496 }; |
449 | 497 |
498 static void LaunchNaClHelper() { | |
499 VLOG(1) << "LaunchNaClHelper"; | |
500 int fds[2]; | |
501 | |
502 // Confirm a couple hard-wired assumptions. | |
503 // The NaCl constants are from chrome/nacl/nacl_linux_helper.h | |
504 DCHECK(kNaClZygoteDescriptor == kBrowserDescriptor); | |
505 DCHECK(kNaClSandboxDescriptor == kMagicSandboxIPCDescriptor); | |
506 | |
507 CHECK(socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds) == 0); | |
508 base::file_handle_mapping_vector fds_to_map; | |
509 fds_to_map.push_back(std::make_pair(fds[1], 3)); | |
510 | |
511 const char* nacl_zygote_exe = getenv("NACL_NEW_ZYGOTE"); | |
512 g_naclpid = -1; | |
513 if (NULL != nacl_zygote_exe) { | |
514 CommandLine::StringVector argv = CommandLine::ForCurrentProcess()->argv(); | |
515 argv[0] = nacl_zygote_exe; | |
516 base::LaunchAppWithClone(argv, fds_to_map, false, &g_naclpid, | |
Mark Seaborn
2011/06/15 16:09:04
If this takes fds_to_map, and you're passing CLONE
Brad Chen
2011/06/15 18:47:47
This code works, which suggests I might not be mes
| |
517 CLONE_FS | SIGCHLD); | |
518 } | |
519 close(fds[1]); | |
520 if (g_naclpid > 0) { | |
521 const int kExpectedLength = sizeof(kNaClHelperMagic); | |
522 char buf[kExpectedLength]; | |
523 | |
524 VLOG(1) << "NaCl Launcher PID is " << g_naclpid; | |
525 int nread = read(fds[0], buf, sizeof(buf)); | |
Mark Seaborn
2011/06/15 16:09:04
Please add a comment saying why you are waiting fo
Brad Chen
2011/06/15 18:47:47
Done.
| |
526 DCHECK(nread == kExpectedLength) << "Incorrect NaCl helper magic length"; | |
527 DCHECK(0 == strcmp(buf, kNaClHelperMagic)) << "Incorrect nacl helper magic"; | |
Mark Seaborn
2011/06/15 16:09:04
memcmp(), surely? The buffer is not null-terminat
Brad Chen
2011/06/15 18:47:47
Done.
| |
528 // all is well | |
529 g_nacl_helper_fd = fds[0]; | |
530 return; | |
531 } | |
532 // TODO(bradchen): Make this LOG(ERROR) when the NaCl helper | |
533 // becomes the default. | |
534 VLOG(1) << "Could not launch NaCl helper"; | |
535 g_naclpid = -1; | |
536 g_nacl_helper_fd = -1; | |
537 close(fds[0]); | |
538 | |
539 return; | |
Mark Seaborn
2011/06/15 16:09:04
Not needed.
Brad Chen
2011/06/15 18:47:47
Done.
| |
540 } | |
541 | |
450 // With SELinux we can carve out a precise sandbox, so we don't have to play | 542 // With SELinux we can carve out a precise sandbox, so we don't have to play |
451 // with intercepting libc calls. | 543 // with intercepting libc calls. |
452 #if !defined(CHROMIUM_SELINUX) | 544 #if !defined(CHROMIUM_SELINUX) |
453 | 545 |
454 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, | 546 static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output, |
455 char* timezone_out, | 547 char* timezone_out, |
456 size_t timezone_out_len) { | 548 size_t timezone_out_len) { |
457 Pickle request; | 549 Pickle request; |
458 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME); | 550 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME); |
459 request.WriteString( | 551 request.WriteString( |
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
721 if (CommandLine::ForCurrentProcess()->HasSwitch( | 813 if (CommandLine::ForCurrentProcess()->HasSwitch( |
722 switches::kEnableSeccompSandbox)) { | 814 switches::kEnableSeccompSandbox)) { |
723 g_proc_fd = open("/proc", O_DIRECTORY | O_RDONLY); | 815 g_proc_fd = open("/proc", O_DIRECTORY | O_RDONLY); |
724 if (g_proc_fd < 0) { | 816 if (g_proc_fd < 0) { |
725 LOG(ERROR) << "WARNING! Cannot access \"/proc\". Disabling seccomp " | 817 LOG(ERROR) << "WARNING! Cannot access \"/proc\". Disabling seccomp " |
726 "sandboxing."; | 818 "sandboxing."; |
727 } | 819 } |
728 } | 820 } |
729 #endif // SECCOMP_SANDBOX | 821 #endif // SECCOMP_SANDBOX |
730 | 822 |
823 // launch Native Client helper before entering the sandbox | |
824 LaunchNaClHelper(); | |
825 | |
731 // Turn on the SELinux or SUID sandbox | 826 // Turn on the SELinux or SUID sandbox |
732 if (!EnterSandbox()) { | 827 if (!EnterSandbox()) { |
733 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " | 828 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: " |
734 << errno << ")"; | 829 << errno << ")"; |
735 return false; | 830 return false; |
736 } | 831 } |
737 | 832 |
738 int sandbox_flags = 0; | 833 int sandbox_flags = 0; |
739 if (getenv("SBX_D")) | 834 if (getenv("SBX_D")) |
740 sandbox_flags |= ZygoteHost::kSandboxSUID; | 835 sandbox_flags |= ZygoteHost::kSandboxSUID; |
(...skipping 20 matching lines...) Expand all Loading... | |
761 VLOG(1) << "Enabling experimental Seccomp sandbox."; | 856 VLOG(1) << "Enabling experimental Seccomp sandbox."; |
762 sandbox_flags |= ZygoteHost::kSandboxSeccomp; | 857 sandbox_flags |= ZygoteHost::kSandboxSeccomp; |
763 } | 858 } |
764 } | 859 } |
765 #endif // SECCOMP_SANDBOX | 860 #endif // SECCOMP_SANDBOX |
766 | 861 |
767 Zygote zygote(sandbox_flags); | 862 Zygote zygote(sandbox_flags); |
768 // This function call can return multiple times, once per fork(). | 863 // This function call can return multiple times, once per fork(). |
769 return zygote.ProcessRequests(); | 864 return zygote.ProcessRequests(); |
770 } | 865 } |
OLD | NEW |