diff options
| author | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-23 23:36:39 +0000 |
|---|---|---|
| committer | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-23 23:36:39 +0000 |
| commit | a73ed203c32bbc70cf57e0a87a0fd79d9791fa7a (patch) | |
| tree | d533423258feb0dc657b323555dfdc3b09fe934b /sandbox | |
| parent | 9f5b0fdb85dbc429ec99b7e0af9380f85bba2932 (diff) | |
| download | chromium_src-a73ed203c32bbc70cf57e0a87a0fd79d9791fa7a.zip chromium_src-a73ed203c32bbc70cf57e0a87a0fd79d9791fa7a.tar.gz chromium_src-a73ed203c32bbc70cf57e0a87a0fd79d9791fa7a.tar.bz2 | |
Create a new SetuidSandboxClient class.
We move the setuid sandbox "client" code to its own location in
/sandbox/linux/suid/client and we create a SetuidSandboxClient class
to use it.
NOTRY=true
Review URL: https://chromiumcodereview.appspot.com/10807059
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@147993 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
| -rw-r--r-- | sandbox/linux/sandbox_linux.gypi | 24 | ||||
| -rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_client.cc | 168 | ||||
| -rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_client.h | 54 | ||||
| -rw-r--r-- | sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc | 78 | ||||
| -rw-r--r-- | sandbox/linux/suid/common/sandbox.h | 43 | ||||
| -rw-r--r-- | sandbox/linux/suid/common/suid_unsafe_environment_variables.h (renamed from sandbox/linux/suid/suid_unsafe_environment_variables.h) | 18 | ||||
| -rw-r--r-- | sandbox/linux/suid/sandbox.c | 28 | ||||
| -rw-r--r-- | sandbox/linux/suid/sandbox.h | 23 |
8 files changed, 389 insertions, 47 deletions
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi index 5286921..e124007 100644 --- a/sandbox/linux/sandbox_linux.gypi +++ b/sandbox/linux/sandbox_linux.gypi @@ -11,6 +11,9 @@ { 'target_name': 'sandbox', 'type': 'none', + 'dependencies': [ + 'suid_sandbox_client', + ], 'conditions': [ # Only compile in the seccomp mode 1 code for the flag combination # where we support it. @@ -38,6 +41,7 @@ ], 'sources': [ 'tests/unit_tests.cc', + 'suid/client/setuid_sandbox_client_unittest.cc', ], 'include_dirs': [ '../..', @@ -71,11 +75,12 @@ 'target_name': 'chrome_sandbox', 'type': 'executable', 'sources': [ + 'suid/common/sandbox.h', + 'suid/common/suid_unsafe_environment_variables.h', 'suid/linux_util.c', 'suid/linux_util.h', 'suid/process_util.h', 'suid/process_util_linux.c', - 'suid/sandbox.h', 'suid/sandbox.c', ], 'cflags': [ @@ -100,5 +105,22 @@ '..', ], }, + { + 'target_name': 'suid_sandbox_client', + 'type': 'static_library', + 'sources': [ + 'suid/common/sandbox.h', + 'suid/common/suid_unsafe_environment_variables.h', + 'suid/client/setuid_sandbox_client.cc', + 'suid/client/setuid_sandbox_client.h', + ], + 'dependencies': [ + '../base/base.gyp:base', + ], + 'include_dirs': [ + '..', + ], + }, + ], } diff --git a/sandbox/linux/suid/client/setuid_sandbox_client.cc b/sandbox/linux/suid/client/setuid_sandbox_client.cc new file mode 100644 index 0000000..07bff73 --- /dev/null +++ b/sandbox/linux/suid/client/setuid_sandbox_client.cc @@ -0,0 +1,168 @@ +// 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 <sys/types.h> +#include <sys/wait.h> + +#include "base/eintr_wrapper.h" +#include "base/environment.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" + +#include "sandbox/linux/suid/common/sandbox.h" +#include "sandbox/linux/suid/common/suid_unsafe_environment_variables.h" +#include "setuid_sandbox_client.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)); +} + +// 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* envvar) { + char* const saved_env_var = SandboxSavedEnvironmentVariable(envvar); + 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* const 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()); + } +} + +int GetHelperApi(base::Environment* env) { + std::string api_string; + int api_number = 0; // Assume API version 0 if no environment was found. + if (env->GetVar(sandbox::kSandboxEnvironmentApiProvides, &api_string) && + !base::StringToInt(api_string, &api_number)) { + // It's an error if we could not convert the API number. + api_number = -1; + } + return api_number; +} + +// Convert |var_name| from the environment |env| to an int. +// Return -1 if the variable does not exist or the value cannot be converted. +int EnvToInt(base::Environment* env, const char* var_name) { + std::string var_string; + int var_value = -1; + if (env->GetVar(var_name, &var_string) && + !base::StringToInt(var_string, &var_value)) { + var_value = -1; + } + return var_value; +} + +pid_t GetHelperPID(base::Environment* env) { + return EnvToInt(env, sandbox::kSandboxHelperPidEnvironmentVarName); +} + +// Get the IPC file descriptor used to communicate with the setuid helper. +int GetIPCDescriptor(base::Environment* env) { + return EnvToInt(env, sandbox::kSandboxDescriptorEnvironmentVarName); +} + +} // 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; +} + +SetuidSandboxClient::SetuidSandboxClient() { + env_ = NULL; +} + +SetuidSandboxClient::~SetuidSandboxClient() { + delete env_; +} + +bool SetuidSandboxClient::ChrootMe() { + int fd = GetIPCDescriptor(env_); + + if (fd < 0) { + LOG(ERROR) << "Failed to obtain the sandbox IPC descriptor"; + return false; + } + + if (HANDLE_EINTR(write(fd, &kMsgChrootMe, 1)) != 1) { + PLOG(ERROR) << "Failed to write to chroot pipe"; + return false; + } + + // We need to reap the chroot helper process in any event. + pid_t helper_pid = GetHelperPID(env_); + // If helper_pid is -1 we wait for any child. + if (waitpid(helper_pid, NULL, 0) < 0) { + PLOG(ERROR) << "Failed to wait for setuid helper to die"; + return false; + } + + char reply; + if (HANDLE_EINTR(read(fd, &reply, 1)) != 1) { + PLOG(ERROR) << "Failed to read from chroot pipe"; + return false; + } + + if (reply != kMsgChrootSuccessful) { + LOG(ERROR) << "Error code reply from chroot helper"; + return false; + } + return true; +} + +bool SetuidSandboxClient::IsSuidSandboxUpToDate() const { + return GetHelperApi(env_) == kSUIDSandboxApiNumber; +} + +bool SetuidSandboxClient::IsSuidSandboxChild() const { + return GetIPCDescriptor(env_) >= 0; +} + +bool SetuidSandboxClient::IsInNewPIDNamespace() const { + return env_->HasVar(kSandboxPIDNSEnvironmentVarName); +} + +bool SetuidSandboxClient::IsInNewNETNamespace() const { + return env_->HasVar(kSandboxNETNSEnvironmentVarName); +} + +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 new file mode 100644 index 0000000..afbde0a --- /dev/null +++ b/sandbox/linux/suid/client/setuid_sandbox_client.h @@ -0,0 +1,54 @@ +// 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. + +#ifndef SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_ +#define SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_ + +#include "base/basictypes.h" +#include "base/environment.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. +// +// A typical use would be: +// 1. The browser calls SetupLaunchEnvironment() +// 2. The browser launches a renderer through the setuid sandbox. +// 3. The renderer requests being chroot-ed through ChrootMe() and +// requests other sandboxing status via the status functions. +class SetuidSandboxClient { + public: + // All instantation should go through this factory method. + static class SetuidSandboxClient* Create(); + ~SetuidSandboxClient(); + + // Ask the setuid helper over the setuid sandbox IPC channel to chroot() us + // to an empty directory. + // Will only work if we have been launched through the setuid helper. + bool ChrootMe(); + + // Did we get launched through an up to date setuid binary ? + bool IsSuidSandboxUpToDate() const; + // Did we get launched through the setuid helper ? + bool IsSuidSandboxChild() const; + // Did the setuid helper create a new PID namespace ? + bool IsInNewPIDNamespace() const; + // Did the setuid helper create a new network namespace ? + bool IsInNewNETNamespace() const; + + // Set-up the environment. This should be done prior to launching the setuid + // helper. + void SetupLaunchEnvironment(); + + private: + // Holds the environment. Will never be NULL. + base::Environment* env_; + DISALLOW_IMPLICIT_CONSTRUCTORS(SetuidSandboxClient); +}; + +} // namespace sandbox + +#endif // SANDBOX_LINUX_SUID_SETUID_SANDBOX_CLIENT_H_ diff --git a/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc new file mode 100644 index 0000000..59b02eb --- /dev/null +++ b/sandbox/linux/suid/client/setuid_sandbox_client_unittest.cc @@ -0,0 +1,78 @@ +// 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 "base/environment.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_number_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "sandbox/linux/suid/common/sandbox.h" +#include "setuid_sandbox_client.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); + + // 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")); +} + +TEST(SetuidSandboxClient, SandboxedClientAPI) { + scoped_ptr<base::Environment> env(base::Environment::Create()); + EXPECT_TRUE(env != NULL); + + scoped_ptr<SetuidSandboxClient> + sandbox_client(SetuidSandboxClient::Create()); + EXPECT_TRUE(sandbox_client != NULL); + + // Set-up a fake environment as if we went through the setuid sandbox. + EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides, + base::IntToString(kSUIDSandboxApiNumber))); + EXPECT_TRUE(env->SetVar(kSandboxDescriptorEnvironmentVarName, "1")); + EXPECT_TRUE(env->SetVar(kSandboxPIDNSEnvironmentVarName, "1")); + EXPECT_TRUE(env->UnSetVar(kSandboxNETNSEnvironmentVarName)); + + // Check the API. + EXPECT_TRUE(sandbox_client->IsSuidSandboxUpToDate()); + EXPECT_TRUE(sandbox_client->IsSuidSandboxChild()); + EXPECT_TRUE(sandbox_client->IsInNewPIDNamespace()); + EXPECT_FALSE(sandbox_client->IsInNewNETNamespace()); + + // Forge an incorrect API version and check. + EXPECT_TRUE(env->SetVar(kSandboxEnvironmentApiProvides, + base::IntToString(kSUIDSandboxApiNumber + 1))); + EXPECT_FALSE(sandbox_client->IsSuidSandboxUpToDate()); +} + +} // namespace sandbox diff --git a/sandbox/linux/suid/common/sandbox.h b/sandbox/linux/suid/common/sandbox.h new file mode 100644 index 0000000..aad4ff8 --- /dev/null +++ b/sandbox/linux/suid/common/sandbox.h @@ -0,0 +1,43 @@ +// 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. + +#ifndef SANDBOX_LINUX_SUID_SANDBOX_H_ +#define SANDBOX_LINUX_SUID_SANDBOX_H_ + +#if defined(__cplusplus) +namespace sandbox { +#endif + +// These are command line switches that may be used by other programs +// (e.g. Chrome) to construct a command line for the sandbox. +static const char kAdjustOOMScoreSwitch[] = "--adjust-oom-score"; +#if defined(OS_CHROMEOS) +static const char kAdjustLowMemMarginSwitch[] = "--adjust-low-mem"; +#endif + +static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; +static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID"; + +static const long kSUIDSandboxApiNumber = 1; +static const char kSandboxEnvironmentApiRequest[] = "SBX_CHROME_API_RQ"; +static const char kSandboxEnvironmentApiProvides[] = "SBX_CHROME_API_PRV"; + +// This number must be kept in sync with common/zygote_commands_linux.h +static const int kZygoteIdFd = 7; + +// These are the magic byte values which the sandboxed process uses to request +// that it be chrooted. +static const char kMsgChrootMe = 'C'; +static const char kMsgChrootSuccessful = 'O'; + +// These are set if we have respectively switched to a new PID or NET namespace +// by going through the setuid binary helper. +static const char kSandboxPIDNSEnvironmentVarName[] = "SBX_PID_NS"; +static const char kSandboxNETNSEnvironmentVarName[] = "SBX_NET_NS"; + +#if defined(__cplusplus) +} // namespace sandbox +#endif + +#endif // SANDBOX_LINUX_SUID_SANDBOX_H_ diff --git a/sandbox/linux/suid/suid_unsafe_environment_variables.h b/sandbox/linux/suid/common/suid_unsafe_environment_variables.h index 4e3329f..ee4db76 100644 --- a/sandbox/linux/suid/suid_unsafe_environment_variables.h +++ b/sandbox/linux/suid/common/suid_unsafe_environment_variables.h @@ -16,6 +16,14 @@ #ifndef SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_ #define SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_ +#if defined(__cplusplus) +#include <limits> +#define SIZE_MAX std::numeric_limits<size_t>::max() +#endif + +#include <stdlib.h> // malloc +#include <string.h> // memcpy + static const char* kSUIDUnsafeEnvironmentVariables[] = { "LD_AOUT_LIBRARY_PATH", "LD_AOUT_PRELOAD", @@ -48,8 +56,12 @@ static const char* kSUIDUnsafeEnvironmentVariables[] = { // name for a given environment variable. static inline char* SandboxSavedEnvironmentVariable(const char* envvar) { const size_t envvar_len = strlen(envvar); + + if (envvar_len > SIZE_MAX - 1 -8) + return NULL; + const size_t saved_envvarlen = envvar_len + 1 /* NUL terminator */ + - 8 /* strlen("SANDBOX_") */; + 8 /* strlen("SANDBOX_") */; char* const saved_envvar = (char*) malloc(saved_envvarlen); if (!saved_envvar) return NULL; @@ -61,4 +73,8 @@ static inline char* SandboxSavedEnvironmentVariable(const char* envvar) { return saved_envvar; } +#if defined(__cplusplus) +#undef SIZE_MAX +#endif + #endif // SANDBOX_LINUX_SUID_SUID_UNSAFE_ENVIRONMENT_VARIABLES_H_ diff --git a/sandbox/linux/suid/sandbox.c b/sandbox/linux/suid/sandbox.c index 9c2ecde..32435a7 100644 --- a/sandbox/linux/suid/sandbox.c +++ b/sandbox/linux/suid/sandbox.c @@ -4,7 +4,7 @@ // http://code.google.com/p/chromium/wiki/LinuxSUIDSandbox -#include "sandbox.h" +#include "common/sandbox.h" #define _GNU_SOURCE #include <asm/unistd.h> @@ -31,7 +31,7 @@ #include "linux_util.h" #include "process_util.h" -#include "suid_unsafe_environment_variables.h" +#include "common/suid_unsafe_environment_variables.h" #if !defined(CLONE_NEWPID) #define CLONE_NEWPID 0x20000000 @@ -40,22 +40,6 @@ #define CLONE_NEWNET 0x40000000 #endif -static const char kSandboxDescriptorEnvironmentVarName[] = "SBX_D"; -static const char kSandboxHelperPidEnvironmentVarName[] = "SBX_HELPER_PID"; - -// Should be kept in sync with base/linux_util.h -static const long kSUIDSandboxApiNumber = 1; -static const char kSandboxEnvironmentApiRequest[] = "SBX_CHROME_API_RQ"; -static const char kSandboxEnvironmentApiProvides[] = "SBX_CHROME_API_PRV"; - -// This number must be kept in sync with common/zygote_commands_linux.h -static const int kZygoteIdFd = 7; - -// These are the magic byte values which the sandboxed process uses to request -// that it be chrooted. -static const char kMsgChrootMe = 'C'; -static const char kMsgChrootSuccessful = 'O'; - static bool DropRoot(); #define HANDLE_EINTR(x) TEMP_FAILURE_RETRY(x) @@ -280,15 +264,15 @@ static bool MoveToNewNamespaces() { FatalError("close"); if (kCloneExtraFlags[i] & CLONE_NEWPID) { - setenv("SBX_PID_NS", "", 1 /* overwrite */); + setenv(kSandboxPIDNSEnvironmentVarName, "", 1 /* overwrite */); } else { - unsetenv("SBX_PID_NS"); + unsetenv(kSandboxPIDNSEnvironmentVarName); } if (kCloneExtraFlags[i] & CLONE_NEWNET) { - setenv("SBX_NET_NS", "", 1 /* overwrite */); + setenv(kSandboxNETNSEnvironmentVarName, "", 1 /* overwrite */); } else { - unsetenv("SBX_NET_NS"); + unsetenv(kSandboxNETNSEnvironmentVarName); } break; diff --git a/sandbox/linux/suid/sandbox.h b/sandbox/linux/suid/sandbox.h deleted file mode 100644 index 066003a..0000000 --- a/sandbox/linux/suid/sandbox.h +++ /dev/null @@ -1,23 +0,0 @@ -// 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. - -#ifndef SANDBOX_LINUX_SUID_SANDBOX_H_ -#define SANDBOX_LINUX_SUID_SANDBOX_H_ - -#if defined(__cplusplus) -namespace sandbox { -#endif - -// These are command line switches that may be used by other programs -// (e.g. Chrome) to construct a command line for the sandbox. -static const char kAdjustOOMScoreSwitch[] = "--adjust-oom-score"; -#if defined(OS_CHROMEOS) -static const char kAdjustLowMemMarginSwitch[] = "--adjust-low-mem"; -#endif - -#if defined(__cplusplus) -} // namespace sandbox -#endif - -#endif // SANDBOX_LINUX_SUID_SANDBOX_H_ |
