blob: b56a8a05ef7c4170258f5a27ccac669c332e0c51 [file] [log] [blame]
[email protected]17773042010-07-28 17:35:071// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]cc8f1462009-06-12 17:36:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]a0f200ea2009-08-06 18:44:065#include <dlfcn.h>
[email protected]a9c54a172009-11-07 06:09:386#include <fcntl.h>
[email protected]724df992010-09-21 18:19:107#include <pthread.h>
[email protected]83a0cbe2009-11-04 04:22:478#include <sys/epoll.h>
[email protected]83a0cbe2009-11-04 04:22:479#include <sys/prctl.h>
[email protected]8ecd3aad2009-11-04 08:32:2210#include <sys/signal.h>
11#include <sys/socket.h>
[email protected]a9c54a172009-11-07 06:09:3812#include <sys/stat.h>
[email protected]8ecd3aad2009-11-04 08:32:2213#include <sys/types.h>
[email protected]83a0cbe2009-11-04 04:22:4714#include <sys/wait.h>
[email protected]8ecd3aad2009-11-04 08:32:2215#include <unistd.h>
16
17#if defined(CHROMIUM_SELINUX)
18#include <selinux/selinux.h>
19#include <selinux/context.h>
20#endif
[email protected]cc8f1462009-06-12 17:36:5521
[email protected]a0421732011-02-23 03:55:4022#include "content/browser/zygote_host_linux.h"
23
[email protected]789f70c72009-07-24 13:55:4324#include "base/basictypes.h"
[email protected]cc8f1462009-06-12 17:36:5525#include "base/command_line.h"
26#include "base/eintr_wrapper.h"
[email protected]5d91c9e2010-07-28 17:25:2827#include "base/file_path.h"
[email protected]cc8f1462009-06-12 17:36:5528#include "base/global_descriptors_posix.h"
[email protected]8ecd3aad2009-11-04 08:32:2229#include "base/hash_tables.h"
30#include "base/linux_util.h"
[email protected]ed450f32011-03-16 01:26:4931#include "base/nss_util.h"
[email protected]c9e45da02009-08-05 17:35:0832#include "base/path_service.h"
[email protected]cc8f1462009-06-12 17:36:5533#include "base/pickle.h"
[email protected]4d36536b2010-08-20 06:23:2734#include "base/process_util.h"
[email protected]4378a822009-07-08 01:15:1435#include "base/rand_util.h"
[email protected]1831acf2009-10-05 16:38:4136#include "base/scoped_ptr.h"
[email protected]80a086c52009-08-04 17:52:0437#include "base/sys_info.h"
[email protected]8015de32009-11-18 04:13:3338#include "build/build_config.h"
[email protected]cc8f1462009-06-12 17:36:5539#include "chrome/common/chrome_descriptors.h"
[email protected]4730db92009-07-22 00:40:4840#include "chrome/common/chrome_switches.h"
[email protected]cf3ac3972010-12-22 20:02:2941#include "chrome/common/font_config_ipc_linux.h"
[email protected]17773042010-07-28 17:35:0742#include "chrome/common/pepper_plugin_registry.h"
[email protected]cc8f1462009-06-12 17:36:5543#include "chrome/common/process_watcher.h"
[email protected]443b80e2010-12-14 00:42:2344#include "chrome/common/result_codes.h"
[email protected]73fa63992009-07-20 20:30:0745#include "chrome/common/sandbox_methods_linux.h"
[email protected]74e9fa22010-12-29 21:06:4346#include "chrome/common/set_process_title.h"
[email protected]cf3ac3972010-12-22 20:02:2947#include "chrome/common/unix_domain_socket_posix.h"
[email protected]415c2cd2011-03-11 21:56:1148#include "content/common/main_function_params.h"
[email protected]c9e45da02009-08-05 17:35:0849#include "media/base/media.h"
[email protected]808ea572010-09-01 16:22:0150#include "seccompsandbox/sandbox.h"
[email protected]cf3ac3972010-12-22 20:02:2951#include "skia/ext/SkFontHost_fontconfig_control.h"
[email protected]1831acf2009-10-05 16:38:4152#include "unicode/timezone.h"
53
[email protected]58680ce2010-09-18 00:09:1554#if defined(ARCH_CPU_X86_FAMILY) && !defined(CHROMIUM_SELINUX) && \
55 !defined(__clang__)
[email protected]36ea6c6f2010-03-17 20:08:0156// The seccomp sandbox is enabled on all ia32 and x86-64 processor as long as
[email protected]58680ce2010-09-18 00:09:1557// we aren't using SELinux or clang.
[email protected]36ea6c6f2010-03-17 20:08:0158#define SECCOMP_SANDBOX
59#endif
60
[email protected]cc8f1462009-06-12 17:36:5561// http://code.google.com/p/chromium/wiki/LinuxZygote
62
[email protected]8ecd3aad2009-11-04 08:32:2263static const int kBrowserDescriptor = 3;
[email protected]73fa63992009-07-20 20:30:0764static const int kMagicSandboxIPCDescriptor = 5;
[email protected]8ecd3aad2009-11-04 08:32:2265static const int kZygoteIdDescriptor = 7;
66static bool g_suid_sandbox_active = false;
[email protected]36ea6c6f2010-03-17 20:08:0167#if defined(SECCOMP_SANDBOX)
[email protected]4f03cbc2009-11-19 00:50:4868// |g_proc_fd| is used only by the seccomp sandbox.
[email protected]a9c54a172009-11-07 06:09:3869static int g_proc_fd = -1;
[email protected]4f03cbc2009-11-19 00:50:4870#endif
[email protected]73fa63992009-07-20 20:30:0771
[email protected]a89a55dd2010-04-19 14:51:1372#if defined(CHROMIUM_SELINUX)
73static void SELinuxTransitionToTypeOrDie(const char* type) {
74 security_context_t security_context;
75 if (getcon(&security_context))
76 LOG(FATAL) << "Cannot get SELinux context";
77
78 context_t context = context_new(security_context);
79 context_type_set(context, type);
80 const int r = setcon(context_str(context));
81 context_free(context);
82 freecon(security_context);
83
84 if (r) {
85 LOG(FATAL) << "dynamic transition to type '" << type << "' failed. "
86 "(this binary has been built with SELinux support, but maybe "
87 "the policies haven't been loaded into the kernel?)";
88 }
89}
90#endif // CHROMIUM_SELINUX
91
[email protected]cc8f1462009-06-12 17:36:5592// This is the object which implements the zygote. The ZygoteMain function,
[email protected]61883442010-07-12 15:14:3493// which is called from ChromeMain, simply constructs one of these objects and
94// runs it.
[email protected]cc8f1462009-06-12 17:36:5595class Zygote {
96 public:
[email protected]715b4f262010-07-13 14:17:2897 explicit Zygote(int sandbox_flags)
98 : sandbox_flags_(sandbox_flags) {
99 }
100
[email protected]cc8f1462009-06-12 17:36:55101 bool ProcessRequests() {
102 // A SOCK_SEQPACKET socket is installed in fd 3. We get commands from the
103 // browser on it.
[email protected]8ecd3aad2009-11-04 08:32:22104 // A SOCK_DGRAM is installed in fd 5. This is the sandbox IPC channel.
[email protected]abe3ad92009-06-15 18:15:08105 // See http://code.google.com/p/chromium/wiki/LinuxSandboxIPC
[email protected]cc8f1462009-06-12 17:36:55106
107 // We need to accept SIGCHLD, even though our handler is a no-op because
108 // otherwise we cannot wait on children. (According to POSIX 2001.)
109 struct sigaction action;
110 memset(&action, 0, sizeof(action));
111 action.sa_handler = SIGCHLDHandler;
112 CHECK(sigaction(SIGCHLD, &action, NULL) == 0);
113
[email protected]8ecd3aad2009-11-04 08:32:22114 if (g_suid_sandbox_active) {
115 // Let the ZygoteHost know we are ready to go.
116 // The receiving code is in chrome/browser/zygote_host_linux.cc.
117 std::vector<int> empty;
[email protected]cf3ac3972010-12-22 20:02:29118 bool r = UnixDomainSocket::SendMsg(kBrowserDescriptor, kZygoteMagic,
119 sizeof(kZygoteMagic), empty);
[email protected]8ecd3aad2009-11-04 08:32:22120 CHECK(r) << "Sending zygote magic failed";
121 }
122
[email protected]cc8f1462009-06-12 17:36:55123 for (;;) {
[email protected]c548be22010-03-08 12:55:57124 // This function call can return multiple times, once per fork().
[email protected]8ecd3aad2009-11-04 08:32:22125 if (HandleRequestFromBrowser(kBrowserDescriptor))
[email protected]cc8f1462009-06-12 17:36:55126 return true;
127 }
128 }
129
130 private:
131 // See comment below, where sigaction is called.
132 static void SIGCHLDHandler(int signal) { }
133
134 // ---------------------------------------------------------------------------
135 // Requests from the browser...
136
137 // Read and process a request from the browser. Returns true if we are in a
138 // new process and thus need to unwind back into ChromeMain.
139 bool HandleRequestFromBrowser(int fd) {
140 std::vector<int> fds;
[email protected]abe3ad92009-06-15 18:15:08141 static const unsigned kMaxMessageLength = 1024;
[email protected]cc8f1462009-06-12 17:36:55142 char buf[kMaxMessageLength];
[email protected]cf3ac3972010-12-22 20:02:29143 const ssize_t len = UnixDomainSocket::RecvMsg(fd, buf, sizeof(buf), &fds);
[email protected]7ae27b92010-09-24 17:33:36144
145 if (len == 0 || (len == -1 && errno == ECONNRESET)) {
146 // EOF from the browser. We should die.
147 _exit(0);
[email protected]cc8f1462009-06-12 17:36:55148 return false;
149 }
150
[email protected]7ae27b92010-09-24 17:33:36151 if (len == -1) {
152 PLOG(ERROR) << "Error reading message from browser";
[email protected]cc8f1462009-06-12 17:36:55153 return false;
154 }
155
156 Pickle pickle(buf, len);
157 void* iter = NULL;
158
159 int kind;
[email protected]57113ea2009-06-18 02:23:16160 if (pickle.ReadInt(&iter, &kind)) {
161 switch (kind) {
162 case ZygoteHost::kCmdFork:
[email protected]c548be22010-03-08 12:55:57163 // This function call can return multiple times, once per fork().
[email protected]57113ea2009-06-18 02:23:16164 return HandleForkRequest(fd, pickle, iter, fds);
165 case ZygoteHost::kCmdReap:
166 if (!fds.empty())
167 break;
[email protected]c548be22010-03-08 12:55:57168 HandleReapRequest(fd, pickle, iter);
169 return false;
[email protected]443b80e2010-12-14 00:42:23170 case ZygoteHost::kCmdGetTerminationStatus:
[email protected]57113ea2009-06-18 02:23:16171 if (!fds.empty())
172 break;
[email protected]443b80e2010-12-14 00:42:23173 HandleGetTerminationStatus(fd, pickle, iter);
[email protected]c548be22010-03-08 12:55:57174 return false;
[email protected]715b4f262010-07-13 14:17:28175 case ZygoteHost::kCmdGetSandboxStatus:
176 HandleGetSandboxStatus(fd, pickle, iter);
177 return false;
[email protected]57113ea2009-06-18 02:23:16178 default:
179 NOTREACHED();
180 break;
181 }
[email protected]cc8f1462009-06-12 17:36:55182 }
183
[email protected]cc8f1462009-06-12 17:36:55184 LOG(WARNING) << "Error parsing message from browser";
185 for (std::vector<int>::const_iterator
186 i = fds.begin(); i != fds.end(); ++i)
187 close(*i);
188 return false;
189 }
190
[email protected]c548be22010-03-08 12:55:57191 void HandleReapRequest(int fd, const Pickle& pickle, void* iter) {
[email protected]8ecd3aad2009-11-04 08:32:22192 base::ProcessId child;
193 base::ProcessId actual_child;
[email protected]cc8f1462009-06-12 17:36:55194
195 if (!pickle.ReadInt(&iter, &child)) {
196 LOG(WARNING) << "Error parsing reap request from browser";
[email protected]c548be22010-03-08 12:55:57197 return;
[email protected]cc8f1462009-06-12 17:36:55198 }
199
[email protected]8ecd3aad2009-11-04 08:32:22200 if (g_suid_sandbox_active) {
201 actual_child = real_pids_to_sandbox_pids[child];
202 if (!actual_child)
[email protected]c548be22010-03-08 12:55:57203 return;
[email protected]8ecd3aad2009-11-04 08:32:22204 real_pids_to_sandbox_pids.erase(child);
205 } else {
206 actual_child = child;
207 }
208
209 ProcessWatcher::EnsureProcessTerminated(actual_child);
[email protected]cc8f1462009-06-12 17:36:55210 }
211
[email protected]443b80e2010-12-14 00:42:23212 void HandleGetTerminationStatus(int fd, const Pickle& pickle, void* iter) {
[email protected]57113ea2009-06-18 02:23:16213 base::ProcessHandle child;
214
215 if (!pickle.ReadInt(&iter, &child)) {
[email protected]443b80e2010-12-14 00:42:23216 LOG(WARNING) << "Error parsing GetTerminationStatus request "
217 << "from browser";
[email protected]c548be22010-03-08 12:55:57218 return;
[email protected]57113ea2009-06-18 02:23:16219 }
220
[email protected]443b80e2010-12-14 00:42:23221 base::TerminationStatus status;
222 int exit_code;
[email protected]8ecd3aad2009-11-04 08:32:22223 if (g_suid_sandbox_active)
224 child = real_pids_to_sandbox_pids[child];
[email protected]443b80e2010-12-14 00:42:23225 if (child) {
226 status = base::GetTerminationStatus(child, &exit_code);
227 } else {
228 // Assume that if we can't find the child in the sandbox, then
229 // it terminated normally.
230 status = base::TERMINATION_STATUS_NORMAL_TERMINATION;
231 exit_code = ResultCodes::NORMAL_EXIT;
232 }
[email protected]57113ea2009-06-18 02:23:16233
234 Pickle write_pickle;
[email protected]443b80e2010-12-14 00:42:23235 write_pickle.WriteInt(static_cast<int>(status));
236 write_pickle.WriteInt(exit_code);
[email protected]8a861402011-01-28 19:59:11237 ssize_t written =
238 HANDLE_EINTR(write(fd, write_pickle.data(), write_pickle.size()));
239 if (written != static_cast<ssize_t>(write_pickle.size()))
[email protected]19cb9292010-04-16 23:00:15240 PLOG(ERROR) << "write";
[email protected]57113ea2009-06-18 02:23:16241 }
242
[email protected]28ecba32010-09-16 10:03:09243 // This is equivalent to fork(), except that, when using the SUID
244 // sandbox, it returns the real PID of the child process as it
245 // appears outside the sandbox, rather than returning the PID inside
246 // the sandbox.
247 int ForkWithRealPid() {
248 if (!g_suid_sandbox_active)
249 return fork();
250
251 int dummy_fd;
252 ino_t dummy_inode;
253 int pipe_fds[2] = { -1, -1 };
[email protected]073040b2010-09-26 02:35:45254 base::ProcessId pid = 0;
[email protected]28ecba32010-09-16 10:03:09255
256 dummy_fd = socket(PF_UNIX, SOCK_DGRAM, 0);
257 if (dummy_fd < 0) {
258 LOG(ERROR) << "Failed to create dummy FD";
259 goto error;
260 }
261 if (!base::FileDescriptorGetInode(&dummy_inode, dummy_fd)) {
262 LOG(ERROR) << "Failed to get inode for dummy FD";
263 goto error;
264 }
265 if (pipe(pipe_fds) != 0) {
266 LOG(ERROR) << "Failed to create pipe";
267 goto error;
268 }
269
270 pid = fork();
271 if (pid < 0) {
272 goto error;
273 } else if (pid == 0) {
274 // In the child process.
275 close(pipe_fds[1]);
276 char buffer[1];
277 // Wait until the parent process has discovered our PID. We
278 // should not fork any child processes (which the seccomp
279 // sandbox does) until then, because that can interfere with the
280 // parent's discovery of our PID.
281 if (HANDLE_EINTR(read(pipe_fds[0], buffer, 1)) != 1 ||
282 buffer[0] != 'x') {
283 LOG(FATAL) << "Failed to synchronise with parent zygote process";
284 }
285 close(pipe_fds[0]);
286 close(dummy_fd);
287 return 0;
288 } else {
289 // In the parent process.
290 close(dummy_fd);
291 dummy_fd = -1;
292 close(pipe_fds[0]);
293 pipe_fds[0] = -1;
294 uint8_t reply_buf[512];
295 Pickle request;
296 request.WriteInt(LinuxSandbox::METHOD_GET_CHILD_WITH_INODE);
297 request.WriteUInt64(dummy_inode);
298
[email protected]cf3ac3972010-12-22 20:02:29299 const ssize_t r = UnixDomainSocket::SendRecvMsg(
300 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL,
301 request);
[email protected]28ecba32010-09-16 10:03:09302 if (r == -1) {
303 LOG(ERROR) << "Failed to get child process's real PID";
304 goto error;
305 }
306
307 base::ProcessId real_pid;
308 Pickle reply(reinterpret_cast<char*>(reply_buf), r);
309 void* iter2 = NULL;
310 if (!reply.ReadInt(&iter2, &real_pid))
311 goto error;
[email protected]073040b2010-09-26 02:35:45312 if (real_pid <= 0) {
313 // METHOD_GET_CHILD_WITH_INODE failed. Did the child die already?
314 LOG(ERROR) << "METHOD_GET_CHILD_WITH_INODE failed";
315 goto error;
316 }
[email protected]28ecba32010-09-16 10:03:09317 real_pids_to_sandbox_pids[real_pid] = pid;
318 if (HANDLE_EINTR(write(pipe_fds[1], "x", 1)) != 1) {
319 LOG(ERROR) << "Failed to synchronise with child process";
320 goto error;
321 }
322 close(pipe_fds[1]);
323 return real_pid;
324 }
325
326 error:
[email protected]72f891f42011-01-12 17:00:52327 if (pid > 0) {
328 if (waitpid(pid, NULL, WNOHANG) == -1)
329 LOG(ERROR) << "Failed to wait for process";
330 }
[email protected]28ecba32010-09-16 10:03:09331 if (dummy_fd >= 0)
332 close(dummy_fd);
333 if (pipe_fds[0] >= 0)
334 close(pipe_fds[0]);
335 if (pipe_fds[1] >= 0)
336 close(pipe_fds[1]);
337 return -1;
338 }
339
[email protected]cc8f1462009-06-12 17:36:55340 // Handle a 'fork' request from the browser: this means that the browser
341 // wishes to start a new renderer.
[email protected]8ecd3aad2009-11-04 08:32:22342 bool HandleForkRequest(int fd, const Pickle& pickle, void* iter,
[email protected]cc8f1462009-06-12 17:36:55343 std::vector<int>& fds) {
344 std::vector<std::string> args;
345 int argc, numfds;
346 base::GlobalDescriptors::Mapping mapping;
[email protected]8ecd3aad2009-11-04 08:32:22347 base::ProcessId child;
[email protected]cc8f1462009-06-12 17:36:55348
349 if (!pickle.ReadInt(&iter, &argc))
350 goto error;
351
352 for (int i = 0; i < argc; ++i) {
353 std::string arg;
354 if (!pickle.ReadString(&iter, &arg))
355 goto error;
356 args.push_back(arg);
357 }
358
359 if (!pickle.ReadInt(&iter, &numfds))
360 goto error;
361 if (numfds != static_cast<int>(fds.size()))
362 goto error;
363
364 for (int i = 0; i < numfds; ++i) {
365 base::GlobalDescriptors::Key key;
366 if (!pickle.ReadUInt32(&iter, &key))
367 goto error;
368 mapping.push_back(std::make_pair(key, fds[i]));
369 }
370
[email protected]abe3ad92009-06-15 18:15:08371 mapping.push_back(std::make_pair(
[email protected]8ecd3aad2009-11-04 08:32:22372 static_cast<uint32_t>(kSandboxIPCChannel), kMagicSandboxIPCDescriptor));
373
[email protected]28ecba32010-09-16 10:03:09374 child = ForkWithRealPid();
[email protected]cc8f1462009-06-12 17:36:55375
376 if (!child) {
[email protected]36ea6c6f2010-03-17 20:08:01377#if defined(SECCOMP_SANDBOX)
[email protected]a9c54a172009-11-07 06:09:38378 // Try to open /proc/self/maps as the seccomp sandbox needs access to it
379 if (g_proc_fd >= 0) {
380 int proc_self_maps = openat(g_proc_fd, "self/maps", O_RDONLY);
381 if (proc_self_maps >= 0) {
382 SeccompSandboxSetProcSelfMaps(proc_self_maps);
383 }
384 close(g_proc_fd);
385 g_proc_fd = -1;
386 }
[email protected]8015de32009-11-18 04:13:33387#endif
[email protected]a9c54a172009-11-07 06:09:38388
[email protected]8ecd3aad2009-11-04 08:32:22389 close(kBrowserDescriptor); // our socket from the browser
[email protected]cbcf9cc32010-02-17 04:05:59390 if (g_suid_sandbox_active)
391 close(kZygoteIdDescriptor); // another socket from the browser
[email protected]9b85081af2010-12-07 21:26:47392 base::GlobalDescriptors::GetInstance()->Reset(mapping);
[email protected]0189bbd2009-10-12 22:50:39393
[email protected]a89a55dd2010-04-19 14:51:13394#if defined(CHROMIUM_SELINUX)
395 SELinuxTransitionToTypeOrDie("chromium_renderer_t");
396#endif
397
[email protected]0189bbd2009-10-12 22:50:39398 // Reset the process-wide command line to our new command line.
[email protected]cc8f1462009-06-12 17:36:55399 CommandLine::Reset();
[email protected]0189bbd2009-10-12 22:50:39400 CommandLine::Init(0, NULL);
401 CommandLine::ForCurrentProcess()->InitFromArgv(args);
[email protected]74e9fa22010-12-29 21:06:43402
403 // Update the process title. The argv was already cached by the call to
404 // SetProcessTitleFromCommandLine in ChromeMain, so we can pass NULL here
405 // (we don't have the original argv at this point).
406 SetProcessTitleFromCommandLine(NULL);
407
[email protected]c548be22010-03-08 12:55:57408 // The fork() request is handled further up the call stack.
[email protected]cc8f1462009-06-12 17:36:55409 return true;
[email protected]8ecd3aad2009-11-04 08:32:22410 } else if (child < 0) {
[email protected]466cffd2010-06-09 18:10:56411 LOG(ERROR) << "Zygote could not fork: " << errno;
[email protected]8ecd3aad2009-11-04 08:32:22412 goto error;
[email protected]cc8f1462009-06-12 17:36:55413 }
414
[email protected]28ecba32010-09-16 10:03:09415 for (std::vector<int>::const_iterator
416 i = fds.begin(); i != fds.end(); ++i)
417 close(*i);
[email protected]83a0cbe2009-11-04 04:22:47418
[email protected]28ecba32010-09-16 10:03:09419 if (HANDLE_EINTR(write(fd, &child, sizeof(child))) < 0)
420 PLOG(ERROR) << "write";
421 return false;
[email protected]83a0cbe2009-11-04 04:22:47422
423 error:
[email protected]8ecd3aad2009-11-04 08:32:22424 LOG(ERROR) << "Error parsing fork request from browser";
[email protected]83a0cbe2009-11-04 04:22:47425 for (std::vector<int>::const_iterator
426 i = fds.begin(); i != fds.end(); ++i)
427 close(*i);
[email protected]cc8f1462009-06-12 17:36:55428 return false;
429 }
[email protected]8ecd3aad2009-11-04 08:32:22430
[email protected]715b4f262010-07-13 14:17:28431 bool HandleGetSandboxStatus(int fd, const Pickle& pickle, void* iter) {
432 if (HANDLE_EINTR(write(fd, &sandbox_flags_, sizeof(sandbox_flags_)) !=
433 sizeof(sandbox_flags_))) {
434 PLOG(ERROR) << "write";
435 }
436
437 return false;
438 }
439
[email protected]8ecd3aad2009-11-04 08:32:22440 // In the SUID sandbox, we try to use a new PID namespace. Thus the PIDs
441 // fork() returns are not the real PIDs, so we need to map the Real PIDS
442 // into the sandbox PID namespace.
443 typedef base::hash_map<base::ProcessHandle, base::ProcessHandle> ProcessMap;
444 ProcessMap real_pids_to_sandbox_pids;
[email protected]715b4f262010-07-13 14:17:28445
446 const int sandbox_flags_;
[email protected]cc8f1462009-06-12 17:36:55447};
448
[email protected]ad6d2c42009-09-15 20:13:38449// With SELinux we can carve out a precise sandbox, so we don't have to play
450// with intercepting libc calls.
451#if !defined(CHROMIUM_SELINUX)
452
[email protected]a0f200ea2009-08-06 18:44:06453static void ProxyLocaltimeCallToBrowser(time_t input, struct tm* output,
454 char* timezone_out,
455 size_t timezone_out_len) {
[email protected]73fa63992009-07-20 20:30:07456 Pickle request;
457 request.WriteInt(LinuxSandbox::METHOD_LOCALTIME);
458 request.WriteString(
459 std::string(reinterpret_cast<char*>(&input), sizeof(input)));
460
461 uint8_t reply_buf[512];
[email protected]cf3ac3972010-12-22 20:02:29462 const ssize_t r = UnixDomainSocket::SendRecvMsg(
[email protected]73fa63992009-07-20 20:30:07463 kMagicSandboxIPCDescriptor, reply_buf, sizeof(reply_buf), NULL, request);
464 if (r == -1) {
465 memset(output, 0, sizeof(struct tm));
466 return;
467 }
468
469 Pickle reply(reinterpret_cast<char*>(reply_buf), r);
470 void* iter = NULL;
[email protected]9debcda52009-07-22 20:06:51471 std::string result, timezone;
[email protected]73fa63992009-07-20 20:30:07472 if (!reply.ReadString(&iter, &result) ||
[email protected]9debcda52009-07-22 20:06:51473 !reply.ReadString(&iter, &timezone) ||
[email protected]73fa63992009-07-20 20:30:07474 result.size() != sizeof(struct tm)) {
475 memset(output, 0, sizeof(struct tm));
476 return;
477 }
478
479 memcpy(output, result.data(), sizeof(struct tm));
[email protected]9debcda52009-07-22 20:06:51480 if (timezone_out_len) {
481 const size_t copy_len = std::min(timezone_out_len - 1, timezone.size());
482 memcpy(timezone_out, timezone.data(), copy_len);
483 timezone_out[copy_len] = 0;
484 output->tm_zone = timezone_out;
485 } else {
486 output->tm_zone = NULL;
487 }
[email protected]73fa63992009-07-20 20:30:07488}
489
[email protected]a0f200ea2009-08-06 18:44:06490static bool g_am_zygote_or_renderer = false;
491
492// Sandbox interception of libc calls.
493//
494// Because we are running in a sandbox certain libc calls will fail (localtime
495// being the motivating example - it needs to read /etc/localtime). We need to
496// intercept these calls and proxy them to the browser. However, these calls
497// may come from us or from our libraries. In some cases we can't just change
498// our code.
499//
500// It's for these cases that we have the following setup:
501//
502// We define global functions for those functions which we wish to override.
503// Since we will be first in the dynamic resolution order, the dynamic linker
504// will point callers to our versions of these functions. However, we have the
505// same binary for both the browser and the renderers, which means that our
506// overrides will apply in the browser too.
507//
508// The global |g_am_zygote_or_renderer| is true iff we are in a zygote or
509// renderer process. It's set in ZygoteMain and inherited by the renderers when
510// they fork. (This means that it'll be incorrect for global constructor
511// functions and before ZygoteMain is called - beware).
512//
513// Our replacement functions can check this global and either proxy
514// the call to the browser over the sandbox IPC
515// (http://code.google.com/p/chromium/wiki/LinuxSandboxIPC) or they can use
516// dlsym with RTLD_NEXT to resolve the symbol, ignoring any symbols in the
517// current module.
518//
519// Other avenues:
520//
521// Our first attempt involved some assembly to patch the GOT of the current
522// module. This worked, but was platform specific and doesn't catch the case
523// where a library makes a call rather than current module.
524//
525// We also considered patching the function in place, but this would again by
526// platform specific and the above technique seems to work well enough.
527
[email protected]724df992010-09-21 18:19:10528typedef struct tm* (*LocaltimeFunction)(const time_t* timep);
529typedef struct tm* (*LocaltimeRFunction)(const time_t* timep,
530 struct tm* result);
531
532static pthread_once_t g_libc_localtime_funcs_guard = PTHREAD_ONCE_INIT;
533static LocaltimeFunction g_libc_localtime;
534static LocaltimeRFunction g_libc_localtime_r;
535
536static void InitLibcLocaltimeFunctions() {
537 g_libc_localtime = reinterpret_cast<LocaltimeFunction>(
538 dlsym(RTLD_NEXT, "localtime"));
539 g_libc_localtime_r = reinterpret_cast<LocaltimeRFunction>(
540 dlsym(RTLD_NEXT, "localtime_r"));
541
542 if (!g_libc_localtime || !g_libc_localtime_r) {
543 // http://code.google.com/p/chromium/issues/detail?id=16800
544 //
545 // Nvidia's libGL.so overrides dlsym for an unknown reason and replaces
546 // it with a version which doesn't work. In this case we'll get a NULL
547 // result. There's not a lot we can do at this point, so we just bodge it!
548 LOG(ERROR) << "Your system is broken: dlsym doesn't work! This has been "
549 "reported to be caused by Nvidia's libGL. You should expect"
550 " time related functions to misbehave. "
551 "http://code.google.com/p/chromium/issues/detail?id=16800";
552 }
553
554 if (!g_libc_localtime)
555 g_libc_localtime = gmtime;
556 if (!g_libc_localtime_r)
557 g_libc_localtime_r = gmtime_r;
558}
[email protected]a5d7bb82009-09-08 22:30:58559
[email protected]73fa63992009-07-20 20:30:07560struct tm* localtime(const time_t* timep) {
[email protected]a0f200ea2009-08-06 18:44:06561 if (g_am_zygote_or_renderer) {
562 static struct tm time_struct;
563 static char timezone_string[64];
564 ProxyLocaltimeCallToBrowser(*timep, &time_struct, timezone_string,
565 sizeof(timezone_string));
566 return &time_struct;
567 } else {
[email protected]724df992010-09-21 18:19:10568 CHECK_EQ(0, pthread_once(&g_libc_localtime_funcs_guard,
569 InitLibcLocaltimeFunctions));
570 return g_libc_localtime(timep);
[email protected]a0f200ea2009-08-06 18:44:06571 }
[email protected]73fa63992009-07-20 20:30:07572}
573
574struct tm* localtime_r(const time_t* timep, struct tm* result) {
[email protected]a0f200ea2009-08-06 18:44:06575 if (g_am_zygote_or_renderer) {
576 ProxyLocaltimeCallToBrowser(*timep, result, NULL, 0);
577 return result;
578 } else {
[email protected]724df992010-09-21 18:19:10579 CHECK_EQ(0, pthread_once(&g_libc_localtime_funcs_guard,
580 InitLibcLocaltimeFunctions));
581 return g_libc_localtime_r(timep, result);
[email protected]a0f200ea2009-08-06 18:44:06582 }
[email protected]73fa63992009-07-20 20:30:07583}
584
[email protected]ad6d2c42009-09-15 20:13:38585#endif // !CHROMIUM_SELINUX
[email protected]a5d7bb82009-09-08 22:30:58586
[email protected]ad6d2c42009-09-15 20:13:38587// This function triggers the static and lazy construction of objects that need
588// to be created before imposing the sandbox.
589static void PreSandboxInit() {
[email protected]b8419412010-03-29 20:18:29590 base::RandUint64();
[email protected]4378a822009-07-08 01:15:14591
[email protected]b8419412010-03-29 20:18:29592 base::SysInfo::MaxSharedMemorySize();
[email protected]80a086c52009-08-04 17:52:04593
[email protected]b8419412010-03-29 20:18:29594 // ICU DateFormat class (used in base/time_format.cc) needs to get the
595 // Olson timezone ID by accessing the zoneinfo files on disk. After
596 // TimeZone::createDefault is called once here, the timezone ID is
597 // cached and there's no more need to access the file system.
598 scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
[email protected]1831acf2009-10-05 16:38:41599
[email protected]b8419412010-03-29 20:18:29600 FilePath module_path;
601 if (PathService::Get(base::DIR_MODULE, &module_path))
602 media::InitializeMediaLibrary(module_path);
[email protected]17773042010-07-28 17:35:07603
[email protected]ed450f32011-03-16 01:26:49604 // Remoting requires NSS to function properly. It is not used for other
605 // reasons so load NSS only if remoting is enabled.
606 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
607 if (command_line.HasSwitch(switches::kEnableRemoting)) {
608 // We are going to fork to engage the sandbox and we have not loaded
609 // any security modules so it is safe to disable the fork check in NSS.
610 base::DisableNSSForkCheck();
611
612 // Initialize NSS so that we load the necessary library files
613 // before we enter the sandbox.
614 base::ForceNSSNoDBInit();
615 base::EnsureNSSInit();
616 }
617
[email protected]17773042010-07-28 17:35:07618 // Ensure access to the Pepper plugins before the sandbox is turned on.
619 PepperPluginRegistry::PreloadModules();
[email protected]ad6d2c42009-09-15 20:13:38620}
621
622#if !defined(CHROMIUM_SELINUX)
623static bool EnterSandbox() {
[email protected]b8419412010-03-29 20:18:29624 // The SUID sandbox sets this environment variable to a file descriptor
625 // over which we can signal that we have completed our startup and can be
626 // chrooted.
[email protected]ad6d2c42009-09-15 20:13:38627 const char* const sandbox_fd_string = getenv("SBX_D");
[email protected]ad6d2c42009-09-15 20:13:38628
[email protected]0dc32322010-09-16 09:46:59629 if (sandbox_fd_string) {
630 // Use the SUID sandbox. This still allows the seccomp sandbox to
631 // be enabled by the process later.
[email protected]8ecd3aad2009-11-04 08:32:22632 g_suid_sandbox_active = true;
633
[email protected]ad6d2c42009-09-15 20:13:38634 char* endptr;
635 const long fd_long = strtol(sandbox_fd_string, &endptr, 10);
636 if (!*sandbox_fd_string || *endptr || fd_long < 0 || fd_long > INT_MAX)
637 return false;
638 const int fd = fd_long;
639
640 PreSandboxInit();
[email protected]c9e45da02009-08-05 17:35:08641
[email protected]eaebefaf2010-02-23 22:58:18642 static const char kMsgChrootMe = 'C';
643 static const char kMsgChrootSuccessful = 'O';
[email protected]abe3ad92009-06-15 18:15:08644
[email protected]eaebefaf2010-02-23 22:58:18645 if (HANDLE_EINTR(write(fd, &kMsgChrootMe, 1)) != 1) {
[email protected]0e1611122009-07-10 18:17:32646 LOG(ERROR) << "Failed to write to chroot pipe: " << errno;
[email protected]abe3ad92009-06-15 18:15:08647 return false;
[email protected]0e1611122009-07-10 18:17:32648 }
[email protected]abe3ad92009-06-15 18:15:08649
[email protected]87ed6952009-07-16 02:52:15650 // We need to reap the chroot helper process in any event:
651 wait(NULL);
652
[email protected]abe3ad92009-06-15 18:15:08653 char reply;
[email protected]87f8ce62009-07-10 19:14:31654 if (HANDLE_EINTR(read(fd, &reply, 1)) != 1) {
[email protected]0e1611122009-07-10 18:17:32655 LOG(ERROR) << "Failed to read from chroot pipe: " << errno;
[email protected]abe3ad92009-06-15 18:15:08656 return false;
[email protected]0e1611122009-07-10 18:17:32657 }
[email protected]87f8ce62009-07-10 19:14:31658
[email protected]eaebefaf2010-02-23 22:58:18659 if (reply != kMsgChrootSuccessful) {
[email protected]0e1611122009-07-10 18:17:32660 LOG(ERROR) << "Error code reply from chroot helper";
[email protected]abe3ad92009-06-15 18:15:08661 return false;
[email protected]0e1611122009-07-10 18:17:32662 }
[email protected]abe3ad92009-06-15 18:15:08663
[email protected]cf3ac3972010-12-22 20:02:29664 SkiaFontConfigSetImplementation(
665 new FontConfigIPC(kMagicSandboxIPCDescriptor));
[email protected]abe3ad92009-06-15 18:15:08666
[email protected]415493be2009-07-10 17:50:24667 // Previously, we required that the binary be non-readable. This causes the
668 // kernel to mark the process as non-dumpable at startup. The thinking was
669 // that, although we were putting the renderers into a PID namespace (with
670 // the SUID sandbox), they would nonetheless be in the /same/ PID
671 // namespace. So they could ptrace each other unless they were non-dumpable.
672 //
673 // If the binary was readable, then there would be a window between process
674 // startup and the point where we set the non-dumpable flag in which a
675 // compromised renderer could ptrace attach.
676 //
677 // However, now that we have a zygote model, only the (trusted) zygote
678 // exists at this point and we can set the non-dumpable flag which is
679 // inherited by all our renderer children.
[email protected]4730db92009-07-22 00:40:48680 //
681 // Note: a non-dumpable process can't be debugged. To debug sandbox-related
682 // issues, one can specify --allow-sandbox-debugging to let the process be
683 // dumpable.
684 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
685 if (!command_line.HasSwitch(switches::kAllowSandboxDebugging)) {
686 prctl(PR_SET_DUMPABLE, 0, 0, 0, 0);
687 if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0)) {
688 LOG(ERROR) << "Failed to set non-dumpable flag";
689 return false;
690 }
[email protected]0e1611122009-07-10 18:17:32691 }
[email protected]cc9a9a882011-03-03 20:09:10692 } else if (CommandLine::ForCurrentProcess()->HasSwitch(
693 switches::kEnableSeccompSandbox)) {
[email protected]0dc32322010-09-16 09:46:59694 PreSandboxInit();
[email protected]cf3ac3972010-12-22 20:02:29695 SkiaFontConfigSetImplementation(
696 new FontConfigIPC(kMagicSandboxIPCDescriptor));
[email protected]abe3ad92009-06-15 18:15:08697 } else {
698 SkiaFontConfigUseDirectImplementation();
699 }
700
701 return true;
702}
[email protected]ad6d2c42009-09-15 20:13:38703#else // CHROMIUM_SELINUX
704
705static bool EnterSandbox() {
706 PreSandboxInit();
707 SkiaFontConfigUseIPCImplementation(kMagicSandboxIPCDescriptor);
[email protected]ad6d2c42009-09-15 20:13:38708 return true;
709}
710
711#endif // CHROMIUM_SELINUX
[email protected]abe3ad92009-06-15 18:15:08712
[email protected]cc8f1462009-06-12 17:36:55713bool ZygoteMain(const MainFunctionParams& params) {
[email protected]ad6d2c42009-09-15 20:13:38714#if !defined(CHROMIUM_SELINUX)
[email protected]a0f200ea2009-08-06 18:44:06715 g_am_zygote_or_renderer = true;
[email protected]ad6d2c42009-09-15 20:13:38716#endif
[email protected]a0f200ea2009-08-06 18:44:06717
[email protected]36ea6c6f2010-03-17 20:08:01718#if defined(SECCOMP_SANDBOX)
[email protected]a9c54a172009-11-07 06:09:38719 // The seccomp sandbox needs access to files in /proc, which might be denied
720 // after one of the other sandboxes have been started. So, obtain a suitable
721 // file handle in advance.
[email protected]cc9a9a882011-03-03 20:09:10722 if (CommandLine::ForCurrentProcess()->HasSwitch(
723 switches::kEnableSeccompSandbox)) {
[email protected]a9c54a172009-11-07 06:09:38724 g_proc_fd = open("/proc", O_DIRECTORY | O_RDONLY);
725 if (g_proc_fd < 0) {
726 LOG(ERROR) << "WARNING! Cannot access \"/proc\". Disabling seccomp "
727 "sandboxing.";
728 }
729 }
[email protected]36ea6c6f2010-03-17 20:08:01730#endif // SECCOMP_SANDBOX
[email protected]a9c54a172009-11-07 06:09:38731
732 // Turn on the SELinux or SUID sandbox
733 if (!EnterSandbox()) {
734 LOG(FATAL) << "Failed to enter sandbox. Fail safe abort. (errno: "
735 << errno << ")";
736 return false;
737 }
738
[email protected]715b4f262010-07-13 14:17:28739 int sandbox_flags = 0;
740 if (getenv("SBX_D"))
741 sandbox_flags |= ZygoteHost::kSandboxSUID;
742 if (getenv("SBX_PID_NS"))
743 sandbox_flags |= ZygoteHost::kSandboxPIDNS;
744 if (getenv("SBX_NET_NS"))
745 sandbox_flags |= ZygoteHost::kSandboxNetNS;
746
[email protected]36ea6c6f2010-03-17 20:08:01747#if defined(SECCOMP_SANDBOX)
[email protected]a9c54a172009-11-07 06:09:38748 // The seccomp sandbox will be turned on when the renderers start. But we can
749 // already check if sufficient support is available so that we only need to
750 // print one error message for the entire browser session.
[email protected]cc9a9a882011-03-03 20:09:10751 if (g_proc_fd >= 0 && CommandLine::ForCurrentProcess()->HasSwitch(
752 switches::kEnableSeccompSandbox)) {
[email protected]a9c54a172009-11-07 06:09:38753 if (!SupportsSeccompSandbox(g_proc_fd)) {
[email protected]e8c916a2009-11-04 17:52:47754 // There are a good number of users who cannot use the seccomp sandbox
755 // (e.g. because their distribution does not enable seccomp mode by
756 // default). While we would prefer to deny execution in this case, it
757 // seems more realistic to continue in degraded mode.
[email protected]1b5d28f2010-02-18 15:53:36758 LOG(ERROR) << "WARNING! This machine lacks support needed for the "
759 "Seccomp sandbox. Running renderers with Seccomp "
760 "sandboxing disabled.";
[email protected]e8c916a2009-11-04 17:52:47761 } else {
[email protected]8e96e502010-10-21 20:57:12762 VLOG(1) << "Enabling experimental Seccomp sandbox.";
[email protected]715b4f262010-07-13 14:17:28763 sandbox_flags |= ZygoteHost::kSandboxSeccomp;
[email protected]e8c916a2009-11-04 17:52:47764 }
765 }
[email protected]36ea6c6f2010-03-17 20:08:01766#endif // SECCOMP_SANDBOX
[email protected]e8c916a2009-11-04 17:52:47767
[email protected]715b4f262010-07-13 14:17:28768 Zygote zygote(sandbox_flags);
[email protected]c548be22010-03-08 12:55:57769 // This function call can return multiple times, once per fork().
[email protected]cc8f1462009-06-12 17:36:55770 return zygote.ProcessRequests();
771}