OLD | NEW |
---|---|
(Empty) | |
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_non_sfi_(false), | |
49 proc_fd_(-1) { | |
50 proc_fd_.reset( | |
51 HANDLE_EINTR(open("/proc", O_DIRECTORY | O_RDONLY | O_CLOEXEC))); | |
52 PCHECK(proc_fd_.is_valid()); | |
53 } | |
54 | |
55 NaClSandbox::~NaClSandbox() { | |
56 } | |
57 | |
58 bool NaClSandbox::IsSingleThreaded() { | |
59 CHECK(proc_fd_.is_valid()); | |
60 base::ScopedFD proc_self_task(HANDLE_EINTR(openat( | |
61 proc_fd_.get(), "self/task/", O_RDONLY | O_DIRECTORY | O_CLOEXEC))); | |
62 PCHECK(proc_self_task.is_valid()); | |
63 return sandbox::ThreadHelpers::IsSingleThreaded(proc_self_task.get()); | |
64 } | |
65 | |
66 bool NaClSandbox::HasOpenDirectory() { | |
67 CHECK(proc_fd_.is_valid()); | |
68 sandbox::Credentials credentials; | |
69 return credentials.HasOpenDirectory(proc_fd_.get()); | |
70 } | |
71 | |
72 void NaClSandbox::InitializeLayerOneSandbox() { | |
73 // Check that IsSandboxed() works. We should not be sandboxed at this point. | |
74 CHECK(!IsSandboxed()) << "Unexpectedly sandboxed!"; | |
75 scoped_ptr<sandbox::SetuidSandboxClient> setuid_sandbox_client( | |
76 sandbox::SetuidSandboxClient::Create()); | |
77 // Close the file descriptor that is an artefact of how the setuid sandbox | |
78 // works. | |
79 PCHECK(0 == IGNORE_EINTR(close( | |
80 setuid_sandbox_client->GetUniqueToChildFileDescriptor()))); | |
81 const bool suid_sandbox_child = setuid_sandbox_client->IsSuidSandboxChild(); | |
82 | |
83 if (suid_sandbox_child) { | |
84 // Make sure that no directory file descriptor is open, as it would bypass | |
85 // the setuid sandbox model. | |
86 CHECK(!HasOpenDirectory()); | |
87 | |
88 // Get sandboxed. | |
89 CHECK(setuid_sandbox_client->ChrootMe()); | |
90 CHECK(IsSandboxed()); | |
91 layer_one_enabled_ = true; | |
92 } | |
93 } | |
94 | |
95 void NaClSandbox::InitializeLayerTwoSandbox(bool uses_nonsfi_mode) { | |
96 // seccomp-bpf only applies to the current thread, so it's critical to only | |
97 // have a single thread running here. | |
98 DCHECK(!layer_one_sealed_); | |
99 CHECK(IsSingleThreaded()); | |
100 if (uses_nonsfi_mode) { | |
101 layer_two_enabled_ = nacl::nonsfi::InitializeBPFSandbox(); | |
102 layer_two_is_non_sfi_ = true; | |
103 } else { | |
104 layer_two_enabled_ = nacl::InitializeBPFSandbox(); | |
105 } | |
106 } | |
107 | |
108 void NaClSandbox::SealLayerOneSandbox() { | |
109 if (!layer_two_enabled_) { | |
110 // If nothing prevents us, check that there is no superfluous directory | |
111 // open. | |
112 CHECK(!HasOpenDirectory()); | |
113 } | |
114 proc_fd_.reset(); | |
115 layer_one_sealed_ = true; | |
116 } | |
117 | |
118 void NaClSandbox::CheckSandboxingStateWithPolicy() { | |
119 static const char kItIsDangerousMsg[] = " it is dangerous."; | |
Mark Seaborn
2014/04/29 00:28:22
How about "this is dangerous", otherwise it seems
jln (very slow on Chromium)
2014/04/29 01:28:32
Done.
| |
120 static const char kItIsNotAllowedMsg[] = | |
121 " this is not allowed in this configuration."; | |
122 | |
123 const bool no_sandbox_for_non_sfi_ok = | |
124 CommandLine::ForCurrentProcess()->HasSwitch( | |
125 switches::kNaClDangerousNoSandboxNonSfi); | |
126 const bool can_be_no_sandbox = | |
127 !layer_two_is_non_sfi_ || no_sandbox_for_non_sfi_ok; | |
128 | |
129 if (!layer_one_enabled_ || !layer_one_sealed_) { | |
130 static const char kNoSuidMsg[] = "The SUID sandbox is not engaged,"; | |
Mark Seaborn
2014/04/29 00:28:22
Nit: comma splice, use ":" or ";" instead of ",".
jln (very slow on Chromium)
2014/04/29 01:28:32
Done.
| |
131 if (can_be_no_sandbox) | |
132 LOG(ERROR) << kNoSuidMsg << kItIsDangerousMsg; | |
133 else | |
134 LOG(FATAL) << kNoSuidMsg << kItIsNotAllowedMsg; | |
135 } | |
136 | |
137 if (!layer_two_enabled_) { | |
138 static const char kNoBpfMsg[] = "The seccomp-bpf sandbox is not engaged,"; | |
139 if (can_be_no_sandbox) | |
140 LOG(ERROR) << kNoBpfMsg << kItIsDangerousMsg; | |
141 else | |
142 LOG(FATAL) << kNoBpfMsg << kItIsNotAllowedMsg; | |
143 } | |
144 } | |
145 | |
146 } // namespace nacl | |
OLD | NEW |