15c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Copyright 2014 The Chromium Authors. All rights reserved.
25c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// Use of this source code is governed by a BSD-style license that can be
35c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// found in the LICENSE file.
45c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
55c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h"
65c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
75c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <errno.h>
85c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <fcntl.h>
95c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <sys/stat.h>
105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <sys/types.h>
115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include <unistd.h>
125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "base/basictypes.h"
145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "base/callback.h"
155c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "base/command_line.h"
165c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "base/compiler_specific.h"
175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "base/logging.h"
185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "base/memory/scoped_ptr.h"
195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "base/posix/eintr_wrapper.h"
205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "build/build_config.h"
215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "components/nacl/common/nacl_switches.h"
225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "components/nacl/loader/nonsfi/nonsfi_sandbox.h"
235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "components/nacl/loader/sandbox_linux/nacl_bpf_sandbox_linux.h"
245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "sandbox/linux/services/credentials.h"
255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "sandbox/linux/services/thread_helpers.h"
265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liunamespace nacl {
295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liunamespace {
315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu// This is a poor man's check on whether we are sandboxed.
335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubool IsSandboxed() {
345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  int proc_fd = open("/proc/self/exe", O_RDONLY);
355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (proc_fd >= 0) {
365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    PCHECK(0 == IGNORE_EINTR(close(proc_fd)));
375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return false;
385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return true;
405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}  // namespace
435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuNaClSandbox::NaClSandbox()
455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    : layer_one_enabled_(false),
465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      layer_one_sealed_(false),
475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      layer_two_enabled_(false),
485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      layer_two_is_nonsfi_(false),
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      proc_fd_(-1),
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  proc_fd_.reset(
525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC)));
535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  PCHECK(proc_fd_.is_valid());
545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo LiuNaClSandbox::~NaClSandbox() {
575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubool NaClSandbox::IsSingleThreaded() {
605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  CHECK(proc_fd_.is_valid());
615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  base::ScopedFD proc_self_task(HANDLE_EINTR(openat(
625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      proc_fd_.get(), "self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC)));
635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  PCHECK(proc_self_task.is_valid());
645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get());
655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
665c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
675c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liubool NaClSandbox::HasOpenDirectory() {
685c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  CHECK(proc_fd_.is_valid());
695c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  sandbox::Credentials credentials;
705c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  return credentials.HasOpenDirectory(proc_fd_.get());
715c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
725c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
735c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid NaClSandbox::InitializeLayerOneSandbox() {
745c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // Check that IsSandboxed() works. We should not be sandboxed at this point.
755c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  CHECK(!IsSandboxed()) << "Unexpectedly sandboxed!";
765c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (setuid_sandbox_client_->IsSuidSandboxChild()) {
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    setuid_sandbox_client_->CloseDummyFile();
79010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
805c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Make sure that no directory file descriptor is open, as it would bypass
815c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // the setuid sandbox model.
825c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    CHECK(!HasOpenDirectory());
835c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
845c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // Get sandboxed.
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    CHECK(setuid_sandbox_client_->ChrootMe());
865c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    CHECK(IsSandboxed());
875c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    layer_one_enabled_ = true;
885c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
895c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
905c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void NaClSandbox::CheckForExpectedNumberOfOpenFds() {
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (setuid_sandbox_client_->IsSuidSandboxChild()) {
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // We expect to have the following FDs open:
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //  1-3) stdin, stdout, stderr.
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //  4) The /dev/urandom FD used by base::GetUrandomFD().
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //  5) A dummy pipe FD used to overwrite kSandboxIPCChannel.
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //  6) The socket created by the SUID sandbox helper, used by ChrootMe().
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //     After ChrootMe(), this is no longer connected to anything.
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //     (Only present when running under the SUID sandbox.)
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //  7) The socket for the Chrome IPC channel that's connected to the
101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //     browser process, kPrimaryIPCChannel.
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    //
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // This sanity check ensures that dynamically loaded libraries don't
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // leave any FDs open before we enable the sandbox.
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    sandbox::Credentials credentials;
106cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    CHECK_EQ(7, credentials.CountOpenFds(proc_fd_.get()));
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1105c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid NaClSandbox::InitializeLayerTwoSandbox(bool uses_nonsfi_mode) {
1115c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // seccomp-bpf only applies to the current thread, so it's critical to only
1125c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  // have a single thread running here.
1135c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  DCHECK(!layer_one_sealed_);
1145c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  CHECK(IsSingleThreaded());
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  CheckForExpectedNumberOfOpenFds();
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
1175c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (uses_nonsfi_mode) {
1185c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    layer_two_enabled_ = nacl::nonsfi::InitializeBPFSandbox();
1195c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    layer_two_is_nonsfi_ = true;
1205c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  } else {
1215c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    layer_two_enabled_ = nacl::InitializeBPFSandbox();
1225c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1235c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1245c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1255c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid NaClSandbox::SealLayerOneSandbox() {
1265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!layer_two_enabled_) {
1275c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // If nothing prevents us, check that there is no superfluous directory
1285c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    // open.
1295c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    CHECK(!HasOpenDirectory());
1305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1315c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  proc_fd_.reset();
1325c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  layer_one_sealed_ = true;
1335c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1345c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1355c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liuvoid NaClSandbox::CheckSandboxingStateWithPolicy() {
1365c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  static const char kItIsDangerousMsg[] = " this is dangerous.";
1375c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  static const char kItIsNotAllowedMsg[] =
1385c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      " this is not allowed in this configuration.";
1395c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1405c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  const bool no_sandbox_for_nonsfi_ok =
1415c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      CommandLine::ForCurrentProcess()->HasSwitch(
1425c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu          switches::kNaClDangerousNoSandboxNonSfi);
1435c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  const bool can_be_no_sandbox =
1445c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      !layer_two_is_nonsfi_ || no_sandbox_for_nonsfi_ok;
1455c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1465c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!layer_one_enabled_ || !layer_one_sealed_) {
1475c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    static const char kNoSuidMsg[] =
1485c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        "The SUID sandbox is not engaged for NaCl:";
1495c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (can_be_no_sandbox)
1505c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      LOG(ERROR) << kNoSuidMsg << kItIsDangerousMsg;
1515c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    else
1525c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      LOG(FATAL) << kNoSuidMsg << kItIsNotAllowedMsg;
1535c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1545c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1555c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  if (!layer_two_enabled_) {
1565c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    static const char kNoBpfMsg[] =
1575c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu        "The seccomp-bpf sandbox is not engaged for NaCl:";
1585c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    if (can_be_no_sandbox)
1595c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      LOG(ERROR) << kNoBpfMsg << kItIsDangerousMsg;
1605c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    else
1615c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      LOG(FATAL) << kNoBpfMsg << kItIsNotAllowedMsg;
1625c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu  }
1635c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}
1645c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu
1655c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu}  // namespace nacl
166