Home | History | Annotate | Download | only in sandbox_linux
      1 // Copyright 2014 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 "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h"
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <sys/stat.h>
     10 #include <sys/types.h>
     11 #include <unistd.h>
     12 
     13 #include "base/basictypes.h"
     14 #include "base/callback.h"
     15 #include "base/command_line.h"
     16 #include "base/compiler_specific.h"
     17 #include "base/logging.h"
     18 #include "base/memory/scoped_ptr.h"
     19 #include "base/posix/eintr_wrapper.h"
     20 #include "build/build_config.h"
     21 #include "components/nacl/common/nacl_switches.h"
     22 #include "components/nacl/loader/nonsfi/nonsfi_sandbox.h"
     23 #include "components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.h"
     24 #include "sandbox/linux/services/credentials.h"
     25 #include "sandbox/linux/services/thread_helpers.h"
     26 #include "sandbox/linux/suid/client/setuid_sandbox_client.h"
     27 
     28 namespace nacl {
     29 
     30 namespace {
     31 
     32 // This is a poor man's check on whether we are sandboxed.
     33 bool IsSandboxed() {
     34   int proc_fd = open("/proc/self/exe", O_RDONLY);
     35   if (proc_fd >= 0) {
     36     PCHECK(0 == IGNORE_EINTR(close(proc_fd)));
     37     return false;
     38   }
     39   return true;
     40 }
     41 
     42 }  // namespace
     43 
     44 NaClSandbox::NaClSandbox()
     45     : layer_one_enabled_(false),
     46       layer_one_sealed_(false),
     47       layer_two_enabled_(false),
     48       layer_two_is_nonsfi_(false),
     49       proc_fd_(-1),
     50       setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
     51   proc_fd_.reset(
     52       HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)));
     53   PCHECK(proc_fd_.is_valid());
     54 }
     55 
     56 NaClSandbox::~NaClSandbox() {
     57 }
     58 
     59 bool NaClSandbox::IsSingleThreaded() {
     60   CHECK(proc_fd_.is_valid());
     61   base::ScopedFD proc_self_task(HANDLE_EINTR(openat(
     62       proc_fd_.get(), "self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)));
     63   PCHECK(proc_self_task.is_valid());
     64   return sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
     65 }
     66 
     67 bool NaClSandbox::HasOpenDirectory() {
     68   CHECK(proc_fd_.is_valid());
     69   sandbox::Credentials credentials;
     70   return credentials.HasOpenDirectory(proc_fd_.get());
     71 }
     72 
     73 void NaClSandbox::InitializeLayerOneSandbox() {
     74   // Check that IsSandboxed() works. We should not be sandboxed at this point.
     75   CHECK(!IsSandboxed()) << "Unexpectedly sandboxed!";
     76 
     77   if (setuid_sandbox_client_->IsSuidSandboxChild()) {
     78     setuid_sandbox_client_->CloseDummyFile();
     79 
     80     // Make sure that no directory file descriptor is open, as it would bypass
     81     // the setuid sandbox model.
     82     CHECK(!HasOpenDirectory());
     83 
     84     // Get sandboxed.
     85     CHECK(setuid_sandbox_client_->ChrootMe());
     86     CHECK(IsSandboxed());
     87     layer_one_enabled_ = true;
     88   }
     89 }
     90 
     91 void NaClSandbox::CheckForExpectedNumberOfOpenFds() {
     92   if (setuid_sandbox_client_->IsSuidSandboxChild()) {
     93     // We expect to have the following FDs open:
     94     //  1-3) stdin, stdout, stderr.
     95     //  4) The /dev/urandom FD used by base::GetUrandomFD().
     96     //  5) A dummy pipe FD used to overwrite kSandboxIPCChannel.
     97     //  6) The socket created by the SUID sandbox helper, used by ChrootMe().
     98     //     After ChrootMe(), this is no longer connected to anything.
     99     //     (Only present when running under the SUID sandbox.)
    100     //  7) The socket for the Chrome IPC channel that's connected to the
    101     //     browser process, kPrimaryIPCChannel.
    102     //
    103     // This sanity check ensures that dynamically loaded libraries don't
    104     // leave any FDs open before we enable the sandbox.
    105     sandbox::Credentials credentials;
    106     CHECK_EQ(7, credentials.CountOpenFds(proc_fd_.get()));
    107   }
    108 }
    109 
    110 void NaClSandbox::InitializeLayerTwoSandbox(bool uses_nonsfi_mode) {
    111   // seccomp-bpf only applies to the current thread, so it's critical to only
    112   // have a single thread running here.
    113   DCHECK(!layer_one_sealed_);
    114   CHECK(IsSingleThreaded());
    115   CheckForExpectedNumberOfOpenFds();
    116 
    117   if (uses_nonsfi_mode) {
    118     layer_two_enabled_ = nacl::nonsfi::InitializeBPFSandbox();
    119     layer_two_is_nonsfi_ = true;
    120   } else {
    121     layer_two_enabled_ = nacl::InitializeBPFSandbox();
    122   }
    123 }
    124 
    125 void NaClSandbox::SealLayerOneSandbox() {
    126   if (!layer_two_enabled_) {
    127     // If nothing prevents us, check that there is no superfluous directory
    128     // open.
    129     CHECK(!HasOpenDirectory());
    130   }
    131   proc_fd_.reset();
    132   layer_one_sealed_ = true;
    133 }
    134 
    135 void NaClSandbox::CheckSandboxingStateWithPolicy() {
    136   static const char kItIsDangerousMsg[] = " this is dangerous.";
    137   static const char kItIsNotAllowedMsg[] =
    138       " this is not allowed in this configuration.";
    139 
    140   const bool no_sandbox_for_nonsfi_ok =
    141       CommandLine::ForCurrentProcess()->HasSwitch(
    142           switches::kNaClDangerousNoSandboxNonSfi);
    143   const bool can_be_no_sandbox =
    144       !layer_two_is_nonsfi_ || no_sandbox_for_nonsfi_ok;
    145 
    146   if (!layer_one_enabled_ || !layer_one_sealed_) {
    147     static const char kNoSuidMsg[] =
    148         "The SUID sandbox is not engaged for NaCl:";
    149     if (can_be_no_sandbox)
    150       LOG(ERROR) << kNoSuidMsg << kItIsDangerousMsg;
    151     else
    152       LOG(FATAL) << kNoSuidMsg << kItIsNotAllowedMsg;
    153   }
    154 
    155   if (!layer_two_enabled_) {
    156     static const char kNoBpfMsg[] =
    157         "The seccomp-bpf sandbox is not engaged for NaCl:";
    158     if (can_be_no_sandbox)
    159       LOG(ERROR) << kNoBpfMsg << kItIsDangerousMsg;
    160     else
    161       LOG(FATAL) << kNoBpfMsg << kItIsNotAllowedMsg;
    162   }
    163 }
    164 
    165 }  // namespace nacl
    166