OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include <asm/unistd.h> | |
6 #include <dlfcn.h> | |
7 #include <errno.h> | |
8 #include <fcntl.h> | |
9 #include <linux/net.h> | |
10 #include <signal.h> | |
11 #include <string.h> | |
12 #include <sys/ioctl.h> | |
13 #include <sys/mman.h> | |
14 #include <sys/prctl.h> | |
15 #include <sys/socket.h> | |
16 #include <sys/stat.h> | |
17 #include <sys/types.h> | |
18 #include <ucontext.h> | |
19 #include <unistd.h> | |
20 | |
21 #include <vector> | |
22 | |
23 #include "base/basictypes.h" | |
24 #include "base/command_line.h" | |
25 #include "base/logging.h" | |
26 #include "build/build_config.h" | |
27 #include "content/public/common/content_switches.h" | |
28 | |
29 // These are the only architectures supported for now. | |
30 #if defined(__i386__) || defined(__x86_64__) || \ | |
31 (defined(__arm__) && (defined(__thumb__) || defined(__ARM_EABI__))) | |
32 #define SECCOMP_BPF_SANDBOX | |
33 #endif | |
34 | |
35 #if defined(SECCOMP_BPF_SANDBOX) | |
36 #include "base/posix/eintr_wrapper.h" | |
37 #include "content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.h" | |
38 #include "content/common/sandbox_linux/bpf_gpu_policy_linux.h" | |
39 #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h" | |
40 #include "content/common/sandbox_linux/sandbox_linux.h" | |
41 #include "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h" | |
42 #include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h" | |
43 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" | |
44 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h" | |
45 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h" | |
46 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" | |
47 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" | |
48 #include "sandbox/linux/services/linux_syscalls.h" | |
49 | |
50 using sandbox::BaselinePolicy; | |
51 using sandbox::ErrorCode; | |
52 using sandbox::SandboxBPF; | |
53 using sandbox::SyscallSets; | |
54 using sandbox::arch_seccomp_data; | |
55 | |
56 namespace content { | |
57 | |
58 namespace { | |
59 | |
60 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy); | |
61 | |
62 inline bool IsChromeOS() { | |
63 #if defined(OS_CHROMEOS) | |
64 return true; | |
65 #else | |
66 return false; | |
67 #endif | |
68 } | |
69 | |
70 inline bool IsArchitectureArm() { | |
71 #if defined(__arm__) | |
72 return true; | |
73 #else | |
74 return false; | |
75 #endif | |
76 } | |
77 | |
78 inline bool IsUsingToolKitGtk() { | |
79 #if defined(TOOLKIT_GTK) | |
80 return true; | |
81 #else | |
82 return false; | |
83 #endif | |
84 } | |
85 | |
86 // Policy for renderer and worker processes. | |
87 // TODO(jln): move to renderer/ | |
88 | |
89 class RendererOrWorkerProcessPolicy : public SandboxBPFBasePolicy { | |
90 public: | |
91 RendererOrWorkerProcessPolicy() {} | |
92 virtual ~RendererOrWorkerProcessPolicy() {} | |
93 | |
94 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
95 int system_call_number) const OVERRIDE; | |
96 | |
97 private: | |
98 DISALLOW_COPY_AND_ASSIGN(RendererOrWorkerProcessPolicy); | |
99 }; | |
100 | |
101 ErrorCode RendererOrWorkerProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, | |
102 int sysno) const { | |
103 switch (sysno) { | |
104 case __NR_clone: | |
105 return sandbox::RestrictCloneToThreadsAndEPERMFork(sandbox); | |
106 case __NR_ioctl: | |
107 return sandbox::RestrictIoctl(sandbox); | |
108 case __NR_prctl: | |
109 return sandbox::RestrictPrctl(sandbox); | |
110 // Allow the system calls below. | |
111 case __NR_fdatasync: | |
112 case __NR_fsync: | |
113 case __NR_getpriority: | |
114 #if defined(__i386__) || defined(__x86_64__) | |
115 case __NR_getrlimit: | |
116 #endif | |
117 #if defined(__i386__) || defined(__arm__) | |
118 case __NR_ugetrlimit: | |
119 #endif | |
120 case __NR_mremap: // See crbug.com/149834. | |
121 case __NR_pread64: | |
122 case __NR_pwrite64: | |
123 case __NR_sched_getaffinity: | |
124 case __NR_sched_get_priority_max: | |
125 case __NR_sched_get_priority_min: | |
126 case __NR_sched_getparam: | |
127 case __NR_sched_getscheduler: | |
128 case __NR_sched_setscheduler: | |
129 case __NR_setpriority: | |
130 case __NR_sysinfo: | |
131 case __NR_times: | |
132 case __NR_uname: | |
133 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
134 case __NR_prlimit64: | |
135 return ErrorCode(EPERM); // See crbug.com/160157. | |
136 default: | |
137 if (IsUsingToolKitGtk()) { | |
138 #if defined(__x86_64__) || defined(__arm__) | |
139 if (SyscallSets::IsSystemVSharedMemory(sysno)) | |
140 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
141 #endif | |
142 #if defined(__i386__) | |
143 if (SyscallSets::IsSystemVIpc(sysno)) | |
144 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
145 #endif | |
146 } | |
147 | |
148 // Default on the content baseline policy. | |
149 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno); | |
150 } | |
151 } | |
152 | |
153 // Policy for PPAPI plugins. | |
154 // TODO(jln): move to ppapi_plugin/. | |
155 class FlashProcessPolicy : public SandboxBPFBasePolicy { | |
156 public: | |
157 FlashProcessPolicy() {} | |
158 virtual ~FlashProcessPolicy() {} | |
159 | |
160 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
161 int system_call_number) const OVERRIDE; | |
162 | |
163 private: | |
164 DISALLOW_COPY_AND_ASSIGN(FlashProcessPolicy); | |
165 }; | |
166 | |
167 ErrorCode FlashProcessPolicy::EvaluateSyscall(SandboxBPF* sandbox, | |
168 int sysno) const { | |
169 switch (sysno) { | |
170 case __NR_clone: | |
171 return sandbox::RestrictCloneToThreadsAndEPERMFork(sandbox); | |
172 case __NR_pread64: | |
173 case __NR_pwrite64: | |
174 case __NR_sched_get_priority_max: | |
175 case __NR_sched_get_priority_min: | |
176 case __NR_sched_getaffinity: | |
177 case __NR_sched_getparam: | |
178 case __NR_sched_getscheduler: | |
179 case __NR_sched_setscheduler: | |
180 case __NR_times: | |
181 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
182 case __NR_ioctl: | |
183 return ErrorCode(ENOTTY); // Flash Access. | |
184 default: | |
185 if (IsUsingToolKitGtk()) { | |
186 #if defined(__x86_64__) || defined(__arm__) | |
187 if (SyscallSets::IsSystemVSharedMemory(sysno)) | |
188 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
189 #endif | |
190 #if defined(__i386__) | |
191 if (SyscallSets::IsSystemVIpc(sysno)) | |
192 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
193 #endif | |
194 } | |
195 | |
196 // Default on the baseline policy. | |
197 return SandboxBPFBasePolicy::EvaluateSyscall(sandbox, sysno); | |
198 } | |
199 } | |
200 | |
201 class BlacklistDebugAndNumaPolicy : public SandboxBPFBasePolicy { | |
202 public: | |
203 BlacklistDebugAndNumaPolicy() {} | |
204 virtual ~BlacklistDebugAndNumaPolicy() {} | |
205 | |
206 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
207 int system_call_number) const OVERRIDE; | |
208 | |
209 private: | |
210 DISALLOW_COPY_AND_ASSIGN(BlacklistDebugAndNumaPolicy); | |
211 }; | |
212 | |
213 ErrorCode BlacklistDebugAndNumaPolicy::EvaluateSyscall(SandboxBPF* sandbox, | |
214 int sysno) const { | |
215 if (!SandboxBPF::IsValidSyscallNumber(sysno)) { | |
216 // TODO(jln) we should not have to do that in a trivial policy. | |
217 return ErrorCode(ENOSYS); | |
218 } | |
219 if (SyscallSets::IsDebug(sysno) || SyscallSets::IsNuma(sysno)) | |
220 return sandbox->Trap(sandbox::CrashSIGSYS_Handler, NULL); | |
221 | |
222 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
223 } | |
224 | |
225 class AllowAllPolicy : public SandboxBPFBasePolicy { | |
226 public: | |
227 AllowAllPolicy() {} | |
228 virtual ~AllowAllPolicy() {} | |
229 | |
230 virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler, | |
231 int system_call_number) const OVERRIDE; | |
232 | |
233 private: | |
234 DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy); | |
235 }; | |
236 | |
237 // Allow all syscalls. | |
238 // This will still deny x32 or IA32 calls in 64 bits mode or | |
239 // 64 bits system calls in compatibility mode. | |
240 ErrorCode AllowAllPolicy::EvaluateSyscall(SandboxBPF*, int sysno) const { | |
241 if (!SandboxBPF::IsValidSyscallNumber(sysno)) { | |
242 // TODO(jln) we should not have to do that in a trivial policy. | |
243 return ErrorCode(ENOSYS); | |
244 } else { | |
245 return ErrorCode(ErrorCode::ERR_ALLOWED); | |
246 } | |
247 } | |
248 | |
249 // If a BPF policy is engaged for |process_type|, run a few sanity checks. | |
250 void RunSandboxSanityChecks(const std::string& process_type) { | |
251 if (process_type == switches::kRendererProcess || | |
252 process_type == switches::kWorkerProcess || | |
253 process_type == switches::kGpuProcess || | |
254 process_type == switches::kPpapiPluginProcess) { | |
255 int syscall_ret; | |
256 errno = 0; | |
257 | |
258 // Without the sandbox, this would EBADF. | |
259 syscall_ret = fchmod(-1, 07777); | |
260 CHECK_EQ(-1, syscall_ret); | |
261 CHECK_EQ(EPERM, errno); | |
262 | |
263 // Run most of the sanity checks only in DEBUG mode to avoid a perf. | |
264 // impact. | |
265 #if !defined(NDEBUG) | |
266 // open() must be restricted. | |
267 syscall_ret = open("/etc/passwd", O_RDONLY); | |
268 CHECK_EQ(-1, syscall_ret); | |
269 CHECK_EQ(SandboxBPFBasePolicy::GetFSDeniedErrno(), errno); | |
270 | |
271 // We should never allow the creation of netlink sockets. | |
272 syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0); | |
273 CHECK_EQ(-1, syscall_ret); | |
274 CHECK_EQ(EPERM, errno); | |
275 #endif // !defined(NDEBUG) | |
276 } | |
277 } | |
278 | |
279 | |
280 // This function takes ownership of |policy|. | |
281 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy) { | |
282 // Starting the sandbox is a one-way operation. The kernel doesn't allow | |
283 // us to unload a sandbox policy after it has been started. Nonetheless, | |
284 // in order to make the use of the "Sandbox" object easier, we allow for | |
285 // the object to be destroyed after the sandbox has been started. Note that | |
286 // doing so does not stop the sandbox. | |
287 SandboxBPF sandbox; | |
288 sandbox.SetSandboxPolicy(policy); | |
289 sandbox.StartSandbox(); | |
290 } | |
291 | |
292 // nacl_helper needs to be tiny and includes only part of content/ | |
293 // in its dependencies. Make sure to not link things that are not needed. | |
294 #if !defined(IN_NACL_HELPER) | |
295 scoped_ptr<SandboxBPFBasePolicy> GetGpuProcessSandbox() { | |
296 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
297 bool allow_sysv_shm = false; | |
298 if (command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm)) { | |
299 DCHECK(IsArchitectureArm()); | |
300 allow_sysv_shm = true; | |
301 } | |
302 | |
303 if (IsChromeOS() && IsArchitectureArm()) { | |
304 return scoped_ptr<SandboxBPFBasePolicy>( | |
305 new CrosArmGpuProcessPolicy(allow_sysv_shm)); | |
306 } else { | |
307 return scoped_ptr<SandboxBPFBasePolicy>(new GpuProcessPolicy); | |
308 } | |
309 } | |
310 | |
311 // Initialize the seccomp-bpf sandbox. | |
312 bool StartBPFSandbox(const CommandLine& command_line, | |
313 const std::string& process_type) { | |
314 scoped_ptr<SandboxBPFBasePolicy> policy; | |
315 | |
316 if (process_type == switches::kGpuProcess) { | |
317 policy.reset(GetGpuProcessSandbox().release()); | |
318 } else if (process_type == switches::kRendererProcess || | |
319 process_type == switches::kWorkerProcess) { | |
320 policy.reset(new RendererOrWorkerProcessPolicy); | |
321 } else if (process_type == switches::kPpapiPluginProcess) { | |
322 policy.reset(new FlashProcessPolicy); | |
323 } else if (process_type == switches::kUtilityProcess) { | |
324 policy.reset(new BlacklistDebugAndNumaPolicy); | |
325 } else { | |
326 NOTREACHED(); | |
327 policy.reset(new AllowAllPolicy); | |
328 } | |
329 | |
330 CHECK(policy->PreSandboxHook()); | |
331 StartSandboxWithPolicy(policy.release()); | |
332 | |
333 RunSandboxSanityChecks(process_type); | |
334 return true; | |
335 } | |
336 #else // defined(IN_NACL_HELPER) | |
337 bool StartBPFSandbox(const CommandLine& command_line, | |
338 const std::string& process_type) { | |
339 NOTREACHED(); | |
340 // Avoid -Wunused-function with no-op code. | |
341 ignore_result(IsChromeOS); | |
342 ignore_result(IsArchitectureArm); | |
343 ignore_result(RunSandboxSanityChecks); | |
344 return false; | |
345 } | |
346 #endif // !defined(IN_NACL_HELPER) | |
347 | |
348 } // namespace | |
349 | |
350 #endif // SECCOMP_BPF_SANDBOX | |
351 | |
352 // Is seccomp BPF globally enabled? | |
353 bool SandboxSeccompBPF::IsSeccompBPFDesired() { | |
354 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
355 if (!command_line.HasSwitch(switches::kNoSandbox) && | |
356 !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) { | |
357 return true; | |
358 } else { | |
359 return false; | |
360 } | |
361 } | |
362 | |
363 bool SandboxSeccompBPF::ShouldEnableSeccompBPF( | |
364 const std::string& process_type) { | |
365 #if defined(SECCOMP_BPF_SANDBOX) | |
366 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
367 if (process_type == switches::kGpuProcess) | |
368 return !command_line.HasSwitch(switches::kDisableGpuSandbox); | |
369 | |
370 return true; | |
371 #endif // SECCOMP_BPF_SANDBOX | |
372 return false; | |
373 } | |
374 | |
375 bool SandboxSeccompBPF::SupportsSandbox() { | |
376 #if defined(SECCOMP_BPF_SANDBOX) | |
377 // TODO(jln): pass the saved proc_fd_ from the LinuxSandbox singleton | |
378 // here. | |
379 SandboxBPF::SandboxStatus bpf_sandbox_status = | |
380 SandboxBPF::SupportsSeccompSandbox(-1); | |
381 // Kernel support is what we are interested in here. Other status | |
382 // such as STATUS_UNAVAILABLE (has threads) still indicate kernel support. | |
383 // We make this a negative check, since if there is a bug, we would rather | |
384 // "fail closed" (expect a sandbox to be available and try to start it). | |
385 if (bpf_sandbox_status != SandboxBPF::STATUS_UNSUPPORTED) { | |
386 return true; | |
387 } | |
388 #endif | |
389 return false; | |
390 } | |
391 | |
392 bool SandboxSeccompBPF::StartSandbox(const std::string& process_type) { | |
393 #if defined(SECCOMP_BPF_SANDBOX) | |
394 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
395 | |
396 if (IsSeccompBPFDesired() && // Global switches policy. | |
397 ShouldEnableSeccompBPF(process_type) && // Process-specific policy. | |
398 SupportsSandbox()) { | |
399 // If the kernel supports the sandbox, and if the command line says we | |
400 // should enable it, enable it or die. | |
401 bool started_sandbox = StartBPFSandbox(command_line, process_type); | |
402 CHECK(started_sandbox); | |
403 return true; | |
404 } | |
405 #endif | |
406 return false; | |
407 } | |
408 | |
409 bool SandboxSeccompBPF::StartSandboxWithExternalPolicy( | |
410 scoped_ptr<sandbox::SandboxBPFPolicy> policy) { | |
411 #if defined(SECCOMP_BPF_SANDBOX) | |
412 if (IsSeccompBPFDesired() && SupportsSandbox()) { | |
413 CHECK(policy); | |
414 StartSandboxWithPolicy(policy.release()); | |
415 return true; | |
416 } | |
417 #endif // defined(SECCOMP_BPF_SANDBOX) | |
418 return false; | |
419 } | |
420 | |
421 scoped_ptr<sandbox::SandboxBPFPolicy> | |
422 SandboxSeccompBPF::GetBaselinePolicy() { | |
423 #if defined(SECCOMP_BPF_SANDBOX) | |
424 return scoped_ptr<sandbox::SandboxBPFPolicy>(new BaselinePolicy); | |
425 #else | |
426 return scoped_ptr<sandbox::SandboxBPFPolicy>(); | |
427 #endif // defined(SECCOMP_BPF_SANDBOX) | |
428 } | |
429 | |
430 } // namespace content | |
OLD | NEW |