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