summaryrefslogtreecommitdiff
path: root/chromium/sandbox
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-01-31 16:33:43 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-02-06 16:33:22 +0000
commitda51f56cc21233c2d30f0fe0d171727c3102b2e0 (patch)
tree4e579ab70ce4b19bee7984237f3ce05a96d59d83 /chromium/sandbox
parentc8c2d1901aec01e934adf561a9fdf0cc776cdef8 (diff)
downloadqtwebengine-chromium-da51f56cc21233c2d30f0fe0d171727c3102b2e0.tar.gz
BASELINE: Update Chromium to 65.0.3525.40
Also imports missing submodules Change-Id: I36901b7c6a325cda3d2c10cedb2186c25af3b79b Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/sandbox')
-rw-r--r--chromium/sandbox/OWNERS5
-rw-r--r--chromium/sandbox/linux/BUILD.gn14
-rw-r--r--chromium/sandbox/linux/OWNERS2
-rw-r--r--chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc27
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc7
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc1
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc16
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h3
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc16
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc5
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc8
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc7
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h2
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall.h2
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc39
-rw-r--r--chromium/sandbox/linux/services/credentials.cc14
-rw-r--r--chromium/sandbox/linux/services/namespace_utils.cc8
-rw-r--r--chromium/sandbox/linux/services/syscall_wrappers.cc1
-rw-r--r--chromium/sandbox/linux/services/syscall_wrappers_unittest.cc1
-rw-r--r--chromium/sandbox/linux/services/thread_helpers_unittests.cc15
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.cc390
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.h64
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_command.cc99
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_command.h114
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_common.h44
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_file_permission.cc54
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_file_permission.h37
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc4
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_host.cc284
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_host.h11
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_permission_list.cc (renamed from chromium/sandbox/linux/syscall_broker/broker_policy.cc)68
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_permission_list.h (renamed from chromium/sandbox/linux/syscall_broker/broker_policy.h)37
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process.cc150
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process.h58
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc996
-rw-r--r--chromium/sandbox/linux/system_headers/linux_time.h8
-rw-r--r--chromium/sandbox/mac/OWNERS1
-rw-r--r--chromium/sandbox/win/BUILD.gn2
-rw-r--r--chromium/sandbox/win/OWNERS4
-rw-r--r--chromium/sandbox/win/src/app_container_test.cc288
-rw-r--r--chromium/sandbox/win/src/broker_services.cc44
-rw-r--r--chromium/sandbox/win/src/crosscall_server.cc4
-rw-r--r--chromium/sandbox/win/src/sandbox_policy.h5
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.cc23
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.h6
-rw-r--r--chromium/sandbox/win/src/service_resolver_unittest.cc81
-rw-r--r--chromium/sandbox/win/src/target_process.cc78
-rw-r--r--chromium/sandbox/win/src/target_process.h10
48 files changed, 2408 insertions, 749 deletions
diff --git a/chromium/sandbox/OWNERS b/chromium/sandbox/OWNERS
index 38a06dc7770..c7304374f32 100644
--- a/chromium/sandbox/OWNERS
+++ b/chromium/sandbox/OWNERS
@@ -1,7 +1,8 @@
-cpu@chromium.org
-jln@chromium.org
+jorgelo@chromium.org
jschuh@chromium.org
+palmer@chromium.org
rsesek@chromium.org
+tsepez@chromium.org
# TEAM: security-dev@chromium.org
# COMPONENT: Internals>Sandbox
diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn
index 8c9cf2c9458..9d4726f89e4 100644
--- a/chromium/sandbox/linux/BUILD.gn
+++ b/chromium/sandbox/linux/BUILD.gn
@@ -358,13 +358,14 @@ component("sandbox_services") {
"syscall_broker/broker_channel.h",
"syscall_broker/broker_client.cc",
"syscall_broker/broker_client.h",
- "syscall_broker/broker_common.h",
+ "syscall_broker/broker_command.cc",
+ "syscall_broker/broker_command.h",
"syscall_broker/broker_file_permission.cc",
"syscall_broker/broker_file_permission.h",
"syscall_broker/broker_host.cc",
"syscall_broker/broker_host.h",
- "syscall_broker/broker_policy.cc",
- "syscall_broker/broker_policy.h",
+ "syscall_broker/broker_permission_list.cc",
+ "syscall_broker/broker_permission_list.h",
"syscall_broker/broker_process.cc",
"syscall_broker/broker_process.h",
]
@@ -406,13 +407,14 @@ component("sandbox_services") {
"syscall_broker/broker_channel.h",
"syscall_broker/broker_client.cc",
"syscall_broker/broker_client.h",
- "syscall_broker/broker_common.h",
+ "syscall_broker/broker_command.cc",
+ "syscall_broker/broker_command.h",
"syscall_broker/broker_file_permission.cc",
"syscall_broker/broker_file_permission.h",
"syscall_broker/broker_host.cc",
"syscall_broker/broker_host.h",
- "syscall_broker/broker_policy.cc",
- "syscall_broker/broker_policy.h",
+ "syscall_broker/broker_permission_list.cc",
+ "syscall_broker/broker_permission_list.h",
"syscall_broker/broker_process.cc",
"syscall_broker/broker_process.h",
]
diff --git a/chromium/sandbox/linux/OWNERS b/chromium/sandbox/linux/OWNERS
index 35461850b4c..7ea24db8239 100644
--- a/chromium/sandbox/linux/OWNERS
+++ b/chromium/sandbox/linux/OWNERS
@@ -1,5 +1,5 @@
-jln@chromium.org
jorgelo@chromium.org
+palmer@chromium.org
rsesek@chromium.org
# TEAM: security-dev@chromium.org
diff --git a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
index 73dc58c92b8..b1990041425 100644
--- a/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
+++ b/chromium/sandbox/linux/integration_tests/seccomp_broker_process_unittest.cc
@@ -20,6 +20,7 @@
#include "sandbox/linux/bpf_dsl/seccomp_macros.h"
#include "sandbox/linux/seccomp-bpf/bpf_tests.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
#include "sandbox/linux/syscall_broker/broker_file_permission.h"
#include "sandbox/linux/syscall_broker/broker_process.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
@@ -43,27 +44,27 @@ bool NoOpCallback() {
class InitializedOpenBroker {
public:
InitializedOpenBroker() : initialized_(false) {
- std::vector<syscall_broker::BrokerFilePermission> permissions;
- permissions.push_back(
- syscall_broker::BrokerFilePermission::ReadOnly("/proc/allowed"));
- permissions.push_back(
- syscall_broker::BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
-
- broker_process_.reset(
- new syscall_broker::BrokerProcess(EPERM, permissions));
- BPF_ASSERT(broker_process() != NULL);
+ syscall_broker::BrokerCommandSet command_set;
+ command_set.set(syscall_broker::COMMAND_OPEN);
+ command_set.set(syscall_broker::COMMAND_ACCESS);
+ std::vector<syscall_broker::BrokerFilePermission> permissions = {
+ syscall_broker::BrokerFilePermission::ReadOnly("/proc/allowed"),
+ syscall_broker::BrokerFilePermission::ReadOnly("/proc/cpuinfo")};
+ broker_process_ = std::make_unique<syscall_broker::BrokerProcess>(
+ EPERM, command_set, permissions);
BPF_ASSERT(broker_process_->Init(base::Bind(&NoOpCallback)));
-
initialized_ = true;
}
- bool initialized() { return initialized_; }
- class syscall_broker::BrokerProcess* broker_process() {
+
+ bool initialized() const { return initialized_; }
+
+ syscall_broker::BrokerProcess* broker_process() const {
return broker_process_.get();
}
private:
bool initialized_;
- std::unique_ptr<class syscall_broker::BrokerProcess> broker_process_;
+ std::unique_ptr<syscall_broker::BrokerProcess> broker_process_;
DISALLOW_COPY_AND_ASSIGN(InitializedOpenBroker);
};
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
index d06e7653a0c..6aab37a88f4 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy.cc
@@ -268,9 +268,10 @@ ResultExpr EvaluateSyscallImpl(int fs_denied_errno,
} // namespace.
-// Unfortunately C++03 doesn't allow delegated constructors.
-// Call other constructor when C++11 lands.
-BaselinePolicy::BaselinePolicy() : BaselinePolicy(EPERM) {}
+BaselinePolicy::BaselinePolicy() : BaselinePolicy(EPERM) {
+ // Allocate crash keys set by Seccomp signal handlers.
+ AllocateCrashKeys();
+}
BaselinePolicy::BaselinePolicy(int fs_denied_errno)
: fs_denied_errno_(fs_denied_errno), policy_pid_(sys_getpid()) {
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
index ca6f08a2c2b..9a37225780b 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/baseline_policy_android.cc
@@ -205,6 +205,7 @@ ResultExpr BaselinePolicyAndroid::EvaluateSyscall(int sysno) const {
return If(AllOf(level == SOL_SOCKET,
AnyOf(option == SO_SNDTIMEO,
option == SO_RCVTIMEO,
+ option == SO_SNDBUF,
option == SO_REUSEADDR)),
Allow())
.Else(BaselinePolicy::EvaluateSyscall(sysno));
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
index 68890d2536c..8386b0f906e 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.cc
@@ -35,6 +35,10 @@
namespace {
+#if !defined(OS_NACL_NONSFI)
+base::debug::CrashKeyString* seccomp_crash_key = nullptr;
+#endif
+
inline bool IsArchitectureX86_64() {
#if defined(__x86_64__)
return true;
@@ -185,7 +189,7 @@ void SetSeccompCrashKey(const struct sandbox::arch_seccomp_data& args) {
}
}
- base::debug::SetCrashKeyValue("seccomp-sigsys", crash_key);
+ base::debug::SetCrashKeyString(seccomp_crash_key, crash_key);
#endif
}
@@ -362,6 +366,16 @@ bpf_dsl::ResultExpr RewriteSchedSIGSYS() {
return bpf_dsl::Trap(SIGSYSSchedHandler, NULL);
}
+void AllocateCrashKeys() {
+#if !defined(OS_NACL_NONSFI)
+ if (seccomp_crash_key)
+ return;
+
+ seccomp_crash_key = base::debug::AllocateCrashKeyString(
+ "seccomp-sigsys", base::debug::CrashKeySize::Size256);
+#endif
+}
+
const char* GetErrorMessageContentForTests() {
return SECCOMP_MESSAGE_COMMON_CONTENT;
}
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
index c64e994172e..b32fd6fedd4 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h
@@ -68,6 +68,9 @@ SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSKill();
SANDBOX_EXPORT bpf_dsl::ResultExpr CrashSIGSYSFutex();
SANDBOX_EXPORT bpf_dsl::ResultExpr RewriteSchedSIGSYS();
+// Allocates a crash key so that Seccomp information can be recorded.
+void AllocateCrashKeys();
+
// Following four functions return substrings of error messages used
// in the above four functions. They are useful in death tests.
SANDBOX_EXPORT const char* GetErrorMessageContentForTests();
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
index 440ef20681f..ada0b8f23ae 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -341,8 +341,13 @@ ResultExpr RestrictGetrusage() {
ResultExpr RestrictClockID() {
static_assert(4 == sizeof(clockid_t), "clockid_t is not 32bit");
const Arg<clockid_t> clockid(0);
- return Switch(clockid)
- .CASES((
+
+ // Clock IDs < 0 are per pid/tid or are clockfds.
+ const unsigned int kIsPidBit = 1u<<31;
+
+ return
+ If((clockid & kIsPidBit) == 0,
+ Switch(clockid).CASES((
#if defined(OS_ANDROID)
CLOCK_BOOTTIME,
#endif
@@ -353,7 +358,12 @@ ResultExpr RestrictClockID() {
CLOCK_REALTIME_COARSE,
CLOCK_THREAD_CPUTIME_ID),
Allow())
- .Default(CrashSIGSYS());
+ .Default(CrashSIGSYS()))
+#if defined(OS_ANDROID)
+ // Allow per-pid and per-tid clocks.
+ .ElseIf((clockid & CPUCLOCK_CLOCK_MASK) != CLOCKFD, Allow())
+#endif
+ .Else(CrashSIGSYS());
}
#if !defined(GRND_NONBLOCK)
diff --git a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
index c068cd2d04f..b572a618ff7 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions_unittests.cc
@@ -86,6 +86,11 @@ BPF_TEST_C(ParameterRestrictions,
CheckClock(CLOCK_REALTIME);
CheckClock(CLOCK_REALTIME_COARSE);
CheckClock(CLOCK_THREAD_CPUTIME_ID);
+#if defined(OS_ANDROID)
+ clockid_t clock_id;
+ pthread_getcpuclockid(pthread_self(), &clock_id);
+ CheckClock(clock_id);
+#endif
}
BPF_DEATH_TEST_C(ParameterRestrictions,
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index 9ff79d79658..72a79670d32 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -15,8 +15,6 @@
#include "base/logging.h"
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/third_party/valgrind/valgrind.h"
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include "sandbox/linux/bpf_dsl/codegen.h"
#include "sandbox/linux/bpf_dsl/policy.h"
@@ -122,12 +120,6 @@ SandboxBPF::~SandboxBPF() {
// static
bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) {
- // Never pretend to support seccomp with Valgrind, as it
- // throws the tool off.
- if (RunningOnValgrind()) {
- return false;
- }
-
switch (level) {
case SeccompLevel::SINGLE_THREADED:
return KernelSupportsSeccompBPF();
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
index 5f59cbbcc65..36f3744b76e 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
@@ -9,7 +9,6 @@
#include <memory>
#include "base/logging.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "sandbox/linux/bpf_dsl/policy.h"
#include "sandbox/linux/seccomp-bpf/die.h"
#include "sandbox/linux/seccomp-bpf/sandbox_bpf.h"
@@ -44,9 +43,9 @@ void SandboxBPFTestRunner::Run() {
bpf_tester_delegate_->RunTestFunction();
} else {
printf("This BPF test is not fully running in this configuration!\n");
- // Android and Valgrind are the only configurations where we accept not
- // having kernel BPF support.
- if (!IsAndroid() && !RunningOnValgrind()) {
+ // Android is the only configuration where we accept not having kernel
+ // BPF support.
+ if (!IsAndroid()) {
const bool seccomp_bpf_is_supported = false;
SANDBOX_ASSERT(seccomp_bpf_is_supported);
}
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
index 1cf1c2f856f..4fc3c5d1696 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.h
@@ -40,7 +40,7 @@ class BPFTesterDelegate {
// run a test function (via |bpf_tester_delegate|) if the current kernel
// configuration allows it. If it can not run the test under seccomp-bpf,
// Run() will still compile the policy which should allow to get some coverage
-// under tools such as Valgrind.
+// under tools that behave like Valgrind.
class SandboxBPFTestRunner : public SandboxTestRunner {
public:
// This constructor takes ownership of the |bpf_tester_delegate| object.
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall.h b/chromium/sandbox/linux/seccomp-bpf/syscall.h
index ccfc88dcb30..3b02a6723f5 100644
--- a/chromium/sandbox/linux/seccomp-bpf/syscall.h
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall.h
@@ -28,7 +28,7 @@ class SANDBOX_EXPORT Syscall {
// System calls can take up to six parameters (up to eight on some
// architectures). Traditionally, glibc
// implements this property by using variadic argument lists. This works, but
- // confuses modern tools such as valgrind, because we are nominally passing
+ // confuses tools that behave like Valgrind, because we are nominally passing
// uninitialized data whenever we call through this function and pass less
// than the full six arguments.
// So, instead, we use C++'s template system to achieve a very similar
diff --git a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
index 057f0d4b2d1..e96f51b00c8 100644
--- a/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/syscall_unittest.cc
@@ -18,6 +18,7 @@
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/process/process_metrics.h"
#include "build/build_config.h"
#include "sandbox/linux/bpf_dsl/bpf_dsl.h"
#include "sandbox/linux/bpf_dsl/policy.h"
@@ -163,6 +164,8 @@ BPF_TEST(Syscall,
TEST(Syscall, ComplexSyscallSixArgs) {
int fd;
+ const size_t kPageSize = base::GetPageSize();
+
ASSERT_LE(0,
fd = Syscall::Call(__NR_openat, AT_FDCWD, "/dev/null", O_RDWR, 0L));
@@ -172,7 +175,7 @@ TEST(Syscall, ComplexSyscallSixArgs) {
(char*)NULL,
addr0 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
(void*)NULL,
- 4096,
+ kPageSize,
PROT_READ,
MAP_PRIVATE | MAP_ANONYMOUS,
fd,
@@ -184,7 +187,7 @@ TEST(Syscall, ComplexSyscallSixArgs) {
addr1 = reinterpret_cast<char*>(
Syscall::Call(kMMapNr,
addr0,
- 4096L,
+ kPageSize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
fd,
@@ -192,7 +195,7 @@ TEST(Syscall, ComplexSyscallSixArgs) {
++*addr1; // This should not seg fault
// Clean up
- EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr1, 4096L));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr1, kPageSize));
EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
// Check that the offset argument (i.e. the sixth argument) is processed
@@ -202,32 +205,42 @@ TEST(Syscall, ComplexSyscallSixArgs) {
0);
char* addr2, *addr3;
ASSERT_NE((char*)NULL,
- addr2 = reinterpret_cast<char*>(Syscall::Call(
- kMMapNr, (void*)NULL, 8192L, PROT_READ, MAP_PRIVATE, fd, 0L)));
+ addr2 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
+ (void*)NULL,
+ 2 * kPageSize,
+ PROT_READ,
+ MAP_PRIVATE,
+ fd,
+ 0L
+ )));
ASSERT_NE((char*)NULL,
addr3 = reinterpret_cast<char*>(Syscall::Call(kMMapNr,
(void*)NULL,
- 4096L,
+ kPageSize,
PROT_READ,
MAP_PRIVATE,
fd,
#if defined(__NR_mmap2)
1L
#else
- 4096L
+ kPageSize
#endif
)));
- EXPECT_EQ(0, memcmp(addr2 + 4096, addr3, 4096));
+ EXPECT_EQ(0, memcmp(addr2 + kPageSize, addr3, kPageSize));
// Just to be absolutely on the safe side, also verify that the file
// contents matches what we are getting from a read() operation.
- char buf[8192];
- EXPECT_EQ(8192, Syscall::Call(__NR_read, fd, buf, 8192L));
- EXPECT_EQ(0, memcmp(addr2, buf, 8192));
+ char buf[2 * kPageSize];
+ EXPECT_EQ(2 * kPageSize, static_cast<size_t>(Syscall::Call(__NR_read,
+ fd,
+ buf,
+ 2 * kPageSize
+ )));
+ EXPECT_EQ(0, memcmp(addr2, buf, 2 * kPageSize));
// Clean up
- EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr2, 8192L));
- EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr3, 4096L));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr2, 2 * kPageSize));
+ EXPECT_EQ(0, Syscall::Call(__NR_munmap, addr3, kPageSize));
EXPECT_EQ(0, IGNORE_EINTR(Syscall::Call(__NR_close, fd)));
}
diff --git a/chromium/sandbox/linux/services/credentials.cc b/chromium/sandbox/linux/services/credentials.cc
index d97f0946621..65b63adfe2f 100644
--- a/chromium/sandbox/linux/services/credentials.cc
+++ b/chromium/sandbox/linux/services/credentials.cc
@@ -23,8 +23,6 @@
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/launch.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/third_party/valgrind/valgrind.h"
#include "build/build_config.h"
#include "sandbox/linux/services/namespace_utils.h"
#include "sandbox/linux/services/proc_util.h"
@@ -115,10 +113,10 @@ bool ChrootToSafeEmptyDir() {
void CheckCloneNewUserErrno(int error) {
// EPERM can happen if already in a chroot. EUSERS if too many nested
// namespaces are used. EINVAL for kernels that don't support the feature.
- // Valgrind will ENOSYS unshare(). ENOSPC can occur when the system has
- // reached its maximum configured number of user namespaces.
+ // ENOSPC can occur when the system has reached its maximum configured
+ // number of user namespaces.
PCHECK(error == EPERM || error == EUSERS || error == EINVAL ||
- error == ENOSYS || error == ENOSPC);
+ error == ENOSPC);
}
// Converts a Capability to the corresponding Linux CAP_XXX value.
@@ -256,12 +254,6 @@ bool Credentials::HasCapability(Capability cap) {
// static
bool Credentials::CanCreateProcessInNewUserNS() {
- // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
- // so always consider UserNS unsupported there.
- if (RunningOnValgrind()) {
- return false;
- }
-
#if defined(THREAD_SANITIZER)
// With TSAN, processes will always have threads running and can never
// enter a new user namespace with MoveToNewUserNS().
diff --git a/chromium/sandbox/linux/services/namespace_utils.cc b/chromium/sandbox/linux/services/namespace_utils.cc
index 83749adf037..376b179f4f6 100644
--- a/chromium/sandbox/linux/services/namespace_utils.cc
+++ b/chromium/sandbox/linux/services/namespace_utils.cc
@@ -20,8 +20,6 @@
#include "base/posix/eintr_wrapper.h"
#include "base/process/launch.h"
#include "base/strings/safe_sprintf.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/third_party/valgrind/valgrind.h"
namespace sandbox {
@@ -52,12 +50,6 @@ bool NamespaceUtils::WriteToIdMapFile(const char* map_file, generic_id_t id) {
// static
bool NamespaceUtils::KernelSupportsUnprivilegedNamespace(int type) {
- // Valgrind will let clone(2) pass-through, but doesn't support unshare(),
- // so always consider namespaces unsupported there.
- if (RunningOnValgrind()) {
- return false;
- }
-
// As of Linux 3.8, /proc/self/ns/* files exist for all namespace types. Since
// user namespaces were added in 3.8, it is OK to rely on the existence of
// /proc/self/ns/*.
diff --git a/chromium/sandbox/linux/services/syscall_wrappers.cc b/chromium/sandbox/linux/services/syscall_wrappers.cc
index 9c7727cee50..fcfd2aa129d 100644
--- a/chromium/sandbox/linux/services/syscall_wrappers.cc
+++ b/chromium/sandbox/linux/services/syscall_wrappers.cc
@@ -16,7 +16,6 @@
#include "base/compiler_specific.h"
#include "base/logging.h"
-#include "base/third_party/valgrind/valgrind.h"
#include "build/build_config.h"
#include "sandbox/linux/system_headers/capability.h"
#include "sandbox/linux/system_headers/linux_signal.h"
diff --git a/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc b/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc
index 34ac7409307..b28e6381bff 100644
--- a/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc
+++ b/chromium/sandbox/linux/services/syscall_wrappers_unittest.cc
@@ -13,7 +13,6 @@
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
-#include "base/third_party/valgrind/valgrind.h"
#include "build/build_config.h"
#include "sandbox/linux/system_headers/linux_signal.h"
#include "sandbox/linux/tests/test_utils.h"
diff --git a/chromium/sandbox/linux/services/thread_helpers_unittests.cc b/chromium/sandbox/linux/services/thread_helpers_unittests.cc
index 78e6d837e20..5e574599fcb 100644
--- a/chromium/sandbox/linux/services/thread_helpers_unittests.cc
+++ b/chromium/sandbox/linux/services/thread_helpers_unittests.cc
@@ -14,7 +14,6 @@
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/process_metrics.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "build/build_config.h"
@@ -30,13 +29,7 @@ namespace {
// These tests fail under ThreadSanitizer, see http://crbug.com/342305
#if !defined(THREAD_SANITIZER)
-int GetRaceTestIterations() {
- if (RunningOnValgrind()) {
- return 2;
- } else {
- return 1000;
- }
-}
+const int kRaceTestIterations = 1000;
class ScopedProc {
public:
@@ -81,7 +74,7 @@ TEST(ThreadHelpers, IsSingleThreadedIterated) {
ASSERT_TRUE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
// Iterate to check for race conditions.
- for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ for (int i = 0; i < kRaceTestIterations; ++i) {
base::Thread thread("sandbox_tests");
ASSERT_TRUE(
ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread));
@@ -98,7 +91,7 @@ TEST(ThreadHelpers, IsSingleThreadedStartAndStop) {
base::Thread thread("sandbox_tests");
// This is testing for a race condition, so iterate.
// Manually, this has been tested with more that 1M iterations.
- for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ for (int i = 0; i < kRaceTestIterations; ++i) {
ASSERT_TRUE(
ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread));
ASSERT_FALSE(ThreadHelpers::IsSingleThreaded(proc_fd.fd()));
@@ -116,7 +109,7 @@ SANDBOX_TEST(ThreadHelpers, AssertSingleThreadedAfterThreadStopped) {
base::Thread thread1("sandbox_tests");
base::Thread thread2("sandbox_tests");
- for (int i = 0; i < GetRaceTestIterations(); ++i) {
+ for (int i = 0; i < kRaceTestIterations; ++i) {
SANDBOX_ASSERT(
ThreadHelpers::StartThreadAndWatchProcFS(proc_fd.fd(), &thread1));
SANDBOX_ASSERT(
diff --git a/chromium/sandbox/linux/syscall_broker/broker_client.cc b/chromium/sandbox/linux/syscall_broker/broker_client.cc
index 277e61a2c4f..6ae93045b2a 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.cc
@@ -13,150 +13,191 @@
#include <utility>
#include "base/logging.h"
-#include "base/pickle.h"
#include "base/posix/unix_domain_socket.h"
#include "build/build_config.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
-#include "sandbox/linux/syscall_broker/broker_common.h"
-#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
+#include "sandbox/linux/syscall_broker/broker_permission_list.h"
#if defined(OS_ANDROID) && !defined(MSG_CMSG_CLOEXEC)
#define MSG_CMSG_CLOEXEC 0x40000000
#endif
namespace sandbox {
-
namespace syscall_broker {
-// Make a remote system call over IPC for syscalls that take a path and flags
-// as arguments, currently open() and access().
-// Will return -errno like a real system call.
-// This function needs to be async signal safe.
-int BrokerClient::PathAndFlagsSyscall(IPCCommand syscall_type,
- const char* pathname,
- int flags) const {
- int recvmsg_flags = 0;
- RAW_CHECK(syscall_type == COMMAND_OPEN || syscall_type == COMMAND_ACCESS);
+BrokerClient::BrokerClient(const BrokerPermissionList& broker_permission_list,
+ BrokerChannel::EndPoint ipc_channel,
+ const BrokerCommandSet& allowed_command_set,
+ bool fast_check_in_client,
+ bool quiet_failures_for_tests)
+ : broker_permission_list_(broker_permission_list),
+ ipc_channel_(std::move(ipc_channel)),
+ allowed_command_set_(allowed_command_set),
+ fast_check_in_client_(fast_check_in_client),
+ quiet_failures_for_tests_(quiet_failures_for_tests) {}
+
+BrokerClient::~BrokerClient() {}
+
+int BrokerClient::Access(const char* pathname, int mode) const {
if (!pathname)
return -EFAULT;
- // For this "remote system call" to work, we need to handle any flag that
- // cannot be sent over a Unix socket in a special way.
- // See the comments around kCurrentProcessOpenFlagsMask.
- if (syscall_type == COMMAND_OPEN && (flags & kCurrentProcessOpenFlagsMask)) {
- // This implementation only knows about O_CLOEXEC, someone needs to look at
- // this code if other flags are added.
- RAW_CHECK(kCurrentProcessOpenFlagsMask == O_CLOEXEC);
- recvmsg_flags |= MSG_CMSG_CLOEXEC;
- flags &= ~O_CLOEXEC;
+ if (fast_check_in_client_ &&
+ !CommandAccessIsSafe(allowed_command_set_, broker_permission_list_,
+ pathname, mode, nullptr)) {
+ return -broker_permission_list_.denied_errno();
}
+ return PathAndFlagsSyscall(COMMAND_ACCESS, pathname, mode);
+}
- // There is no point in forwarding a request that we know will be denied.
- // Of course, the real security check needs to be on the other side of the
- // IPC.
- if (fast_check_in_client_) {
- if (syscall_type == COMMAND_OPEN &&
- !broker_policy_.GetFileNameIfAllowedToOpen(
- pathname, flags, NULL /* file_to_open */,
- NULL /* unlink_after_open */)) {
- return -broker_policy_.denied_errno();
- }
- if (syscall_type == COMMAND_ACCESS &&
- !broker_policy_.GetFileNameIfAllowedToAccess(pathname, flags, NULL)) {
- return -broker_policy_.denied_errno();
- }
+int BrokerClient::Mkdir(const char* pathname, int mode) const {
+ if (!pathname)
+ return -EFAULT;
+
+ if (fast_check_in_client_ &&
+ !CommandMkdirIsSafe(allowed_command_set_, broker_permission_list_,
+ pathname, nullptr)) {
+ return -broker_permission_list_.denied_errno();
+ }
+ return PathAndFlagsSyscall(COMMAND_MKDIR, pathname, mode);
+}
+
+int BrokerClient::Open(const char* pathname, int flags) const {
+ if (!pathname)
+ return -EFAULT;
+
+ if (fast_check_in_client_ &&
+ !CommandOpenIsSafe(allowed_command_set_, broker_permission_list_,
+ pathname, flags, nullptr, nullptr)) {
+ return -broker_permission_list_.denied_errno();
+ }
+ return PathAndFlagsSyscallReturningFD(COMMAND_OPEN, pathname, flags);
+}
+
+int BrokerClient::Readlink(const char* path, char* buf, size_t bufsize) const {
+ if (!path || !buf)
+ return -EFAULT;
+
+ if (fast_check_in_client_ &&
+ !CommandReadlinkIsSafe(allowed_command_set_, broker_permission_list_,
+ path, nullptr)) {
+ return -broker_permission_list_.denied_errno();
}
base::Pickle write_pickle;
- write_pickle.WriteInt(syscall_type);
- write_pickle.WriteString(pathname);
- write_pickle.WriteInt(flags);
+ write_pickle.WriteInt(COMMAND_READLINK);
+ write_pickle.WriteString(path);
RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength];
+ ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
+ sizeof(reply_buf), &returned_fd);
+ if (msg_len < 0)
+ return msg_len;
- // Send a request (in write_pickle) as well that will include a new
- // temporary socketpair (created internally by SendRecvMsg()).
- // Then read the reply on this new socketpair in reply_buf and put an
- // eventual attached file descriptor in |returned_fd|.
- ssize_t msg_len = base::UnixDomainSocket::SendRecvMsgWithFlags(
- ipc_channel_.get(), reply_buf, sizeof(reply_buf), recvmsg_flags,
- &returned_fd, write_pickle);
- if (msg_len <= 0) {
- if (!quiet_failures_for_tests_)
- RAW_LOG(ERROR, "Could not make request to broker process");
+ base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+ base::PickleIterator iter(read_pickle);
+ int return_value = -1;
+ int return_length = 0;
+ const char* return_data = nullptr;
+ if (!iter.ReadInt(&return_value))
return -ENOMEM;
+ if (return_value < 0)
+ return return_value;
+ if (!iter.ReadData(&return_data, &return_length))
+ return -ENOMEM;
+ if (return_length < 0)
+ return -ENOMEM;
+ if (static_cast<size_t>(return_length) > bufsize)
+ return -ENAMETOOLONG;
+ memcpy(buf, return_data, return_length);
+ return return_value;
+}
+
+int BrokerClient::Rename(const char* oldpath, const char* newpath) const {
+ if (!oldpath || !newpath)
+ return -EFAULT;
+
+ if (fast_check_in_client_ &&
+ !CommandRenameIsSafe(allowed_command_set_, broker_permission_list_,
+ oldpath, newpath, nullptr, nullptr)) {
+ return -broker_permission_list_.denied_errno();
}
+ base::Pickle write_pickle;
+ write_pickle.WriteInt(COMMAND_RENAME);
+ write_pickle.WriteString(oldpath);
+ write_pickle.WriteString(newpath);
+ RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+
+ int returned_fd = -1;
+ uint8_t reply_buf[kMaxMessageLength];
+ ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
+ sizeof(reply_buf), &returned_fd);
+ if (msg_len < 0)
+ return msg_len;
+
base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
base::PickleIterator iter(read_pickle);
int return_value = -1;
- // Now deserialize the return value and eventually return the file
- // descriptor.
- if (iter.ReadInt(&return_value)) {
- switch (syscall_type) {
- case COMMAND_ACCESS:
- // We should never have a fd to return.
- RAW_CHECK(returned_fd == -1);
- return return_value;
- case COMMAND_OPEN:
- if (return_value < 0) {
- RAW_CHECK(returned_fd == -1);
- return return_value;
- } else {
- // We have a real file descriptor to return.
- RAW_CHECK(returned_fd >= 0);
- return returned_fd;
- }
- default:
- RAW_LOG(ERROR, "Unsupported command");
- return -ENOSYS;
- }
- } else {
- RAW_LOG(ERROR, "Could not read pickle");
- NOTREACHED();
+ if (!iter.ReadInt(&return_value))
return -ENOMEM;
- }
-}
-BrokerClient::BrokerClient(const BrokerPolicy& broker_policy,
- BrokerChannel::EndPoint ipc_channel,
- bool fast_check_in_client,
- bool quiet_failures_for_tests)
- : broker_policy_(broker_policy),
- ipc_channel_(std::move(ipc_channel)),
- fast_check_in_client_(fast_check_in_client),
- quiet_failures_for_tests_(quiet_failures_for_tests) {}
-
-BrokerClient::~BrokerClient() {
+ return return_value;
}
-int BrokerClient::Access(const char* pathname, int mode) const {
- return PathAndFlagsSyscall(COMMAND_ACCESS, pathname, mode);
-}
+int BrokerClient::Rmdir(const char* path) const {
+ if (!path)
+ return -EFAULT;
-int BrokerClient::Open(const char* pathname, int flags) const {
- return PathAndFlagsSyscall(COMMAND_OPEN, pathname, flags);
+ if (fast_check_in_client_ &&
+ !CommandRmdirIsSafe(allowed_command_set_, broker_permission_list_, path,
+ nullptr)) {
+ return -broker_permission_list_.denied_errno();
+ }
+ return PathOnlySyscall(COMMAND_RMDIR, path);
}
-int BrokerClient::Stat(const char* pathname, struct stat* sb) {
+int BrokerClient::Stat(const char* pathname, struct stat* sb) const {
+ if (!pathname || !sb)
+ return -EFAULT;
+
+ if (fast_check_in_client_ &&
+ !CommandStatIsSafe(allowed_command_set_, broker_permission_list_,
+ pathname, nullptr)) {
+ return -broker_permission_list_.denied_errno();
+ }
return StatFamilySyscall(COMMAND_STAT, pathname, sb, sizeof(*sb));
}
-int BrokerClient::Stat64(const char* pathname, struct stat64* sb) {
+int BrokerClient::Stat64(const char* pathname, struct stat64* sb) const {
+ if (!pathname || !sb)
+ return -EFAULT;
+
+ if (fast_check_in_client_ &&
+ !CommandStatIsSafe(allowed_command_set_, broker_permission_list_,
+ pathname, nullptr)) {
+ return -broker_permission_list_.denied_errno();
+ }
return StatFamilySyscall(COMMAND_STAT64, pathname, sb, sizeof(*sb));
}
-int BrokerClient::StatFamilySyscall(IPCCommand syscall_type,
- const char* pathname,
- void* result_ptr,
- size_t expected_result_size) const {
+int BrokerClient::Unlink(const char* path) const {
+ if (!path)
+ return -EFAULT;
+
if (fast_check_in_client_ &&
- !broker_policy_.GetFileNameIfAllowedToAccess(pathname, R_OK, nullptr)) {
- return -broker_policy_.denied_errno();
+ !CommandUnlinkIsSafe(allowed_command_set_, broker_permission_list_, path,
+ nullptr)) {
+ return -broker_permission_list_.denied_errno();
}
+ return PathOnlySyscall(COMMAND_UNLINK, path);
+}
+int BrokerClient::PathOnlySyscall(BrokerCommand syscall_type,
+ const char* pathname) const {
base::Pickle write_pickle;
write_pickle.WriteInt(syscall_type);
write_pickle.WriteString(pathname);
@@ -164,70 +205,151 @@ int BrokerClient::StatFamilySyscall(IPCCommand syscall_type,
int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = base::UnixDomainSocket::SendRecvMsg(
- ipc_channel_.get(), reply_buf, sizeof(reply_buf), &returned_fd,
- write_pickle);
+ ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
+ sizeof(reply_buf), &returned_fd);
+ if (msg_len < 0)
+ return msg_len;
- if (msg_len <= 0) {
- if (!quiet_failures_for_tests_)
- RAW_LOG(ERROR, "Could not make request to broker process");
+ base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+ base::PickleIterator iter(read_pickle);
+ int return_value = -1;
+ if (!iter.ReadInt(&return_value))
return -ENOMEM;
- }
+ return return_value;
+}
+
+// Make a remote system call over IPC for syscalls that take a path and
+// flags (currently access() and mkdir()) but do not return a FD.
+// Will return -errno like a real system call.
+// This function needs to be async signal safe.
+int BrokerClient::PathAndFlagsSyscall(BrokerCommand syscall_type,
+ const char* pathname,
+ int flags) const {
+ base::Pickle write_pickle;
+ write_pickle.WriteInt(syscall_type);
+ write_pickle.WriteString(pathname);
+ write_pickle.WriteInt(flags);
+ RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+
+ int returned_fd = -1;
+ uint8_t reply_buf[kMaxMessageLength];
+ ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
+ sizeof(reply_buf), &returned_fd);
+ if (msg_len < 0)
+ return msg_len;
base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
base::PickleIterator iter(read_pickle);
int return_value = -1;
- int return_length = 0;
- const char* return_data = nullptr;
if (!iter.ReadInt(&return_value))
return -ENOMEM;
- if (return_value < 0)
- return return_value;
- if (!iter.ReadData(&return_data, &return_length))
- return -ENOMEM;
- if (static_cast<size_t>(return_length) != expected_result_size)
- return -ENOMEM;
- memcpy(result_ptr, return_data, expected_result_size);
+
return return_value;
}
-int BrokerClient::Rename(const char* oldpath, const char* newpath) {
- if (fast_check_in_client_) {
- bool ignore;
- if (!broker_policy_.GetFileNameIfAllowedToOpen(oldpath, O_RDWR, nullptr,
- &ignore) ||
- !broker_policy_.GetFileNameIfAllowedToOpen(newpath, O_RDWR, nullptr,
- &ignore)) {
- return -broker_policy_.denied_errno();
- }
+// Make a remote system call over IPC for syscalls that take a path and flags
+// as arguments and return FDs (currently open()).
+// Will return -errno like a real system call.
+// This function needs to be async signal safe.
+int BrokerClient::PathAndFlagsSyscallReturningFD(BrokerCommand syscall_type,
+ const char* pathname,
+ int flags) const {
+ // For this "remote system call" to work, we need to handle any flag that
+ // cannot be sent over a Unix socket in a special way.
+ // See the comments around kCurrentProcessOpenFlagsMask.
+ int recvmsg_flags = 0;
+ if (syscall_type == COMMAND_OPEN && (flags & kCurrentProcessOpenFlagsMask)) {
+ // This implementation only knows about O_CLOEXEC, someone needs to look at
+ // this code if other flags are added.
+ static_assert(kCurrentProcessOpenFlagsMask == O_CLOEXEC,
+ "Must update broker client to handle other flags");
+
+ recvmsg_flags |= MSG_CMSG_CLOEXEC;
+ flags &= ~kCurrentProcessOpenFlagsMask;
}
base::Pickle write_pickle;
- write_pickle.WriteInt(COMMAND_RENAME);
- write_pickle.WriteString(oldpath);
- write_pickle.WriteString(newpath);
+ write_pickle.WriteInt(syscall_type);
+ write_pickle.WriteString(pathname);
+ write_pickle.WriteInt(flags);
RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
int returned_fd = -1;
uint8_t reply_buf[kMaxMessageLength];
- ssize_t msg_len = base::UnixDomainSocket::SendRecvMsg(
- ipc_channel_.get(), reply_buf, sizeof(reply_buf), &returned_fd,
- write_pickle);
+ ssize_t msg_len = SendRecvRequest(write_pickle, recvmsg_flags, reply_buf,
+ sizeof(reply_buf), &returned_fd);
+ if (msg_len < 0)
+ return msg_len;
- if (msg_len <= 0) {
- if (!quiet_failures_for_tests_)
- RAW_LOG(ERROR, "Could not make request to broker process");
+ base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
+ base::PickleIterator iter(read_pickle);
+ int return_value = -1;
+ if (!iter.ReadInt(&return_value))
return -ENOMEM;
- }
+ if (return_value < 0)
+ return return_value;
+
+ // We have a real file descriptor to return.
+ RAW_CHECK(returned_fd >= 0);
+ return returned_fd;
+}
+
+// Make a remote system call over IPC for syscalls that take a path
+// and return stat buffers (currently stat() and stat64()).
+// Will return -errno like a real system call.
+// This function needs to be async signal safe.
+int BrokerClient::StatFamilySyscall(BrokerCommand syscall_type,
+ const char* pathname,
+ void* result_ptr,
+ size_t expected_result_size) const {
+ base::Pickle write_pickle;
+ write_pickle.WriteInt(syscall_type);
+ write_pickle.WriteString(pathname);
+ RAW_CHECK(write_pickle.size() <= kMaxMessageLength);
+
+ int returned_fd = -1;
+ uint8_t reply_buf[kMaxMessageLength];
+ ssize_t msg_len = SendRecvRequest(write_pickle, 0, reply_buf,
+ sizeof(reply_buf), &returned_fd);
+ if (msg_len < 0)
+ return msg_len;
base::Pickle read_pickle(reinterpret_cast<char*>(reply_buf), msg_len);
base::PickleIterator iter(read_pickle);
int return_value = -1;
+ int return_length = 0;
+ const char* return_data = nullptr;
if (!iter.ReadInt(&return_value))
return -ENOMEM;
-
+ if (return_value < 0)
+ return return_value;
+ if (!iter.ReadData(&return_data, &return_length))
+ return -ENOMEM;
+ if (static_cast<size_t>(return_length) != expected_result_size)
+ return -ENOMEM;
+ memcpy(result_ptr, return_data, expected_result_size);
return return_value;
}
+// Send a request (in request_pickle) that will include a new temporary
+// socketpair as well (created internally by SendRecvMsg()). Then read the
+// reply on this new socketpair in |reply_buf| and put an eventual attached
+// file descriptor (if any) in |returned_fd|.
+ssize_t BrokerClient::SendRecvRequest(const base::Pickle& request_pickle,
+ int recvmsg_flags,
+ uint8_t* reply_buf,
+ size_t reply_buf_size,
+ int* returned_fd) const {
+ ssize_t msg_len = base::UnixDomainSocket::SendRecvMsgWithFlags(
+ ipc_channel_.get(), reply_buf, reply_buf_size, recvmsg_flags, returned_fd,
+ request_pickle);
+ if (msg_len <= 0) {
+ if (!quiet_failures_for_tests_)
+ RAW_LOG(ERROR, "Could not make request to broker process");
+ return -ENOMEM;
+ }
+ return msg_len;
+}
+
} // namespace syscall_broker
} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_client.h b/chromium/sandbox/linux/syscall_broker/broker_client.h
index 9c818578235..fc108f1abb4 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.h
@@ -10,14 +10,14 @@
#include <unistd.h>
#include "base/macros.h"
+#include "base/pickle.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
-#include "sandbox/linux/syscall_broker/broker_common.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
namespace sandbox {
-
namespace syscall_broker {
-class BrokerPolicy;
+class BrokerPermissionList;
// This class can be embedded in a sandboxed process and can be
// used to perform certain system calls in another, presumably
@@ -34,52 +34,74 @@ class BrokerClient {
// |ipc_channel| needs to be a suitable SOCK_SEQPACKET unix socket.
// |fast_check_in_client| should be set to true and
// |quiet_failures_for_tests| to false unless you are writing tests.
- BrokerClient(const BrokerPolicy& policy,
+ BrokerClient(const BrokerPermissionList& policy,
BrokerChannel::EndPoint ipc_channel,
+ const BrokerCommandSet& allowed_command_set,
bool fast_check_in_client,
bool quiet_failures_for_tests);
~BrokerClient();
+ // Get the file descriptor used for IPC. This is used for tests.
+ int GetIPCDescriptor() const { return ipc_channel_.get(); }
+
+ // The following public methods can be used in place of the equivalently
+ // name system calls. They all return -errno on errors. They are all async
+ // signal safe so they may be called from a SIGSYS trap handler.
+
// Can be used in place of access().
// X_OK will always return an error in practice since the broker process
// doesn't support execute permissions.
- // It's similar to the access() system call and will return -errno on errors.
- // This is async signal safe.
int Access(const char* pathname, int mode) const;
+ // Can be used in place of mkdir().
+ int Mkdir(const char* path, int mode) const;
+
// Can be used in place of open().
// The implementation only supports certain white listed flags and will
// return -EPERM on other flags.
- // It's similar to the open() system call and will return -errno on errors.
- // This is async signal safe.
int Open(const char* pathname, int flags) const;
- // Can be used in place of stat()/stat64().
- // It's similar to the stat() system call and will return -errno on errors.
- // This is async signal safe.
- int Stat(const char* pathname, struct stat* sb);
- int Stat64(const char* pathname, struct stat64* sb);
+ // Can be used in place of Readlink().
+ int Readlink(const char* path, char* buf, size_t bufsize) const;
// Can be used in place of rename().
- // It's similar to the rename() system call and will return -errno on errors.
- // This is async signal safe.
- int Rename(const char* oldpath, const char* newpath);
+ int Rename(const char* oldpath, const char* newpath) const;
- // Get the file descriptor used for IPC. This is used for tests.
- int GetIPCDescriptor() const { return ipc_channel_.get(); }
+ // Can be used in place of rmdir().
+ int Rmdir(const char* path) const;
+
+ // Can be used in place of stat()/stat64().
+ int Stat(const char* pathname, struct stat* sb) const;
+ int Stat64(const char* pathname, struct stat64* sb) const;
+
+ // Can be used in place of rmdir().
+ int Unlink(const char* unlink) const;
private:
- int PathAndFlagsSyscall(IPCCommand syscall_type,
+ int PathOnlySyscall(BrokerCommand syscall_type, const char* pathname) const;
+
+ int PathAndFlagsSyscall(BrokerCommand syscall_type,
const char* pathname,
int flags) const;
- int StatFamilySyscall(IPCCommand syscall_type,
+ int PathAndFlagsSyscallReturningFD(BrokerCommand syscall_type,
+ const char* pathname,
+ int flags) const;
+
+ int StatFamilySyscall(BrokerCommand syscall_type,
const char* pathname,
void* result_ptr,
size_t expected_result_size) const;
- const BrokerPolicy& broker_policy_;
+ ssize_t SendRecvRequest(const base::Pickle& request_pickle,
+ int recvmsg_flags,
+ uint8_t* reply_buf,
+ size_t reply_buf_size,
+ int* returned_fd) const;
+
+ const BrokerPermissionList& broker_permission_list_;
const BrokerChannel::EndPoint ipc_channel_;
+ const BrokerCommandSet allowed_command_set_;
const bool fast_check_in_client_; // Whether to forward a request that we
// know will be denied to the broker. (Used
// for tests).
diff --git a/chromium/sandbox/linux/syscall_broker/broker_command.cc b/chromium/sandbox/linux/syscall_broker/broker_command.cc
new file mode 100644
index 00000000000..46533d381dc
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/broker_command.cc
@@ -0,0 +1,99 @@
+// Copyright 2017 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 <unistd.h>
+
+#include "sandbox/linux/syscall_broker/broker_command.h"
+#include "sandbox/linux/syscall_broker/broker_permission_list.h"
+
+namespace sandbox {
+namespace syscall_broker {
+
+bool CommandAccessIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ int requested_mode,
+ const char** filename_to_use) {
+ return command_set.test(COMMAND_ACCESS) &&
+ policy.GetFileNameIfAllowedToAccess(requested_filename, requested_mode,
+ filename_to_use);
+}
+
+bool CommandMkdirIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use) {
+ return command_set.test(COMMAND_MKDIR) &&
+ policy.GetFileNameIfAllowedToOpen(requested_filename,
+ O_RDWR | O_CREAT | O_EXCL,
+ filename_to_use, nullptr);
+}
+
+bool CommandOpenIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ int requested_flags,
+ const char** filename_to_use,
+ bool* unlink_after_open) {
+ return command_set.test(COMMAND_OPEN) &&
+ policy.GetFileNameIfAllowedToOpen(
+ requested_filename,
+ requested_flags & ~kCurrentProcessOpenFlagsMask, filename_to_use,
+ unlink_after_open);
+}
+
+bool CommandReadlinkIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use) {
+ return command_set.test(COMMAND_READLINK) &&
+ policy.GetFileNameIfAllowedToOpen(requested_filename, O_RDONLY,
+ filename_to_use, nullptr);
+}
+
+bool CommandRenameIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* old_filename,
+ const char* new_filename,
+ const char** old_filename_to_use,
+ const char** new_filename_to_use) {
+ return command_set.test(COMMAND_RENAME) &&
+ policy.GetFileNameIfAllowedToOpen(old_filename,
+ O_RDWR | O_CREAT | O_EXCL,
+ old_filename_to_use, nullptr) &&
+ policy.GetFileNameIfAllowedToOpen(new_filename,
+ O_RDWR | O_CREAT | O_EXCL,
+ new_filename_to_use, nullptr);
+}
+
+bool CommandRmdirIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use) {
+ return command_set.test(COMMAND_RMDIR) &&
+ policy.GetFileNameIfAllowedToOpen(requested_filename,
+ O_RDWR | O_CREAT | O_EXCL,
+ filename_to_use, nullptr);
+}
+
+bool CommandStatIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use) {
+ return command_set.test(COMMAND_STAT) &&
+ policy.GetFileNameIfAllowedToStat(requested_filename, filename_to_use);
+}
+
+bool CommandUnlinkIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use) {
+ return command_set.test(COMMAND_UNLINK) &&
+ policy.GetFileNameIfAllowedToOpen(requested_filename,
+ O_RDWR | O_CREAT | O_EXCL,
+ filename_to_use, nullptr);
+}
+
+} // namespace syscall_broker
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_command.h b/chromium/sandbox/linux/syscall_broker/broker_command.h
new file mode 100644
index 00000000000..aa22b94aba7
--- /dev/null
+++ b/chromium/sandbox/linux/syscall_broker/broker_command.h
@@ -0,0 +1,114 @@
+// Copyright 2014 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_SYSCALL_BROKER_BROKER_COMMAND_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_COMMAND_H_
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <bitset>
+#include <initializer_list>
+
+namespace sandbox {
+namespace syscall_broker {
+
+class BrokerPermissionList;
+
+constexpr size_t kMaxMessageLength = 4096;
+
+// Some flags are local to the current process and cannot be sent over a Unix
+// socket. They need special treatment from the client.
+// O_CLOEXEC is tricky because in theory another thread could call execve()
+// before special treatment is made on the client, so a client needs to call
+// recvmsg(2) with MSG_CMSG_CLOEXEC.
+// To make things worse, there are two CLOEXEC related flags, FD_CLOEXEC (see
+// F_GETFD in fcntl(2)) and O_CLOEXEC (see F_GETFL in fcntl(2)). O_CLOEXEC
+// doesn't affect the semantics on execve(), it's merely a note that the
+// descriptor was originally opened with O_CLOEXEC as a flag. And it is sent
+// over unix sockets just fine, so a receiver that would (incorrectly) look at
+// O_CLOEXEC instead of FD_CLOEXEC may be tricked in thinking that the file
+// descriptor will or won't be closed on execve().
+constexpr int kCurrentProcessOpenFlagsMask = O_CLOEXEC;
+
+enum BrokerCommand {
+ COMMAND_INVALID = 0,
+ COMMAND_ACCESS,
+ COMMAND_MKDIR,
+ COMMAND_OPEN,
+ COMMAND_READLINK,
+ COMMAND_RENAME,
+ COMMAND_RMDIR,
+ COMMAND_STAT,
+ COMMAND_STAT64,
+ COMMAND_UNLINK,
+
+ // NOTE: update when adding new commands.
+ COMMAND_MAX = COMMAND_UNLINK
+};
+
+using BrokerCommandSet = std::bitset<COMMAND_MAX + 1>;
+
+// Helper function since std::bitset lacks an initializer list constructor.
+inline BrokerCommandSet MakeBrokerCommandSet(
+ const std::initializer_list<BrokerCommand>& args) {
+ BrokerCommandSet result;
+ for (const auto& arg : args)
+ result.set(arg);
+ return result;
+}
+
+// Helper functions to perform the same permissions test on either side
+// (client or broker process) of a broker IPC command. The implementations
+// must be safe when called from an async signal handler.
+bool CommandAccessIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ int requested_mode, // e.g. F_OK, R_OK, W_OK.
+ const char** filename_to_use);
+
+bool CommandMkdirIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use);
+
+bool CommandOpenIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ int requested_flags, // e.g. O_RDONLY, O_RDWR.
+ const char** filename_to_use,
+ bool* unlink_after_open);
+
+bool CommandReadlinkIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use);
+
+bool CommandRenameIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* old_filename,
+ const char* new_filename,
+ const char** old_filename_to_use,
+ const char** new_filename_to_use);
+
+bool CommandRmdirIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use);
+
+bool CommandStatIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use);
+
+bool CommandUnlinkIsSafe(const BrokerCommandSet& command_set,
+ const BrokerPermissionList& policy,
+ const char* requested_filename,
+ const char** filename_to_use);
+
+} // namespace syscall_broker
+} // namespace sandbox
+
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_COMMAND_H_
diff --git a/chromium/sandbox/linux/syscall_broker/broker_common.h b/chromium/sandbox/linux/syscall_broker/broker_common.h
deleted file mode 100644
index 30b2ce19762..00000000000
--- a/chromium/sandbox/linux/syscall_broker/broker_common.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2014 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_SYSCALL_BROKER_BROKER_COMMON_H_
-#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_COMMON_H_
-
-#include <fcntl.h>
-#include <stddef.h>
-
-namespace sandbox {
-
-namespace syscall_broker {
-
-const size_t kMaxMessageLength = 4096;
-
-// Some flags are local to the current process and cannot be sent over a Unix
-// socket. They need special treatment from the client.
-// O_CLOEXEC is tricky because in theory another thread could call execve()
-// before special treatment is made on the client, so a client needs to call
-// recvmsg(2) with MSG_CMSG_CLOEXEC.
-// To make things worse, there are two CLOEXEC related flags, FD_CLOEXEC (see
-// F_GETFD in fcntl(2)) and O_CLOEXEC (see F_GETFL in fcntl(2)). O_CLOEXEC
-// doesn't affect the semantics on execve(), it's merely a note that the
-// descriptor was originally opened with O_CLOEXEC as a flag. And it is sent
-// over unix sockets just fine, so a receiver that would (incorrectly) look at
-// O_CLOEXEC instead of FD_CLOEXEC may be tricked in thinking that the file
-// descriptor will or won't be closed on execve().
-const int kCurrentProcessOpenFlagsMask = O_CLOEXEC;
-
-enum IPCCommand {
- COMMAND_INVALID = 0,
- COMMAND_OPEN,
- COMMAND_ACCESS,
- COMMAND_STAT,
- COMMAND_STAT64,
- COMMAND_RENAME,
-};
-
-} // namespace syscall_broker
-
-} // namespace sandbox
-
-#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_COMMON_H_
diff --git a/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc b/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc
index 3947145eaf6..05c783068ae 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc
@@ -7,11 +7,12 @@
#include <fcntl.h>
#include <stddef.h>
#include <string.h>
+#include <unistd.h>
#include <string>
#include "base/logging.h"
-#include "sandbox/linux/syscall_broker/broker_common.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
namespace sandbox {
namespace syscall_broker {
@@ -83,6 +84,13 @@ bool BrokerFilePermission::CheckAccess(const char* requested_filename,
if (!ValidatePath(requested_filename))
return false;
+ return CheckAccessInternal(requested_filename, mode, file_to_access);
+}
+
+bool BrokerFilePermission::CheckAccessInternal(
+ const char* requested_filename,
+ int mode,
+ const char** file_to_access) const {
if (!MatchPath(requested_filename))
return false;
@@ -148,13 +156,9 @@ bool BrokerFilePermission::CheckOpen(const char* requested_filename,
return false;
}
- // If O_CREAT is present, ensure O_EXCL
- if ((flags & O_CREAT) && !(flags & O_EXCL)) {
- return false;
- }
-
- // If this file is to be temporary, ensure it's created.
- if (temporary_only_ && !(flags & O_CREAT)) {
+ // If this file is to be temporary, ensure it is created, not pre-existing.
+ // See https://crbug.com/415681#c17
+ if (temporary_only_ && (!(flags & O_CREAT) || !(flags & O_EXCL))) {
return false;
}
@@ -173,7 +177,6 @@ bool BrokerFilePermission::CheckOpen(const char* requested_filename,
const int unknown_flags = ~known_flags;
const bool has_unknown_flags = creation_and_status_flags & unknown_flags;
-
if (has_unknown_flags)
return false;
@@ -186,6 +189,38 @@ bool BrokerFilePermission::CheckOpen(const char* requested_filename,
return true;
}
+bool BrokerFilePermission::CheckStat(const char* requested_filename,
+ const char** file_to_access) const {
+ if (!ValidatePath(requested_filename))
+ return false;
+
+ // Ability to access implies ability to stat().
+ if (CheckAccessInternal(requested_filename, F_OK, file_to_access))
+ return true;
+
+ // Allow stat() on leading directories if have create permission.
+ if (!allow_create_)
+ return false;
+
+ // NOTE: ValidatePath proved requested_length != 0;
+ size_t requested_length = strlen(requested_filename);
+ CHECK(requested_length);
+
+ // Special case for root: only one slash, otherwise must have a second
+ // slash in the right spot to avoid substring matches.
+ if ((requested_length == 1 && requested_filename[0] == '/') ||
+ (requested_length < path_.length() &&
+ memcmp(path_.c_str(), requested_filename, requested_length) == 0 &&
+ path_.c_str()[requested_length] == '/')) {
+ if (file_to_access)
+ *file_to_access = requested_filename;
+
+ return true;
+ }
+
+ return false;
+}
+
const char* BrokerFilePermission::GetErrorMessageForTests() {
return "Invalid BrokerFilePermission";
}
@@ -221,5 +256,4 @@ BrokerFilePermission::BrokerFilePermission(const std::string& path,
}
} // namespace syscall_broker
-
} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_file_permission.h b/chromium/sandbox/linux/syscall_broker/broker_file_permission.h
index b39ac6ca1b9..4261eb357ac 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_file_permission.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_file_permission.h
@@ -49,11 +49,30 @@ class SANDBOX_EXPORT BrokerFilePermission {
return BrokerFilePermission(path, true, false, true, true, true);
}
+ // Temporary files must always be newly created and do not confer rights to
+ // use pre-existing files of the same name.
+ static BrokerFilePermission ReadWriteCreateTemporary(
+ const std::string& path) {
+ return BrokerFilePermission(path, false, true, true, true, true);
+ }
+
static BrokerFilePermission ReadWriteCreateTemporaryRecursive(
const std::string& path) {
return BrokerFilePermission(path, true, true, true, true, true);
}
+ // Returns true if |requested_filename| is allowed to be accessed
+ // by this permission as per access(2).
+ // If |file_to_access| is not NULL, it is set to point to either
+ // the |requested_filename| in the case of a recursive match,
+ // or a pointer to the matched path in the whitelist if an absolute
+ // match.
+ // |mode| is per mode argument of access(2).
+ // Async signal safe if |file_to_access| is NULL
+ bool CheckAccess(const char* requested_filename,
+ int mode,
+ const char** file_to_access) const;
+
// Returns true if |requested_filename| is allowed to be opened
// by this permission.
// If |file_to_open| is not NULL it is set to point to either
@@ -68,17 +87,17 @@ class SANDBOX_EXPORT BrokerFilePermission {
const char** file_to_open,
bool* unlink_after_open) const;
- // Returns true if |requested_filename| is allowed to be accessed
- // by this permission as per access(2).
+ // Returns true if |requested_filename| is allowed to be stat'd
+ // by this permission as per stat(2). Differs from CheckAccess()
+ // in that if create permission is granted to a file, we permit
+ // stat() on all of its leading components.
// If |file_to_open| is not NULL, it is set to point to either
// the |requested_filename| in the case of a recursive match,
// or a pointer to the matched path in the whitelist if an absolute
// match.
- // |mode| is per mode argument of access(2).
// Async signal safe if |file_to_access| is NULL
- bool CheckAccess(const char* requested_filename,
- int mode,
- const char** file_to_access) const;
+ bool CheckStat(const char* requested_filename,
+ const char** file_to_access) const;
private:
friend class BrokerFilePermissionTester;
@@ -101,6 +120,12 @@ class SANDBOX_EXPORT BrokerFilePermission {
// MatchPath returns true if |requested_filename| is covered by this instance
bool MatchPath(const char* requested_filename) const;
+ // Helper routine for CheckAccess() and CheckStat(). Must be safe to call
+ // from an async signal context.
+ bool CheckAccessInternal(const char* requested_filename,
+ int mode,
+ const char** file_to_access) const;
+
// Used in by BrokerFilePermissionTester for tests.
static const char* GetErrorMessageForTests();
diff --git a/chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
index dc56b4cd417..0071016758d 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
@@ -161,8 +161,9 @@ void CheckPerm(const BrokerFilePermission& perm,
ASSERT_TRUE(
perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
break;
- case O_CLOEXEC:
case O_CREAT:
+ continue; // Handled below.
+ case O_CLOEXEC:
default:
ASSERT_FALSE(
perm.CheckOpen(path, access_flags | flag, &file_to_open, NULL));
@@ -225,7 +226,6 @@ void CheckUnlink(BrokerFilePermission& perm,
int access_flags) {
bool unlink;
ASSERT_FALSE(perm.CheckOpen(path, access_flags, NULL, &unlink));
- ASSERT_FALSE(perm.CheckOpen(path, access_flags | O_CREAT, NULL, &unlink));
ASSERT_TRUE(
perm.CheckOpen(path, access_flags | O_CREAT | O_EXCL, NULL, &unlink));
ASSERT_TRUE(unlink);
diff --git a/chromium/sandbox/linux/syscall_broker/broker_host.cc b/chromium/sandbox/linux/syscall_broker/broker_host.cc
index 375747e19f2..bdfa8753a6d 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_host.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_host.cc
@@ -6,6 +6,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/stat.h>
@@ -22,14 +23,11 @@
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/unix_domain_socket.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/third_party/valgrind/valgrind.h"
-#include "sandbox/linux/syscall_broker/broker_common.h"
-#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
+#include "sandbox/linux/syscall_broker/broker_permission_list.h"
#include "sandbox/linux/system_headers/linux_syscalls.h"
namespace sandbox {
-
namespace syscall_broker {
namespace {
@@ -40,82 +38,153 @@ namespace {
int sys_open(const char* pathname, int flags) {
// Hardcode mode to rw------- when creating files.
int mode = (flags & O_CREAT) ? 0600 : 0;
- if (RunningOnValgrind()) {
- // Valgrind does not support AT_FDCWD, just use libc's open() in this case.
- return open(pathname, flags, mode);
- }
return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
}
-// Open |requested_filename| with |flags| if allowed by our policy.
+// Perform access(2) on |requested_filename| with mode |mode| if allowed by our
+// permission_list. Write the syscall return value (-errno) to |write_pickle|.
+void AccessFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
+ const std::string& requested_filename,
+ int mode,
+ base::Pickle* write_pickle) {
+ const char* file_to_access = NULL;
+ if (!CommandAccessIsSafe(allowed_command_set, permission_list,
+ requested_filename.c_str(), mode, &file_to_access)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
+ return;
+ }
+
+ CHECK(file_to_access);
+ if (access(file_to_access, mode) < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
+ }
+
+ write_pickle->WriteInt(0);
+}
+
+void MkdirFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
+ const std::string& filename,
+ int mode,
+ base::Pickle* write_pickle) {
+ const char* file_to_access = nullptr;
+ if (!CommandMkdirIsSafe(allowed_command_set, permission_list,
+ filename.c_str(), &file_to_access)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
+ return;
+ }
+ if (mkdir(file_to_access, mode) < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
+ }
+ write_pickle->WriteInt(0);
+}
+
+// Open |requested_filename| with |flags| if allowed by our permission list.
// Write the syscall return value (-errno) to |write_pickle| and append
// a file descriptor to |opened_files| if relevant.
-void OpenFileForIPC(const BrokerPolicy& policy,
+void OpenFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
const std::string& requested_filename,
int flags,
base::Pickle* write_pickle,
std::vector<int>* opened_files) {
- DCHECK(write_pickle);
- DCHECK(opened_files);
const char* file_to_open = NULL;
bool unlink_after_open = false;
- const bool safe_to_open_file = policy.GetFileNameIfAllowedToOpen(
- requested_filename.c_str(), flags, &file_to_open, &unlink_after_open);
+ if (!CommandOpenIsSafe(allowed_command_set, permission_list,
+ requested_filename.c_str(), flags, &file_to_open,
+ &unlink_after_open)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
+ return;
+ }
- if (safe_to_open_file) {
- CHECK(file_to_open);
- int opened_fd = sys_open(file_to_open, flags);
- if (opened_fd < 0) {
- write_pickle->WriteInt(-errno);
- } else {
- // Success.
- if (unlink_after_open) {
- unlink(file_to_open);
- }
- opened_files->push_back(opened_fd);
- write_pickle->WriteInt(0);
- }
- } else {
- write_pickle->WriteInt(-policy.denied_errno());
+ CHECK(file_to_open);
+ int opened_fd = sys_open(file_to_open, flags);
+ if (opened_fd < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
}
+
+ if (unlink_after_open)
+ unlink(file_to_open);
+
+ opened_files->push_back(opened_fd);
+ write_pickle->WriteInt(0);
}
-// Perform access(2) on |requested_filename| with mode |mode| if allowed by our
-// policy. Write the syscall return value (-errno) to |write_pickle|.
-void AccessFileForIPC(const BrokerPolicy& policy,
- const std::string& requested_filename,
- int mode,
+// Perform rename(2) on |old_filename| to |new_filename| and marshal the
+// result to |write_pickle|.
+void RenameFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
+ const std::string& old_filename,
+ const std::string& new_filename,
base::Pickle* write_pickle) {
- DCHECK(write_pickle);
- const char* file_to_access = NULL;
- const bool safe_to_access_file = policy.GetFileNameIfAllowedToAccess(
- requested_filename.c_str(), mode, &file_to_access);
-
- if (safe_to_access_file) {
- CHECK(file_to_access);
- int access_ret = access(file_to_access, mode);
- int access_errno = errno;
- if (!access_ret)
- write_pickle->WriteInt(0);
- else
- write_pickle->WriteInt(-access_errno);
- } else {
- write_pickle->WriteInt(-policy.denied_errno());
+ const char* old_file_to_access = nullptr;
+ const char* new_file_to_access = nullptr;
+ if (!CommandRenameIsSafe(allowed_command_set, permission_list,
+ old_filename.c_str(), new_filename.c_str(),
+ &old_file_to_access, &new_file_to_access)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
+ return;
+ }
+ if (rename(old_file_to_access, new_file_to_access) < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
+ }
+ write_pickle->WriteInt(0);
+}
+
+// Perform readlink(2) on |filename| using a buffer of MAX_PATH bytes.
+void ReadlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
+ const std::string& filename,
+ base::Pickle* write_pickle) {
+ const char* file_to_access = nullptr;
+ if (!CommandReadlinkIsSafe(allowed_command_set, permission_list,
+ filename.c_str(), &file_to_access)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
+ return;
+ }
+ char buf[PATH_MAX];
+ ssize_t result = readlink(file_to_access, buf, sizeof(buf));
+ if (result < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
}
+ write_pickle->WriteInt(result);
+ write_pickle->WriteData(buf, result);
+}
+
+void RmdirFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
+ const std::string& requested_filename,
+ base::Pickle* write_pickle) {
+ const char* file_to_access = nullptr;
+ if (!CommandRmdirIsSafe(allowed_command_set, permission_list,
+ requested_filename.c_str(), &file_to_access)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
+ return;
+ }
+ if (rmdir(file_to_access) < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
+ }
+ write_pickle->WriteInt(0);
}
// Perform stat(2) on |requested_filename| and marshal the result to
// |write_pickle|.
-void StatFileForIPC(const BrokerPolicy& policy,
- IPCCommand command_type,
+void StatFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
+ BrokerCommand command_type,
const std::string& requested_filename,
base::Pickle* write_pickle) {
- DCHECK(write_pickle);
- DCHECK(command_type == COMMAND_STAT || command_type == COMMAND_STAT64);
const char* file_to_access = nullptr;
- if (!policy.GetFileNameIfAllowedToAccess(requested_filename.c_str(), F_OK,
- &file_to_access)) {
- write_pickle->WriteInt(-policy.denied_errno());
+ if (!CommandStatIsSafe(allowed_command_set, permission_list,
+ requested_filename.c_str(), &file_to_access)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
return;
}
if (command_type == COMMAND_STAT) {
@@ -127,6 +196,13 @@ void StatFileForIPC(const BrokerPolicy& policy,
write_pickle->WriteInt(0);
write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb));
} else {
+ DCHECK(command_type == COMMAND_STAT64);
+#if defined(__ANDROID_API__) && __ANDROID_API__ < 21
+ // stat64 is not defined for older Android API versions in newer NDK
+ // versions.
+ write_pickle->WriteInt(-ENOSYS);
+ return;
+#else
struct stat64 sb;
if (stat64(file_to_access, &sb) < 0) {
write_pickle->WriteInt(-errno);
@@ -134,27 +210,21 @@ void StatFileForIPC(const BrokerPolicy& policy,
}
write_pickle->WriteInt(0);
write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb));
+#endif // defined(__ANDROID_API__) && __ANDROID_API__ < 21
}
}
-// Perform rename(2) on |old_filename| to |new_filename| and marshal the
-// result to |write_pickle|.
-void RenameFileForIPC(const BrokerPolicy& policy,
- const std::string& old_filename,
- const std::string& new_filename,
+void UnlinkFileForIPC(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
+ const std::string& requested_filename,
base::Pickle* write_pickle) {
- DCHECK(write_pickle);
- bool ignore;
- const char* old_file_to_access = nullptr;
- const char* new_file_to_access = nullptr;
- if (!policy.GetFileNameIfAllowedToOpen(old_filename.c_str(), O_RDWR,
- &old_file_to_access, &ignore) ||
- !policy.GetFileNameIfAllowedToOpen(new_filename.c_str(), O_RDWR,
- &new_file_to_access, &ignore)) {
- write_pickle->WriteInt(-policy.denied_errno());
+ const char* file_to_access = nullptr;
+ if (!CommandUnlinkIsSafe(allowed_command_set, permission_list,
+ requested_filename.c_str(), &file_to_access)) {
+ write_pickle->WriteInt(-permission_list.denied_errno());
return;
}
- if (rename(old_file_to_access, new_file_to_access) < 0) {
+ if (unlink(file_to_access) < 0) {
write_pickle->WriteInt(-errno);
return;
}
@@ -163,7 +233,8 @@ void RenameFileForIPC(const BrokerPolicy& policy,
// Handle a |command_type| request contained in |iter| and write the reply
// to |write_pickle|, adding any files opened to |opened_files|.
-bool HandleRemoteCommand(const BrokerPolicy& policy,
+bool HandleRemoteCommand(const BrokerCommandSet& allowed_command_set,
+ const BrokerPermissionList& permission_list,
base::PickleIterator iter,
base::Pickle* write_pickle,
std::vector<int>* opened_files) {
@@ -177,7 +248,17 @@ bool HandleRemoteCommand(const BrokerPolicy& policy,
int flags = 0;
if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
return false;
- AccessFileForIPC(policy, requested_filename, flags, write_pickle);
+ AccessFileForIPC(allowed_command_set, permission_list, requested_filename,
+ flags, write_pickle);
+ break;
+ }
+ case COMMAND_MKDIR: {
+ std::string requested_filename;
+ int mode = 0;
+ if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&mode))
+ return false;
+ MkdirFileForIPC(allowed_command_set, permission_list, requested_filename,
+ mode, write_pickle);
break;
}
case COMMAND_OPEN: {
@@ -185,8 +266,33 @@ bool HandleRemoteCommand(const BrokerPolicy& policy,
int flags = 0;
if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
return false;
- OpenFileForIPC(policy, requested_filename, flags, write_pickle,
- opened_files);
+ OpenFileForIPC(allowed_command_set, permission_list, requested_filename,
+ flags, write_pickle, opened_files);
+ break;
+ }
+ case COMMAND_READLINK: {
+ std::string filename;
+ if (!iter.ReadString(&filename))
+ return false;
+ ReadlinkFileForIPC(allowed_command_set, permission_list, filename,
+ write_pickle);
+ break;
+ }
+ case COMMAND_RENAME: {
+ std::string old_filename;
+ std::string new_filename;
+ if (!iter.ReadString(&old_filename) || !iter.ReadString(&new_filename))
+ return false;
+ RenameFileForIPC(allowed_command_set, permission_list, old_filename,
+ new_filename, write_pickle);
+ break;
+ }
+ case COMMAND_RMDIR: {
+ std::string requested_filename;
+ if (!iter.ReadString(&requested_filename))
+ return false;
+ RmdirFileForIPC(allowed_command_set, permission_list, requested_filename,
+ write_pickle);
break;
}
case COMMAND_STAT:
@@ -194,16 +300,17 @@ bool HandleRemoteCommand(const BrokerPolicy& policy,
std::string requested_filename;
if (!iter.ReadString(&requested_filename))
return false;
- StatFileForIPC(policy, static_cast<IPCCommand>(command_type),
+ StatFileForIPC(allowed_command_set, permission_list,
+ static_cast<BrokerCommand>(command_type),
requested_filename, write_pickle);
break;
}
- case COMMAND_RENAME: {
- std::string old_filename;
- std::string new_filename;
- if (!iter.ReadString(&old_filename) || !iter.ReadString(&new_filename))
+ case COMMAND_UNLINK: {
+ std::string requested_filename;
+ if (!iter.ReadString(&requested_filename))
return false;
- RenameFileForIPC(policy, old_filename, new_filename, write_pickle);
+ UnlinkFileForIPC(allowed_command_set, permission_list, requested_filename,
+ write_pickle);
break;
}
default:
@@ -215,12 +322,14 @@ bool HandleRemoteCommand(const BrokerPolicy& policy,
} // namespace
-BrokerHost::BrokerHost(const BrokerPolicy& broker_policy,
+BrokerHost::BrokerHost(const BrokerPermissionList& broker_permission_list,
+ const BrokerCommandSet& allowed_command_set,
BrokerChannel::EndPoint ipc_channel)
- : broker_policy_(broker_policy), ipc_channel_(std::move(ipc_channel)) {}
+ : broker_permission_list_(broker_permission_list),
+ allowed_command_set_(allowed_command_set),
+ ipc_channel_(std::move(ipc_channel)) {}
-BrokerHost::~BrokerHost() {
-}
+BrokerHost::~BrokerHost() {}
// Handle a request on the IPC channel ipc_channel_.
// A request should have a file descriptor attached on which we will reply and
@@ -252,7 +361,8 @@ BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
base::Pickle write_pickle;
std::vector<int> opened_files;
bool result =
- HandleRemoteCommand(broker_policy_, iter, &write_pickle, &opened_files);
+ HandleRemoteCommand(allowed_command_set_, broker_permission_list_, iter,
+ &write_pickle, &opened_files);
if (result) {
CHECK_LE(write_pickle.size(), kMaxMessageLength);
diff --git a/chromium/sandbox/linux/syscall_broker/broker_host.h b/chromium/sandbox/linux/syscall_broker/broker_host.h
index 9866507d1c6..b54e32d3cf7 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_host.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_host.h
@@ -7,28 +7,31 @@
#include "base/macros.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
namespace sandbox {
namespace syscall_broker {
-class BrokerPolicy;
+class BrokerPermissionList;
// The BrokerHost class should be embedded in a (presumably not sandboxed)
// process. It will honor IPC requests from a BrokerClient sent over
-// |ipc_channel| according to |broker_policy|.
+// |ipc_channel| according to |broker_permission_list|.
class BrokerHost {
public:
enum class RequestStatus { LOST_CLIENT = 0, SUCCESS, FAILURE };
- BrokerHost(const BrokerPolicy& broker_policy,
+ BrokerHost(const BrokerPermissionList& broker_permission_list,
+ const BrokerCommandSet& allowed_command_set,
BrokerChannel::EndPoint ipc_channel);
~BrokerHost();
RequestStatus HandleRequest() const;
private:
- const BrokerPolicy& broker_policy_;
+ const BrokerPermissionList& broker_permission_list_;
+ const BrokerCommandSet allowed_command_set_;
const BrokerChannel::EndPoint ipc_channel_;
DISALLOW_COPY_AND_ASSIGN(BrokerHost);
diff --git a/chromium/sandbox/linux/syscall_broker/broker_policy.cc b/chromium/sandbox/linux/syscall_broker/broker_permission_list.cc
index cd09245d374..8032b3a9e56 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_policy.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_permission_list.cc
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/linux/syscall_broker/broker_permission_list.h"
#include <fcntl.h>
#include <stddef.h>
@@ -13,13 +13,29 @@
#include <vector>
#include "base/logging.h"
-#include "sandbox/linux/syscall_broker/broker_common.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
namespace sandbox {
namespace syscall_broker {
-BrokerPolicy::BrokerPolicy(int denied_errno,
- const std::vector<BrokerFilePermission>& permissions)
+namespace {
+
+bool CheckCallerArgs(const char** file_to_access) {
+ if (file_to_access && *file_to_access) {
+ // Make sure that callers never pass a non-empty string. In case callers
+ // wrongly forget to check the return value and look at the string
+ // instead, this could catch bugs.
+ RAW_LOG(FATAL, "*file_to_access should be NULL");
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+BrokerPermissionList::BrokerPermissionList(
+ int denied_errno,
+ const std::vector<BrokerFilePermission>& permissions)
: denied_errno_(denied_errno),
permissions_(permissions),
num_of_permissions_(permissions.size()) {
@@ -33,8 +49,7 @@ BrokerPolicy::BrokerPolicy(int denied_errno,
}
}
-BrokerPolicy::~BrokerPolicy() {
-}
+BrokerPermissionList::~BrokerPermissionList() {}
// Check if calling access() should be allowed on |requested_filename| with
// mode |requested_mode|.
@@ -47,17 +62,13 @@ BrokerPolicy::~BrokerPolicy() {
// return true if calling access() on this file should be allowed, false
// otherwise.
// Async signal safe if and only if |file_to_access| is NULL.
-bool BrokerPolicy::GetFileNameIfAllowedToAccess(
+bool BrokerPermissionList::GetFileNameIfAllowedToAccess(
const char* requested_filename,
int requested_mode,
const char** file_to_access) const {
- if (file_to_access && *file_to_access) {
- // Make sure that callers never pass a non-empty string. In case callers
- // wrongly forget to check the return value and look at the string
- // instead, this could catch bugs.
- RAW_LOG(FATAL, "*file_to_access should be NULL");
+ if (!CheckCallerArgs(file_to_access))
return false;
- }
+
for (size_t i = 0; i < num_of_permissions_; i++) {
if (permissions_array_[i].CheckAccess(requested_filename, requested_mode,
file_to_access)) {
@@ -75,17 +86,14 @@ bool BrokerPolicy::GetFileNameIfAllowedToAccess(
// string comparison mechanism.
// Return true if opening should be allowed, false otherwise.
// Async signal safe if and only if |file_to_open| is NULL.
-bool BrokerPolicy::GetFileNameIfAllowedToOpen(const char* requested_filename,
- int requested_flags,
- const char** file_to_open,
- bool* unlink_after_open) const {
- if (file_to_open && *file_to_open) {
- // Make sure that callers never pass a non-empty string. In case callers
- // wrongly forget to check the return value and look at the string
- // instead, this could catch bugs.
- RAW_LOG(FATAL, "*file_to_open should be NULL");
+bool BrokerPermissionList::GetFileNameIfAllowedToOpen(
+ const char* requested_filename,
+ int requested_flags,
+ const char** file_to_open,
+ bool* unlink_after_open) const {
+ if (!CheckCallerArgs(file_to_open))
return false;
- }
+
for (size_t i = 0; i < num_of_permissions_; i++) {
if (permissions_array_[i].CheckOpen(requested_filename, requested_flags,
file_to_open, unlink_after_open)) {
@@ -95,6 +103,18 @@ bool BrokerPolicy::GetFileNameIfAllowedToOpen(const char* requested_filename,
return false;
}
-} // namespace syscall_broker
+bool BrokerPermissionList::GetFileNameIfAllowedToStat(
+ const char* requested_filename,
+ const char** file_to_stat) const {
+ if (!CheckCallerArgs(file_to_stat))
+ return false;
+
+ for (size_t i = 0; i < num_of_permissions_; i++) {
+ if (permissions_array_[i].CheckStat(requested_filename, file_to_stat))
+ return true;
+ }
+ return false;
+}
+} // namespace syscall_broker
} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_policy.h b/chromium/sandbox/linux/syscall_broker/broker_permission_list.h
index 58bc29a76e8..2cb30f16141 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_policy.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_permission_list.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
-#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
+#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_PERMISSION_LIST_H_
+#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_PERMISSION_LIST_H_
#include <stddef.h>
@@ -17,22 +17,21 @@
namespace sandbox {
namespace syscall_broker {
-// BrokerPolicy allows to define the security policy enforced by a
-// BrokerHost. The BrokerHost will evaluate requests sent over its
-// IPC channel according to the BrokerPolicy.
-// Some of the methods of this class can be used in an async-signal safe
-// way.
-class BrokerPolicy {
+// BrokerPermissionList defines the file access control list enforced by
+// a BrokerHost. The BrokerHost will evaluate requests to manipulate files
+// sent over its IPC channel according to the BrokerPermissionList.
+// The methods of this class must be usable in an async-signal safe manner.
+class BrokerPermissionList {
public:
// |denied_errno| is the error code returned when IPC requests for system
// calls such as open() or access() are denied because a file is not in the
// whitelist. EACCESS would be a typical value.
// |permissions| is a list of BrokerPermission objects that define
// what the broker will allow.
- BrokerPolicy(int denied_errno,
- const std::vector<BrokerFilePermission>& permissions);
+ BrokerPermissionList(int denied_errno,
+ const std::vector<BrokerFilePermission>& permissions);
- ~BrokerPolicy();
+ ~BrokerPermissionList();
// Check if calling access() should be allowed on |requested_filename| with
// mode |requested_mode|.
@@ -66,6 +65,17 @@ class BrokerPolicy {
int requested_flags,
const char** file_to_open,
bool* unlink_after_open) const;
+
+ // Check if calling stat() should be allowed on |requested_filename|.
+ // Async signal safe if and only if |file_to_open| is NULL. This is
+ // similar to GetFileNameIfAllowedToAccess(), except that if we have
+ // create permission on file, we permit stat() on all its leading
+ // components, otherwise checking for missing intermediate directories
+ // can't happen proplery during a base::CreateDirectory() call.
+ // Async signal safe if and only if |file_to_open| is NULL.
+ bool GetFileNameIfAllowedToStat(const char* requested_filename,
+ const char** file_to_access) const;
+
int denied_errno() const { return denied_errno_; }
private:
@@ -79,11 +89,10 @@ class BrokerPolicy {
const BrokerFilePermission* permissions_array_;
const size_t num_of_permissions_;
- DISALLOW_COPY_AND_ASSIGN(BrokerPolicy);
+ DISALLOW_COPY_AND_ASSIGN(BrokerPermissionList);
};
} // namespace syscall_broker
-
} // namespace sandbox
-#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_POLICY_H_
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_PERMISSION_LIST_H_
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.cc b/chromium/sandbox/linux/syscall_broker/broker_process.cc
index 46e207ede34..8ebce31669e 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_process.cc
@@ -32,14 +32,16 @@ namespace syscall_broker {
BrokerProcess::BrokerProcess(
int denied_errno,
+ const syscall_broker::BrokerCommandSet& allowed_command_set,
const std::vector<syscall_broker::BrokerFilePermission>& permissions,
bool fast_check_in_client,
bool quiet_failures_for_tests)
: initialized_(false),
+ broker_pid_(-1),
fast_check_in_client_(fast_check_in_client),
quiet_failures_for_tests_(quiet_failures_for_tests),
- broker_pid_(-1),
- broker_policy_(denied_errno, permissions) {}
+ allowed_command_set_(allowed_command_set),
+ broker_permission_list_(denied_errno, permissions) {}
BrokerProcess::~BrokerProcess() {
if (initialized_) {
@@ -74,9 +76,9 @@ bool BrokerProcess::Init(
// We are the parent and we have just forked our broker process.
ipc_reader.reset();
broker_pid_ = child_pid;
- broker_client_.reset(new BrokerClient(broker_policy_, std::move(ipc_writer),
- fast_check_in_client_,
- quiet_failures_for_tests_));
+ broker_client_ = std::make_unique<BrokerClient>(
+ broker_permission_list_, std::move(ipc_writer), allowed_command_set_,
+ fast_check_in_client_, quiet_failures_for_tests_);
initialized_ = true;
return true;
}
@@ -85,7 +87,8 @@ bool BrokerProcess::Init(
// we get notified if the client disappears.
ipc_writer.reset();
CHECK(broker_process_init_callback.Run());
- BrokerHost broker_host(broker_policy_, std::move(ipc_reader));
+ BrokerHost broker_host(broker_permission_list_, allowed_command_set_,
+ std::move(ipc_reader));
for (;;) {
switch (broker_host.HandleRequest()) {
case BrokerHost::RequestStatus::LOST_CLIENT:
@@ -109,11 +112,31 @@ int BrokerProcess::Access(const char* pathname, int mode) const {
return broker_client_->Access(pathname, mode);
}
+int BrokerProcess::Mkdir(const char* path, int mode) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Mkdir(path, mode);
+}
+
int BrokerProcess::Open(const char* pathname, int flags) const {
RAW_CHECK(initialized_);
return broker_client_->Open(pathname, flags);
}
+int BrokerProcess::Readlink(const char* path, char* buf, size_t bufsize) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Readlink(path, buf, bufsize);
+}
+
+int BrokerProcess::Rename(const char* oldpath, const char* newpath) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Rename(oldpath, newpath);
+}
+
+int BrokerProcess::Rmdir(const char* pathname) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Rmdir(pathname);
+}
+
int BrokerProcess::Stat(const char* pathname, struct stat* sb) const {
RAW_CHECK(initialized_);
return broker_client_->Stat(pathname, sb);
@@ -124,9 +147,9 @@ int BrokerProcess::Stat64(const char* pathname, struct stat64* sb) const {
return broker_client_->Stat64(pathname, sb);
}
-int BrokerProcess::Rename(const char* oldpath, const char* newpath) const {
+int BrokerProcess::Unlink(const char* pathname) const {
RAW_CHECK(initialized_);
- return broker_client_->Rename(oldpath, newpath);
+ return broker_client_->Unlink(pathname);
}
#if defined(MEMORY_SANITIZER)
@@ -146,24 +169,6 @@ intptr_t BrokerProcess::SIGSYS_Handler(const sandbox::arch_seccomp_data& args,
return broker_process->Access(reinterpret_cast<const char*>(args.args[0]),
static_cast<int>(args.args[1]));
#endif
-#if defined(__NR_open)
- case __NR_open:
- // http://crbug.com/372840
- BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
- return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
- static_cast<int>(args.args[1]));
-#endif
-#if defined(__NR_stat)
- case __NR_stat:
- return broker_process->Stat(reinterpret_cast<const char*>(args.args[0]),
- reinterpret_cast<struct stat*>(args.args[1]));
-#endif
-#if defined(__NR_stat64)
- case __NR_stat64:
- return broker_process->Stat64(
- reinterpret_cast<const char*>(args.args[0]),
- reinterpret_cast<struct stat64*>(args.args[1]));
-#endif
#if defined(__NR_faccessat)
case __NR_faccessat:
if (static_cast<int>(args.args[0]) != AT_FDCWD)
@@ -171,30 +176,49 @@ intptr_t BrokerProcess::SIGSYS_Handler(const sandbox::arch_seccomp_data& args,
return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
static_cast<int>(args.args[2]));
#endif
+#if defined(__NR_mkdir)
+ case __NR_mkdir:
+ return broker_process->Mkdir(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
+#if defined(__NR_mkdirat)
+ case __NR_mkdirat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ return broker_process->Mkdir(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#endif
+#if defined(__NR_open)
+ case __NR_open:
+ // http://crbug.com/372840
+ BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[0]));
+ return broker_process->Open(reinterpret_cast<const char*>(args.args[0]),
+ static_cast<int>(args.args[1]));
+#endif
#if defined(__NR_openat)
case __NR_openat:
if (static_cast<int>(args.args[0]) != AT_FDCWD)
return -EPERM;
+ // http://crbug.com/372840
+ BROKER_UNPOISON_STRING(reinterpret_cast<const char*>(args.args[1]));
return broker_process->Open(reinterpret_cast<const char*>(args.args[1]),
static_cast<int>(args.args[2]));
#endif
-#if defined(__NR_fstatat)
- case __NR_fstatat:
- if (static_cast<int>(args.args[0]) != AT_FDCWD)
- return -EPERM;
- if (static_cast<int>(args.args[3]) != 0)
- return -EINVAL;
- return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
- reinterpret_cast<struct stat*>(args.args[2]));
+#if defined(__NR_readlink)
+ case __NR_readlink:
+ return broker_process->Readlink(
+ reinterpret_cast<const char*>(args.args[0]),
+ reinterpret_cast<char*>(args.args[1]),
+ static_cast<size_t>(args.args[2]));
#endif
-#if defined(__NR_newfstatat)
- case __NR_newfstatat:
+#if defined(__NR_readlinkat)
+ case __NR_readlinkat:
if (static_cast<int>(args.args[0]) != AT_FDCWD)
return -EPERM;
- if (static_cast<int>(args.args[3]) != 0)
- return -EINVAL;
- return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
- reinterpret_cast<struct stat*>(args.args[2]));
+ return broker_process->Readlink(
+ reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<char*>(args.args[2]),
+ static_cast<size_t>(args.args[3]));
#endif
#if defined(__NR_rename)
case __NR_rename:
@@ -224,6 +248,52 @@ intptr_t BrokerProcess::SIGSYS_Handler(const sandbox::arch_seccomp_data& args,
reinterpret_cast<const char*>(args.args[1]),
reinterpret_cast<const char*>(args.args[3]));
#endif
+#if defined(__NR_rmdir)
+ case __NR_rmdir:
+ return broker_process->Rmdir(reinterpret_cast<const char*>(args.args[0]));
+#endif
+#if defined(__NR_stat)
+ case __NR_stat:
+ return broker_process->Stat(reinterpret_cast<const char*>(args.args[0]),
+ reinterpret_cast<struct stat*>(args.args[1]));
+#endif
+#if defined(__NR_stat64)
+ case __NR_stat64:
+ return broker_process->Stat64(
+ reinterpret_cast<const char*>(args.args[0]),
+ reinterpret_cast<struct stat64*>(args.args[1]));
+#endif
+#if defined(__NR_fstatat)
+ case __NR_fstatat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ if (static_cast<int>(args.args[3]) != 0)
+ return -EINVAL;
+ return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<struct stat*>(args.args[2]));
+#endif
+#if defined(__NR_newfstatat)
+ case __NR_newfstatat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ if (static_cast<int>(args.args[3]) != 0)
+ return -EINVAL;
+ return broker_process->Stat(reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<struct stat*>(args.args[2]));
+#endif
+#if defined(__NR_unlink)
+ case __NR_unlink:
+ return broker_process->Unlink(
+ reinterpret_cast<const char*>(args.args[0]));
+#endif
+#if defined(__NR_unlinkat)
+ case __NR_unlinkat:
+ // TODO(tsepez): does not support AT_REMOVEDIR flag.
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ return broker_process->Unlink(
+ reinterpret_cast<const char*>(args.args[1]));
+#endif
default:
RAW_CHECK(false);
return -ENOSYS;
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.h b/chromium/sandbox/linux/syscall_broker/broker_process.h
index efa84bb8342..f1dc0c606fb 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_process.h
@@ -14,7 +14,8 @@
#include "base/pickle.h"
#include "base/process/process.h"
#include "sandbox/linux/bpf_dsl/trap_registry.h"
-#include "sandbox/linux/syscall_broker/broker_policy.h"
+#include "sandbox/linux/syscall_broker/broker_command.h"
+#include "sandbox/linux/syscall_broker/broker_permission_list.h"
#include "sandbox/sandbox_export.h"
namespace sandbox {
@@ -36,9 +37,16 @@ class BrokerFilePermission;
// 4. Use open_broker.Open() to open files.
class SANDBOX_EXPORT BrokerProcess {
public:
+ // Handler to be used with a bpf_dsl Trap() function to forward system calls
+ // to the methods below.
+ static intptr_t SIGSYS_Handler(const arch_seccomp_data& args,
+ void* aux_broker_process);
+
// |denied_errno| is the error code returned when methods such as Open()
// or Access() are invoked on a file which is not in the whitelist (EACCESS
- // would be a typical value). |permissions| describes the whitelisted set
+ // would be a typical value). |allowed_command_mask| is a bitwise-or of
+ // kBrokerCommand*Mask constants from broker_command.h that further restrict
+ // the syscalls to execute. |permissions| describes the whitelisted set
// of files the broker is is allowed to access. |fast_check_in_client|
// controls whether doomed requests are first filtered on the client side
// before being proxied. Apart from tests, this should always be true since
@@ -50,6 +58,7 @@ class SANDBOX_EXPORT BrokerProcess {
// don't use it.
BrokerProcess(
int denied_errno,
+ const syscall_broker::BrokerCommandSet& allowed_command_set,
const std::vector<syscall_broker::BrokerFilePermission>& permissions,
bool fast_check_in_client = true,
bool quiet_failures_for_tests = false);
@@ -62,33 +71,42 @@ class SANDBOX_EXPORT BrokerProcess {
// after fork() returns.
bool Init(const base::Callback<bool(void)>& broker_process_init_callback);
- // Can be used in place of access(). Will be async signal safe.
+ // Return the PID of the child created by Init().
+ int broker_pid() const { return broker_pid_; }
+
+ // The following methods are used in place of the equivalently-named
+ // syscalls by the trap handler. They, in turn, forward the call onto
+ // |broker_client_| for further processing. They will all be async signal
+ // safe. They all return -errno on errors.
+
+ // Can be used in place of access().
// X_OK will always return an error in practice since the broker process
// doesn't support execute permissions.
- // It's similar to the access() system call and will return -errno on errors.
int Access(const char* pathname, int mode) const;
- // Can be used in place of open(). Will be async signal safe.
+ // Can be used in place of mkdir().
+ int Mkdir(const char* path, int mode) const;
+
+ // Can be used in place of open()
// The implementation only supports certain white listed flags and will
// return -EPERM on other flags.
- // It's similar to the open() system call and will return -errno on errors.
int Open(const char* pathname, int flags) const;
- // Can be used in place of stat()/stat64(). Will be async signal safe.
- // It's similar to the stat() system call and will return -errno on errors.
- int Stat(const char* pathname, struct stat* sb) const;
- int Stat64(const char* pathname, struct stat64* sb) const;
+ // Can be used in place of readlink().
+ int Readlink(const char* path, char* buf, size_t bufsize) const;
- // Can be used in place of rename(). Will be async signal safe.
- // It's similar to the rename() system call and will return -errno on errors.
+ // Can be used in place of rename().
int Rename(const char* oldpath, const char* newpath) const;
- int broker_pid() const { return broker_pid_; }
+ // Can be used in place of rmdir().
+ int Rmdir(const char* path) const;
- // Handler to be used with a bpf_dsl Trap() function to forward system calls
- // to the methods above.
- static intptr_t SIGSYS_Handler(const arch_seccomp_data& args,
- void* aux_broker_process);
+ // Can be used in place of stat()/stat64().
+ int Stat(const char* pathname, struct stat* sb) const;
+ int Stat64(const char* pathname, struct stat64* sb) const;
+
+ // Can be used in place of unlink().
+ int Unlink(const char* path) const;
private:
friend class BrokerProcessTestHelper;
@@ -98,10 +116,12 @@ class SANDBOX_EXPORT BrokerProcess {
void CloseChannel();
bool initialized_; // Whether we've been through Init() yet.
+ pid_t broker_pid_; // The PID of the broker (child) created in Init().
const bool fast_check_in_client_;
const bool quiet_failures_for_tests_;
- pid_t broker_pid_; // The PID of the broker (child).
- syscall_broker::BrokerPolicy broker_policy_; // Access policy to enforce.
+ syscall_broker::BrokerCommandSet allowed_command_set_;
+ syscall_broker::BrokerPermissionList
+ broker_permission_list_; // File access whitelist.
std::unique_ptr<syscall_broker::BrokerClient> broker_client_;
DISALLOW_COPY_AND_ASSIGN(BrokerProcess);
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
index a53600337a1..ee4754c0cf1 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -26,6 +26,7 @@
#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/unix_domain_socket.h"
+#include "build/build_config.h"
#include "sandbox/linux/syscall_broker/broker_client.h"
#include "sandbox/linux/tests/scoped_temporary_file.h"
#include "sandbox/linux/tests/test_utils.h"
@@ -33,7 +34,6 @@
#include "testing/gtest/include/gtest/gtest.h"
namespace sandbox {
-
namespace syscall_broker {
class BrokerProcessTestHelper {
@@ -48,6 +48,8 @@ class BrokerProcessTestHelper {
namespace {
+const int kFakeErrnoSentinel = 99999;
+
bool NoOpCallback() {
return true;
}
@@ -55,23 +57,25 @@ bool NoOpCallback() {
} // namespace
TEST(BrokerProcess, CreateAndDestroy) {
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
-
- std::unique_ptr<BrokerProcess> open_broker(
- new BrokerProcess(EPERM, permissions));
- ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
-
- ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ {
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly("/proc/cpuinfo")};
+ BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
+ permissions);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ }
// Destroy the broker and check it has exited properly.
- open_broker.reset();
ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
}
TEST(BrokerProcess, TestOpenAccessNull) {
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
+
std::vector<BrokerFilePermission> empty;
- BrokerProcess open_broker(EPERM, empty);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, empty);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
int fd = open_broker.Open(NULL, O_RDONLY);
ASSERT_EQ(fd, -EFAULT);
@@ -89,15 +93,17 @@ void TestOpenFilePerms(bool fast_check_in_client, int denied_errno) {
const char kRW_WhiteListed[] = "/proc/DOESNOTEXIST3";
const char k_NotWhitelisted[] = "/proc/DOESNOTEXIST4";
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly(kR_WhiteListed));
- permissions.push_back(
- BrokerFilePermission::ReadOnly(kR_WhiteListedButDenied));
- permissions.push_back(BrokerFilePermission::WriteOnly(kW_WhiteListed));
- permissions.push_back(BrokerFilePermission::ReadWrite(kRW_WhiteListed));
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
- BrokerProcess open_broker(denied_errno, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(kR_WhiteListed),
+ BrokerFilePermission::ReadOnly(kR_WhiteListedButDenied),
+ BrokerFilePermission::WriteOnly(kW_WhiteListed),
+ BrokerFilePermission::ReadWrite(kRW_WhiteListed)};
+ BrokerProcess open_broker(denied_errno, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
int fd = -1;
fd = open_broker.Open(kR_WhiteListed, O_RDONLY);
@@ -249,44 +255,47 @@ void TestBadPaths(bool fast_check_in_client) {
const char kDotDotEnd[] = "/proc/..";
const char kTrailingSlash[] = "/proc/";
- std::vector<BrokerFilePermission> permissions;
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
+
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnlyRecursive("/proc/")};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
- permissions.push_back(BrokerFilePermission::ReadOnlyRecursive("/proc/"));
- std::unique_ptr<BrokerProcess> open_broker(
- new BrokerProcess(EPERM, permissions, fast_check_in_client));
- ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
// Open cpuinfo via the broker.
- int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
+ int cpuinfo_fd = open_broker.Open(kFileCpuInfo, O_RDONLY);
base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
ASSERT_GE(cpuinfo_fd, 0);
int fd = -1;
int can_access;
- can_access = open_broker->Access(kNotAbsPath, R_OK);
- ASSERT_EQ(can_access, -EPERM);
- fd = open_broker->Open(kNotAbsPath, O_RDONLY);
- ASSERT_EQ(fd, -EPERM);
-
- can_access = open_broker->Access(kDotDotStart, R_OK);
- ASSERT_EQ(can_access, -EPERM);
- fd = open_broker->Open(kDotDotStart, O_RDONLY);
- ASSERT_EQ(fd, -EPERM);
-
- can_access = open_broker->Access(kDotDotMiddle, R_OK);
- ASSERT_EQ(can_access, -EPERM);
- fd = open_broker->Open(kDotDotMiddle, O_RDONLY);
- ASSERT_EQ(fd, -EPERM);
-
- can_access = open_broker->Access(kDotDotEnd, R_OK);
- ASSERT_EQ(can_access, -EPERM);
- fd = open_broker->Open(kDotDotEnd, O_RDONLY);
- ASSERT_EQ(fd, -EPERM);
-
- can_access = open_broker->Access(kTrailingSlash, R_OK);
- ASSERT_EQ(can_access, -EPERM);
- fd = open_broker->Open(kTrailingSlash, O_RDONLY);
- ASSERT_EQ(fd, -EPERM);
+ can_access = open_broker.Access(kNotAbsPath, R_OK);
+ ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = open_broker.Open(kNotAbsPath, O_RDONLY);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = open_broker.Access(kDotDotStart, R_OK);
+ ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = open_broker.Open(kDotDotStart, O_RDONLY);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = open_broker.Access(kDotDotMiddle, R_OK);
+ ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = open_broker.Open(kDotDotMiddle, O_RDONLY);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = open_broker.Access(kDotDotEnd, R_OK);
+ ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = open_broker.Open(kDotDotEnd, O_RDONLY);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ can_access = open_broker.Access(kTrailingSlash, R_OK);
+ ASSERT_EQ(can_access, -kFakeErrnoSentinel);
+ fd = open_broker.Open(kTrailingSlash, O_RDONLY);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
}
TEST(BrokerProcess, BadPathsClientCheck) {
@@ -305,54 +314,56 @@ void TestOpenCpuinfo(bool fast_check_in_client, bool recursive) {
const char kFileCpuInfo[] = "/proc/cpuinfo";
const char kDirProc[] = "/proc/";
- std::vector<BrokerFilePermission> permissions;
- if (recursive)
- permissions.push_back(BrokerFilePermission::ReadOnlyRecursive(kDirProc));
- else
- permissions.push_back(BrokerFilePermission::ReadOnly(kFileCpuInfo));
-
- std::unique_ptr<BrokerProcess> open_broker(
- new BrokerProcess(EPERM, permissions, fast_check_in_client));
- ASSERT_TRUE(open_broker->Init(base::Bind(&NoOpCallback)));
-
- int fd = -1;
- fd = open_broker->Open(kFileCpuInfo, O_RDWR);
- base::ScopedFD fd_closer(fd);
- ASSERT_EQ(fd, -EPERM);
+ {
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
- // Check we can read /proc/cpuinfo.
- int can_access = open_broker->Access(kFileCpuInfo, R_OK);
- ASSERT_EQ(can_access, 0);
- can_access = open_broker->Access(kFileCpuInfo, W_OK);
- ASSERT_EQ(can_access, -EPERM);
- // Check we can not write /proc/cpuinfo.
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(
+ recursive ? BrokerFilePermission::ReadOnlyRecursive(kDirProc)
+ : BrokerFilePermission::ReadOnly(kFileCpuInfo));
+
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+
+ int fd = open_broker.Open(kFileCpuInfo, O_RDWR);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ // Check we can read /proc/cpuinfo.
+ int can_access = open_broker.Access(kFileCpuInfo, R_OK);
+ EXPECT_EQ(can_access, 0);
+ can_access = open_broker.Access(kFileCpuInfo, W_OK);
+ EXPECT_EQ(can_access, -kFakeErrnoSentinel);
+ // Check we can not write /proc/cpuinfo.
+
+ // Open cpuinfo via the broker.
+ int cpuinfo_fd = open_broker.Open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
+ EXPECT_GE(cpuinfo_fd, 0);
+ char buf[3];
+ memset(buf, 0, sizeof(buf));
+ int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
+ EXPECT_GT(read_len1, 0);
+
+ // Open cpuinfo directly.
+ int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
+ base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
+ EXPECT_GE(cpuinfo_fd2, 0);
+ char buf2[3];
+ memset(buf2, 1, sizeof(buf2));
+ int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
+ EXPECT_GT(read_len1, 0);
+
+ // The following is not guaranteed true, but will be in practice.
+ EXPECT_EQ(read_len1, read_len2);
+ // Compare the cpuinfo as returned by the broker with the one we opened
+ // ourselves.
+ EXPECT_EQ(memcmp(buf, buf2, read_len1), 0);
+
+ ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
+ }
- // Open cpuinfo via the broker.
- int cpuinfo_fd = open_broker->Open(kFileCpuInfo, O_RDONLY);
- base::ScopedFD cpuinfo_fd_closer(cpuinfo_fd);
- ASSERT_GE(cpuinfo_fd, 0);
- char buf[3];
- memset(buf, 0, sizeof(buf));
- int read_len1 = read(cpuinfo_fd, buf, sizeof(buf));
- ASSERT_GT(read_len1, 0);
-
- // Open cpuinfo directly.
- int cpuinfo_fd2 = open(kFileCpuInfo, O_RDONLY);
- base::ScopedFD cpuinfo_fd2_closer(cpuinfo_fd2);
- ASSERT_GE(cpuinfo_fd2, 0);
- char buf2[3];
- memset(buf2, 1, sizeof(buf2));
- int read_len2 = read(cpuinfo_fd2, buf2, sizeof(buf2));
- ASSERT_GT(read_len1, 0);
-
- // The following is not guaranteed true, but will be in practice.
- ASSERT_EQ(read_len1, read_len2);
- // Compare the cpuinfo as returned by the broker with the one we opened
- // ourselves.
- ASSERT_EQ(memcmp(buf, buf2, read_len1), 0);
-
- ASSERT_TRUE(TestUtils::CurrentProcessHasChildren());
- open_broker.reset();
ASSERT_FALSE(TestUtils::CurrentProcessHasChildren());
}
@@ -386,11 +397,13 @@ TEST(BrokerProcess, OpenFileRW) {
ScopedTemporaryFile tempfile;
const char* tempfile_name = tempfile.full_file_name();
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadWrite(tempfile_name));
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
- BrokerProcess open_broker(EPERM, permissions);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadWrite(tempfile_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
// Check we can access that file with read or write.
int can_access = open_broker.Access(tempfile_name, R_OK | W_OK);
@@ -420,12 +433,16 @@ TEST(BrokerProcess, OpenFileRW) {
// and we want this to happen in a subprocess.
SANDBOX_TEST(BrokerProcess, BrokerDied) {
const char kCpuInfo[] = "/proc/cpuinfo";
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
- BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
+
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(kCpuInfo)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ true /* fast_check_in_client */,
true /* quiet_failures_for_tests */);
- SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+ SANDBOX_ASSERT(open_broker.Init(base::BindRepeating(&NoOpCallback)));
const pid_t broker_pid = open_broker.broker_pid();
SANDBOX_ASSERT(kill(broker_pid, SIGKILL) == 0);
@@ -445,11 +462,16 @@ SANDBOX_TEST(BrokerProcess, BrokerDied) {
void TestOpenComplexFlags(bool fast_check_in_client) {
const char kCpuInfo[] = "/proc/cpuinfo";
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
+
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(kCpuInfo)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+
// Test that we do the right thing for O_CLOEXEC and O_NONBLOCK.
int fd = -1;
int ret = 0;
@@ -525,8 +547,6 @@ SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, MAYBE_RecvMsgDescriptorLeak) {
*std::max_element(available_fds,
available_fds + arraysize(available_fds));
- // Valgrind doesn't allow changing the hard descriptor limit, so we only
- // change the soft descriptor limit here.
struct rlimit rlim;
SANDBOX_ASSERT(0 == getrlimit(RLIMIT_NOFILE, &rlim));
SANDBOX_ASSERT(fd_limit <= rlim.rlim_cur);
@@ -534,11 +554,14 @@ SANDBOX_TEST_ALLOW_NOISE(BrokerProcess, MAYBE_RecvMsgDescriptorLeak) {
SANDBOX_ASSERT(0 == setrlimit(RLIMIT_NOFILE, &rlim));
static const char kCpuInfo[] = "/proc/cpuinfo";
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly(kCpuInfo));
- BrokerProcess open_broker(EPERM, permissions);
- SANDBOX_ASSERT(open_broker.Init(base::Bind(&NoOpCallback)));
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
+
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(kCpuInfo)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions);
+ SANDBOX_ASSERT(open_broker.Init(base::BindRepeating(&NoOpCallback)));
const int ipc_fd = BrokerProcessTestHelper::GetIPCDescriptor(&open_broker);
SANDBOX_ASSERT(ipc_fd >= 0);
@@ -578,17 +601,21 @@ bool WaitForClosedPipeWriter(int reader, int timeout_in_ms) {
// Closing the broker client's IPC channel should terminate the broker
// process.
TEST(BrokerProcess, BrokerDiesOnClosedChannel) {
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly("/proc/cpuinfo"));
-
// Get the writing end of a pipe into the broker (child) process so
// that we can reliably detect when it dies.
int lifeline_fds[2];
PCHECK(0 == pipe(lifeline_fds));
- BrokerProcess open_broker(EPERM, permissions, true /* fast_check_in_client */,
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
+
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly("/proc/cpuinfo")};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ true /* fast_check_in_client */,
false /* quiet_failures_for_tests */);
- ASSERT_TRUE(open_broker.Init(base::Bind(&CloseFD, lifeline_fds[0])));
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&CloseFD, lifeline_fds[0])));
+
// Make sure the writing end only exists in the broker process.
CloseFD(lifeline_fds[1]);
base::ScopedFD reader(lifeline_fds[0]);
@@ -614,96 +641,206 @@ TEST(BrokerProcess, BrokerDiesOnClosedChannel) {
TEST(BrokerProcess, CreateFile) {
std::string temp_str;
+ std::string perm_str;
{
- ScopedTemporaryFile tmp_file;
- temp_str = tmp_file.full_file_name();
+ ScopedTemporaryFile temp_file;
+ ScopedTemporaryFile perm_file;
+ temp_str = temp_file.full_file_name();
+ perm_str = perm_file.full_file_name();
}
const char* tempfile_name = temp_str.c_str();
+ const char* permfile_name = perm_str.c_str();
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadWriteCreate(tempfile_name));
+ BrokerCommandSet command_set =
+ MakeBrokerCommandSet({COMMAND_ACCESS, COMMAND_OPEN});
- BrokerProcess open_broker(EPERM, permissions);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadWriteCreateTemporary(tempfile_name),
+ BrokerFilePermission::ReadWriteCreate(permfile_name),
+ };
+
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
int fd = -1;
- // Try without O_EXCL
+ // Opening a temp file using O_CREAT but not O_EXCL must not be allowed
+ // by the broker so as to prevent spying on any pre-existing files.
fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT);
- ASSERT_EQ(fd, -EPERM);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
- const char kTestText[] = "TESTTESTTEST";
- // Create a file
+ // Opening a temp file in a normal way must not be allowed by the broker,
+ // either.
+ fd = open_broker.Open(tempfile_name, O_RDWR);
+ ASSERT_EQ(fd, -kFakeErrnoSentinel);
+
+ // Opening a temp file with both O_CREAT and O_EXCL is allowed since the
+ // file is known not to exist outside the scope of ScopedTemporaryFile.
fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
ASSERT_GE(fd, 0);
- {
- base::ScopedFD scoped_fd(fd);
+ close(fd);
- // Confirm fail if file exists
- int bad_fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
- ASSERT_EQ(bad_fd, -EEXIST);
+ // Manually create a conflict for the temp filename.
+ fd = open(tempfile_name, O_RDWR | O_CREAT, 0600);
+ ASSERT_GE(fd, 0);
+ close(fd);
- // Write to the descriptor opened by the broker.
+ // Opening a temp file with both O_CREAT and O_EXCL is allowed but fails
+ // per the OS when there is a conflict with a pre-existing file.
+ fd = open_broker.Open(tempfile_name, O_RDWR | O_CREAT | O_EXCL);
+ ASSERT_EQ(fd, -EEXIST);
+ // Opening a new permanent file without specifying O_EXCL is allowed.
+ fd = open_broker.Open(permfile_name, O_RDWR | O_CREAT);
+ ASSERT_GE(fd, 0);
+ close(fd);
+
+ // Opening an existing permanent file without specifying O_EXCL is allowed.
+ fd = open_broker.Open(permfile_name, O_RDWR | O_CREAT);
+ ASSERT_GE(fd, 0);
+ close(fd);
+
+ // Opening an existing file with O_EXCL is allowed but fails per the OS.
+ fd = open_broker.Open(permfile_name, O_RDWR | O_CREAT | O_EXCL);
+ ASSERT_EQ(fd, -EEXIST);
+
+ const char kTestText[] = "TESTTESTTEST";
+
+ fd = open_broker.Open(permfile_name, O_RDWR);
+ ASSERT_GE(fd, 0);
+ {
+ // Write to the descriptor opened by the broker and close.
+ base::ScopedFD scoped_fd(fd);
ssize_t len = HANDLE_EINTR(write(fd, kTestText, sizeof(kTestText)));
ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
}
- int fd_check = open(tempfile_name, O_RDONLY);
+ int fd_check = open(permfile_name, O_RDONLY);
ASSERT_GE(fd_check, 0);
{
base::ScopedFD scoped_fd(fd_check);
char buf[1024];
ssize_t len = HANDLE_EINTR(read(fd_check, buf, sizeof(buf)));
-
ASSERT_EQ(len, static_cast<ssize_t>(sizeof(kTestText)));
ASSERT_EQ(memcmp(kTestText, buf, sizeof(kTestText)), 0);
}
+
+ // Cleanup.
+ unlink(tempfile_name);
+ unlink(permfile_name);
}
-TEST(BrokerProcess, StatFile) {
+void TestStatHelper(bool fast_check_in_client) {
ScopedTemporaryFile tmp_file;
EXPECT_EQ(12, write(tmp_file.fd(), "blahblahblah", 12));
std::string temp_str = tmp_file.full_file_name();
const char* tempfile_name = temp_str.c_str();
- const char* nonesuch_name = "/mbogo/nonesuch";
- const bool fast_check_in_client = false;
+ const char* nonesuch_name = "/mbogo/fictitious/nonesuch";
+ const char* leading_path1 = "/mbogo/fictitious";
+ const char* leading_path2 = "/mbogo";
+ const char* leading_path3 = "/";
+ const char* bad_leading_path1 = "/mbog";
+ const char* bad_leading_path2 = "/mboga";
+ const char* bad_leading_path3 = "/mbogos";
+ const char* bad_leading_path4 = "/mbogo/fictitiou";
+ const char* bad_leading_path5 = "/mbogo/fictitioux";
+ const char* bad_leading_path6 = "/mbogo/fictitiousa";
+
struct stat sb;
+
+ {
+ // Actual file with permissions to see file but command not allowed.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(tempfile_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
+ permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+
+ memset(&sb, 0, sizeof(sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(tempfile_name, &sb));
+ }
+
+ BrokerCommandSet command_set;
+ command_set.set(COMMAND_STAT);
+
{
// Nonexistent file with no permissions to see file.
std::vector<BrokerFilePermission> permissions;
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(-EPERM, open_broker.Stat(nonesuch_name, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(nonesuch_name, &sb));
}
{
// Actual file with no permission to see file.
std::vector<BrokerFilePermission> permissions;
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
- EXPECT_EQ(-EPERM, open_broker.Stat(tempfile_name, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(tempfile_name, &sb));
}
{
// Nonexistent file with permissions to see file.
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly(nonesuch_name));
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+
+ memset(&sb, 0, sizeof(sb));
+ EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, &sb));
+
+ // Gets denied all the way back to root since no create permission.
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(leading_path1, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(leading_path2, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(leading_path3, &sb));
+
+ // Not fooled by substrings.
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path1, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path2, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path3, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path4, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path5, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path6, &sb));
+ }
+ {
+ // Nonexistent file with permissions to create file.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, &sb));
+
+ // Gets ENOENT all the way back to root since it has create permission.
+ EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path1, &sb));
+ EXPECT_EQ(-ENOENT, open_broker.Stat(leading_path2, &sb));
+
+ // But can always get the root.
+ EXPECT_EQ(0, open_broker.Stat(leading_path3, &sb));
+
+ // Not fooled by substrings.
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path1, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path2, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path3, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path4, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path5, &sb));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Stat(bad_leading_path6, &sb));
}
{
// Actual file with permissions to see file.
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly(tempfile_name));
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(tempfile_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
memset(&sb, 0, sizeof(sb));
EXPECT_EQ(0, open_broker.Stat(tempfile_name, &sb));
@@ -713,11 +850,13 @@ TEST(BrokerProcess, StatFile) {
EXPECT_NE(0u, static_cast<unsigned int>(sb.st_dev));
EXPECT_NE(0u, static_cast<unsigned int>(sb.st_ino));
EXPECT_NE(0u, static_cast<unsigned int>(sb.st_mode));
- EXPECT_NE(0u, static_cast<unsigned int>(sb.st_uid));
- EXPECT_NE(0u, static_cast<unsigned int>(sb.st_gid));
EXPECT_NE(0u, static_cast<unsigned int>(sb.st_blksize));
EXPECT_NE(0u, static_cast<unsigned int>(sb.st_blocks));
+ // We are the ones that made the file.
+ EXPECT_EQ(geteuid(), sb.st_uid);
+ EXPECT_EQ(getegid(), sb.st_gid);
+
// Wrote 12 bytes above which should fit in one block.
EXPECT_EQ(12, sb.st_size);
@@ -728,7 +867,15 @@ TEST(BrokerProcess, StatFile) {
}
}
-TEST(BrokerProcess, RenameFile) {
+TEST(BrokerProcess, StatFileClient) {
+ TestStatHelper(true);
+}
+
+TEST(BrokerProcess, StatFileHost) {
+ TestStatHelper(false);
+}
+
+void TestRenameHelper(bool fast_check_in_client) {
std::string oldpath;
std::string newpath;
{
@@ -747,15 +894,35 @@ TEST(BrokerProcess, RenameFile) {
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
EXPECT_TRUE(access(newpath.c_str(), F_OK) < 0);
+ std::vector<BrokerFilePermission> rwc_permissions = {
+ BrokerFilePermission::ReadWriteCreate(oldpath),
+ BrokerFilePermission::ReadWriteCreate(newpath)};
+
{
- // Check rename fails when no permission to new file.
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadWrite(oldpath));
+ // Check rename fails with write permissions to both files but command
+ // itself is not allowed.
+ BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
+ rwc_permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Rename(oldpath.c_str(), newpath.c_str()));
- bool fast_check_in_client = false;
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
- EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ // ... and no files moved around.
+ EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
+ EXPECT_TRUE(access(newpath.c_str(), F_OK) < 0);
+ }
+
+ BrokerCommandSet command_set = MakeBrokerCommandSet({COMMAND_RENAME});
+
+ {
+ // Check rename fails when no permission to new file.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadWriteCreate(oldpath)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Rename(oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -763,13 +930,13 @@ TEST(BrokerProcess, RenameFile) {
}
{
// Check rename fails when no permission to old file.
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadWrite(newpath));
-
- bool fast_check_in_client = false;
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
- EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadWriteCreate(newpath)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Rename(oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -777,29 +944,29 @@ TEST(BrokerProcess, RenameFile) {
}
{
// Check rename fails when only read permission to first file.
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadOnly(oldpath));
- permissions.push_back(BrokerFilePermission::ReadWrite(newpath));
-
- bool fast_check_in_client = false;
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
- EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(oldpath),
+ BrokerFilePermission::ReadWriteCreate(newpath)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Rename(oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
EXPECT_TRUE(access(newpath.c_str(), F_OK) < 0);
}
{
- // Check rename fails when only read permission to first file.
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadWrite(oldpath));
- permissions.push_back(BrokerFilePermission::ReadOnly(newpath));
-
- bool fast_check_in_client = false;
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
- EXPECT_EQ(-EPERM, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+ // Check rename fails when only read permission to second file.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadWriteCreate(oldpath),
+ BrokerFilePermission::ReadOnly(newpath)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Rename(oldpath.c_str(), newpath.c_str()));
// ... and no files moved around.
EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
@@ -807,13 +974,9 @@ TEST(BrokerProcess, RenameFile) {
}
{
// Check rename passes with write permissions to both files.
- std::vector<BrokerFilePermission> permissions;
- permissions.push_back(BrokerFilePermission::ReadWrite(oldpath));
- permissions.push_back(BrokerFilePermission::ReadWrite(newpath));
-
- bool fast_check_in_client = false;
- BrokerProcess open_broker(EPERM, permissions, fast_check_in_client);
- ASSERT_TRUE(open_broker.Init(base::Bind(&NoOpCallback)));
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
EXPECT_EQ(0, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
// ... and files were moved around.
@@ -825,5 +988,448 @@ TEST(BrokerProcess, RenameFile) {
unlink(newpath.c_str());
}
+TEST(BrokerProcess, RenameFileClient) {
+ TestRenameHelper(true);
+}
+
+TEST(BrokerProcess, RenameFileHost) {
+ TestRenameHelper(false);
+}
+
+void TestReadlinkHelper(bool fast_check_in_client) {
+ std::string oldpath;
+ std::string newpath;
+ {
+ // Just to generate names and ensure they do not exist upon scope exit.
+ ScopedTemporaryFile oldfile;
+ ScopedTemporaryFile newfile;
+ oldpath = oldfile.full_file_name();
+ newpath = newfile.full_file_name();
+ }
+
+ // Now make a link from old to new path name.
+ EXPECT_TRUE(symlink(oldpath.c_str(), newpath.c_str()) == 0);
+
+ const char* nonesuch_name = "/mbogo/nonesuch";
+ const char* oldpath_name = oldpath.c_str();
+ const char* newpath_name = newpath.c_str();
+ char buf[1024];
+ {
+ // Actual file with permissions to see file but command itself not allowed.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(newpath_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
+ permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Readlink(newpath_name, buf, sizeof(buf)));
+ }
+
+ BrokerCommandSet command_set;
+ command_set.set(COMMAND_READLINK);
+
+ {
+ // Nonexistent file with no permissions to see file.
+ std::vector<BrokerFilePermission> permissions;
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Readlink(nonesuch_name, buf, sizeof(buf)));
+ }
+ {
+ // Actual file with no permissions to see file.
+ std::vector<BrokerFilePermission> permissions;
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel,
+ open_broker.Readlink(newpath_name, buf, sizeof(buf)));
+ }
+ {
+ // Nonexistent file with permissions to see file.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-ENOENT, open_broker.Readlink(nonesuch_name, buf, sizeof(buf)));
+ }
+ {
+ // Actual file with permissions to see file.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(newpath_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ ssize_t retlen = open_broker.Readlink(newpath_name, buf, sizeof(buf));
+ EXPECT_TRUE(retlen == static_cast<ssize_t>(strlen(oldpath_name)));
+ EXPECT_EQ(0, memcmp(oldpath_name, buf, retlen));
+ }
+ {
+ // Actual file with permissions to see file, but too small a buffer.
+ std::vector<BrokerFilePermission> permissions = {
+ BrokerFilePermission::ReadOnly(newpath_name)};
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-ENAMETOOLONG, open_broker.Readlink(newpath_name, buf, 4));
+ }
+
+ // Cleanup both paths.
+ unlink(oldpath.c_str());
+ unlink(newpath.c_str());
+}
+
+TEST(BrokerProcess, ReadlinkFileClient) {
+ TestReadlinkHelper(true);
+}
+
+TEST(BrokerProcess, ReadlinkFileHost) {
+ TestReadlinkHelper(false);
+}
+
+void TestMkdirHelper(bool fast_check_in_client) {
+ std::string path;
+ {
+ // Generate name and ensure it does not exist upon scope exit.
+ ScopedTemporaryFile file;
+ path = file.full_file_name();
+ }
+ const char* path_name = path.c_str();
+ const char* nonesuch_name = "/mbogo/nonesuch";
+
+ std::vector<BrokerFilePermission> no_permissions;
+ std::vector<BrokerFilePermission> ro_permissions = {
+ BrokerFilePermission::ReadOnly(path_name),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+
+ std::vector<BrokerFilePermission> rw_permissions = {
+ BrokerFilePermission::ReadWrite(path_name),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+
+ std::vector<BrokerFilePermission> rwc_permissions = {
+ BrokerFilePermission::ReadWriteCreate(path_name),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+
+ {
+ // Actual file with permissions to use but command itself not allowed.
+ BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
+ rw_permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ }
+
+ BrokerCommandSet command_set = MakeBrokerCommandSet({COMMAND_MKDIR});
+
+ {
+ // Nonexistent file with no permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(nonesuch_name, 0600));
+ }
+ {
+ // Actual file with no permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ }
+ {
+ // Nonexistent file with insufficient permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(nonesuch_name, 0600));
+ }
+ {
+ // Actual file with insufficient permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ }
+ {
+ // Nonexistent file with insufficient permissions to see file, case 2.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(nonesuch_name, 0600));
+ }
+ {
+ // Actual file with insufficient permissions to see file, case 2.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Mkdir(path_name, 0600));
+ }
+ {
+ // Nonexistent file with permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-ENOENT, open_broker.Mkdir(nonesuch_name, 0600));
+ }
+ {
+ // Actual file with permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(0, open_broker.Mkdir(path_name, 0600));
+ }
+
+ // Cleanup.
+ rmdir(path_name);
+}
+
+TEST(BrokerProcess, MkdirClient) {
+ TestMkdirHelper(true);
+}
+
+TEST(BrokerProcess, MkdirHost) {
+ TestMkdirHelper(false);
+}
+
+void TestRmdirHelper(bool fast_check_in_client) {
+ std::string path;
+ {
+ // Generate name and ensure it does not exist upon scope exit.
+ ScopedTemporaryFile file;
+ path = file.full_file_name();
+ }
+ const char* path_name = path.c_str();
+ const char* nonesuch_name = "/mbogo/nonesuch";
+
+ EXPECT_EQ(0, mkdir(path_name, 0600));
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ std::vector<BrokerFilePermission> no_permissions;
+ std::vector<BrokerFilePermission> ro_permissions = {
+ BrokerFilePermission::ReadOnly(path_name),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+
+ std::vector<BrokerFilePermission> rw_permissions = {
+ BrokerFilePermission::ReadWrite(path_name),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+
+ std::vector<BrokerFilePermission> rwc_permissions = {
+ BrokerFilePermission::ReadWriteCreate(path_name),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+
+ {
+ // Actual dir with permissions to use but command itself not allowed.
+ BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
+ rw_permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ BrokerCommandSet command_set = MakeBrokerCommandSet({COMMAND_RMDIR});
+
+ {
+ // Nonexistent dir with no permissions to see dir.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(nonesuch_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual dir with no permissions to see dir.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Nonexistent dir with insufficient permissions to see dir.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(nonesuch_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual dir with insufficient permissions to see dir.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Nonexistent dir with insufficient permissions to see dir, case 2.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(nonesuch_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual dir with insufficient permissions to see dir, case 2.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Rmdir(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Nonexistent dir with permissions to see dir.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_TRUE(open_broker.Rmdir(nonesuch_name) < 0);
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual dir with permissions to see dir.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(0, open_broker.Rmdir(path_name));
+ }
+ // Confirm it was erased.
+ EXPECT_EQ(-1, access(path_name, F_OK));
+}
+
+TEST(BrokerProcess, RmdirClient) {
+ TestRmdirHelper(true);
+}
+
+TEST(BrokerProcess, RmdirHost) {
+ TestRmdirHelper(false);
+}
+
+void TestUnlinkHelper(bool fast_check_in_client) {
+ std::string path;
+ {
+ // Generate name and ensure it does not exist upon scope exit.
+ ScopedTemporaryFile file;
+ path = file.full_file_name();
+ }
+ const char* path_name = path.c_str();
+ const char* nonesuch_name = "/mbogo/nonesuch";
+
+ int fd = open(path_name, O_RDWR | O_CREAT, 0600);
+ EXPECT_TRUE(fd >= 0);
+ close(fd);
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ std::vector<BrokerFilePermission> no_permissions;
+ std::vector<BrokerFilePermission> ro_permissions = {
+ BrokerFilePermission::ReadOnly(path_name),
+ BrokerFilePermission::ReadOnly(nonesuch_name)};
+
+ std::vector<BrokerFilePermission> rw_permissions = {
+ BrokerFilePermission::ReadWrite(path_name),
+ BrokerFilePermission::ReadWrite(nonesuch_name)};
+
+ std::vector<BrokerFilePermission> rwc_permissions = {
+ BrokerFilePermission::ReadWriteCreate(path_name),
+ BrokerFilePermission::ReadWriteCreate(nonesuch_name)};
+
+ {
+ // Actual file with permissions to use but command itself not allowed.
+ BrokerProcess open_broker(kFakeErrnoSentinel, BrokerCommandSet(),
+ rwc_permissions, fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ BrokerCommandSet command_set = MakeBrokerCommandSet({COMMAND_UNLINK});
+
+ {
+ // Nonexistent file with no permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(nonesuch_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual file with no permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, no_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Nonexistent file with insufficient permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(nonesuch_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual file with insufficient permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, ro_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Nonexistent file with insufficient permissions to see file, case 2.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(nonesuch_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual file with insufficient permissions to see file, case 2.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rw_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(-kFakeErrnoSentinel, open_broker.Unlink(path_name));
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Nonexistent file with permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_TRUE(open_broker.Unlink(nonesuch_name) < 0);
+ }
+ EXPECT_EQ(0, access(path_name, F_OK));
+
+ {
+ // Actual file with permissions to see file.
+ BrokerProcess open_broker(kFakeErrnoSentinel, command_set, rwc_permissions,
+ fast_check_in_client);
+ ASSERT_TRUE(open_broker.Init(base::BindRepeating(&NoOpCallback)));
+ EXPECT_EQ(0, open_broker.Unlink(path_name));
+ }
+ // Confirm it was erased.
+ EXPECT_EQ(-1, access(path_name, F_OK));
+}
+
+TEST(BrokerProcess, UnlinkClient) {
+ TestUnlinkHelper(true);
+}
+
+TEST(BrokerProcess, UnlinkHost) {
+ TestUnlinkHelper(false);
+}
+
} // namespace syscall_broker
} // namespace sandbox
diff --git a/chromium/sandbox/linux/system_headers/linux_time.h b/chromium/sandbox/linux/system_headers/linux_time.h
index e6c8112b864..8de1cc100d2 100644
--- a/chromium/sandbox/linux/system_headers/linux_time.h
+++ b/chromium/sandbox/linux/system_headers/linux_time.h
@@ -7,6 +7,14 @@
#include <time.h>
+#if !defined(CPUCLOCK_CLOCK_MASK)
+#define CPUCLOCK_CLOCK_MASK 3
+#endif
+
+#if !defined(CLOCKFD)
+#define CLOCKFD 3
+#endif
+
#if !defined(CLOCK_REALTIME_COARSE)
#define CLOCK_REALTIME_COARSE 5
#endif
diff --git a/chromium/sandbox/mac/OWNERS b/chromium/sandbox/mac/OWNERS
index 6e9e9684954..636437fd110 100644
--- a/chromium/sandbox/mac/OWNERS
+++ b/chromium/sandbox/mac/OWNERS
@@ -1,3 +1,4 @@
+kerrnel@chromium.org
mark@chromium.org
rsesek@chromium.org
diff --git a/chromium/sandbox/win/BUILD.gn b/chromium/sandbox/win/BUILD.gn
index 48f4b1e76a0..01448d4e1e8 100644
--- a/chromium/sandbox/win/BUILD.gn
+++ b/chromium/sandbox/win/BUILD.gn
@@ -6,7 +6,7 @@ import("//testing/test.gni")
# This needs to be a static library rather than a sources set because small
# portions of this are used in some contexts (like chrome_elf), and it
-# doesnn't seem to dead-code strip very well. This saves 12K on chrome_elf.dll,
+# doesn't seem to dead-code strip very well. This saves 12K on chrome_elf.dll,
# over a source set, for example.
static_library("sandbox") {
sources = [
diff --git a/chromium/sandbox/win/OWNERS b/chromium/sandbox/win/OWNERS
index 4a33d017759..da58563c3d9 100644
--- a/chromium/sandbox/win/OWNERS
+++ b/chromium/sandbox/win/OWNERS
@@ -1,5 +1,7 @@
-cpu@chromium.org
forshaw@chromium.org
jschuh@chromium.org
pennymac@chromium.org
wfh@chromium.org
+
+# TEAM: security-dev@chromium.org
+# COMPONENT: Internals>Sandbox
diff --git a/chromium/sandbox/win/src/app_container_test.cc b/chromium/sandbox/win/src/app_container_test.cc
index 06ca7885221..7167354b144 100644
--- a/chromium/sandbox/win/src/app_container_test.cc
+++ b/chromium/sandbox/win/src/app_container_test.cc
@@ -4,24 +4,187 @@
#include <windows.h>
-#include "base/strings/string16.h"
+#include <sddl.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile.h"
#include "sandbox/win/src/sync_policy_test.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "sandbox/win/tests/common/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace sandbox {
+
namespace {
const wchar_t kAppContainerSid[] =
L"S-1-15-2-3251537155-1984446955-2931258699-841473695-1938553385-"
L"924012148-2839372144";
+std::wstring GenerateRandomPackageName() {
+ return base::StringPrintf(L"%016lX%016lX", base::RandUint64(),
+ base::RandUint64());
+}
+
+const char* TokenTypeToName(TOKEN_TYPE token_type) {
+ return token_type == ::TokenPrimary ? "Primary Token" : "Impersonation Token";
+}
+
+void CheckToken(HANDLE token,
+ TOKEN_TYPE token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ BOOL restricted) {
+ ASSERT_EQ(restricted, ::IsTokenRestricted(token))
+ << TokenTypeToName(token_type);
+
+ DWORD appcontainer;
+ DWORD return_length;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenIsAppContainer, &appcontainer,
+ sizeof(appcontainer), &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_TRUE(appcontainer) << TokenTypeToName(token_type);
+ TOKEN_TYPE token_type_real;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenType, &token_type_real,
+ sizeof(token_type_real), &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_EQ(token_type_real, token_type) << TokenTypeToName(token_type);
+ if (token_type == ::TokenImpersonation) {
+ SECURITY_IMPERSONATION_LEVEL imp_level;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenImpersonationLevel,
+ &imp_level, sizeof(imp_level),
+ &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_EQ(imp_level, ::SecurityImpersonation)
+ << TokenTypeToName(token_type);
+ }
+
+ std::unique_ptr<Sid> package_sid;
+ ASSERT_TRUE(GetTokenAppContainerSid(token, &package_sid))
+ << TokenTypeToName(token_type);
+ EXPECT_TRUE(::EqualSid(security_capabilities->AppContainerSid,
+ package_sid->GetPSID()))
+ << TokenTypeToName(token_type);
+
+ std::vector<SidAndAttributes> capabilities;
+ ASSERT_TRUE(GetTokenGroups(token, ::TokenCapabilities, &capabilities))
+ << TokenTypeToName(token_type);
+
+ ASSERT_EQ(capabilities.size(), security_capabilities->CapabilityCount)
+ << TokenTypeToName(token_type);
+ for (size_t index = 0; index < capabilities.size(); ++index) {
+ EXPECT_EQ(capabilities[index].GetAttributes(),
+ security_capabilities->Capabilities[index].Attributes)
+ << TokenTypeToName(token_type);
+ EXPECT_TRUE(::EqualSid(capabilities[index].GetPSID(),
+ security_capabilities->Capabilities[index].Sid))
+ << TokenTypeToName(token_type);
+ }
+}
+
+void CheckProcessToken(HANDLE process,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool restricted) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ CheckToken(token_handle, ::TokenPrimary, security_capabilities, restricted);
+}
+
+void CheckThreadToken(HANDLE thread,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool restricted) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenThreadToken(thread, TOKEN_ALL_ACCESS, TRUE, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ CheckToken(token_handle, ::TokenImpersonation, security_capabilities,
+ restricted);
+}
+
+// Check for LPAC using an access check. We could query for a security attribute
+// but that's undocumented and has the potential to change.
+void CheckLpacToken(HANDLE process) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ ASSERT_TRUE(
+ ::DuplicateToken(token.Get(), ::SecurityImpersonation, &token_handle));
+ token.Set(token_handle);
+ PSECURITY_DESCRIPTOR security_desc_ptr;
+ // AC is AllPackages, S-1-15-2-2 is AllRestrictedPackages. An LPAC token
+ // will get granted access of 2, where as a normal AC token will get 3.
+ ASSERT_TRUE(::ConvertStringSecurityDescriptorToSecurityDescriptor(
+ L"O:SYG:SYD:(A;;0x3;;;WD)(A;;0x1;;;AC)(A;;0x2;;;S-1-15-2-2)",
+ SDDL_REVISION_1, &security_desc_ptr, nullptr));
+ std::unique_ptr<void, LocalFreeDeleter> security_desc(security_desc_ptr);
+ GENERIC_MAPPING generic_mapping = {};
+ PRIVILEGE_SET priv_set = {};
+ DWORD priv_set_length = sizeof(PRIVILEGE_SET);
+ DWORD granted_access;
+ BOOL access_status;
+ ASSERT_TRUE(::AccessCheck(security_desc_ptr, token.Get(), MAXIMUM_ALLOWED,
+ &generic_mapping, &priv_set, &priv_set_length,
+ &granted_access, &access_status));
+ ASSERT_TRUE(access_status);
+ ASSERT_EQ(DWORD{2}, granted_access);
+}
+
+class AppContainerProfileTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+ package_name_ = GenerateRandomPackageName();
+ broker_services_ = GetBroker();
+ profile_ = AppContainerProfile::Create(package_name_.c_str(), L"Name",
+ L"Description");
+ policy_ = broker_services_->CreatePolicy();
+ policy_->SetAppContainerProfile(profile_.get());
+ }
+
+ void TearDown() override {
+ if (scoped_process_info_.IsValid())
+ ::TerminateProcess(scoped_process_info_.process_handle(), 0);
+ if (profile_)
+ AppContainerProfile::Delete(package_name_.c_str());
+ }
+
+ protected:
+ void CreateProcess() {
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH] = {};
+ ASSERT_NE(DWORD{0}, ::GetModuleFileNameW(nullptr, prog_name, MAX_PATH));
+
+ PROCESS_INFORMATION process_info = {};
+ ResultCode last_warning = SBOX_ALL_OK;
+ DWORD last_error = 0;
+ ResultCode result = broker_services_->SpawnTarget(
+ prog_name, prog_name, policy_, &last_warning, &last_error,
+ &process_info);
+ ASSERT_EQ(SBOX_ALL_OK, result) << "Last Error: " << last_error;
+ scoped_process_info_.Set(process_info);
+ }
+
+ std::wstring package_name_;
+ BrokerServices* broker_services_;
+ scoped_refptr<AppContainerProfile> profile_;
+ scoped_refptr<TargetPolicy> policy_;
+ base::win::ScopedProcessInformation scoped_process_info_;
+};
+
} // namespace
-namespace sandbox {
TEST(AppContainerTest, DenyOpenEventForLowBox) {
- if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
return;
TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED);
@@ -30,7 +193,7 @@ TEST(AppContainerTest, DenyOpenEventForLowBox) {
// Run test once, this ensures the app container directory exists, we
// ignore the result.
runner.RunTest(L"Event_Open f test");
- base::string16 event_name = L"AppContainerNamedObjects\\";
+ std::wstring event_name = L"AppContainerNamedObjects\\";
event_name += kAppContainerSid;
event_name += L"\\test";
@@ -41,7 +204,120 @@ TEST(AppContainerTest, DenyOpenEventForLowBox) {
EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test"));
}
-// TODO(shrikant): Please add some tests to prove usage of lowbox token like
-// socket connection to local server in lock down mode.
+TEST_F(AppContainerProfileTest, CheckIncompatibleOptions) {
+ if (!profile_)
+ return;
+ EXPECT_EQ(SBOX_ERROR_BAD_PARAMS,
+ policy_->SetIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED));
+ EXPECT_EQ(SBOX_ERROR_BAD_PARAMS, policy_->SetLowBox(kAppContainerSid));
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilities) {
+ if (!profile_)
+ return;
+
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilitiesRestricted) {
+ if (!profile_)
+ return;
+
+ policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), TRUE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), TRUE);
+}
+
+TEST_F(AppContainerProfileTest, WithCapabilities) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+}
+
+TEST_F(AppContainerProfileTest, WithCapabilitiesRestricted) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), TRUE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), TRUE);
+}
+
+TEST_F(AppContainerProfileTest, WithImpersonationCapabilities) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ profile_->AddImpersonationCapability(kPrivateNetworkClientServer);
+ profile_->AddImpersonationCapability(kPicturesLibrary);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ SecurityCapabilities impersonation_security_capabilities(
+ profile_->GetPackageSid(), profile_->GetImpersonationCapabilities());
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ &impersonation_security_capabilities, FALSE);
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilitiesLPAC) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN10_RS1)
+ return;
+
+ profile_->SetEnableLowPrivilegeAppContainer(true);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+ CheckLpacToken(scoped_process_info_.process_handle());
+}
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/broker_services.cc b/chromium/sandbox/win/src/broker_services.cc
index 525531bc520..ab899956a88 100644
--- a/chromium/sandbox/win/src/broker_services.cc
+++ b/chromium/sandbox/win/src/broker_services.cc
@@ -18,6 +18,7 @@
#include "base/win/scoped_process_information.h"
#include "base/win/startup_information.h"
#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile.h"
#include "sandbox/win/src/process_mitigations.h"
#include "sandbox/win/src/sandbox.h"
#include "sandbox/win/src/sandbox_policy_base.h"
@@ -367,6 +368,20 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
if (inherited_handle_list.size())
++attribute_count;
+ scoped_refptr<AppContainerProfile> profile =
+ policy_base->GetAppContainerProfile();
+ if (profile) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ // LPAC first supported in RS1.
+ if (base::win::GetVersion() < base::win::VERSION_WIN10_RS1)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ }
+ }
+
if (!startup_info.InitializeProcThreadAttributeList(attribute_count))
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
@@ -401,6 +416,28 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
inherit_handles = true;
}
+ // Declared here to ensure they stay in scope until after process creation.
+ std::unique_ptr<SecurityCapabilities> security_capabilities;
+ DWORD all_applications_package_policy =
+ PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT;
+
+ if (profile) {
+ security_capabilities = profile->GetSecurityCapabilities();
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
+ security_capabilities.get(), sizeof(SECURITY_CAPABILITIES))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY,
+ &all_applications_package_policy,
+ sizeof(all_applications_package_policy))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+ }
+
// Construct the thread pool here in case it is expensive.
// The thread pool is shared by all the targets
if (!thread_pool_)
@@ -409,9 +446,10 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
// Create the TargetProcess object and spawn the target suspended. Note that
// Brokerservices does not own the target object. It is owned by the Policy.
base::win::ScopedProcessInformation process_info;
- TargetProcess* target =
- new TargetProcess(std::move(initial_token), std::move(lockdown_token),
- job.Get(), thread_pool_.get());
+ TargetProcess* target = new TargetProcess(
+ std::move(initial_token), std::move(lockdown_token), job.Get(),
+ thread_pool_.get(),
+ profile ? profile->GetImpersonationCapabilities() : std::vector<Sid>());
result = target->Create(exe_path, command_line, inherit_handles, startup_info,
&process_info, last_error);
diff --git a/chromium/sandbox/win/src/crosscall_server.cc b/chromium/sandbox/win/src/crosscall_server.cc
index 0125a52c457..15cfa5f4ea1 100644
--- a/chromium/sandbox/win/src/crosscall_server.cc
+++ b/chromium/sandbox/win/src/crosscall_server.cc
@@ -16,6 +16,10 @@
#include "sandbox/win/src/crosscall_client.h"
#include "sandbox/win/src/crosscall_params.h"
+// See comment in atomicops.h. This is needed any time windows.h is included
+// after atomicops.h.
+#undef MemoryBarrier
+
// This code performs the ipc message validation. Potential security flaws
// on the ipc are likelier to be found in this code than in the rest of
// the ipc code.
diff --git a/chromium/sandbox/win/src/sandbox_policy.h b/chromium/sandbox/win/src/sandbox_policy.h
index 29f8705bff7..4a110fa9d93 100644
--- a/chromium/sandbox/win/src/sandbox_policy.h
+++ b/chromium/sandbox/win/src/sandbox_policy.h
@@ -14,6 +14,8 @@
namespace sandbox {
+class AppContainerProfile;
+
class TargetPolicy {
public:
// Windows subsystems that can have specific rules.
@@ -251,6 +253,9 @@ class TargetPolicy {
// Enable OPM API emulation when in Win32k lockdown.
virtual bool GetEnableOPMRedirection() = 0;
+ // Configure policy to use an AppContainer profile.
+ virtual ResultCode SetAppContainerProfile(AppContainerProfile* profile) = 0;
+
protected:
~TargetPolicy() {}
};
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.cc b/chromium/sandbox/win/src/sandbox_policy_base.cc
index 867a549651b..471fc75a325 100644
--- a/chromium/sandbox/win/src/sandbox_policy_base.cc
+++ b/chromium/sandbox/win/src/sandbox_policy_base.cc
@@ -267,6 +267,8 @@ void PolicyBase::DestroyAlternateDesktop() {
}
ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ if (_app_container_profile)
+ return SBOX_ERROR_BAD_PARAMS;
integrity_level_ = integrity_level;
return SBOX_ALL_OK;
}
@@ -282,11 +284,11 @@ ResultCode PolicyBase::SetDelayedIntegrityLevel(
}
ResultCode PolicyBase::SetLowBox(const wchar_t* sid) {
- if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
return SBOX_ERROR_UNSUPPORTED;
DCHECK(sid);
- if (lowbox_sid_)
+ if (lowbox_sid_ || _app_container_profile)
return SBOX_ERROR_BAD_PARAMS;
if (!ConvertStringSidToSid(sid, &lowbox_sid_))
@@ -594,6 +596,23 @@ bool PolicyBase::GetEnableOPMRedirection() {
return enable_opm_redirection_;
}
+ResultCode PolicyBase::SetAppContainerProfile(AppContainerProfile* profile) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ DCHECK(profile);
+ if (lowbox_sid_ || _app_container_profile ||
+ integrity_level_ != INTEGRITY_LEVEL_LAST)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ _app_container_profile = profile;
+ return SBOX_ALL_OK;
+}
+
+scoped_refptr<AppContainerProfile> PolicyBase::GetAppContainerProfile() {
+ return _app_container_profile;
+}
+
ResultCode PolicyBase::SetupAllInterceptions(TargetProcess* target) {
InterceptionManager manager(target, relaxed_interceptions_);
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.h b/chromium/sandbox/win/src/sandbox_policy_base.h
index 61a66419ada..fddea2b2e19 100644
--- a/chromium/sandbox/win/src/sandbox_policy_base.h
+++ b/chromium/sandbox/win/src/sandbox_policy_base.h
@@ -19,6 +19,7 @@
#include "base/process/launch.h"
#include "base/strings/string16.h"
#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/app_container_profile.h"
#include "sandbox/win/src/crosscall_server.h"
#include "sandbox/win/src/handle_closer.h"
#include "sandbox/win/src/ipc_tags.h"
@@ -72,6 +73,7 @@ class PolicyBase final : public TargetPolicy {
void SetLockdownDefaultDacl() override;
void SetEnableOPMRedirection() override;
bool GetEnableOPMRedirection() override;
+ ResultCode SetAppContainerProfile(AppContainerProfile* profile) override;
// Creates a Job object with the level specified in a previous call to
// SetJobLevel().
@@ -100,6 +102,8 @@ class PolicyBase final : public TargetPolicy {
HANDLE GetStdoutHandle();
HANDLE GetStderrHandle();
+ scoped_refptr<AppContainerProfile> GetAppContainerProfile();
+
// Returns the list of handles being shared with the target process.
const base::HandlesToInheritVector& GetHandlesBeingShared();
@@ -170,6 +174,8 @@ class PolicyBase final : public TargetPolicy {
base::HandlesToInheritVector handles_to_share_;
bool enable_opm_redirection_;
+ scoped_refptr<AppContainerProfile> _app_container_profile;
+
DISALLOW_COPY_AND_ASSIGN(PolicyBase);
};
diff --git a/chromium/sandbox/win/src/service_resolver_unittest.cc b/chromium/sandbox/win/src/service_resolver_unittest.cc
index 3d85e05eeb8..30b13c4a5ef 100644
--- a/chromium/sandbox/win/src/service_resolver_unittest.cc
+++ b/chromium/sandbox/win/src/service_resolver_unittest.cc
@@ -19,17 +19,30 @@
namespace {
+class ResolverThunkTest {
+ public:
+ virtual ~ResolverThunkTest() {}
+
+ virtual sandbox::ServiceResolverThunk* resolver() = 0;
+
+ // Sets the interception target to the desired address.
+ void set_target(void* target) { fake_target_ = target; }
+
+ protected:
+ // Holds the address of the fake target.
+ void* fake_target_;
+};
+
// This is the concrete resolver used to perform service-call type functions
// inside ntdll.dll.
template <typename T>
-class ResolverThunkTest : public T {
+class ResolverThunkTestImpl : public T, public ResolverThunkTest {
public:
// The service resolver needs a child process to write to.
- explicit ResolverThunkTest(bool relaxed)
+ explicit ResolverThunkTestImpl(bool relaxed)
: T(::GetCurrentProcess(), relaxed) {}
- // Sets the interception target to the desired address.
- void set_target(void* target) { fake_target_ = target; }
+ sandbox::ServiceResolverThunk* resolver() { return this; }
protected:
// Overrides Resolver::Init
@@ -51,20 +64,18 @@ class ResolverThunkTest : public T {
return ret;
};
- private:
- // Holds the address of the fake target.
- void* fake_target_;
-
- DISALLOW_COPY_AND_ASSIGN(ResolverThunkTest);
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunkTestImpl);
};
-typedef ResolverThunkTest<sandbox::ServiceResolverThunk> WinXpResolverTest;
+typedef ResolverThunkTestImpl<sandbox::ServiceResolverThunk> WinXpResolverTest;
#if !defined(_WIN64)
-typedef ResolverThunkTest<sandbox::Win8ResolverThunk> Win8ResolverTest;
-typedef ResolverThunkTest<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
-typedef ResolverThunkTest<sandbox::Wow64W8ResolverThunk> Wow64W8ResolverTest;
-typedef ResolverThunkTest<sandbox::Wow64W10ResolverThunk> Wow64W10ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Win8ResolverThunk> Win8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W8ResolverThunk>
+ Wow64W8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W10ResolverThunk>
+ Wow64W10ResolverTest;
#endif
const BYTE kJump32 = 0xE9;
@@ -92,7 +103,7 @@ void CheckJump(void* source, void* target) {
NTSTATUS PatchNtdllWithResolver(const char* function,
bool relaxed,
- sandbox::ServiceResolverThunk* resolver) {
+ ResolverThunkTest* thunk_test) {
HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
EXPECT_TRUE(ntdll_base);
@@ -104,8 +115,9 @@ NTSTATUS PatchNtdllWithResolver(const char* function,
BYTE service[50];
memcpy(service, target, sizeof(service));
- static_cast<WinXpResolverTest*>(resolver)->set_target(service);
+ thunk_test->set_target(service);
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
// Any pointer will do as an interception_entry_point
void* function_entry = resolver;
size_t thunk_size = resolver->GetThunkSize();
@@ -134,32 +146,29 @@ NTSTATUS PatchNtdllWithResolver(const char* function,
return ret;
}
-sandbox::ServiceResolverThunk* GetTestResolver(bool relaxed) {
+std::unique_ptr<ResolverThunkTest> GetTestResolver(bool relaxed) {
#if defined(_WIN64)
- return new WinXpResolverTest(relaxed);
+ return std::make_unique<WinXpResolverTest>(relaxed);
#else
base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
if (os_info->version() >= base::win::VERSION_WIN10)
- return new Wow64W10ResolverTest(relaxed);
+ return std::make_unique<Wow64W10ResolverTest>(relaxed);
if (os_info->version() >= base::win::VERSION_WIN8)
- return new Wow64W8ResolverTest(relaxed);
- return new Wow64ResolverTest(relaxed);
+ return std::make_unique<Wow64W8ResolverTest>(relaxed);
+ return std::make_unique<Wow64ResolverTest>(relaxed);
}
if (os_info->version() >= base::win::VERSION_WIN8)
- return new Win8ResolverTest(relaxed);
+ return std::make_unique<Win8ResolverTest>(relaxed);
- return new WinXpResolverTest(relaxed);
+ return std::make_unique<WinXpResolverTest>(relaxed);
#endif
}
NTSTATUS PatchNtdll(const char* function, bool relaxed) {
- sandbox::ServiceResolverThunk* resolver = GetTestResolver(relaxed);
-
- NTSTATUS ret = PatchNtdllWithResolver(function, relaxed, resolver);
- delete resolver;
- return ret;
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(relaxed);
+ return PatchNtdllWithResolver(function, relaxed, thunk_test.get());
}
TEST(ServiceResolverTest, PatchesServices) {
@@ -210,27 +219,26 @@ TEST(ServiceResolverTest, PatchesPatchedServices) {
TEST(ServiceResolverTest, MultiplePatchedServices) {
// We don't support "relaxed mode" for Win64 apps.
#if !defined(_WIN64)
- sandbox::ServiceResolverThunk* resolver = GetTestResolver(true);
- NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, resolver);
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
+ NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, thunk_test.get());
EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
- ret = PatchNtdllWithResolver("NtCreateFile", true, resolver);
+ ret = PatchNtdllWithResolver("NtCreateFile", true, thunk_test.get());
EXPECT_EQ(STATUS_SUCCESS, ret)
<< "NtCreateFile, last error: " << ::GetLastError();
- ret = PatchNtdllWithResolver("NtCreateMutant", true, resolver);
+ ret = PatchNtdllWithResolver("NtCreateMutant", true, thunk_test.get());
EXPECT_EQ(STATUS_SUCCESS, ret)
<< "NtCreateMutant, last error: " << ::GetLastError();
- ret = PatchNtdllWithResolver("NtMapViewOfSection", true, resolver);
+ ret = PatchNtdllWithResolver("NtMapViewOfSection", true, thunk_test.get());
EXPECT_EQ(STATUS_SUCCESS, ret)
<< "NtMapViewOfSection, last error: " << ::GetLastError();
- delete resolver;
#endif
}
TEST(ServiceResolverTest, LocalPatchesAllowed) {
- sandbox::ServiceResolverThunk* resolver = GetTestResolver(true);
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
ASSERT_TRUE(ntdll_base);
@@ -242,8 +250,9 @@ TEST(ServiceResolverTest, LocalPatchesAllowed) {
BYTE service[50];
memcpy(service, target, sizeof(service));
- static_cast<WinXpResolverTest*>(resolver)->set_target(service);
+ thunk_test->set_target(service);
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
// Any pointer will do as an interception_entry_point
void* function_entry = resolver;
size_t thunk_size = resolver->GetThunkSize();
diff --git a/chromium/sandbox/win/src/target_process.cc b/chromium/sandbox/win/src/target_process.cc
index 4f3e103352d..72b3e1bd382 100644
--- a/chromium/sandbox/win/src/target_process.cc
+++ b/chromium/sandbox/win/src/target_process.cc
@@ -9,18 +9,25 @@
#include <memory>
#include <utility>
+#include <vector>
#include "base/macros.h"
#include "base/memory/free_deleter.h"
+#include "base/numerics/safe_conversions.h"
#include "base/win/startup_information.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/crosscall_client.h"
#include "sandbox/win/src/crosscall_server.h"
#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/restricted_token_utils.h"
#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/security_capabilities.h"
#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "sandbox/win/src/sid.h"
#include "sandbox/win/src/win_utils.h"
+namespace sandbox {
+
namespace {
void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
@@ -41,9 +48,53 @@ void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
}
}
-} // namespace
+bool GetTokenAppContainerSid(HANDLE token_handle,
+ std::unique_ptr<Sid>* app_container_sid) {
+ std::vector<char> app_container_info(sizeof(TOKEN_APPCONTAINER_INFORMATION) +
+ SECURITY_MAX_SID_SIZE);
+ DWORD return_length;
+
+ if (!::GetTokenInformation(
+ token_handle, TokenAppContainerSid, app_container_info.data(),
+ base::checked_cast<DWORD>(app_container_info.size()),
+ &return_length)) {
+ return false;
+ }
-namespace sandbox {
+ PTOKEN_APPCONTAINER_INFORMATION info =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(
+ app_container_info.data());
+ if (!info->TokenAppContainer)
+ return false;
+ *app_container_sid = std::unique_ptr<Sid>(new Sid(info->TokenAppContainer));
+ return true;
+}
+
+bool GetProcessAppContainerSid(HANDLE process,
+ std::unique_ptr<Sid>* app_container_sid) {
+ HANDLE token_handle;
+ if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle))
+ return false;
+ base::win::ScopedHandle process_token(token_handle);
+
+ return GetTokenAppContainerSid(process_token.Get(), app_container_sid);
+}
+
+bool GetAppContainerImpersonationToken(
+ HANDLE process,
+ HANDLE initial_token,
+ const std::vector<Sid>& capabilities,
+ base::win::ScopedHandle* impersonation_token) {
+ std::unique_ptr<Sid> app_container_sid;
+ if (!GetProcessAppContainerSid(process, &app_container_sid)) {
+ return false;
+ }
+ SecurityCapabilities security_caps(*app_container_sid, capabilities);
+ return CreateLowBoxToken(initial_token, IMPERSONATION, &security_caps,
+ nullptr, 0, impersonation_token) == ERROR_SUCCESS;
+}
+
+} // namespace
SANDBOX_INTERCEPT HANDLE g_shared_section;
SANDBOX_INTERCEPT size_t g_shared_IPC_size;
@@ -52,7 +103,8 @@ SANDBOX_INTERCEPT size_t g_shared_policy_size;
TargetProcess::TargetProcess(base::win::ScopedHandle initial_token,
base::win::ScopedHandle lockdown_token,
HANDLE job,
- ThreadProvider* thread_pool)
+ ThreadProvider* thread_pool,
+ const std::vector<Sid>& impersonation_capabilities)
// This object owns everything initialized here except thread_pool and
// the job_ handle. The Job handle is closed by BrokerServices and results
// eventually in a call to our dtor.
@@ -60,7 +112,8 @@ TargetProcess::TargetProcess(base::win::ScopedHandle initial_token,
initial_token_(std::move(initial_token)),
job_(job),
thread_pool_(thread_pool),
- base_address_(nullptr) {}
+ base_address_(nullptr),
+ impersonation_capabilities_(impersonation_capabilities) {}
TargetProcess::~TargetProcess() {
// Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
@@ -132,14 +185,20 @@ ResultCode TargetProcess::Create(
}
if (initial_token_.IsValid()) {
+ HANDLE impersonation_token = initial_token_.Get();
+ base::win::ScopedHandle app_container_token;
+ if (GetAppContainerImpersonationToken(
+ process_info.process_handle(), impersonation_token,
+ impersonation_capabilities_, &app_container_token)) {
+ impersonation_token = app_container_token.Get();
+ }
+
// Change the token of the main thread of the new process for the
// impersonation token with more rights. This allows the target to start;
// otherwise it will crash too early for us to help.
HANDLE temp_thread = process_info.thread_handle();
- if (!::SetThreadToken(&temp_thread, initial_token_.Get())) {
+ if (!::SetThreadToken(&temp_thread, impersonation_token)) {
*win_error = ::GetLastError();
- // It might be a security breach if we let the target run outside the job
- // so kill it before it causes damage.
::TerminateProcess(process_info.process_handle(), 0);
return SBOX_ERROR_SET_THREAD_TOKEN;
}
@@ -311,8 +370,9 @@ ResultCode TargetProcess::AssignLowBoxToken(
}
TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
- TargetProcess* target = new TargetProcess(
- base::win::ScopedHandle(), base::win::ScopedHandle(), nullptr, nullptr);
+ TargetProcess* target =
+ new TargetProcess(base::win::ScopedHandle(), base::win::ScopedHandle(),
+ nullptr, nullptr, std::vector<Sid>());
PROCESS_INFORMATION process_info = {};
process_info.hProcess = process;
target->sandbox_process_info_.Set(process_info);
diff --git a/chromium/sandbox/win/src/target_process.h b/chromium/sandbox/win/src/target_process.h
index d4b4b27d9df..24059c970f4 100644
--- a/chromium/sandbox/win/src/target_process.h
+++ b/chromium/sandbox/win/src/target_process.h
@@ -11,6 +11,7 @@
#include <stdint.h>
#include <memory>
+#include <vector>
#include "base/macros.h"
#include "base/memory/free_deleter.h"
@@ -30,18 +31,19 @@ class StartupInformation;
namespace sandbox {
class SharedMemIPCServer;
+class Sid;
class ThreadProvider;
// TargetProcess models a target instance (child process). Objects of this
// class are owned by the Policy used to create them.
class TargetProcess {
public:
- // The constructor takes ownership of |initial_token|, |lockdown_token|
- // and |lowbox_token|.
+ // The constructor takes ownership of |initial_token| and |lockdown_token|
TargetProcess(base::win::ScopedHandle initial_token,
base::win::ScopedHandle lockdown_token,
HANDLE job,
- ThreadProvider* thread_pool);
+ ThreadProvider* thread_pool,
+ const std::vector<Sid>& impersonation_capabilities);
~TargetProcess();
// TODO(cpu): Currently there does not seem to be a reason to implement
@@ -120,6 +122,8 @@ class TargetProcess {
void* base_address_;
// Full name of the target executable.
std::unique_ptr<wchar_t, base::FreeDeleter> exe_name_;
+ /// List of capability sids for use when impersonating in an AC process.
+ std::vector<Sid> impersonation_capabilities_;
// Function used for testing.
friend TargetProcess* MakeTestTargetProcess(HANDLE process,