Index: chrome/browser/zygote_main_linux.cc |
=================================================================== |
--- chrome/browser/zygote_main_linux.cc (revision 30939) |
+++ chrome/browser/zygote_main_linux.cc (working copy) |
@@ -3,18 +3,25 @@ |
// found in the LICENSE file. |
#include <dlfcn.h> |
-#include <unistd.h> |
#include <sys/epoll.h> |
+#include <sys/prctl.h> |
+#include <sys/signal.h> |
+#include <sys/socket.h> |
#include <sys/types.h> |
-#include <sys/socket.h> |
-#include <sys/signal.h> |
-#include <sys/prctl.h> |
#include <sys/wait.h> |
+#include <unistd.h> |
+#if defined(CHROMIUM_SELINUX) |
+#include <selinux/selinux.h> |
+#include <selinux/context.h> |
+#endif |
+ |
#include "base/basictypes.h" |
#include "base/command_line.h" |
#include "base/eintr_wrapper.h" |
#include "base/global_descriptors_posix.h" |
+#include "base/hash_tables.h" |
+#include "base/linux_util.h" |
#include "base/path_service.h" |
#include "base/pickle.h" |
#include "base/rand_util.h" |
@@ -33,16 +40,14 @@ |
#include "skia/ext/SkFontHost_fontconfig_control.h" |
-#if defined(CHROMIUM_SELINUX) |
-#include <selinux/selinux.h> |
-#include <selinux/context.h> |
-#endif |
- |
#include "unicode/timezone.h" |
// http://code.google.com/p/chromium/wiki/LinuxZygote |
+static const int kBrowserDescriptor = 3; |
static const int kMagicSandboxIPCDescriptor = 5; |
+static const int kZygoteIdDescriptor = 7; |
+static bool g_suid_sandbox_active = false; |
// This is the object which implements the zygote. The ZygoteMain function, |
// which is called from ChromeMain, at the the bottom and simple constructs one |
@@ -52,7 +57,7 @@ |
bool ProcessRequests() { |
// A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the |
// browser on it. |
- // A SOCK_DGRAM is installed in fd 4. This is the sandbox IPC channel. |
+ // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel. |
// See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC |
// We need to accept SIGCHLD, even though our handler is a no-op because |
@@ -62,8 +67,17 @@ |
action.sa_handler = SIGCHLDHandler; |
CHECK(sigaction(SIGCHLD, &action, NULL) == 0); |
+ if (g_suid_sandbox_active) { |
+ // Let the ZygoteHost know we are ready to go. |
+ // The receiving code is in chrome/browser/zygote_host_linux.cc. |
+ std::vector<int> empty; |
+ bool r = base::SendMsg(kBrowserDescriptor, kZygoteMagic, |
+ sizeof(kZygoteMagic), empty); |
+ CHECK(r) << "Sending zygote magic failed"; |
+ } |
+ |
for (;;) { |
- if (HandleRequestFromBrowser(3)) |
+ if (HandleRequestFromBrowser(kBrowserDescriptor)) |
return true; |
} |
} |
@@ -122,20 +136,30 @@ |
return false; |
} |
- bool HandleReapRequest(int fd, Pickle& pickle, void* iter) { |
- pid_t child; |
+ bool HandleReapRequest(int fd, const Pickle& pickle, void* iter) { |
+ base::ProcessId child; |
+ base::ProcessId actual_child; |
if (!pickle.ReadInt(&iter, &child)) { |
LOG(WARNING) << "Error parsing reap request from browser"; |
return false; |
} |
- ProcessWatcher::EnsureProcessTerminated(child); |
+ if (g_suid_sandbox_active) { |
+ actual_child = real_pids_to_sandbox_pids[child]; |
+ if (!actual_child) |
+ return false; |
+ real_pids_to_sandbox_pids.erase(child); |
+ } else { |
+ actual_child = child; |
+ } |
+ ProcessWatcher::EnsureProcessTerminated(actual_child); |
+ |
return false; |
} |
- bool HandleDidProcessCrash(int fd, Pickle& pickle, void* iter) { |
+ bool HandleDidProcessCrash(int fd, const Pickle& pickle, void* iter) { |
base::ProcessHandle child; |
if (!pickle.ReadInt(&iter, &child)) { |
@@ -144,7 +168,13 @@ |
} |
bool child_exited; |
- bool did_crash = base::DidProcessCrash(&child_exited, child); |
+ bool did_crash; |
+ if (g_suid_sandbox_active) |
+ child = real_pids_to_sandbox_pids[child]; |
+ if (child) |
+ did_crash = base::DidProcessCrash(&child_exited, child); |
+ else |
+ did_crash = child_exited = false; |
Pickle write_pickle; |
write_pickle.WriteBool(did_crash); |
@@ -156,12 +186,14 @@ |
// Handle a 'fork' request from the browser: this means that the browser |
// wishes to start a new renderer. |
- bool HandleForkRequest(int fd, Pickle& pickle, void* iter, |
+ bool HandleForkRequest(int fd, const Pickle& pickle, void* iter, |
std::vector<int>& fds) { |
std::vector<std::string> args; |
int argc, numfds; |
base::GlobalDescriptors::Mapping mapping; |
- pid_t child; |
+ base::ProcessId child; |
+ uint64_t dummy_inode = 0; |
+ int dummy_fd = -1; |
if (!pickle.ReadInt(&iter, &argc)) |
goto error; |
@@ -186,12 +218,22 @@ |
} |
mapping.push_back(std::make_pair( |
- static_cast<uint32_t>(kSandboxIPCChannel), 5)); |
+ static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor)); |
+ if (g_suid_sandbox_active) { |
+ dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0); |
+ if (dummy_fd < 0) |
+ goto error; |
+ |
+ if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) |
+ goto error; |
+ } |
+ |
child = fork(); |
if (!child) { |
- close(3); // our socket from the browser is in fd 3 |
+ close(kBrowserDescriptor); // our socket from the browser |
+ close(kZygoteIdDescriptor); // another socket from the browser |
Singleton<base::GlobalDescriptors>()->Reset(mapping); |
// Reset the process-wide command line to our new command line. |
@@ -200,22 +242,59 @@ |
CommandLine::ForCurrentProcess()->InitFromArgv(args); |
CommandLine::SetProcTitle(); |
return true; |
+ } else if (child < 0) { |
+ LOG(ERROR) << "Zygote could not fork"; |
+ goto error; |
} |
- for (std::vector<int>::const_iterator |
- i = fds.begin(); i != fds.end(); ++i) |
- close(*i); |
+ { |
+ base::ProcessId proc_id; |
+ if (g_suid_sandbox_active) { |
+ close(dummy_fd); |
+ dummy_fd = -1; |
+ uint8_t reply_buf[512]; |
+ Pickle request; |
+ request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE); |
+ request.WriteUInt64(dummy_inode); |
- HANDLE_EINTR(write(fd, &child, sizeof(child))); |
- return false; |
+ const ssize_t r = base::SendRecvMsg(kMagicSandboxIPCDescriptor, |
+ reply_buf, sizeof(reply_buf), |
+ NULL, request); |
+ if (r == -1) |
+ goto error; |
+ Pickle reply(reinterpret_cast<char*>(reply_buf), r); |
+ void* iter2 = NULL; |
+ if (!reply.ReadInt(&iter2, &proc_id)) |
+ goto error; |
+ real_pids_to_sandbox_pids[proc_id] = child; |
+ } else { |
+ proc_id = child; |
+ } |
+ |
+ for (std::vector<int>::const_iterator |
+ i = fds.begin(); i != fds.end(); ++i) |
+ close(*i); |
+ |
+ HANDLE_EINTR(write(fd, &proc_id, sizeof(proc_id))); |
+ return false; |
+ } |
+ |
error: |
- LOG(WARNING) << "Error parsing fork request from browser"; |
+ LOG(ERROR) << "Error parsing fork request from browser"; |
for (std::vector<int>::const_iterator |
i = fds.begin(); i != fds.end(); ++i) |
close(*i); |
+ if (dummy_fd >= 0) |
+ close(dummy_fd); |
return false; |
} |
+ |
+ // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs |
+ // fork() returns are not the real PIDs, so we need to map the Real PIDS |
+ // into the sandbox PID namespace. |
+ typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap; |
+ ProcessMap real_pids_to_sandbox_pids; |
}; |
// With SELinux we can carve out a precise sandbox, so we don't have to play |
@@ -402,6 +481,8 @@ |
// over which we can signal that we have completed our startup and can be |
// chrooted. |
+ g_suid_sandbox_active = true; |
+ |
char* endptr; |
const long fd_long = strtol(sandbox_fd_string, &endptr, 10); |
if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX) |