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