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 |