diff options
author | mdempsky <mdempsky@chromium.org> | 2015-02-04 19:29:25 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-05 03:30:32 +0000 |
commit | 3cc942a3dfecc18e0b20fdc509a9608644b67036 (patch) | |
tree | 01e8d5629705e231e91b5a2079dd5deb6d1de0bf /sandbox/linux/suid | |
parent | 88444c0bae2e77ab555a81a727d5ffa49500adf2 (diff) | |
download | chromium_src-3cc942a3dfecc18e0b20fdc509a9608644b67036.zip chromium_src-3cc942a3dfecc18e0b20fdc509a9608644b67036.tar.gz chromium_src-3cc942a3dfecc18e0b20fdc509a9608644b67036.tar.bz2 |
sandbox: extract SetuidSandboxHost code from SetuidSandboxClient
This separates the code used to "host" the setuid sandbox binary from
the code used to run underneath it (i.e., the client). The primary
motivation for this is so that lightweight clients (e.g., the BMM
non-SFI sandbox) can avoid all of the additional dependencies required
only for hosting the setuid sandbox.
TBR=mseaborn@chromium.org,nasko@chromium.org
BUG=455087
Review URL: https://codereview.chromium.org/877153005
Cr-Commit-Position: refs/heads/master@{#314734}
Diffstat (limited to 'sandbox/linux/suid')
-rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_client.cc | 184 | ||||
-rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_client.h | 49 | ||||
-rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc | 59 | ||||
-rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_host.cc | 195 | ||||
-rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_host.h | 70 | ||||
-rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc | 72 |
6 files changed, 359 insertions, 270 deletions
diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc index 9eb538a..12ef7f9 100644 --- a/sandbox/linux/suid/client/setuid_sandbox_client.cc +++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc @@ -5,94 +5,24 @@ #include "sandbox/linux/suid/client/setuid_sandbox_client.h" #include <fcntl.h> -#include <stdlib.h> -#include <sys/socket.h> #include <sys/stat.h> -#include <sys/types.h> #include <sys/wait.h> #include <unistd.h> -#include "base/command_line.h" +#include <string> + #include "base/environment.h" -#include "base/files/file_path.h" -#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/path_service.h" #include "base/posix/eintr_wrapper.h" -#include "base/process/launch.h" -#include "base/process/process_metrics.h" #include "base/strings/string_number_conversions.h" -#include "sandbox/linux/services/init_process_reaper.h" #include "sandbox/linux/suid/common/sandbox.h" -#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h" namespace { bool IsFileSystemAccessDenied() { - base::ScopedFD self_exe(HANDLE_EINTR(open("/", O_RDONLY))); - return !self_exe.is_valid(); -} - -// Set an environment variable that reflects the API version we expect from the -// setuid sandbox. Old versions of the sandbox will ignore this. -void SetSandboxAPIEnvironmentVariable(base::Environment* env) { - env->SetVar(sandbox::kSandboxEnvironmentApiRequest, - base::IntToString(sandbox::kSUIDSandboxApiNumber)); -} - -// Unset environment variables that are expected to be set by the setuid -// sandbox. This is to allow nesting of one instance of the SUID sandbox -// inside another. -void UnsetExpectedEnvironmentVariables(base::EnvironmentMap* env_map) { - DCHECK(env_map); - const base::NativeEnvironmentString environment_vars[] = { - sandbox::kSandboxDescriptorEnvironmentVarName, - sandbox::kSandboxHelperPidEnvironmentVarName, - sandbox::kSandboxEnvironmentApiProvides, - sandbox::kSandboxPIDNSEnvironmentVarName, - sandbox::kSandboxNETNSEnvironmentVarName, - }; - - for (size_t i = 0; i < arraysize(environment_vars); ++i) { - // Setting values in EnvironmentMap to an empty-string will make - // sure that they get unset from the environment via AlterEnvironment(). - (*env_map)[environment_vars[i]] = base::NativeEnvironmentString(); - } -} - -// Wrapper around a shared C function. -// Returns the "saved" environment variable name corresponding to |envvar| -// in a new string or NULL. -std::string* CreateSavedVariableName(const char* env_var) { - char* const saved_env_var = SandboxSavedEnvironmentVariable(env_var); - if (!saved_env_var) - return NULL; - std::string* saved_env_var_copy = new std::string(saved_env_var); - // SandboxSavedEnvironmentVariable is the C function that we wrap and uses - // malloc() to allocate memory. - free(saved_env_var); - return saved_env_var_copy; -} - -// The ELF loader will clear many environment variables so we save them to -// different names here so that the SUID sandbox can resolve them for the -// renderer. -void SaveSUIDUnsafeEnvironmentVariables(base::Environment* env) { - for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) { - const char* env_var = kSUIDUnsafeEnvironmentVariables[i]; - // Get the saved environment variable corresponding to envvar. - scoped_ptr<std::string> saved_env_var(CreateSavedVariableName(env_var)); - if (saved_env_var == NULL) - continue; - - std::string value; - if (env->GetVar(env_var, &value)) - env->SetVar(saved_env_var->c_str(), value); - else - env->UnSetVar(saved_env_var->c_str()); - } + base::ScopedFD root_dir(HANDLE_EINTR(open("/", O_RDONLY))); + return !root_dir.is_valid(); } int GetHelperApi(base::Environment* env) { @@ -127,30 +57,21 @@ int GetIPCDescriptor(base::Environment* env) { return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName); } -const char* GetDevelSandboxPath() { - return getenv("CHROME_DEVEL_SANDBOX"); -} - } // namespace namespace sandbox { SetuidSandboxClient* SetuidSandboxClient::Create() { base::Environment* environment(base::Environment::Create()); - SetuidSandboxClient* sandbox_client(new SetuidSandboxClient); - CHECK(environment); - sandbox_client->env_ = environment; - return sandbox_client; + return new SetuidSandboxClient(environment); } -SetuidSandboxClient::SetuidSandboxClient() - : env_(NULL), - sandboxed_(false) { +SetuidSandboxClient::SetuidSandboxClient(base::Environment* env) + : env_(env), sandboxed_(false) { } SetuidSandboxClient::~SetuidSandboxClient() { - delete env_; } void SetuidSandboxClient::CloseDummyFile() { @@ -169,7 +90,7 @@ void SetuidSandboxClient::CloseDummyFile() { } bool SetuidSandboxClient::ChrootMe() { - int ipc_fd = GetIPCDescriptor(env_); + int ipc_fd = GetIPCDescriptor(env_.get()); if (ipc_fd < 0) { LOG(ERROR) << "Failed to obtain the sandbox IPC descriptor"; @@ -182,7 +103,7 @@ bool SetuidSandboxClient::ChrootMe() { } // We need to reap the chroot helper process in any event. - pid_t helper_pid = GetHelperPID(env_); + pid_t helper_pid = GetHelperPID(env_.get()); // If helper_pid is -1 we wait for any child. if (HANDLE_EINTR(waitpid(helper_pid, NULL, 0)) < 0) { PLOG(ERROR) << "Failed to wait for setuid helper to die"; @@ -208,11 +129,11 @@ bool SetuidSandboxClient::ChrootMe() { } bool SetuidSandboxClient::IsSuidSandboxUpToDate() const { - return GetHelperApi(env_) == kSUIDSandboxApiNumber; + return GetHelperApi(env_.get()) == kSUIDSandboxApiNumber; } bool SetuidSandboxClient::IsSuidSandboxChild() const { - return GetIPCDescriptor(env_) >= 0; + return GetIPCDescriptor(env_.get()) >= 0; } bool SetuidSandboxClient::IsInNewPIDNamespace() const { @@ -227,87 +148,4 @@ bool SetuidSandboxClient::IsSandboxed() const { return sandboxed_; } -// Check if CHROME_DEVEL_SANDBOX is set but empty. This currently disables -// the setuid sandbox. TODO(jln): fix this (crbug.com/245376). -bool SetuidSandboxClient::IsDisabledViaEnvironment() { - const char* devel_sandbox_path = GetDevelSandboxPath(); - if (devel_sandbox_path && '\0' == *devel_sandbox_path) { - return true; - } - return false; -} - -base::FilePath SetuidSandboxClient::GetSandboxBinaryPath() { - base::FilePath sandbox_binary; - base::FilePath exe_dir; - if (PathService::Get(base::DIR_EXE, &exe_dir)) { - base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox"); - if (base::PathExists(sandbox_candidate)) - sandbox_binary = sandbox_candidate; - } - - // In user-managed builds, including development builds, an environment - // variable is required to enable the sandbox. See - // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment - struct stat st; - if (sandbox_binary.empty() && stat(base::kProcSelfExe, &st) == 0 && - st.st_uid == getuid()) { - const char* devel_sandbox_path = GetDevelSandboxPath(); - if (devel_sandbox_path) { - sandbox_binary = base::FilePath(devel_sandbox_path); - } - } - - return sandbox_binary; -} - -void SetuidSandboxClient::PrependWrapper(base::CommandLine* cmd_line) { - std::string sandbox_binary(GetSandboxBinaryPath().value()); - struct stat st; - if (sandbox_binary.empty() || stat(sandbox_binary.c_str(), &st) != 0) { - LOG(FATAL) << "The SUID sandbox helper binary is missing: " - << sandbox_binary << " Aborting now. See " - "https://code.google.com/p/chromium/wiki/" - "LinuxSUIDSandboxDevelopment."; - } - - if (access(sandbox_binary.c_str(), X_OK) != 0 || (st.st_uid != 0) || - ((st.st_mode & S_ISUID) == 0) || ((st.st_mode & S_IXOTH)) == 0) { - LOG(FATAL) << "The SUID sandbox helper binary was found, but is not " - "configured correctly. Rather than run without sandboxing " - "I'm aborting now. You need to make sure that " - << sandbox_binary << " is owned by root and has mode 4755."; - } - - cmd_line->PrependWrapper(sandbox_binary); -} - -void SetuidSandboxClient::SetupLaunchOptions( - base::LaunchOptions* options, - base::FileHandleMappingVector* fds_to_remap, - base::ScopedFD* dummy_fd) { - DCHECK(options); - DCHECK(fds_to_remap); - - // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used. - options->allow_new_privs = true; - UnsetExpectedEnvironmentVariables(&options->environ); - - // Set dummy_fd to the reading end of a closed pipe. - int pipe_fds[2]; - PCHECK(0 == pipe(pipe_fds)); - PCHECK(0 == IGNORE_EINTR(close(pipe_fds[1]))); - dummy_fd->reset(pipe_fds[0]); - - // We no longer need a dummy socket for discovering the child's PID, - // but the sandbox is still hard-coded to expect a file descriptor at - // kZygoteIdFd. Fixing this requires a sandbox API change. :( - fds_to_remap->push_back(std::make_pair(dummy_fd->get(), kZygoteIdFd)); -} - -void SetuidSandboxClient::SetupLaunchEnvironment() { - SaveSUIDUnsafeEnvironmentVariables(env_); - SetSandboxAPIEnvironmentVariable(env_); -} - } // namespace sandbox diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.h b/sandbox/linux/suid/client/setuid_sandbox_client.h index b24eb4c..026894f 100644 --- a/sandbox/linux/suid/client/setuid_sandbox_client.h +++ b/sandbox/linux/suid/client/setuid_sandbox_client.h @@ -5,29 +5,20 @@ #ifndef SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_ #define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_ -#include "base/basictypes.h" -#include "base/command_line.h" #include "base/environment.h" -#include "base/files/file_path.h" -#include "base/files/scoped_file.h" -#include "base/process/launch.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "sandbox/sandbox_export.h" namespace sandbox { -// Helper class to use the setuid sandbox. This class is to be used both -// before launching the setuid helper and after being executed through the -// setuid helper. +// Helper class to use the setuid sandbox. This class is to be used +// after being executed through the setuid helper. // This class is difficult to use. It has been created by refactoring very old // code scathered through the Chromium code base. // // A typical use for "A" launching a sandboxed process "B" would be: -// 1. A calls SetupLaunchEnvironment() -// 2. A sets up a CommandLine and then amends it with -// PrependWrapper() (or manually, by relying on GetSandboxBinaryPath()). -// 3. A uses SetupLaunchOptions() to arrange for a dummy descriptor for the -// setuid sandbox ABI. -// 4. A launches B with base::LaunchProcess, using the amended CommandLine. +// (Steps 1 through 4 are described in setuid_sandbox_host.h.) // 5. B uses CloseDummyFile() to close the dummy file descriptor. // 6. B performs various initializations that require access to the file // system. @@ -38,13 +29,13 @@ namespace sandbox { // cannot receive any signal from any other process, excluding SIGKILL. // If B dies, all the processes in the namespace will die. // B can fork() and the parent can assume the role of init(1), by using -// CreateInitProcessReaper(). +// sandbox::CreateInitProcessReaper(). // 8. B requests being chroot-ed through ChrootMe() and // requests other sandboxing status via the status functions. class SANDBOX_EXPORT SetuidSandboxClient { public: // All instantation should go through this factory method. - static class SetuidSandboxClient* Create(); + static SetuidSandboxClient* Create(); ~SetuidSandboxClient(); // Close the dummy file descriptor leftover from the sandbox ABI. @@ -65,33 +56,11 @@ class SANDBOX_EXPORT SetuidSandboxClient { // Are we done and fully sandboxed ? bool IsSandboxed() const; - // The setuid sandbox may still be disabled via the environment. - // This is tracked in crbug.com/245376. - bool IsDisabledViaEnvironment(); - // Get the sandbox binary path. This method knows about the - // CHROME_DEVEL_SANDBOX environment variable used for user-managed builds. If - // the sandbox binary cannot be found, it will return an empty FilePath. - base::FilePath GetSandboxBinaryPath(); - // Modify |cmd_line| to launch via the setuid sandbox. Crash if the setuid - // sandbox binary cannot be found. |cmd_line| must not be NULL. - void PrependWrapper(base::CommandLine* cmd_line); - // Set-up the launch options for launching via the setuid sandbox. Caller is - // responsible for keeping |dummy_fd| alive until LaunchProcess() completes. - // |options| and |fds_to_remap| must not be NULL. - // (Keeping |dummy_fd| alive is an unfortunate historical artifact of the - // chrome-sandbox ABI.) - void SetupLaunchOptions(base::LaunchOptions* options, - base::FileHandleMappingVector* fds_to_remap, - base::ScopedFD* dummy_fd); - // Set-up the environment. This should be done prior to launching the setuid - // helper. - void SetupLaunchEnvironment(); - private: - SetuidSandboxClient(); + explicit SetuidSandboxClient(base::Environment* env); // Holds the environment. Will never be NULL. - base::Environment* env_; + scoped_ptr<base::Environment> env_; bool sandboxed_; DISALLOW_COPY_AND_ASSIGN(SetuidSandboxClient); diff --git a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc index d4f7dfe..2acd8fb 100644 --- a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc +++ b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc @@ -2,63 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "sandbox/linux/suid/client/setuid_sandbox_client.h" + #include "base/environment.h" -#include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" -#include "sandbox/linux/suid/client/setuid_sandbox_client.h" #include "sandbox/linux/suid/common/sandbox.h" #include "testing/gtest/include/gtest/gtest.h" namespace sandbox { -TEST(SetuidSandboxClient, SetupLaunchEnvironment) { - const char kTestValue[] = "This is a test"; - scoped_ptr<base::Environment> env(base::Environment::Create()); - EXPECT_TRUE(env != NULL); - - std::string saved_ld_preload; - bool environment_had_ld_preload; - // First, back-up the real LD_PRELOAD if any. - environment_had_ld_preload = env->GetVar("LD_PRELOAD", &saved_ld_preload); - // Setup environment variables to save or not save. - EXPECT_TRUE(env->SetVar("LD_PRELOAD", kTestValue)); - EXPECT_TRUE(env->UnSetVar("LD_ORIGIN_PATH")); - - scoped_ptr<SetuidSandboxClient> - sandbox_client(SetuidSandboxClient::Create()); - EXPECT_TRUE(sandbox_client != NULL); - - // Make sure the environment is clean. - EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiRequest)); - EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiProvides)); - - sandbox_client->SetupLaunchEnvironment(); - - // Check if the requested API environment was set. - std::string api_request; - EXPECT_TRUE(env->GetVar(kSandboxEnvironmentApiRequest, &api_request)); - int api_request_num; - EXPECT_TRUE(base::StringToInt(api_request, &api_request_num)); - EXPECT_EQ(api_request_num, kSUIDSandboxApiNumber); - - // Now check if LD_PRELOAD was saved to SANDBOX_LD_PRELOAD. - std::string sandbox_ld_preload; - EXPECT_TRUE(env->GetVar("SANDBOX_LD_PRELOAD", &sandbox_ld_preload)); - EXPECT_EQ(sandbox_ld_preload, kTestValue); - - // Check that LD_ORIGIN_PATH was not saved. - EXPECT_FALSE(env->HasVar("SANDBOX_LD_ORIGIN_PATH")); - - // We should not forget to restore LD_PRELOAD at the end, or this environment - // variable will affect the next running tests! - if (environment_had_ld_preload) { - EXPECT_TRUE(env->SetVar("LD_PRELOAD", saved_ld_preload)); - } else { - EXPECT_TRUE(env->UnSetVar("LD_PRELOAD")); - } -} - TEST(SetuidSandboxClient, SandboxedClientAPI) { scoped_ptr<base::Environment> env(base::Environment::Create()); EXPECT_TRUE(env != NULL); @@ -89,13 +42,5 @@ TEST(SetuidSandboxClient, SandboxedClientAPI) { EXPECT_FALSE(sandbox_client->IsSandboxed()); } -// This test doesn't accomplish much, but will make sure that analysis tools -// will run this codepath. -TEST(SetuidSandboxClient, GetSandboxBinaryPath) { - scoped_ptr<SetuidSandboxClient> setuid_sandbox_client( - SetuidSandboxClient::Create()); - ignore_result(setuid_sandbox_client->GetSandboxBinaryPath()); -} - } // namespace sandbox diff --git a/sandbox/linux/suid/client/setuid_sandbox_host.cc b/sandbox/linux/suid/client/setuid_sandbox_host.cc new file mode 100644 index 0000000..71171eb --- /dev/null +++ b/sandbox/linux/suid/client/setuid_sandbox_host.cc @@ -0,0 +1,195 @@ +// Copyright 2015 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 "sandbox/linux/suid/client/setuid_sandbox_host.h" + +#include <fcntl.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <string> +#include <utility> + +#include "base/command_line.h" +#include "base/environment.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/path_service.h" +#include "base/posix/eintr_wrapper.h" +#include "base/process/launch.h" +#include "base/process/process_metrics.h" +#include "base/strings/string_number_conversions.h" +#include "sandbox/linux/suid/common/sandbox.h" +#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h" + +namespace { + +// Set an environment variable that reflects the API version we expect from the +// setuid sandbox. Old versions of the sandbox will ignore this. +void SetSandboxAPIEnvironmentVariable(base::Environment* env) { + env->SetVar(sandbox::kSandboxEnvironmentApiRequest, + base::IntToString(sandbox::kSUIDSandboxApiNumber)); +} + +// Unset environment variables that are expected to be set by the setuid +// sandbox. This is to allow nesting of one instance of the SUID sandbox +// inside another. +void UnsetExpectedEnvironmentVariables(base::EnvironmentMap* env_map) { + DCHECK(env_map); + const base::NativeEnvironmentString environment_vars[] = { + sandbox::kSandboxDescriptorEnvironmentVarName, + sandbox::kSandboxHelperPidEnvironmentVarName, + sandbox::kSandboxEnvironmentApiProvides, + sandbox::kSandboxPIDNSEnvironmentVarName, + sandbox::kSandboxNETNSEnvironmentVarName, + }; + + for (size_t i = 0; i < arraysize(environment_vars); ++i) { + // Setting values in EnvironmentMap to an empty-string will make + // sure that they get unset from the environment via AlterEnvironment(). + (*env_map)[environment_vars[i]] = base::NativeEnvironmentString(); + } +} + +// Wrapper around a shared C function. +// Returns the "saved" environment variable name corresponding to |envvar| +// in a new string or NULL. +std::string* CreateSavedVariableName(const char* env_var) { + char* const saved_env_var = SandboxSavedEnvironmentVariable(env_var); + if (!saved_env_var) + return NULL; + std::string* saved_env_var_copy = new std::string(saved_env_var); + // SandboxSavedEnvironmentVariable is the C function that we wrap and uses + // malloc() to allocate memory. + free(saved_env_var); + return saved_env_var_copy; +} + +// The ELF loader will clear many environment variables so we save them to +// different names here so that the SUID sandbox can resolve them for the +// renderer. +void SaveSUIDUnsafeEnvironmentVariables(base::Environment* env) { + for (unsigned i = 0; kSUIDUnsafeEnvironmentVariables[i]; ++i) { + const char* env_var = kSUIDUnsafeEnvironmentVariables[i]; + // Get the saved environment variable corresponding to envvar. + scoped_ptr<std::string> saved_env_var(CreateSavedVariableName(env_var)); + if (saved_env_var == NULL) + continue; + + std::string value; + if (env->GetVar(env_var, &value)) + env->SetVar(saved_env_var->c_str(), value); + else + env->UnSetVar(saved_env_var->c_str()); + } +} + +const char* GetDevelSandboxPath() { + return getenv("CHROME_DEVEL_SANDBOX"); +} + +} // namespace + +namespace sandbox { + +SetuidSandboxHost* SetuidSandboxHost::Create() { + base::Environment* environment(base::Environment::Create()); + CHECK(environment); + return new SetuidSandboxHost(environment); +} + +SetuidSandboxHost::SetuidSandboxHost(base::Environment* env) : env_(env) { +} + +SetuidSandboxHost::~SetuidSandboxHost() { +} + +// Check if CHROME_DEVEL_SANDBOX is set but empty. This currently disables +// the setuid sandbox. TODO(jln): fix this (crbug.com/245376). +bool SetuidSandboxHost::IsDisabledViaEnvironment() { + const char* devel_sandbox_path = GetDevelSandboxPath(); + if (devel_sandbox_path && '\0' == *devel_sandbox_path) { + return true; + } + return false; +} + +base::FilePath SetuidSandboxHost::GetSandboxBinaryPath() { + base::FilePath sandbox_binary; + base::FilePath exe_dir; + if (PathService::Get(base::DIR_EXE, &exe_dir)) { + base::FilePath sandbox_candidate = exe_dir.AppendASCII("chrome-sandbox"); + if (base::PathExists(sandbox_candidate)) + sandbox_binary = sandbox_candidate; + } + + // In user-managed builds, including development builds, an environment + // variable is required to enable the sandbox. See + // http://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment + struct stat st; + if (sandbox_binary.empty() && stat(base::kProcSelfExe, &st) == 0 && + st.st_uid == getuid()) { + const char* devel_sandbox_path = GetDevelSandboxPath(); + if (devel_sandbox_path) { + sandbox_binary = base::FilePath(devel_sandbox_path); + } + } + + return sandbox_binary; +} + +void SetuidSandboxHost::PrependWrapper(base::CommandLine* cmd_line) { + std::string sandbox_binary(GetSandboxBinaryPath().value()); + struct stat st; + if (sandbox_binary.empty() || stat(sandbox_binary.c_str(), &st) != 0) { + LOG(FATAL) << "The SUID sandbox helper binary is missing: " + << sandbox_binary << " Aborting now. See " + "https://code.google.com/p/chromium/wiki/" + "LinuxSUIDSandboxDevelopment."; + } + + if (access(sandbox_binary.c_str(), X_OK) != 0 || (st.st_uid != 0) || + ((st.st_mode & S_ISUID) == 0) || ((st.st_mode & S_IXOTH)) == 0) { + LOG(FATAL) << "The SUID sandbox helper binary was found, but is not " + "configured correctly. Rather than run without sandboxing " + "I'm aborting now. You need to make sure that " + << sandbox_binary << " is owned by root and has mode 4755."; + } + + cmd_line->PrependWrapper(sandbox_binary); +} + +void SetuidSandboxHost::SetupLaunchOptions( + base::LaunchOptions* options, + base::FileHandleMappingVector* fds_to_remap, + base::ScopedFD* dummy_fd) { + DCHECK(options); + DCHECK(fds_to_remap); + + // Launching a setuid binary requires PR_SET_NO_NEW_PRIVS to not be used. + options->allow_new_privs = true; + UnsetExpectedEnvironmentVariables(&options->environ); + + // Set dummy_fd to the reading end of a closed pipe. + int pipe_fds[2]; + PCHECK(0 == pipe(pipe_fds)); + PCHECK(0 == IGNORE_EINTR(close(pipe_fds[1]))); + dummy_fd->reset(pipe_fds[0]); + + // We no longer need a dummy socket for discovering the child's PID, + // but the sandbox is still hard-coded to expect a file descriptor at + // kZygoteIdFd. Fixing this requires a sandbox API change. :( + fds_to_remap->push_back(std::make_pair(dummy_fd->get(), kZygoteIdFd)); +} + +void SetuidSandboxHost::SetupLaunchEnvironment() { + SaveSUIDUnsafeEnvironmentVariables(env_.get()); + SetSandboxAPIEnvironmentVariable(env_.get()); +} + +} // namespace sandbox diff --git a/sandbox/linux/suid/client/setuid_sandbox_host.h b/sandbox/linux/suid/client/setuid_sandbox_host.h new file mode 100644 index 0000000..6788892 --- /dev/null +++ b/sandbox/linux/suid/client/setuid_sandbox_host.h @@ -0,0 +1,70 @@ +// Copyright 2015 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. + +#ifndef SANDBOX_LINUX_SUID_SETUID_SANDBOX_HOST_H_ +#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_HOST_H_ + +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/process/launch.h" +#include "sandbox/sandbox_export.h" + +namespace sandbox { + +// Helper class to use the setuid sandbox. This class is to be used +// before launching the setuid helper. +// This class is difficult to use. It has been created by refactoring very old +// code scathered through the Chromium code base. +// +// A typical use for "A" launching a sandboxed process "B" would be: +// 1. A calls SetupLaunchEnvironment() +// 2. A sets up a base::CommandLine and then amends it with +// PrependWrapper() (or manually, by relying on GetSandboxBinaryPath()). +// 3. A uses SetupLaunchOptions() to arrange for a dummy descriptor for the +// setuid sandbox ABI. +// 4. A launches B with base::LaunchProcess, using the amended +// base::CommandLine. +// (The remaining steps are described within setuid_sandbox_client.h.) +class SANDBOX_EXPORT SetuidSandboxHost { + public: + // All instantation should go through this factory method. + static SetuidSandboxHost* Create(); + ~SetuidSandboxHost(); + + // The setuid sandbox may still be disabled via the environment. + // This is tracked in crbug.com/245376. + bool IsDisabledViaEnvironment(); + // Get the sandbox binary path. This method knows about the + // CHROME_DEVEL_SANDBOX environment variable used for user-managed builds. If + // the sandbox binary cannot be found, it will return an empty FilePath. + base::FilePath GetSandboxBinaryPath(); + // Modify |cmd_line| to launch via the setuid sandbox. Crash if the setuid + // sandbox binary cannot be found. |cmd_line| must not be NULL. + void PrependWrapper(base::CommandLine* cmd_line); + // Set-up the launch options for launching via the setuid sandbox. Caller is + // responsible for keeping |dummy_fd| alive until LaunchProcess() completes. + // |options| and |fds_to_remap| must not be NULL. + // (Keeping |dummy_fd| alive is an unfortunate historical artifact of the + // chrome-sandbox ABI.) + void SetupLaunchOptions(base::LaunchOptions* options, + base::FileHandleMappingVector* fds_to_remap, + base::ScopedFD* dummy_fd); + // Set-up the environment. This should be done prior to launching the setuid + // helper. + void SetupLaunchEnvironment(); + + private: + explicit SetuidSandboxHost(base::Environment* env); + + // Holds the environment. Will never be NULL. + scoped_ptr<base::Environment> env_; + + DISALLOW_COPY_AND_ASSIGN(SetuidSandboxHost); +}; + +} // namespace sandbox + +#endif // SANDBOX_LINUX_SUID_SETUID_SANDBOX_HOST_H_ diff --git a/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc new file mode 100644 index 0000000..8415abb --- /dev/null +++ b/sandbox/linux/suid/client/setuid_sandbox_host_unittest.cc @@ -0,0 +1,72 @@ +// Copyright 2015 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 "sandbox/linux/suid/client/setuid_sandbox_host.h" + +#include <string> + +#include "base/environment.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "sandbox/linux/suid/common/sandbox.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace sandbox { + +TEST(SetuidSandboxHost, SetupLaunchEnvironment) { + const char kTestValue[] = "This is a test"; + scoped_ptr<base::Environment> env(base::Environment::Create()); + EXPECT_TRUE(env != NULL); + + std::string saved_ld_preload; + bool environment_had_ld_preload; + // First, back-up the real LD_PRELOAD if any. + environment_had_ld_preload = env->GetVar("LD_PRELOAD", &saved_ld_preload); + // Setup environment variables to save or not save. + EXPECT_TRUE(env->SetVar("LD_PRELOAD", kTestValue)); + EXPECT_TRUE(env->UnSetVar("LD_ORIGIN_PATH")); + + scoped_ptr<SetuidSandboxHost> sandbox_host(SetuidSandboxHost::Create()); + EXPECT_TRUE(sandbox_host != NULL); + + // Make sure the environment is clean. + EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiRequest)); + EXPECT_TRUE(env->UnSetVar(kSandboxEnvironmentApiProvides)); + + sandbox_host->SetupLaunchEnvironment(); + + // Check if the requested API environment was set. + std::string api_request; + EXPECT_TRUE(env->GetVar(kSandboxEnvironmentApiRequest, &api_request)); + int api_request_num; + EXPECT_TRUE(base::StringToInt(api_request, &api_request_num)); + EXPECT_EQ(api_request_num, kSUIDSandboxApiNumber); + + // Now check if LD_PRELOAD was saved to SANDBOX_LD_PRELOAD. + std::string sandbox_ld_preload; + EXPECT_TRUE(env->GetVar("SANDBOX_LD_PRELOAD", &sandbox_ld_preload)); + EXPECT_EQ(sandbox_ld_preload, kTestValue); + + // Check that LD_ORIGIN_PATH was not saved. + EXPECT_FALSE(env->HasVar("SANDBOX_LD_ORIGIN_PATH")); + + // We should not forget to restore LD_PRELOAD at the end, or this environment + // variable will affect the next running tests! + if (environment_had_ld_preload) { + EXPECT_TRUE(env->SetVar("LD_PRELOAD", saved_ld_preload)); + } else { + EXPECT_TRUE(env->UnSetVar("LD_PRELOAD")); + } +} + +// This test doesn't accomplish much, but will make sure that analysis tools +// will run this codepath. +TEST(SetuidSandboxHost, GetSandboxBinaryPath) { + scoped_ptr<SetuidSandboxHost> setuid_sandbox_host( + SetuidSandboxHost::Create()); + ignore_result(setuid_sandbox_host->GetSandboxBinaryPath()); +} + +} // namespace sandbox |