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.
5 #include "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h"
10 #include <sys/types.h>
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"
32 // This is a poor man's check on whether we are sandboxed.
34 int proc_fd
= open("/proc/self/exe", O_RDONLY
);
36 PCHECK(0 == IGNORE_EINTR(close(proc_fd
)));
44 NaClSandbox::NaClSandbox()
45 : layer_one_enabled_(false),
46 layer_one_sealed_(false),
47 layer_two_enabled_(false),
48 layer_two_is_nonsfi_(false),
50 setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
52 HANDLE_EINTR(open("/proc", O_DIRECTORY
| O_RDONLY
| O_CLOEXEC
)));
53 PCHECK(proc_fd_
.is_valid());
56 NaClSandbox::~NaClSandbox() {
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());
67 bool NaClSandbox::HasOpenDirectory() {
68 CHECK(proc_fd_
.is_valid());
69 sandbox::Credentials credentials
;
70 return credentials
.HasOpenDirectory(proc_fd_
.get());
73 void NaClSandbox::InitializeLayerOneSandbox() {
74 // Check that IsSandboxed() works. We should not be sandboxed at this point.
75 CHECK(!IsSandboxed()) << "Unexpectedly sandboxed!";
77 if (setuid_sandbox_client_
->IsSuidSandboxChild()) {
78 setuid_sandbox_client_
->CloseDummyFile();
80 // Make sure that no directory file descriptor is open, as it would bypass
81 // the setuid sandbox model.
82 CHECK(!HasOpenDirectory());
85 CHECK(setuid_sandbox_client_
->ChrootMe());
87 layer_one_enabled_
= true;
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.
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()));
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();
117 if (uses_nonsfi_mode
) {
118 layer_two_enabled_
= nacl::nonsfi::InitializeBPFSandbox();
119 layer_two_is_nonsfi_
= true;
121 layer_two_enabled_
= nacl::InitializeBPFSandbox();
125 void NaClSandbox::SealLayerOneSandbox() {
126 if (!layer_two_enabled_
) {
127 // If nothing prevents us, check that there is no superfluous directory
129 CHECK(!HasOpenDirectory());
132 layer_one_sealed_
= true;
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.";
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
;
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
;
152 LOG(FATAL
) << kNoSuidMsg
<< kItIsNotAllowedMsg
;
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
;
161 LOG(FATAL
) << kNoBpfMsg
<< kItIsNotAllowedMsg
;