• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "content/common/sandbox_linux/sandbox_seccomp_bpf_linux.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <sys/socket.h>
10 #include <sys/stat.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <sys/types.h>
14 
15 #include "base/basictypes.h"
16 #include "base/command_line.h"
17 #include "base/logging.h"
18 #include "build/build_config.h"
19 #include "content/public/common/content_switches.h"
20 #include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h"
21 
22 #if defined(USE_SECCOMP_BPF)
23 
24 #include "base/posix/eintr_wrapper.h"
25 #include "content/common/sandbox_linux/bpf_cros_arm_gpu_policy_linux.h"
26 #include "content/common/sandbox_linux/bpf_gpu_policy_linux.h"
27 #include "content/common/sandbox_linux/bpf_ppapi_policy_linux.h"
28 #include "content/common/sandbox_linux/bpf_renderer_policy_linux.h"
29 #include "content/common/sandbox_linux/sandbox_bpf_base_policy_linux.h"
30 #include "content/common/sandbox_linux/sandbox_linux.h"
31 #include "sandbox/linux/seccomp-bpf-helpers/baseline_policy.h"
32 #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h"
33 #include "sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.h"
34 #include "sandbox/linux/seccomp-bpf-helpers/syscall_sets.h"
35 #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
36 #include "sandbox/linux/services/linux_syscalls.h"
37 
38 using sandbox::BaselinePolicy;
39 using sandbox::SyscallSets;
40 
41 #else
42 
43 // Make sure that seccomp-bpf does not get disabled by mistake. Also make sure
44 // that we think twice about this when adding a new architecture.
45 #if !defined(ARCH_CPU_MIPS_FAMILY) && !defined(ARCH_CPU_ARM64)
46 #error "Seccomp-bpf disabled on supported architecture!"
47 #endif  // !defined(ARCH_CPU_MIPS_FAMILY) && !defined(ARCH_CPU_ARM64)
48 
49 #endif  //
50 
51 namespace content {
52 
53 #if defined(USE_SECCOMP_BPF)
54 namespace {
55 
56 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy);
57 
IsChromeOS()58 inline bool IsChromeOS() {
59 #if defined(OS_CHROMEOS)
60   return true;
61 #else
62   return false;
63 #endif
64 }
65 
IsArchitectureArm()66 inline bool IsArchitectureArm() {
67 #if defined(__arm__)
68   return true;
69 #else
70   return false;
71 #endif
72 }
73 
74 class BlacklistDebugAndNumaPolicy : public SandboxBPFBasePolicy {
75  public:
BlacklistDebugAndNumaPolicy()76   BlacklistDebugAndNumaPolicy() {}
~BlacklistDebugAndNumaPolicy()77   virtual ~BlacklistDebugAndNumaPolicy() {}
78 
79   virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
80                                     int system_call_number) const OVERRIDE;
81 
82  private:
83   DISALLOW_COPY_AND_ASSIGN(BlacklistDebugAndNumaPolicy);
84 };
85 
EvaluateSyscall(SandboxBPF * sandbox,int sysno) const86 ErrorCode BlacklistDebugAndNumaPolicy::EvaluateSyscall(SandboxBPF* sandbox,
87                                                        int sysno) const {
88   if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
89     // TODO(jln) we should not have to do that in a trivial policy.
90     return ErrorCode(ENOSYS);
91   }
92   if (SyscallSets::IsDebug(sysno) || SyscallSets::IsNuma(sysno))
93     return sandbox->Trap(sandbox::CrashSIGSYS_Handler, NULL);
94 
95   return ErrorCode(ErrorCode::ERR_ALLOWED);
96 }
97 
98 class AllowAllPolicy : public SandboxBPFBasePolicy {
99  public:
AllowAllPolicy()100   AllowAllPolicy() {}
~AllowAllPolicy()101   virtual ~AllowAllPolicy() {}
102 
103   virtual ErrorCode EvaluateSyscall(SandboxBPF* sandbox_compiler,
104                                     int system_call_number) const OVERRIDE;
105 
106  private:
107   DISALLOW_COPY_AND_ASSIGN(AllowAllPolicy);
108 };
109 
110 // Allow all syscalls.
111 // This will still deny x32 or IA32 calls in 64 bits mode or
112 // 64 bits system calls in compatibility mode.
EvaluateSyscall(SandboxBPF *,int sysno) const113 ErrorCode AllowAllPolicy::EvaluateSyscall(SandboxBPF*, int sysno) const {
114   if (!SandboxBPF::IsValidSyscallNumber(sysno)) {
115     // TODO(jln) we should not have to do that in a trivial policy.
116     return ErrorCode(ENOSYS);
117   } else {
118     return ErrorCode(ErrorCode::ERR_ALLOWED);
119   }
120 }
121 
122 // If a BPF policy is engaged for |process_type|, run a few sanity checks.
RunSandboxSanityChecks(const std::string & process_type)123 void RunSandboxSanityChecks(const std::string& process_type) {
124   if (process_type == switches::kRendererProcess ||
125       process_type == switches::kWorkerProcess ||
126       process_type == switches::kGpuProcess ||
127       process_type == switches::kPpapiPluginProcess) {
128     int syscall_ret;
129     errno = 0;
130 
131     // Without the sandbox, this would EBADF.
132     syscall_ret = fchmod(-1, 07777);
133     CHECK_EQ(-1, syscall_ret);
134     CHECK_EQ(EPERM, errno);
135 
136     // Run most of the sanity checks only in DEBUG mode to avoid a perf.
137     // impact.
138 #if !defined(NDEBUG)
139     // open() must be restricted.
140     syscall_ret = open("/etc/passwd", O_RDONLY);
141     CHECK_EQ(-1, syscall_ret);
142     CHECK_EQ(SandboxBPFBasePolicy::GetFSDeniedErrno(), errno);
143 
144     // We should never allow the creation of netlink sockets.
145     syscall_ret = socket(AF_NETLINK, SOCK_DGRAM, 0);
146     CHECK_EQ(-1, syscall_ret);
147     CHECK_EQ(EPERM, errno);
148 #endif  // !defined(NDEBUG)
149   }
150 }
151 
152 
153 // This function takes ownership of |policy|.
StartSandboxWithPolicy(sandbox::SandboxBPFPolicy * policy)154 void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy) {
155   // Starting the sandbox is a one-way operation. The kernel doesn't allow
156   // us to unload a sandbox policy after it has been started. Nonetheless,
157   // in order to make the use of the "Sandbox" object easier, we allow for
158   // the object to be destroyed after the sandbox has been started. Note that
159   // doing so does not stop the sandbox.
160   SandboxBPF sandbox;
161   sandbox.SetSandboxPolicy(policy);
162   CHECK(sandbox.StartSandbox(SandboxBPF::PROCESS_SINGLE_THREADED));
163 }
164 
165 // nacl_helper needs to be tiny and includes only part of content/
166 // in its dependencies. Make sure to not link things that are not needed.
167 #if !defined(IN_NACL_HELPER)
GetGpuProcessSandbox()168 scoped_ptr<SandboxBPFBasePolicy> GetGpuProcessSandbox() {
169   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
170   bool allow_sysv_shm = false;
171   if (command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm)) {
172     DCHECK(IsArchitectureArm());
173     allow_sysv_shm = true;
174   }
175 
176   if (IsChromeOS() && IsArchitectureArm()) {
177     return scoped_ptr<SandboxBPFBasePolicy>(
178         new CrosArmGpuProcessPolicy(allow_sysv_shm));
179   } else {
180     return scoped_ptr<SandboxBPFBasePolicy>(new GpuProcessPolicy);
181   }
182 }
183 
184 // Initialize the seccomp-bpf sandbox.
StartBPFSandbox(const CommandLine & command_line,const std::string & process_type)185 bool StartBPFSandbox(const CommandLine& command_line,
186                      const std::string& process_type) {
187   scoped_ptr<SandboxBPFBasePolicy> policy;
188 
189   if (process_type == switches::kGpuProcess) {
190     policy.reset(GetGpuProcessSandbox().release());
191   } else if (process_type == switches::kRendererProcess ||
192              process_type == switches::kWorkerProcess) {
193     policy.reset(new RendererProcessPolicy);
194   } else if (process_type == switches::kPpapiPluginProcess) {
195     policy.reset(new PpapiProcessPolicy);
196   } else if (process_type == switches::kUtilityProcess) {
197     policy.reset(new BlacklistDebugAndNumaPolicy);
198   } else {
199     NOTREACHED();
200     policy.reset(new AllowAllPolicy);
201   }
202 
203   CHECK(policy->PreSandboxHook());
204   StartSandboxWithPolicy(policy.release());
205 
206   RunSandboxSanityChecks(process_type);
207   return true;
208 }
209 #else  // defined(IN_NACL_HELPER)
StartBPFSandbox(const CommandLine & command_line,const std::string & process_type)210 bool StartBPFSandbox(const CommandLine& command_line,
211                      const std::string& process_type) {
212   NOTREACHED();
213   // Avoid -Wunused-function with no-op code.
214   ignore_result(IsChromeOS);
215   ignore_result(IsArchitectureArm);
216   ignore_result(RunSandboxSanityChecks);
217   return false;
218 }
219 #endif  // !defined(IN_NACL_HELPER)
220 
221 }  // namespace
222 
223 #endif  // USE_SECCOMP_BPF
224 
225 // Is seccomp BPF globally enabled?
IsSeccompBPFDesired()226 bool SandboxSeccompBPF::IsSeccompBPFDesired() {
227   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
228   if (!command_line.HasSwitch(switches::kNoSandbox) &&
229       !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) {
230     return true;
231   } else {
232     return false;
233   }
234 }
235 
ShouldEnableSeccompBPF(const std::string & process_type)236 bool SandboxSeccompBPF::ShouldEnableSeccompBPF(
237     const std::string& process_type) {
238 #if defined(USE_SECCOMP_BPF)
239   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
240   if (process_type == switches::kGpuProcess)
241     return !command_line.HasSwitch(switches::kDisableGpuSandbox);
242 
243   return true;
244 #endif  // USE_SECCOMP_BPF
245   return false;
246 }
247 
SupportsSandbox()248 bool SandboxSeccompBPF::SupportsSandbox() {
249 #if defined(USE_SECCOMP_BPF)
250   // TODO(jln): pass the saved proc_fd_ from the LinuxSandbox singleton
251   // here.
252   SandboxBPF::SandboxStatus bpf_sandbox_status =
253       SandboxBPF::SupportsSeccompSandbox(-1);
254   // Kernel support is what we are interested in here. Other status
255   // such as STATUS_UNAVAILABLE (has threads) still indicate kernel support.
256   // We make this a negative check, since if there is a bug, we would rather
257   // "fail closed" (expect a sandbox to be available and try to start it).
258   if (bpf_sandbox_status != SandboxBPF::STATUS_UNSUPPORTED) {
259     return true;
260   }
261 #endif
262   return false;
263 }
264 
StartSandbox(const std::string & process_type)265 bool SandboxSeccompBPF::StartSandbox(const std::string& process_type) {
266 #if defined(USE_SECCOMP_BPF)
267   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
268 
269   if (IsSeccompBPFDesired() &&  // Global switches policy.
270       ShouldEnableSeccompBPF(process_type) &&  // Process-specific policy.
271       SupportsSandbox()) {
272     // If the kernel supports the sandbox, and if the command line says we
273     // should enable it, enable it or die.
274     bool started_sandbox = StartBPFSandbox(command_line, process_type);
275     CHECK(started_sandbox);
276     return true;
277   }
278 #endif
279   return false;
280 }
281 
StartSandboxWithExternalPolicy(scoped_ptr<sandbox::SandboxBPFPolicy> policy)282 bool SandboxSeccompBPF::StartSandboxWithExternalPolicy(
283     scoped_ptr<sandbox::SandboxBPFPolicy> policy) {
284 #if defined(USE_SECCOMP_BPF)
285   if (IsSeccompBPFDesired() && SupportsSandbox()) {
286     CHECK(policy);
287     StartSandboxWithPolicy(policy.release());
288     return true;
289   }
290 #endif  // defined(USE_SECCOMP_BPF)
291   return false;
292 }
293 
294 scoped_ptr<sandbox::SandboxBPFPolicy>
GetBaselinePolicy()295 SandboxSeccompBPF::GetBaselinePolicy() {
296 #if defined(USE_SECCOMP_BPF)
297   return scoped_ptr<sandbox::SandboxBPFPolicy>(new BaselinePolicy);
298 #else
299   return scoped_ptr<sandbox::SandboxBPFPolicy>();
300 #endif  // defined(USE_SECCOMP_BPF)
301 }
302 
303 }  // namespace content
304