Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / sandbox / linux / suid / client / setuid_sandbox_host.cc
blob71171ebd4fe32b415743bf7c8a54524367c666fe
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"
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
12 #include <string>
13 #include <utility>
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"
30 namespace {
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
41 // inside another.
42 void UnsetExpectedEnvironmentVariables(base::EnvironmentMap* env_map) {
43 DCHECK(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);
64 if (!saved_env_var)
65 return NULL;
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.
69 free(saved_env_var);
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
75 // renderer.
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)
82 continue;
84 std::string value;
85 if (env->GetVar(env_var, &value))
86 env->SetVar(saved_env_var->c_str(), value);
87 else
88 env->UnSetVar(saved_env_var->c_str());
92 const char* GetDevelSandboxPath() {
93 return getenv("CHROME_DEVEL_SANDBOX");
96 } // namespace
98 namespace sandbox {
100 SetuidSandboxHost* SetuidSandboxHost::Create() {
101 base::Environment* environment(base::Environment::Create());
102 CHECK(environment);
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) {
117 return true;
119 return false;
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
134 struct stat st;
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());
148 struct stat st;
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) {
171 DCHECK(options);
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.
179 int pipe_fds[2];
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