diff options
author | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-08 20:28:36 +0000 |
---|---|---|
committer | jln@chromium.org <jln@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-08 20:28:36 +0000 |
commit | 3cb94d8d31c973d00d678a3048b08f51efcee140 (patch) | |
tree | a81661cf1eca5d2ff4c3489b27568447227a2adf /sandbox | |
parent | 0e785ec286e2efe9b3cd5002a0df05947a056a86 (diff) | |
download | chromium_src-3cb94d8d31c973d00d678a3048b08f51efcee140.zip chromium_src-3cb94d8d31c973d00d678a3048b08f51efcee140.tar.gz chromium_src-3cb94d8d31c973d00d678a3048b08f51efcee140.tar.bz2 |
Linux sandbox: make BPF policies an implementation of an interface.
This is the first pass. We renamed SetSandboxPolicy() to
SetSandboxPolicyDeprecated().
The new SetSandboxPolicy() takes a pointer to an instance of a class that
implements a simple interface.
This will improve flexibility by removing the need to pass static pointers
for policies. In particular, this will allow us to greatly improve
readability of the content/ policies and to use C++ inheritance to implement
our policy hierarchy.
BUG=316486
R=rsesek@chromium.org
Review URL: https://codereview.chromium.org/62953011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233967 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sandbox')
-rw-r--r-- | sandbox/linux/sandbox_linux.gypi | 1 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/bpf_tests.h | 4 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf.cc | 166 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf.h | 20 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h | 35 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc | 4 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/verifier.cc | 11 | ||||
-rw-r--r-- | sandbox/linux/seccomp-bpf/verifier.h | 4 |
8 files changed, 151 insertions, 94 deletions
diff --git a/sandbox/linux/sandbox_linux.gypi b/sandbox/linux/sandbox_linux.gypi index 3b2df8b..13b2bfc 100644 --- a/sandbox/linux/sandbox_linux.gypi +++ b/sandbox/linux/sandbox_linux.gypi @@ -103,6 +103,7 @@ 'seccomp-bpf/port.h', 'seccomp-bpf/sandbox_bpf.cc', 'seccomp-bpf/sandbox_bpf.h', + 'seccomp-bpf/sandbox_bpf_policy.h', 'seccomp-bpf/sandbox_bpf_policy_forward.h', 'seccomp-bpf/syscall.cc', 'seccomp-bpf/syscall.h', diff --git a/sandbox/linux/seccomp-bpf/bpf_tests.h b/sandbox/linux/seccomp-bpf/bpf_tests.h index 83fc10a..92cc1ed 100644 --- a/sandbox/linux/seccomp-bpf/bpf_tests.h +++ b/sandbox/linux/seccomp-bpf/bpf_tests.h @@ -89,7 +89,7 @@ class BpfTests : public UnitTests { // Initialize and then start the sandbox with our custom policy playground2::Sandbox sandbox; sandbox.set_proc_fd(proc_fd); - sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_); + sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_); sandbox.Sandbox::StartSandbox(); arg->test()(arg->aux_); @@ -105,7 +105,7 @@ class BpfTests : public UnitTests { // Call the compiler and verify the policy. That's the least we can do, // if we don't have kernel support. playground2::Sandbox sandbox; - sandbox.SetSandboxPolicy(arg->policy(), &arg->aux_); + sandbox.SetSandboxPolicyDeprecated(arg->policy(), &arg->aux_); playground2::Sandbox::Program *program = sandbox.AssembleFilter(true /* force_verification */); delete program; diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc index 1dc5eae..07de144 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf.cc +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.cc @@ -23,19 +23,18 @@ #include "base/posix/eintr_wrapper.h" #endif +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" #include "sandbox/linux/seccomp-bpf/codegen.h" #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" #include "sandbox/linux/seccomp-bpf/syscall.h" #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" #include "sandbox/linux/seccomp-bpf/verifier.h" -namespace { +namespace playground2 { -using playground2::ErrorCode; -using playground2::Instruction; -using playground2::Sandbox; -using playground2::Trap; -using playground2::arch_seccomp_data; +namespace { const int kExpectedExitCode = 100; @@ -179,43 +178,63 @@ void RedirectToUserspace(Instruction *insn, void *aux) { } } -// Stackable wrapper around an Evaluators handler. Changes ErrorCodes -// returned by a system call evaluator to match the changes made by -// RedirectToUserspace(). "aux" should be pointer to wrapped system call -// evaluator. -ErrorCode RedirectToUserspaceEvalWrapper(Sandbox *sandbox, int sysnum, - void *aux) { - // We need to replicate the behavior of RedirectToUserspace(), so that our - // Verifier can still work correctly. - Sandbox::Evaluators *evaluators = - reinterpret_cast<Sandbox::Evaluators *>(aux); - const std::pair<Sandbox::EvaluateSyscall, void *>& evaluator = - *evaluators->begin(); +// This wraps an existing policy and changes its behavior to match the changes +// made by RedirectToUserspace(). This is part of the framework that allows BPF +// evaluation in userland. +// TODO(markus): document the code inside better. +class RedirectToUserSpacePolicyWrapper : public SandboxBpfPolicy { + public: + explicit RedirectToUserSpacePolicyWrapper( + const SandboxBpfPolicy* wrapped_policy) + : wrapped_policy_(wrapped_policy) { + DCHECK(wrapped_policy_); + } - ErrorCode err = evaluator.first(sandbox, sysnum, evaluator.second); - if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { - return sandbox->Trap(ReturnErrno, - reinterpret_cast<void *>(err.err() & SECCOMP_RET_DATA)); + virtual ErrorCode EvaluateSyscall(Sandbox* sandbox_compiler, + int system_call_number) const OVERRIDE { + ErrorCode err = + wrapped_policy_->EvaluateSyscall(sandbox_compiler, system_call_number); + if ((err.err() & SECCOMP_RET_ACTION) == SECCOMP_RET_ERRNO) { + return sandbox_compiler->Trap(ReturnErrno, + reinterpret_cast<void*>(err.err() & SECCOMP_RET_DATA)); + } + return err; } - return err; -} + + private: + const SandboxBpfPolicy* wrapped_policy_; + DISALLOW_COPY_AND_ASSIGN(RedirectToUserSpacePolicyWrapper); +}; intptr_t BpfFailure(const struct arch_seccomp_data&, void *aux) { SANDBOX_DIE(static_cast<char *>(aux)); } -} // namespace +// This class allows compatibility with the old, deprecated SetSandboxPolicy. +class CompatibilityPolicy : public SandboxBpfPolicy { + public: + CompatibilityPolicy(Sandbox::EvaluateSyscall syscall_evaluator, void* aux) + : syscall_evaluator_(syscall_evaluator), + aux_(aux) { DCHECK(syscall_evaluator_); } -// The kernel gives us a sandbox, we turn it into a playground :-) -// This is version 2 of the playground; version 1 was built on top of -// pre-BPF seccomp mode. -namespace playground2 { + virtual ErrorCode EvaluateSyscall(Sandbox* sandbox_compiler, + int system_call_number) const OVERRIDE { + return syscall_evaluator_(sandbox_compiler, system_call_number, aux_); + } + + private: + Sandbox::EvaluateSyscall syscall_evaluator_; + void* aux_; + DISALLOW_COPY_AND_ASSIGN(CompatibilityPolicy); +}; + +} // namespace Sandbox::Sandbox() : quiet_(false), proc_fd_(-1), - evaluators_(new Evaluators), - conds_(new Conds) { + conds_(new Conds), + sandbox_has_started_(false) { } Sandbox::~Sandbox() { @@ -230,9 +249,6 @@ Sandbox::~Sandbox() { // The "if ()" statements are technically superfluous. But let's be explicit // that we really don't want to run any code, when we already destroyed // objects before setting up the sandbox. - if (evaluators_) { - delete evaluators_; - } if (conds_) { delete conds_; } @@ -312,7 +328,7 @@ bool Sandbox::RunFunctionInPolicy(void (*code_in_sandbox)(), #endif } - SetSandboxPolicy(syscall_evaluator, aux); + SetSandboxPolicyDeprecated(syscall_evaluator, aux); StartSandbox(); // Run our code in the sandbox. @@ -427,7 +443,7 @@ void Sandbox::StartSandbox() { if (status_ == STATUS_UNSUPPORTED || status_ == STATUS_UNAVAILABLE) { SANDBOX_DIE("Trying to start sandbox, even though it is known to be " "unavailable"); - } else if (!evaluators_ || !conds_) { + } else if (sandbox_has_started_ || !conds_) { SANDBOX_DIE("Cannot repeatedly start sandbox. Create a separate Sandbox " "object instead."); } @@ -459,11 +475,10 @@ void Sandbox::StartSandbox() { status_ = STATUS_ENABLED; } -void Sandbox::PolicySanityChecks(EvaluateSyscall syscall_evaluator, - void *aux) { +void Sandbox::PolicySanityChecks(SandboxBpfPolicy* policy) { for (SyscallIterator iter(true); !iter.Done(); ) { uint32_t sysnum = iter.Next(); - if (!IsDenied(syscall_evaluator(this, sysnum, aux))) { + if (!IsDenied(policy->EvaluateSyscall(this, sysnum))) { SANDBOX_DIE("Policies should deny system calls that are outside the " "expected range (typically MIN_SYSCALL..MAX_SYSCALL)"); } @@ -471,12 +486,23 @@ void Sandbox::PolicySanityChecks(EvaluateSyscall syscall_evaluator, return; } -void Sandbox::SetSandboxPolicy(EvaluateSyscall syscall_evaluator, void *aux) { - if (!evaluators_ || !conds_) { +// Deprecated API, supported with a wrapper to the new API. +void Sandbox::SetSandboxPolicyDeprecated(EvaluateSyscall syscall_evaluator, + void* aux) { + if (sandbox_has_started_ || !conds_) { + SANDBOX_DIE("Cannot change policy after sandbox has started"); + } + SetSandboxPolicy(new CompatibilityPolicy(syscall_evaluator, aux)); +} + +// Don't take a scoped_ptr here, polymorphism make their use awkward. +void Sandbox::SetSandboxPolicy(SandboxBpfPolicy* policy) { + DCHECK(!policy_); + if (sandbox_has_started_ || !conds_) { SANDBOX_DIE("Cannot change policy after sandbox has started"); } - PolicySanityChecks(syscall_evaluator, aux); - evaluators_->push_back(std::make_pair(syscall_evaluator, aux)); + PolicySanityChecks(policy); + policy_.reset(policy); } void Sandbox::InstallFilter() { @@ -499,11 +525,12 @@ void Sandbox::InstallFilter() { memcpy(bpf, &(*program)[0], sizeof(bpf)); delete program; - // Release memory that is no longer needed - delete evaluators_; + // Make an attempt to release memory that is no longer needed here, rather + // than in the destructor. Try to avoid as much as possible to presume of + // what will be possible to do in the new (sandboxed) execution environment. delete conds_; - evaluators_ = NULL; - conds_ = NULL; + conds_ = NULL; + policy_.reset(); // Install BPF filter program if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { @@ -514,6 +541,8 @@ void Sandbox::InstallFilter() { } } + sandbox_has_started_ = true; + return; } @@ -523,15 +552,7 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { #endif // Verify that the user pushed a policy. - if (evaluators_->empty()) { - SANDBOX_DIE("Failed to configure system call filters"); - } - - // We can't handle stacked evaluators, yet. We'll get there eventually - // though. Hang tight. - if (evaluators_->size() != 1) { - SANDBOX_DIE("Not implemented"); - } + DCHECK(policy_); // Assemble the BPF filter program. CodeGen *gen = new CodeGen(); @@ -585,18 +606,16 @@ Sandbox::Program *Sandbox::AssembleFilter(bool force_verification) { "architecture"); } - EvaluateSyscall evaluateSyscall = evaluators_->begin()->first; - void *aux = evaluators_->begin()->second; - if (!evaluateSyscall(this, __NR_rt_sigprocmask, aux). + if (!policy_->EvaluateSyscall(this, __NR_rt_sigprocmask). Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) || - !evaluateSyscall(this, __NR_rt_sigreturn, aux). + !policy_->EvaluateSyscall(this, __NR_rt_sigreturn). Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) #if defined(__NR_sigprocmask) - || !evaluateSyscall(this, __NR_sigprocmask, aux). + || !policy_->EvaluateSyscall(this, __NR_sigprocmask). Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) #endif #if defined(__NR_sigreturn) - || !evaluateSyscall(this, __NR_sigreturn, aux). + || !policy_->EvaluateSyscall(this, __NR_sigreturn). Equals(ErrorCode(ErrorCode::ERR_ALLOWED)) #endif ) { @@ -689,15 +708,14 @@ void Sandbox::VerifyProgram(const Program& program, bool has_unsafe_traps) { // whenever we return an "errno" value from the filter, then we have to // wrap our system call evaluator to perform the same operation. Otherwise, // the verifier would also report a mismatch in return codes. - Evaluators redirected_evaluators; - redirected_evaluators.push_back( - std::make_pair(RedirectToUserspaceEvalWrapper, evaluators_)); + scoped_ptr<const RedirectToUserSpacePolicyWrapper> redirected_policy( + new RedirectToUserSpacePolicyWrapper(policy_.get())); - const char *err = NULL; + const char* err = NULL; if (!Verifier::VerifyBPF( this, program, - has_unsafe_traps ? redirected_evaluators : *evaluators_, + has_unsafe_traps ? *redirected_policy : *policy_, &err)) { CodeGen::PrintProgram(program); SANDBOX_DIE(err); @@ -710,15 +728,13 @@ void Sandbox::FindRanges(Ranges *ranges) { // deal with this disparity by enumerating from MIN_SYSCALL to MAX_SYSCALL, // and then verifying that the rest of the number range (both positive and // negative) all return the same ErrorCode. - EvaluateSyscall evaluate_syscall = evaluators_->begin()->first; - void *aux = evaluators_->begin()->second; - uint32_t old_sysnum = 0; - ErrorCode old_err = evaluate_syscall(this, old_sysnum, aux); - ErrorCode invalid_err = evaluate_syscall(this, MIN_SYSCALL - 1, - aux); + uint32_t old_sysnum = 0; + ErrorCode old_err = policy_->EvaluateSyscall(this, old_sysnum); + ErrorCode invalid_err = policy_->EvaluateSyscall(this, MIN_SYSCALL - 1); + for (SyscallIterator iter(false); !iter.Done(); ) { uint32_t sysnum = iter.Next(); - ErrorCode err = evaluate_syscall(this, static_cast<int>(sysnum), aux); + ErrorCode err = policy_->EvaluateSyscall(this, static_cast<int>(sysnum)); if (!iter.IsValid(sysnum) && !invalid_err.Equals(err)) { // A proper sandbox policy should always treat system calls outside of // the range MIN_SYSCALL..MAX_SYSCALL (i.e. anything that returns @@ -982,4 +998,4 @@ ErrorCode Sandbox::Kill(const char *msg) { Sandbox::SandboxStatus Sandbox::status_ = STATUS_UNKNOWN; -} // namespace +} // namespace playground2 diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf.h b/sandbox/linux/seccomp-bpf/sandbox_bpf.h index f2653b0..72e17b2 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf.h +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf.h @@ -16,6 +16,7 @@ #include <utility> #include <vector> +#include "base/memory/scoped_ptr.h" #include "sandbox/linux/seccomp-bpf/die.h" #include "sandbox/linux/seccomp-bpf/errorcode.h" #include "sandbox/linux/seccomp-bpf/linux_seccomp.h" @@ -39,6 +40,7 @@ struct arch_sigsys { class CodeGen; class SandboxUnittestHelper; +class SandboxBpfPolicy; struct Instruction; class Sandbox { @@ -108,7 +110,12 @@ class Sandbox { // handler. In this case, of course, the data that is pointed to must remain // valid for the entire time that Trap() handlers can be called; typically, // this would be the lifetime of the program. - void SetSandboxPolicy(EvaluateSyscall syscallEvaluator, void *aux); + // DEPRECATED: use the policy interface below. + void SetSandboxPolicyDeprecated(EvaluateSyscall syscallEvaluator, void *aux); + + // Set the BPF policy as |policy|. Ownership of |policy| is transfered here + // to the sandbox object. + void SetSandboxPolicy(SandboxBpfPolicy* policy); // We can use ErrorCode to request calling of a trap handler. This method // performs the required wrapping of the callback function into an @@ -220,7 +227,7 @@ class Sandbox { bool KernelSupportSeccompBPF(); // Verify that the current policy passes some basic sanity checks. - void PolicySanityChecks(EvaluateSyscall syscall_evaluator, void *aux); + void PolicySanityChecks(SandboxBpfPolicy* policy); // Assembles and installs a filter based on the policy that has previously // been configured with SetSandboxPolicy(). @@ -258,10 +265,11 @@ class Sandbox { static SandboxStatus status_; - bool quiet_; - int proc_fd_; - Evaluators *evaluators_; - Conds *conds_; + bool quiet_; + int proc_fd_; + scoped_ptr<const SandboxBpfPolicy> policy_; + Conds* conds_; + bool sandbox_has_started_; DISALLOW_COPY_AND_ASSIGN(Sandbox); }; diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h b/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h new file mode 100644 index 0000000..99d9e19 --- /dev/null +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h @@ -0,0 +1,35 @@ +// Copyright 2013 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_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_ +#define SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_ + +#include "base/basictypes.h" + +namespace playground2 { + +class ErrorCode; +class Sandbox; + +// This is the interface to implement to define a BPF sandbox policy. +class SandboxBpfPolicy { + public: + SandboxBpfPolicy() {} + virtual ~SandboxBpfPolicy() {} + + // The EvaluateSyscall method is called with the system call number. It can + // decide to allow the system call unconditionally by returning ERR_ALLOWED; + // it can deny the system call unconditionally by returning an appropriate + // "errno" value; or it can request inspection of system call argument(s) by + // returning a suitable ErrorCode. + virtual ErrorCode EvaluateSyscall(Sandbox* sandbox_compiler, + int system_call_number) const = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(SandboxBpfPolicy); +}; + +} // namespace playground2 + +#endif // SANDBOX_LINUX_SECCOMP_BPF_SANDBOX_BPF_POLICY_H_ diff --git a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc index c41f779..d28b06c 100644 --- a/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc +++ b/sandbox/linux/seccomp-bpf/sandbox_bpf_unittest.cc @@ -99,7 +99,7 @@ SANDBOX_TEST(SandboxBpf, DISABLE_ON_TSAN(VerboseAPITesting)) { playground2::Sandbox::STATUS_AVAILABLE) { pid_t test_var = 0; Sandbox sandbox; - sandbox.SetSandboxPolicy(VerboseAPITestingPolicy, &test_var); + sandbox.SetSandboxPolicyDeprecated(VerboseAPITestingPolicy, &test_var); sandbox.StartSandbox(); BPF_ASSERT(test_var == 0); @@ -317,7 +317,7 @@ BPF_TEST(SandboxBpf, StackingPolicy, StackingPolicyPartOne) { // Stack a second sandbox with its own policy. Verify that we can further // restrict filters, but we cannot relax existing filters. Sandbox sandbox; - sandbox.SetSandboxPolicy(StackingPolicyPartTwo, NULL); + sandbox.SetSandboxPolicyDeprecated(StackingPolicyPartTwo, NULL); sandbox.StartSandbox(); errno = 0; diff --git a/sandbox/linux/seccomp-bpf/verifier.cc b/sandbox/linux/seccomp-bpf/verifier.cc index 60c0eab..798aced 100644 --- a/sandbox/linux/seccomp-bpf/verifier.cc +++ b/sandbox/linux/seccomp-bpf/verifier.cc @@ -5,6 +5,7 @@ #include <string.h> #include "sandbox/linux/seccomp-bpf/sandbox_bpf.h" +#include "sandbox/linux/seccomp-bpf/sandbox_bpf_policy.h" #include "sandbox/linux/seccomp-bpf/syscall_iterator.h" #include "sandbox/linux/seccomp-bpf/verifier.h" @@ -360,15 +361,9 @@ namespace playground2 { bool Verifier::VerifyBPF(Sandbox *sandbox, const std::vector<struct sock_filter>& program, - const Sandbox::Evaluators& evaluators, + const SandboxBpfPolicy& policy, const char **err) { *err = NULL; - if (evaluators.size() != 1) { - *err = "Not implemented"; - return false; - } - Sandbox::EvaluateSyscall evaluate_syscall = evaluators.begin()->first; - void *aux = evaluators.begin()->second; for (SyscallIterator iter(false); !iter.Done(); ) { uint32_t sysnum = iter.Next(); // We ideally want to iterate over the full system call range and values @@ -391,7 +386,7 @@ bool Verifier::VerifyBPF(Sandbox *sandbox, } #endif #endif - ErrorCode code = evaluate_syscall(sandbox, sysnum, aux); + ErrorCode code = policy.EvaluateSyscall(sandbox, sysnum); if (!VerifyErrorCode(sandbox, program, &data, code, code, err)) { return false; } diff --git a/sandbox/linux/seccomp-bpf/verifier.h b/sandbox/linux/seccomp-bpf/verifier.h index 3e99a08..02a9d37 100644 --- a/sandbox/linux/seccomp-bpf/verifier.h +++ b/sandbox/linux/seccomp-bpf/verifier.h @@ -13,6 +13,8 @@ namespace playground2 { +class SandboxBpfPolicy; + class Verifier { public: // Evaluate the BPF program for all possible inputs and verify that it @@ -24,7 +26,7 @@ class Verifier { // error message that does not need to be free()'d. static bool VerifyBPF(Sandbox *sandbox, const std::vector<struct sock_filter>& program, - const Sandbox::Evaluators& evaluators, + const SandboxBpfPolicy& policy, const char **err); // Evaluate a given BPF program for a particular set of system call |