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