1 // Copyright 2015 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 "sandbox/linux/suid/client/setuid_sandbox_host.h"
15 #include "base/command_line.h"
16 #include "base/environment.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/files/scoped_file.h"
20 #include "base/logging.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/path_service.h"
23 #include "base/posix/eintr_wrapper.h"
24 #include "base/process/launch.h"
25 #include "base/process/process_metrics.h"
26 #include "base/strings/string_number_conversions.h"
27 #include "sandbox/linux/suid/common/sandbox.h"
28 #include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h"
32 // Set an environment variable that reflects the API version we expect from the
33 // setuid sandbox. Old versions of the sandbox will ignore this.
34 void SetSandboxAPIEnvironmentVariable(base::Environment
* env
) {
35 env
->SetVar(sandbox::kSandboxEnvironmentApiRequest
,
36 base::IntToString(sandbox::kSUIDSandboxApiNumber
));
39 // Unset environment variables that are expected to be set by the setuid
40 // sandbox. This is to allow nesting of one instance of the SUID sandbox
42 void UnsetExpectedEnvironmentVariables(base::EnvironmentMap
* env_map
) {
44 const base::NativeEnvironmentString environment_vars
[] = {
45 sandbox::kSandboxDescriptorEnvironmentVarName
,
46 sandbox::kSandboxHelperPidEnvironmentVarName
,
47 sandbox::kSandboxEnvironmentApiProvides
,
48 sandbox::kSandboxPIDNSEnvironmentVarName
,
49 sandbox::kSandboxNETNSEnvironmentVarName
,
52 for (size_t i
= 0; i
< arraysize(environment_vars
); ++i
) {
53 // Setting values in EnvironmentMap to an empty-string will make
54 // sure that they get unset from the environment via AlterEnvironment().
55 (*env_map
)[environment_vars
[i
]] = base::NativeEnvironmentString();
59 // Wrapper around a shared C function.
60 // Returns the "saved" environment variable name corresponding to |envvar|
61 // in a new string or NULL.
62 std::string
* CreateSavedVariableName(const char* env_var
) {
63 char* const saved_env_var
= SandboxSavedEnvironmentVariable(env_var
);
66 std::string
* saved_env_var_copy
= new std::string(saved_env_var
);
67 // SandboxSavedEnvironmentVariable is the C function that we wrap and uses
68 // malloc() to allocate memory.
70 return saved_env_var_copy
;
73 // The ELF loader will clear many environment variables so we save them to
74 // different names here so that the SUID sandbox can resolve them for the
76 void SaveSUIDUnsafeEnvironmentVariables(base::Environment
* env
) {
77 for (unsigned i
= 0; kSUIDUnsafeEnvironmentVariables
[i
]; ++i
) {
78 const char* env_var
= kSUIDUnsafeEnvironmentVariables
[i
];
79 // Get the saved environment variable corresponding to envvar.
80 scoped_ptr
<std::string
> saved_env_var(CreateSavedVariableName(env_var
));
81 if (saved_env_var
== NULL
)
85 if (env
->GetVar(env_var
, &value
))
86 env
->SetVar(saved_env_var
->c_str(), value
);
88 env
->UnSetVar(saved_env_var
->c_str());
92 const char* GetDevelSandboxPath() {
93 return getenv("CHROME_DEVEL_SANDBOX");
100 SetuidSandboxHost
* SetuidSandboxHost::Create() {
101 base::Environment
* environment(base::Environment::Create());
103 return new SetuidSandboxHost(environment
);
106 SetuidSandboxHost::SetuidSandboxHost(base::Environment
* env
) : env_(env
) {
109 SetuidSandboxHost::~SetuidSandboxHost() {
112 // Check if CHROME_DEVEL_SANDBOX is set but empty. This currently disables
113 // the setuid sandbox. TODO(jln): fix this (crbug.com/245376).
114 bool SetuidSandboxHost::IsDisabledViaEnvironment() {
115 const char* devel_sandbox_path
= GetDevelSandboxPath();
116 if (devel_sandbox_path
&& '\0' == *devel_sandbox_path
) {
122 base::FilePath
SetuidSandboxHost::GetSandboxBinaryPath() {
123 base::FilePath sandbox_binary
;
124 base::FilePath exe_dir
;
125 if (PathService::Get(base::DIR_EXE
, &exe_dir
)) {
126 base::FilePath sandbox_candidate
= exe_dir
.AppendASCII("chrome-sandbox");
127 if (base::PathExists(sandbox_candidate
))
128 sandbox_binary
= sandbox_candidate
;
131 // In user-managed builds, including development builds, an environment
132 // variable is required to enable the sandbox. See
133 // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment
135 if (sandbox_binary
.empty() && stat(base::kProcSelfExe
, &st
) == 0 &&
136 st
.st_uid
== getuid()) {
137 const char* devel_sandbox_path
= GetDevelSandboxPath();
138 if (devel_sandbox_path
) {
139 sandbox_binary
= base::FilePath(devel_sandbox_path
);
143 return sandbox_binary
;
146 void SetuidSandboxHost::PrependWrapper(base::CommandLine
* cmd_line
) {
147 std::string
sandbox_binary(GetSandboxBinaryPath().value());
149 if (sandbox_binary
.empty() || stat(sandbox_binary
.c_str(), &st
) != 0) {
150 LOG(FATAL
) << "The SUID sandbox helper binary is missing: "
151 << sandbox_binary
<< " Aborting now. See "
152 "https://code.google.com/p/chromium/wiki/"
153 "LinuxSUIDSandboxDevelopment.";
156 if (access(sandbox_binary
.c_str(), X_OK
) != 0 || (st
.st_uid
!= 0) ||
157 ((st
.st_mode
& S_ISUID
) == 0) || ((st
.st_mode
& S_IXOTH
)) == 0) {
158 LOG(FATAL
) << "The SUID sandbox helper binary was found, but is not "
159 "configured correctly. Rather than run without sandboxing "
160 "I'm aborting now. You need to make sure that "
161 << sandbox_binary
<< " is owned by root and has mode 4755.";
164 cmd_line
->PrependWrapper(sandbox_binary
);
167 void SetuidSandboxHost::SetupLaunchOptions(
168 base::LaunchOptions
* options
,
169 base::FileHandleMappingVector
* fds_to_remap
,
170 base::ScopedFD
* dummy_fd
) {
172 DCHECK(fds_to_remap
);
174 // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used.
175 options
->allow_new_privs
= true;
176 UnsetExpectedEnvironmentVariables(&options
->environ
);
178 // Set dummy_fd to the reading end of a closed pipe.
180 PCHECK(0 == pipe(pipe_fds
));
181 PCHECK(0 == IGNORE_EINTR(close(pipe_fds
[1])));
182 dummy_fd
->reset(pipe_fds
[0]);
184 // We no longer need a dummy socket for discovering the child's PID,
185 // but the sandbox is still hard-coded to expect a file descriptor at
186 // kZygoteIdFd. Fixing this requires a sandbox API change. :(
187 fds_to_remap
->push_back(std::make_pair(dummy_fd
->get(), kZygoteIdFd
));
190 void SetuidSandboxHost::SetupLaunchEnvironment() {
191 SaveSUIDUnsafeEnvironmentVariables(env_
.get());
192 SetSandboxAPIEnvironmentVariable(env_
.get());
195 } // namespace sandbox