1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/posix/eintr_wrapper.h"
#include "base/time.h"
#include "content/common/sandbox_linux.h"
#include "content/common/seccomp_sandbox.h"
#include "content/common/sandbox_seccomp_bpf_linux.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/sandbox_linux.h"
#include "sandbox/linux/suid/client/setuid_sandbox_client.h"
namespace {
void LogSandboxStarted(const std::string& sandbox_name) {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
const std::string process_type =
command_line.GetSwitchValueASCII(switches::kProcessType);
const std::string activated_sandbox =
"Activated " + sandbox_name + " sandbox for process type: " +
process_type + ".";
#if defined(OS_CHROMEOS)
LOG(WARNING) << activated_sandbox;
#else
VLOG(1) << activated_sandbox;
#endif
}
// Implement the command line enabling logic for seccomp-legacy.
bool IsSeccompLegacyDesired() {
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kNoSandbox)) {
return false;
}
#if defined(SECCOMP_SANDBOX)
#if defined(NDEBUG)
// Off by default. Allow turning on with a switch.
return command_line->HasSwitch(switches::kEnableSeccompSandbox);
#else
// On by default. Allow turning off with a switch.
return !command_line->HasSwitch(switches::kDisableSeccompSandbox);
#endif // NDEBUG
#endif // SECCOMP_SANDBOX
return false;
}
// Our "policy" on whether or not to enable seccomp-legacy. Only renderers are
// supported.
bool ShouldEnableSeccompLegacy(const std::string& process_type) {
if (IsSeccompLegacyDesired() &&
process_type == switches::kRendererProcess) {
return true;
} else {
return false;
}
}
} // namespace
namespace content {
LinuxSandbox::LinuxSandbox()
: proc_fd_(-1),
seccomp_bpf_started_(false),
pre_initialized_(false),
seccomp_legacy_supported_(false),
seccomp_bpf_supported_(false),
setuid_sandbox_client_(sandbox::SetuidSandboxClient::Create()) {
if (setuid_sandbox_client_ == NULL) {
LOG(FATAL) << "Failed to instantiate the setuid sandbox client.";
}
}
LinuxSandbox::~LinuxSandbox() {
}
LinuxSandbox* LinuxSandbox::GetInstance() {
LinuxSandbox* instance = Singleton<LinuxSandbox>::get();
CHECK(instance);
return instance;
}
void LinuxSandbox::PreinitializeSandboxBegin() {
CHECK(!pre_initialized_);
seccomp_legacy_supported_ = false;
seccomp_bpf_supported_ = false;
#if defined(SECCOMP_SANDBOX)
if (IsSeccompLegacyDesired()) {
proc_fd_ = open("/proc", O_DIRECTORY | O_RDONLY);
if (proc_fd_ < 0) {
LOG(ERROR) << "Cannot access \"/proc\". Disabling seccomp-legacy "
"sandboxing.";
// Now is a good time to figure out if we can support seccomp sandboxing
// at all. We will call SupportsSeccompSandbox again later, when actually
// enabling it, but we allow the implementation to cache some information.
// This is the only place where we will log full lack of seccomp-legacy
// support.
} else if (!SupportsSeccompSandbox(proc_fd_)) {
VLOG(1) << "Lacking support for seccomp-legacy sandbox.";
CHECK_EQ(HANDLE_EINTR(close(proc_fd_)), 0);
proc_fd_ = -1;
} else {
seccomp_legacy_supported_ = true;
}
}
#endif // SECCOMP_SANDBOX
// Similarly, we "pre-warm" the code that detects supports for seccomp BPF.
// TODO(jln): Use proc_fd_ here too once we're comfortable it does not create
// an additional security risk.
if (SandboxSeccompBpf::IsSeccompBpfDesired()) {
if (!SandboxSeccompBpf::SupportsSandbox()) {
VLOG(1) << "Lacking support for seccomp-bpf sandbox.";
} else {
seccomp_bpf_supported_ = true;
}
}
pre_initialized_ = true;
}
// Once we finally know our process type, we can cleanup proc_fd_
// or pass it to seccomp-legacy.
void LinuxSandbox::PreinitializeSandboxFinish(
const std::string& process_type) {
CHECK(pre_initialized_);
if (proc_fd_ >= 0) {
if (ShouldEnableSeccompLegacy(process_type)) {
#if defined(SECCOMP_SANDBOX)
SeccompSandboxSetProcFd(proc_fd_);
#endif
} else {
DCHECK_GE(proc_fd_, 0);
CHECK_EQ(HANDLE_EINTR(close(proc_fd_)), 0);
}
proc_fd_ = -1;
}
}
void LinuxSandbox::PreinitializeSandbox(const std::string& process_type) {
PreinitializeSandboxBegin();
PreinitializeSandboxFinish(process_type);
}
int LinuxSandbox::GetStatus() const {
CHECK(pre_initialized_);
int sandbox_flags = 0;
if (setuid_sandbox_client_->IsSandboxed()) {
sandbox_flags |= kSandboxLinuxSUID;
if (setuid_sandbox_client_->IsInNewPIDNamespace())
sandbox_flags |= kSandboxLinuxPIDNS;
if (setuid_sandbox_client_->IsInNewNETNamespace())
sandbox_flags |= kSandboxLinuxNetNS;
}
if (seccomp_bpf_supported() &&
SandboxSeccompBpf::ShouldEnableSeccompBpf(switches::kRendererProcess)) {
// We report whether the sandbox will be activated when renderers go
// through sandbox initialization.
sandbox_flags |= kSandboxLinuxSeccompBpf;
}
// We only try to enable seccomp-legacy when seccomp-bpf is not supported
// or not enabled.
if (!(sandbox_flags & kSandboxLinuxSeccompBpf) &&
seccomp_legacy_supported() &&
ShouldEnableSeccompLegacy(switches::kRendererProcess)) {
// Same here, what we report is what we will do for the renderer.
sandbox_flags |= kSandboxLinuxSeccompLegacy;
}
return sandbox_flags;
}
bool LinuxSandbox::IsSingleThreaded() const {
// TODO(jln): re-implement this properly and use our proc_fd_ if available.
// Possibly racy, but it's ok because this is more of a debug check to catch
// new threaded situations arising during development.
int num_threads = file_util::CountFilesCreatedAfter(
FilePath("/proc/self/task"),
base::Time::UnixEpoch());
// We pass the test if we don't know ( == 0), because the setuid sandbox
// will prevent /proc access in some contexts.
return num_threads == 1 || num_threads == 0;
}
bool LinuxSandbox::seccomp_bpf_started() const {
return seccomp_bpf_started_;
}
sandbox::SetuidSandboxClient*
LinuxSandbox::setuid_sandbox_client() const {
return setuid_sandbox_client_.get();
}
// For seccomp-legacy, we implement the policy inline, here.
bool LinuxSandbox::StartSeccompLegacy(const std::string& process_type) {
if (!pre_initialized_)
PreinitializeSandbox(process_type);
if (seccomp_legacy_supported() && ShouldEnableSeccompLegacy(process_type)) {
// SupportsSeccompSandbox() returns a cached result, as we already
// called it earlier in the PreinitializeSandbox(). Thus, it is OK for us
// to not pass in a file descriptor for "/proc".
#if defined(SECCOMP_SANDBOX)
if (SupportsSeccompSandbox(-1)) {
StartSeccompSandbox();
LogSandboxStarted("seccomp-legacy");
return true;
}
#endif
}
return false;
}
// For seccomp-bpf, we use the SandboxSeccompBpf class.
bool LinuxSandbox::StartSeccompBpf(const std::string& process_type) {
CHECK(!seccomp_bpf_started_);
if (!pre_initialized_)
PreinitializeSandbox(process_type);
if (seccomp_bpf_supported())
seccomp_bpf_started_ = SandboxSeccompBpf::StartSandbox(process_type);
if (seccomp_bpf_started_)
LogSandboxStarted("seccomp-bpf");
return seccomp_bpf_started_;
}
bool LinuxSandbox::seccomp_legacy_supported() const {
CHECK(pre_initialized_);
return seccomp_legacy_supported_;
}
bool LinuxSandbox::seccomp_bpf_supported() const {
CHECK(pre_initialized_);
return seccomp_bpf_supported_;
}
bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) {
(void) process_type;
#if defined(__x86_64__) && !defined(ADDRESS_SANITIZER)
CommandLine* command_line = CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kNoSandbox)) {
return false;
}
// Limit the address space to 8GB.
const rlim_t kNewAddressSpaceMaxSize = 0x200000000L;
struct rlimit old_address_space_limit;
if (getrlimit(RLIMIT_AS, &old_address_space_limit))
return false;
// Make sure we don't raise the existing limit.
const struct rlimit new_address_space_limit = {
std::min(old_address_space_limit.rlim_cur, kNewAddressSpaceMaxSize),
std::min(old_address_space_limit.rlim_max, kNewAddressSpaceMaxSize)
};
int rc = setrlimit(RLIMIT_AS, &new_address_space_limit);
return (rc == 0);
#else
return false;
#endif // __x86_64__ && !defined(ADDRESS_SANITIZER)
}
} // namespace content
|