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