summaryrefslogtreecommitdiff
path: root/chromium/sandbox
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-01-29 16:35:13 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-02-01 15:33:35 +0000
commitc8c2d1901aec01e934adf561a9fdf0cc776cdef8 (patch)
tree9157c3d9815e5870799e070b113813bec53e0535 /chromium/sandbox
parentabefd5095b41dac94ca451d784ab6e27372e981a (diff)
downloadqtwebengine-chromium-c8c2d1901aec01e934adf561a9fdf0cc776cdef8.tar.gz
BASELINE: Update Chromium to 64.0.3282.139
Change-Id: I1cae68fe9c94ff7608b26b8382fc19862cdb293a Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/sandbox')
-rw-r--r--chromium/sandbox/linux/BUILD.gn4
-rw-r--r--chromium/sandbox/linux/PRESUBMIT.py35
-rw-r--r--chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc1
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc5
-rw-r--r--chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc3
-rw-r--r--chromium/sandbox/linux/services/credentials.cc5
-rw-r--r--chromium/sandbox/linux/services/namespace_utils.cc7
-rw-r--r--chromium/sandbox/linux/services/thread_helpers_unittests.cc3
-rw-r--r--chromium/sandbox/linux/syscall_broker/DEPS1
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.cc93
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_client.h29
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_common.h3
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_file_permission.cc113
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_file_permission.h30
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc13
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_host.cc193
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process.cc158
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process.h40
-rw-r--r--chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc165
-rw-r--r--chromium/sandbox/mac/seatbelt_exec.cc8
-rw-r--r--chromium/sandbox/win/BUILD.gn6
-rw-r--r--chromium/sandbox/win/PRESUBMIT.py22
-rw-r--r--chromium/sandbox/win/src/app_container_profile.cc324
-rw-r--r--chromium/sandbox/win/src/app_container_profile.h121
-rw-r--r--chromium/sandbox/win/src/app_container_unittest.cc382
-rw-r--r--chromium/sandbox/win/src/broker_services.h1
-rw-r--r--chromium/sandbox/win/src/filesystem_policy.cc4
-rw-r--r--chromium/sandbox/win/src/nt_internals.h5
-rw-r--r--chromium/sandbox/win/src/policy_engine_opcodes.cc14
-rw-r--r--chromium/sandbox/win/src/policy_engine_opcodes.h4
-rw-r--r--chromium/sandbox/win/src/policy_low_level.cc2
-rw-r--r--chromium/sandbox/win/src/restricted_token_unittest.cc216
-rw-r--r--chromium/sandbox/win/src/restricted_token_utils.cc103
-rw-r--r--chromium/sandbox/win/src/restricted_token_utils.h25
-rw-r--r--chromium/sandbox/win/src/sandbox_policy.h3
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.cc65
-rw-r--r--chromium/sandbox/win/src/sandbox_policy_base.h2
-rw-r--r--chromium/sandbox/win/src/security_capabilities.cc33
-rw-r--r--chromium/sandbox/win/src/security_capabilities.h34
-rw-r--r--chromium/sandbox/win/src/sid.cc99
-rw-r--r--chromium/sandbox/win/src/sid.h16
-rw-r--r--chromium/sandbox/win/src/sid_unittest.cc82
42 files changed, 2032 insertions, 440 deletions
diff --git a/chromium/sandbox/linux/BUILD.gn b/chromium/sandbox/linux/BUILD.gn
index 4b321e2a5c9..8c9cf2c9458 100644
--- a/chromium/sandbox/linux/BUILD.gn
+++ b/chromium/sandbox/linux/BUILD.gn
@@ -112,6 +112,7 @@ source_set("sandbox_linux_unittests_sources") {
":sandbox",
":sandbox_linux_test_utils",
"//base",
+ "//base/third_party/dynamic_annotations",
"//testing/gtest",
]
@@ -250,6 +251,7 @@ component("seccomp_bpf") {
deps = [
":sandbox_services",
"//base",
+ "//base/third_party/dynamic_annotations",
]
if (is_nacl_nonsfi) {
@@ -374,6 +376,7 @@ component("sandbox_services") {
]
deps = [
"//base",
+ "//base/third_party/dynamic_annotations",
]
if (compile_credentials || is_nacl_nonsfi) {
@@ -457,6 +460,7 @@ if (compile_suid_client || is_nacl_nonsfi) {
deps = [
":sandbox_services",
"//base",
+ "//base/third_party/dynamic_annotations",
]
if (is_nacl_nonsfi) {
diff --git a/chromium/sandbox/linux/PRESUBMIT.py b/chromium/sandbox/linux/PRESUBMIT.py
new file mode 100644
index 00000000000..01059c23fac
--- /dev/null
+++ b/chromium/sandbox/linux/PRESUBMIT.py
@@ -0,0 +1,35 @@
+# 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.
+
+def PostUploadHook(cl, change, output_api):
+ """git cl upload will call this hook after the issue is created/modified.
+
+ This will add extra trybot coverage for non-default Android architectures
+ that have a history of breaking with Seccomp changes.
+ """
+ def affects_seccomp(f):
+ seccomp_paths = [
+ 'bpf_dsl/',
+ 'seccomp-bpf/',
+ 'seccomp-bpf-helpers/',
+ 'system_headers/',
+ 'tests/'
+ ]
+ # If the file path contains any of the above fragments, it affects
+ # the Seccomp implementation.
+ affected_any = map(lambda sp: sp in f.LocalPath(), seccomp_paths)
+ return reduce(lambda a, b: a or b, affected_any)
+
+ if not change.AffectedFiles(file_filter=affects_seccomp):
+ return []
+
+ return output_api.EnsureCQIncludeTrybotsAreAdded(
+ cl,
+ [
+ 'master.tryserver.chromium.android:android_arm64_dbg_recipe',
+ 'master.tryserver.chromium.android:android_compile_mips_dbg',
+ 'master.tryserver.chromium.android:android_compile_x64_dbg',
+ 'master.tryserver.chromium.android:android_compile_x86_dbg',
+ ],
+ 'Automatically added Android multi-arch compile bots to run on CQ.')
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 76eccba1199..440ef20681f 100644
--- a/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
+++ b/chromium/sandbox/linux/seccomp-bpf-helpers/syscall_parameters_restrictions.cc
@@ -158,6 +158,7 @@ ResultExpr RestrictPrctl() {
.CASES((PR_GET_NAME, PR_SET_NAME, PR_GET_DUMPABLE, PR_SET_DUMPABLE
#if defined(OS_ANDROID)
, PR_SET_VMA, PR_SET_PTRACER, PR_SET_TIMERSLACK
+ , PR_GET_NO_NEW_PRIVS
// Enable PR_SET_TIMERSLACK_PID, an Android custom prctl which is used in:
// https://android.googlesource.com/platform/system/core/+/lollipop-release/libcutils/sched_policy.c.
diff --git a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
index c61e8e72888..9ff79d79658 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf.cc
@@ -15,6 +15,7 @@
#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"
@@ -36,8 +37,6 @@ namespace sandbox {
namespace {
-bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
-
// Check if the kernel supports seccomp-filter (a.k.a. seccomp mode 2) via
// prctl().
bool KernelSupportsSeccompBPF() {
@@ -125,7 +124,7 @@ SandboxBPF::~SandboxBPF() {
bool SandboxBPF::SupportsSeccompSandbox(SeccompLevel level) {
// Never pretend to support seccomp with Valgrind, as it
// throws the tool off.
- if (IsRunningOnValgrind()) {
+ if (RunningOnValgrind()) {
return false;
}
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 13176096cd0..5f59cbbcc65 100644
--- a/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
+++ b/chromium/sandbox/linux/seccomp-bpf/sandbox_bpf_test_runner.cc
@@ -9,6 +9,7 @@
#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"
@@ -45,7 +46,7 @@ void SandboxBPFTestRunner::Run() {
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() && !IsRunningOnValgrind()) {
+ if (!IsAndroid() && !RunningOnValgrind()) {
const bool seccomp_bpf_is_supported = false;
SANDBOX_ASSERT(seccomp_bpf_is_supported);
}
diff --git a/chromium/sandbox/linux/services/credentials.cc b/chromium/sandbox/linux/services/credentials.cc
index 0a0b6eb2b1a..d97f0946621 100644
--- a/chromium/sandbox/linux/services/credentials.cc
+++ b/chromium/sandbox/linux/services/credentials.cc
@@ -23,6 +23,7 @@
#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"
@@ -36,8 +37,6 @@ namespace sandbox {
namespace {
-bool IsRunningOnValgrind() { return RUNNING_ON_VALGRIND; }
-
const int kExitSuccess = 0;
#if defined(__clang__)
@@ -259,7 +258,7 @@ bool Credentials::HasCapability(Capability cap) {
bool Credentials::CanCreateProcessInNewUserNS() {
// Valgrind will let clone(2) pass-through, but doesn't support unshare(),
// so always consider UserNS unsupported there.
- if (IsRunningOnValgrind()) {
+ if (RunningOnValgrind()) {
return false;
}
diff --git a/chromium/sandbox/linux/services/namespace_utils.cc b/chromium/sandbox/linux/services/namespace_utils.cc
index 97add26f8ff..83749adf037 100644
--- a/chromium/sandbox/linux/services/namespace_utils.cc
+++ b/chromium/sandbox/linux/services/namespace_utils.cc
@@ -20,15 +20,12 @@
#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 {
namespace {
-bool IsRunningOnValgrind() {
- return RUNNING_ON_VALGRIND;
-}
-
const char kProcSelfSetgroups[] = "/proc/self/setgroups";
} // namespace
@@ -57,7 +54,7 @@ bool NamespaceUtils::WriteToIdMapFile(const char* map_file, generic_id_t id) {
bool NamespaceUtils::KernelSupportsUnprivilegedNamespace(int type) {
// Valgrind will let clone(2) pass-through, but doesn't support unshare(),
// so always consider namespaces unsupported there.
- if (IsRunningOnValgrind()) {
+ if (RunningOnValgrind()) {
return false;
}
diff --git a/chromium/sandbox/linux/services/thread_helpers_unittests.cc b/chromium/sandbox/linux/services/thread_helpers_unittests.cc
index fe1080bf9e1..78e6d837e20 100644
--- a/chromium/sandbox/linux/services/thread_helpers_unittests.cc
+++ b/chromium/sandbox/linux/services/thread_helpers_unittests.cc
@@ -14,6 +14,7 @@
#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,7 +31,7 @@ namespace {
#if !defined(THREAD_SANITIZER)
int GetRaceTestIterations() {
- if (IsRunningOnValgrind()) {
+ if (RunningOnValgrind()) {
return 2;
} else {
return 1000;
diff --git a/chromium/sandbox/linux/syscall_broker/DEPS b/chromium/sandbox/linux/syscall_broker/DEPS
index 70d9b18aa10..c477f7d3639 100644
--- a/chromium/sandbox/linux/syscall_broker/DEPS
+++ b/chromium/sandbox/linux/syscall_broker/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+sandbox/linux/system_headers",
+ "+sandbox/linux/bpf_dsl",
]
diff --git a/chromium/sandbox/linux/syscall_broker/broker_client.cc b/chromium/sandbox/linux/syscall_broker/broker_client.cc
index c16d39090d1..277e61a2c4f 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.cc
@@ -9,8 +9,7 @@
#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
+
#include <utility>
#include "base/logging.h"
@@ -141,6 +140,94 @@ int BrokerClient::Open(const char* pathname, int flags) const {
return PathAndFlagsSyscall(COMMAND_OPEN, pathname, flags);
}
-} // namespace syscall_broker
+int BrokerClient::Stat(const char* pathname, struct stat* sb) {
+ return StatFamilySyscall(COMMAND_STAT, pathname, sb, sizeof(*sb));
+}
+
+int BrokerClient::Stat64(const char* pathname, struct stat64* sb) {
+ 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 {
+ if (fast_check_in_client_ &&
+ !broker_policy_.GetFileNameIfAllowedToAccess(pathname, R_OK, nullptr)) {
+ return -broker_policy_.denied_errno();
+ }
+
+ 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 = base::UnixDomainSocket::SendRecvMsg(
+ ipc_channel_.get(), reply_buf, sizeof(reply_buf), &returned_fd,
+ write_pickle);
+
+ if (msg_len <= 0) {
+ if (!quiet_failures_for_tests_)
+ RAW_LOG(ERROR, "Could not make request to broker process");
+ return -ENOMEM;
+ }
+
+ 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();
+ }
+ }
+ 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 = base::UnixDomainSocket::SendRecvMsg(
+ ipc_channel_.get(), reply_buf, sizeof(reply_buf), &returned_fd,
+ write_pickle);
+
+ if (msg_len <= 0) {
+ if (!quiet_failures_for_tests_)
+ RAW_LOG(ERROR, "Could not make request to broker process");
+ return -ENOMEM;
+ }
+
+ 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;
+}
+
+} // 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 2dfef8150ca..9c818578235 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_client.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_client.h
@@ -5,6 +5,10 @@
#ifndef SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
#define SANDBOX_LINUX_SYSCALL_BROKER_BROKER_CLIENT_H_
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
#include "base/macros.h"
#include "sandbox/linux/syscall_broker/broker_channel.h"
#include "sandbox/linux/syscall_broker/broker_common.h"
@@ -42,6 +46,7 @@ class BrokerClient {
// 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 open().
// The implementation only supports certain white listed flags and will
// return -EPERM on other flags.
@@ -49,10 +54,30 @@ class BrokerClient {
// 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 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);
+
// Get the file descriptor used for IPC. This is used for tests.
int GetIPCDescriptor() const { return ipc_channel_.get(); }
private:
+ int PathAndFlagsSyscall(IPCCommand syscall_type,
+ const char* pathname,
+ int flags) const;
+
+ int StatFamilySyscall(IPCCommand syscall_type,
+ const char* pathname,
+ void* result_ptr,
+ size_t expected_result_size) const;
+
const BrokerPolicy& broker_policy_;
const BrokerChannel::EndPoint ipc_channel_;
const bool fast_check_in_client_; // Whether to forward a request that we
@@ -61,10 +86,6 @@ class BrokerClient {
const bool quiet_failures_for_tests_; // Disable certain error message when
// testing for failures.
- int PathAndFlagsSyscall(IPCCommand syscall_type,
- const char* pathname,
- int flags) const;
-
DISALLOW_COPY_AND_ASSIGN(BrokerClient);
};
diff --git a/chromium/sandbox/linux/syscall_broker/broker_common.h b/chromium/sandbox/linux/syscall_broker/broker_common.h
index 25aafa7ed2a..30b2ce19762 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_common.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_common.h
@@ -32,6 +32,9 @@ enum IPCCommand {
COMMAND_INVALID = 0,
COMMAND_OPEN,
COMMAND_ACCESS,
+ COMMAND_STAT,
+ COMMAND_STAT64,
+ COMMAND_RENAME,
};
} // namespace syscall_broker
diff --git a/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc b/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc
index 39073444467..3947145eaf6 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_file_permission.cc
@@ -14,7 +14,6 @@
#include "sandbox/linux/syscall_broker/broker_common.h"
namespace sandbox {
-
namespace syscall_broker {
// Async signal safe
@@ -52,25 +51,21 @@ bool BrokerFilePermission::ValidatePath(const char* path) {
// methods are async signal safe in common standard libs.
// TODO(leecam): remove dependency on std::string
bool BrokerFilePermission::MatchPath(const char* requested_filename) const {
- const char* path = path_.c_str();
- if ((recursive_ && strncmp(requested_filename, path, strlen(path)) == 0)) {
- // Note: This prefix match will allow any path under the whitelisted
- // path, for any number of directory levels. E.g. if the whitelisted
- // path is /good/ then the following will be permitted by the policy.
- // /good/file1
- // /good/folder/file2
- // /good/folder/folder2/file3
- // If an attacker could make 'folder' a symlink to ../../ they would have
- // access to the entire filesystem.
- // Whitelisting with multiple depths is useful, e.g /proc/ but
- // the system needs to ensure symlinks can not be created!
- // That said if an attacker can convert any of the absolute paths
- // to a symlink they can control any file on the system also.
- return true;
- } else if (strcmp(requested_filename, path) == 0) {
- return true;
- }
- return false;
+ // Note: This recursive match will allow any path under the whitelisted
+ // path, for any number of directory levels. E.g. if the whitelisted
+ // path is /good/ then the following will be permitted by the policy.
+ // /good/file1
+ // /good/folder/file2
+ // /good/folder/folder2/file3
+ // If an attacker could make 'folder' a symlink to ../../ they would have
+ // access to the entire filesystem.
+ // Whitelisting with multiple depths is useful, e.g /proc/ but
+ // the system needs to ensure symlinks can not be created!
+ // That said if an attacker can convert any of the absolute paths
+ // to a symlink they can control any file on the system also.
+ return recursive_
+ ? strncmp(requested_filename, path_.c_str(), path_.length()) == 0
+ : strcmp(requested_filename, path_.c_str()) == 0;
}
// Async signal safe.
@@ -82,45 +77,39 @@ bool BrokerFilePermission::CheckAccess(const char* requested_filename,
const char** file_to_access) const {
// First, check if |mode| is existence, ability to read or ability
// to write. We do not support X_OK.
- if (mode != F_OK && mode & ~(R_OK | W_OK)) {
+ if (mode != F_OK && mode & ~(R_OK | W_OK))
return false;
- }
if (!ValidatePath(requested_filename))
return false;
- if (!MatchPath(requested_filename)) {
+ if (!MatchPath(requested_filename))
return false;
- }
+
bool allowed = false;
switch (mode) {
case F_OK:
- if (allow_read_ || allow_write_)
- allowed = true;
+ allowed = allow_read_ || allow_write_;
break;
case R_OK:
- if (allow_read_)
- allowed = true;
+ allowed = allow_read_;
break;
case W_OK:
- if (allow_write_)
- allowed = true;
+ allowed = allow_write_;
break;
case R_OK | W_OK:
- if (allow_read_ && allow_write_)
- allowed = true;
+ allowed = allow_read_ && allow_write_;
break;
default:
- return false;
+ break;
}
+ if (!allowed)
+ return false;
- if (allowed && file_to_access) {
- if (!recursive_)
- *file_to_access = path_.c_str();
- else
- *file_to_access = requested_filename;
- }
- return allowed;
+ if (file_to_access)
+ *file_to_access = recursive_ ? requested_filename : path_.c_str();
+
+ return true;
}
// Async signal safe.
@@ -134,9 +123,8 @@ bool BrokerFilePermission::CheckOpen(const char* requested_filename,
if (!ValidatePath(requested_filename))
return false;
- if (!MatchPath(requested_filename)) {
+ if (!MatchPath(requested_filename))
return false;
- }
// First, check the access mode is valid.
const int access_mode = flags & O_ACCMODE;
@@ -165,8 +153,8 @@ bool BrokerFilePermission::CheckOpen(const char* requested_filename,
return false;
}
- // If this file is to be unlinked, ensure it's created.
- if (unlink_ && !(flags & O_CREAT)) {
+ // If this file is to be temporary, ensure it's created.
+ if (temporary_only_ && !(flags & O_CREAT)) {
return false;
}
@@ -178,7 +166,6 @@ bool BrokerFilePermission::CheckOpen(const char* requested_filename,
// Now check that all the flags are known to us.
const int creation_and_status_flags = flags & ~O_ACCMODE;
-
const int known_flags = O_APPEND | O_ASYNC | O_CLOEXEC | O_CREAT | O_DIRECT |
O_DIRECTORY | O_EXCL | O_LARGEFILE | O_NOATIME |
O_NOCTTY | O_NOFOLLOW | O_NONBLOCK | O_NDELAY |
@@ -190,55 +177,49 @@ bool BrokerFilePermission::CheckOpen(const char* requested_filename,
if (has_unknown_flags)
return false;
- if (file_to_open) {
- if (!recursive_)
- *file_to_open = path_.c_str();
- else
- *file_to_open = requested_filename;
- }
+ if (file_to_open)
+ *file_to_open = recursive_ ? requested_filename : path_.c_str();
+
if (unlink_after_open)
- *unlink_after_open = unlink_;
+ *unlink_after_open = temporary_only_;
return true;
}
const char* BrokerFilePermission::GetErrorMessageForTests() {
- static char kInvalidBrokerFileString[] = "Invalid BrokerFilePermission";
- return kInvalidBrokerFileString;
+ return "Invalid BrokerFilePermission";
}
BrokerFilePermission::BrokerFilePermission(const std::string& path,
bool recursive,
- bool unlink,
+ bool temporary_only,
bool allow_read,
bool allow_write,
bool allow_create)
: path_(path),
recursive_(recursive),
- unlink_(unlink),
+ temporary_only_(temporary_only),
allow_read_(allow_read),
allow_write_(allow_write),
allow_create_(allow_create) {
- // Validate this permission and die if invalid!
-
// Must have enough length for a '/'
CHECK(path_.length() > 0) << GetErrorMessageForTests();
+
// Whitelisted paths must be absolute.
CHECK(path_[0] == '/') << GetErrorMessageForTests();
- // Don't allow unlinking on creation without create permission
- if (unlink_) {
+ // Don't allow temporary creation without create permission
+ if (temporary_only_)
CHECK(allow_create) << GetErrorMessageForTests();
- }
+
+ // Recursive paths must have a trailing slash, absolutes must not.
const char last_char = *(path_.rbegin());
- // Recursive paths must have a trailing slash
- if (recursive_) {
+ if (recursive_)
CHECK(last_char == '/') << GetErrorMessageForTests();
- } else {
+ else
CHECK(last_char != '/') << GetErrorMessageForTests();
- }
}
} // namespace syscall_broker
-} // namespace sandbox \ No newline at end of file
+} // 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 ddc62d56291..b39ac6ca1b9 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_file_permission.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_file_permission.h
@@ -11,7 +11,6 @@
#include "sandbox/sandbox_export.h"
namespace sandbox {
-
namespace syscall_broker {
// BrokerFilePermission defines a path for whitelisting.
@@ -45,11 +44,12 @@ class SANDBOX_EXPORT BrokerFilePermission {
return BrokerFilePermission(path, false, false, true, true, true);
}
- static BrokerFilePermission ReadWriteCreateUnlink(const std::string& path) {
- return BrokerFilePermission(path, false, true, true, true, true);
+ static BrokerFilePermission ReadWriteCreateRecursive(
+ const std::string& path) {
+ return BrokerFilePermission(path, true, false, true, true, true);
}
- static BrokerFilePermission ReadWriteCreateUnlinkRecursive(
+ static BrokerFilePermission ReadWriteCreateTemporaryRecursive(
const std::string& path) {
return BrokerFilePermission(path, true, true, true, true, true);
}
@@ -60,16 +60,17 @@ class SANDBOX_EXPORT BrokerFilePermission {
// the |requested_filename| in the case of a recursive match,
// or a pointer the matched path in the whitelist if an absolute
// match.
- // If not NULL |unlink_after_open| is set to point to true if the
- // caller should unlink the path after opening.
+ // If not NULL, |unlink_after_open| is set to point to true if the
+ // caller is required to unlink the path after opening.
// Async signal safe if |file_to_open| is NULL.
bool CheckOpen(const char* requested_filename,
int flags,
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).
- // If |file_to_open| is not NULL it is set to point to either
+ // 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.
@@ -81,9 +82,11 @@ class SANDBOX_EXPORT BrokerFilePermission {
private:
friend class BrokerFilePermissionTester;
+
+ // NOTE: Validates the permission and dies if invalid!
BrokerFilePermission(const std::string& path,
bool recursive,
- bool unlink,
+ bool temporary_only,
bool allow_read,
bool allow_write,
bool allow_create);
@@ -102,18 +105,17 @@ class SANDBOX_EXPORT BrokerFilePermission {
static const char* GetErrorMessageForTests();
// These are not const as std::vector requires copy-assignment and this class
- // is stored in vectors. All methods are marked const so
- // the compiler will still enforce no changes outside of the constructor.
+ // is stored in vectors. All methods are marked const so the compiler will
+ // still enforce no changes outside of the constructor.
std::string path_;
- bool recursive_; // Allow everything under this path. |path| must be a dir.
- bool unlink_; // unlink after opening.
+ bool recursive_; // Allow everything under |path| (must be a dir).
+ bool temporary_only_; // File must be unlink'd after opening.
bool allow_read_;
bool allow_write_;
bool allow_create_;
};
} // namespace syscall_broker
-
} // namespace sandbox
-#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_ \ No newline at end of file
+#endif // SANDBOX_LINUX_SYSCALL_BROKER_BROKER_FILE_PERMISSION_H_
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 83840779f96..dc56b4cd417 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_file_permission_unittest.cc
@@ -231,20 +231,11 @@ void CheckUnlink(BrokerFilePermission& perm,
ASSERT_TRUE(unlink);
}
-TEST(BrokerFilePermission, ReadWriteCreateUnlink) {
- const char kPath[] = "/tmp/good";
- BrokerFilePermission perm =
- BrokerFilePermission::ReadWriteCreateUnlink(kPath);
- CheckUnlink(perm, kPath, O_RDWR);
- // Don't do anything here, so that ASSERT works in the subfunction as
- // expected.
-}
-
-TEST(BrokerFilePermission, ReadWriteCreateUnlinkRecursive) {
+TEST(BrokerFilePermission, ReadWriteCreateTemporaryRecursive) {
const char kPath[] = "/tmp/good/";
const char kPathFile[] = "/tmp/good/file";
BrokerFilePermission perm =
- BrokerFilePermission::ReadWriteCreateUnlinkRecursive(kPath);
+ BrokerFilePermission::ReadWriteCreateTemporaryRecursive(kPath);
CheckUnlink(perm, kPathFile, O_RDWR);
// Don't do anything here, so that ASSERT works in the subfunction as
// expected.
diff --git a/chromium/sandbox/linux/syscall_broker/broker_host.cc b/chromium/sandbox/linux/syscall_broker/broker_host.cc
index 6e1daccdb95..375747e19f2 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_host.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_host.cc
@@ -22,6 +22,7 @@
#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"
@@ -33,27 +34,17 @@ namespace syscall_broker {
namespace {
-bool IsRunningOnValgrind() {
- return RUNNING_ON_VALGRIND;
-}
-
// A little open(2) wrapper to handle some oddities for us. In the general case
// make a direct system call since we want to keep in control of the broker
// process' system calls profile to be able to loosely sandbox it.
int sys_open(const char* pathname, int flags) {
// Hardcode mode to rw------- when creating files.
- int mode;
- if (flags & O_CREAT) {
- mode = 0600;
- } else {
- mode = 0;
- }
- if (IsRunningOnValgrind()) {
+ 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);
- } else {
- return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
}
+ return syscall(__NR_openat, AT_FDCWD, pathname, flags, mode);
}
// Open |requested_filename| with |flags| if allowed by our policy.
@@ -113,50 +104,111 @@ void AccessFileForIPC(const BrokerPolicy& policy,
}
}
-// Handle a |command_type| request contained in |iter| and send the reply
-// on |reply_ipc|.
-// Currently COMMAND_OPEN and COMMAND_ACCESS are supported.
+// Perform stat(2) on |requested_filename| and marshal the result to
+// |write_pickle|.
+void StatFileForIPC(const BrokerPolicy& policy,
+ IPCCommand 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());
+ return;
+ }
+ if (command_type == COMMAND_STAT) {
+ struct stat sb;
+ if (stat(file_to_access, &sb) < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
+ }
+ write_pickle->WriteInt(0);
+ write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb));
+ } else {
+ struct stat64 sb;
+ if (stat64(file_to_access, &sb) < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
+ }
+ write_pickle->WriteInt(0);
+ write_pickle->WriteData(reinterpret_cast<char*>(&sb), sizeof(sb));
+ }
+}
+
+// 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,
+ 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());
+ return;
+ }
+ if (rename(old_file_to_access, new_file_to_access) < 0) {
+ write_pickle->WriteInt(-errno);
+ return;
+ }
+ write_pickle->WriteInt(0);
+}
+
+// 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,
- IPCCommand command_type,
- int reply_ipc,
- base::PickleIterator iter) {
- // Currently all commands have two arguments: filename and flags.
- std::string requested_filename;
- int flags = 0;
- if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
+ base::PickleIterator iter,
+ base::Pickle* write_pickle,
+ std::vector<int>* opened_files) {
+ int command_type;
+ if (!iter.ReadInt(&command_type))
return false;
- base::Pickle write_pickle;
- std::vector<int> opened_files;
-
switch (command_type) {
- case COMMAND_ACCESS:
- AccessFileForIPC(policy, requested_filename, flags, &write_pickle);
+ case COMMAND_ACCESS: {
+ std::string requested_filename;
+ int flags = 0;
+ if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
+ return false;
+ AccessFileForIPC(policy, requested_filename, flags, write_pickle);
+ break;
+ }
+ case COMMAND_OPEN: {
+ std::string requested_filename;
+ int flags = 0;
+ if (!iter.ReadString(&requested_filename) || !iter.ReadInt(&flags))
+ return false;
+ OpenFileForIPC(policy, requested_filename, flags, write_pickle,
+ opened_files);
break;
- case COMMAND_OPEN:
- OpenFileForIPC(
- policy, requested_filename, flags, &write_pickle, &opened_files);
+ }
+ case COMMAND_STAT:
+ case COMMAND_STAT64: {
+ std::string requested_filename;
+ if (!iter.ReadString(&requested_filename))
+ return false;
+ StatFileForIPC(policy, static_cast<IPCCommand>(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))
+ return false;
+ RenameFileForIPC(policy, old_filename, new_filename, write_pickle);
break;
+ }
default:
LOG(ERROR) << "Invalid IPC command";
- break;
- }
-
- CHECK_LE(write_pickle.size(), kMaxMessageLength);
- ssize_t sent = base::UnixDomainSocket::SendMsg(
- reply_ipc, write_pickle.data(), write_pickle.size(), opened_files);
-
- // Close anything we have opened in this process.
- for (std::vector<int>::iterator it = opened_files.begin();
- it != opened_files.end();
- ++it) {
- int ret = IGNORE_EINTR(close(*it));
- DCHECK(!ret) << "Could not close file descriptor";
- }
-
- if (sent <= 0) {
- LOG(ERROR) << "Could not send IPC reply";
- return false;
+ return false;
}
return true;
}
@@ -197,34 +249,29 @@ BrokerHost::RequestStatus BrokerHost::HandleRequest() const {
base::Pickle pickle(buf, msg_len);
base::PickleIterator iter(pickle);
- int command_type;
- if (iter.ReadInt(&command_type)) {
- bool command_handled = false;
- // Go through all the possible IPC messages.
- switch (command_type) {
- case COMMAND_ACCESS:
- case COMMAND_OPEN:
- // We reply on the file descriptor sent to us via the IPC channel.
- command_handled = HandleRemoteCommand(
- broker_policy_, static_cast<IPCCommand>(command_type),
- temporary_ipc.get(), iter);
- break;
- default:
- NOTREACHED();
- break;
- }
-
- if (command_handled) {
- return RequestStatus::SUCCESS;
- } else {
- return RequestStatus::FAILURE;
+ base::Pickle write_pickle;
+ std::vector<int> opened_files;
+ bool result =
+ HandleRemoteCommand(broker_policy_, iter, &write_pickle, &opened_files);
+
+ if (result) {
+ CHECK_LE(write_pickle.size(), kMaxMessageLength);
+ ssize_t sent = base::UnixDomainSocket::SendMsg(
+ temporary_ipc.get(), write_pickle.data(), write_pickle.size(),
+ opened_files);
+ if (sent <= 0) {
+ LOG(ERROR) << "Could not send IPC reply";
+ result = false;
}
+ }
- NOTREACHED();
+ // Close anything we have opened in this process.
+ for (int fd : opened_files) {
+ int ret = IGNORE_EINTR(close(fd));
+ DCHECK(!ret) << "Could not close file descriptor";
}
- LOG(ERROR) << "Error parsing IPC request";
- return RequestStatus::FAILURE;
+ return result ? RequestStatus::SUCCESS : RequestStatus::FAILURE;
}
} // namespace syscall_broker
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.cc b/chromium/sandbox/linux/syscall_broker/broker_process.cc
index 30713cedcc2..46e207ede34 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_process.cc
@@ -39,8 +39,7 @@ BrokerProcess::BrokerProcess(
fast_check_in_client_(fast_check_in_client),
quiet_failures_for_tests_(quiet_failures_for_tests),
broker_pid_(-1),
- policy_(denied_errno, permissions) {
-}
+ broker_policy_(denied_errno, permissions) {}
BrokerProcess::~BrokerProcess() {
if (initialized_) {
@@ -68,35 +67,35 @@ bool BrokerProcess::Init(
DCHECK_EQ(1, base::GetNumberOfThreads(base::GetCurrentProcessHandle()));
#endif
int child_pid = fork();
- if (child_pid == -1) {
+ if (child_pid == -1)
return false;
- }
+
if (child_pid) {
// We are the parent and we have just forked our broker process.
ipc_reader.reset();
broker_pid_ = child_pid;
- broker_client_.reset(new BrokerClient(policy_, std::move(ipc_writer),
+ broker_client_.reset(new BrokerClient(broker_policy_, std::move(ipc_writer),
fast_check_in_client_,
quiet_failures_for_tests_));
initialized_ = true;
return true;
- } else {
- // We are the broker process. Make sure to close the writer's end so that
- // we get notified if the client disappears.
- ipc_writer.reset();
- CHECK(broker_process_init_callback.Run());
- BrokerHost broker_host(policy_, std::move(ipc_reader));
- for (;;) {
- switch (broker_host.HandleRequest()) {
- case BrokerHost::RequestStatus::LOST_CLIENT:
- _exit(1);
- case BrokerHost::RequestStatus::SUCCESS:
- case BrokerHost::RequestStatus::FAILURE:
- continue;
- }
+ }
+
+ // We are the broker process. Make sure to close the writer's end so that
+ // 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));
+ for (;;) {
+ switch (broker_host.HandleRequest()) {
+ case BrokerHost::RequestStatus::LOST_CLIENT:
+ _exit(1);
+ case BrokerHost::RequestStatus::SUCCESS:
+ case BrokerHost::RequestStatus::FAILURE:
+ continue;
}
- _exit(1);
}
+ _exit(1);
NOTREACHED();
return false;
}
@@ -115,6 +114,121 @@ int BrokerProcess::Open(const char* pathname, int flags) const {
return broker_client_->Open(pathname, flags);
}
-} // namespace syscall_broker
+int BrokerProcess::Stat(const char* pathname, struct stat* sb) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Stat(pathname, sb);
+}
+
+int BrokerProcess::Stat64(const char* pathname, struct stat64* sb) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Stat64(pathname, sb);
+}
+
+int BrokerProcess::Rename(const char* oldpath, const char* newpath) const {
+ RAW_CHECK(initialized_);
+ return broker_client_->Rename(oldpath, newpath);
+}
+
+#if defined(MEMORY_SANITIZER)
+#define BROKER_UNPOISON_STRING(x) __msan_unpoison_string(x)
+#else
+#define BROKER_UNPOISON_STRING(x)
+#endif
-} // namespace sandbox.
+// static
+intptr_t BrokerProcess::SIGSYS_Handler(const sandbox::arch_seccomp_data& args,
+ void* aux_broker_process) {
+ RAW_CHECK(aux_broker_process);
+ auto* broker_process = static_cast<BrokerProcess*>(aux_broker_process);
+ switch (args.nr) {
+#if defined(__NR_access)
+ case __NR_access:
+ 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)
+ return -EPERM;
+ return broker_process->Access(reinterpret_cast<const char*>(args.args[1]),
+ static_cast<int>(args.args[2]));
+#endif
+#if defined(__NR_openat)
+ case __NR_openat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD)
+ return -EPERM;
+ 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]));
+#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_rename)
+ case __NR_rename:
+ return broker_process->Rename(
+ reinterpret_cast<const char*>(args.args[0]),
+ reinterpret_cast<const char*>(args.args[1]));
+#endif
+#if defined(__NR_renameat)
+ case __NR_renameat:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD ||
+ static_cast<int>(args.args[2]) != AT_FDCWD) {
+ return -EPERM;
+ }
+ return broker_process->Rename(
+ reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<const char*>(args.args[3]));
+#endif
+#if defined(__NR_renameat2)
+ case __NR_renameat2:
+ if (static_cast<int>(args.args[0]) != AT_FDCWD ||
+ static_cast<int>(args.args[2]) != AT_FDCWD) {
+ return -EPERM;
+ }
+ if (static_cast<int>(args.args[4]) != 0)
+ return -EINVAL;
+ return broker_process->Rename(
+ reinterpret_cast<const char*>(args.args[1]),
+ reinterpret_cast<const char*>(args.args[3]));
+#endif
+ default:
+ RAW_CHECK(false);
+ return -ENOSYS;
+ }
+}
+
+} // namespace syscall_broker
+} // namespace sandbox
diff --git a/chromium/sandbox/linux/syscall_broker/broker_process.h b/chromium/sandbox/linux/syscall_broker/broker_process.h
index 3c0c8090cf6..efa84bb8342 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process.h
+++ b/chromium/sandbox/linux/syscall_broker/broker_process.h
@@ -13,6 +13,7 @@
#include "base/macros.h"
#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/sandbox_export.h"
@@ -36,14 +37,17 @@ class BrokerFilePermission;
class SANDBOX_EXPORT BrokerProcess {
public:
// |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.
- // |allowed_r_files| and |allowed_w_files| are white lists of files that can
- // be opened later via the Open() API, respectively for reading and writing.
- // A file available read-write should be listed in both.
- // |fast_check_in_client| and |quiet_failures_for_tests| are reserved for
- // unit tests, don't use it.
-
+ // or Access() are invoked on a file which is not in the whitelist (EACCESS
+ // would be a typical value). |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
+ // our main clients are not always well-behaved. They may have third party
+ // libraries that don't know about sandboxing, and typically try to open all
+ // sorts of stuff they don't really need. It's important to reduce this load
+ // given that there is only one pipeline to the broker process, and it is
+ // not multi-threaded. |quiet_failures_for_tests| is reserved for unit tests,
+ // don't use it.
BrokerProcess(
int denied_errno,
const std::vector<syscall_broker::BrokerFilePermission>& permissions,
@@ -51,6 +55,7 @@ class SANDBOX_EXPORT BrokerProcess {
bool quiet_failures_for_tests = false);
~BrokerProcess();
+
// Will initialize the broker process. There should be no threads at this
// point, since we need to fork().
// broker_process_init_callback will be called in the new broker process,
@@ -62,14 +67,29 @@ class SANDBOX_EXPORT BrokerProcess {
// 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.
// 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 rename(). Will be async signal safe.
+ // It's similar to the rename() system call and will return -errno on errors.
+ int Rename(const char* oldpath, const char* newpath) const;
+
int broker_pid() const { return broker_pid_; }
+ // 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);
+
private:
friend class BrokerProcessTestHelper;
@@ -80,8 +100,8 @@ class SANDBOX_EXPORT BrokerProcess {
bool initialized_; // Whether we've been through Init() yet.
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 policy_; // The sandboxing policy.
+ pid_t broker_pid_; // The PID of the broker (child).
+ syscall_broker::BrokerPolicy broker_policy_; // Access policy to enforce.
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 0d61ab7703d..a53600337a1 100644
--- a/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
+++ b/chromium/sandbox/linux/syscall_broker/broker_process_unittest.cc
@@ -661,6 +661,169 @@ TEST(BrokerProcess, CreateFile) {
}
}
-} // namespace syscall_broker
+TEST(BrokerProcess, StatFile) {
+ 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;
+ struct stat sb;
+ {
+ // 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)));
+
+ memset(&sb, 0, sizeof(sb));
+ EXPECT_EQ(-EPERM, 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)));
+
+ memset(&sb, 0, sizeof(sb));
+ EXPECT_EQ(-EPERM, 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)));
+
+ memset(&sb, 0, sizeof(sb));
+ EXPECT_EQ(-ENOENT, open_broker.Stat(nonesuch_name, &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)));
+
+ memset(&sb, 0, sizeof(sb));
+ EXPECT_EQ(0, open_broker.Stat(tempfile_name, &sb));
+
+ // Following fields may never be consistent but should be non-zero.
+ // Don't trust the platform to define fields with any particular sign.
+ 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));
+
+ // Wrote 12 bytes above which should fit in one block.
+ EXPECT_EQ(12, sb.st_size);
+
+ // Can't go backwards in time, 1500000000 was some time ago.
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_atime));
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_mtime));
+ EXPECT_LT(1500000000u, static_cast<unsigned int>(sb.st_ctime));
+ }
+}
+
+TEST(BrokerProcess, RenameFile) {
+ 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 file using old path name.
+ int fd = open(oldpath.c_str(), O_RDWR | O_CREAT, 0600);
+ EXPECT_TRUE(fd > 0);
+ close(fd);
+
+ EXPECT_TRUE(access(oldpath.c_str(), F_OK) == 0);
+ EXPECT_TRUE(access(newpath.c_str(), F_OK) < 0);
+
+ {
+ // Check rename fails when no permission to new file.
+ std::vector<BrokerFilePermission> permissions;
+ permissions.push_back(BrokerFilePermission::ReadWrite(oldpath));
+
+ 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);
+ }
+ {
+ // 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()));
+
+ // ... 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::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()));
+
+ // ... 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()));
+
+ // ... 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 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)));
+ EXPECT_EQ(0, open_broker.Rename(oldpath.c_str(), newpath.c_str()));
+
+ // ... and files were moved around.
+ EXPECT_TRUE(access(oldpath.c_str(), F_OK) < 0);
+ EXPECT_TRUE(access(newpath.c_str(), F_OK) == 0);
+ }
+
+ // Cleanup using new path name.
+ unlink(newpath.c_str());
+}
+
+} // namespace syscall_broker
} // namespace sandbox
diff --git a/chromium/sandbox/mac/seatbelt_exec.cc b/chromium/sandbox/mac/seatbelt_exec.cc
index 52af21e1b29..53a5e7901ae 100644
--- a/chromium/sandbox/mac/seatbelt_exec.cc
+++ b/chromium/sandbox/mac/seatbelt_exec.cc
@@ -4,6 +4,7 @@
#include "sandbox/mac/seatbelt_exec.h"
+#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/socket.h>
@@ -22,6 +23,13 @@ namespace sandbox {
SeatbeltExecClient::SeatbeltExecClient() {
if (pipe(pipe_) != 0)
logging::PFatal("SeatbeltExecClient: pipe failed");
+
+ int pipe_flags = fcntl(pipe_[1], F_GETFL);
+ if (pipe_flags == -1)
+ logging::PFatal("SeatbeltExecClient: fctnl(F_GETFL) failed");
+
+ if (fcntl(pipe_[1], F_SETFL, pipe_flags | O_NONBLOCK) == -1)
+ logging::PFatal("SeatbeltExecClient: fcntl(F_SETFL) failed");
}
SeatbeltExecClient::~SeatbeltExecClient() {
diff --git a/chromium/sandbox/win/BUILD.gn b/chromium/sandbox/win/BUILD.gn
index 22e915dcde8..48f4b1e76a0 100644
--- a/chromium/sandbox/win/BUILD.gn
+++ b/chromium/sandbox/win/BUILD.gn
@@ -12,6 +12,8 @@ static_library("sandbox") {
sources = [
"src/acl.cc",
"src/acl.h",
+ "src/app_container_profile.cc",
+ "src/app_container_profile.h",
"src/broker_services.cc",
"src/broker_services.h",
"src/crosscall_client.h",
@@ -104,6 +106,8 @@ static_library("sandbox") {
"src/sandbox_types.h",
"src/sandbox_utils.cc",
"src/sandbox_utils.h",
+ "src/security_capabilities.cc",
+ "src/security_capabilities.h",
"src/security_level.h",
"src/service_resolver.cc",
"src/service_resolver.h",
@@ -293,6 +297,7 @@ test("sbox_validation_tests") {
test("sbox_unittests") {
sources = [
+ "src/app_container_unittest.cc",
"src/interception_unittest.cc",
"src/ipc_unittest.cc",
"src/job_unittest.cc",
@@ -324,7 +329,6 @@ test("sandbox_poc") {
"sandbox_poc/resource.h",
"sandbox_poc/sandbox.cc",
"sandbox_poc/sandbox.h",
- "sandbox_poc/sandbox.ico",
"sandbox_poc/sandbox.rc",
]
diff --git a/chromium/sandbox/win/PRESUBMIT.py b/chromium/sandbox/win/PRESUBMIT.py
deleted file mode 100644
index 65df23d71c8..00000000000
--- a/chromium/sandbox/win/PRESUBMIT.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2016 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.
-
-"""Presubmit script for sandbox/win.
-
-See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
-for more details about the presubmit API built into depot_tools.
-"""
-
-def PostUploadHook(cl, change, output_api):
- """git cl upload will call this hook after the issue is created/modified.
-
- This hook adds extra try bots list to the CL description in order to run
- tests on the Windows 10 try bot in addition to CQ try bots.
- """
- return output_api.EnsureCQIncludeTrybotsAreAdded(
- cl,
- [
- 'master.tryserver.chromium.win:win10_chromium_x64_rel_ng',
- ],
- 'Automatically added Win10 bot to run on CQ.')
diff --git a/chromium/sandbox/win/src/app_container_profile.cc b/chromium/sandbox/win/src/app_container_profile.cc
new file mode 100644
index 00000000000..b0cb041e72d
--- /dev/null
+++ b/chromium/sandbox/win/src/app_container_profile.cc
@@ -0,0 +1,324 @@
+// 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 <memory>
+
+#include <aclapi.h>
+#include <userenv.h>
+
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_co_mem.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+typedef decltype(::CreateAppContainerProfile) CreateAppContainerProfileFunc;
+
+typedef decltype(::DeriveAppContainerSidFromAppContainerName)
+ DeriveAppContainerSidFromAppContainerNameFunc;
+
+typedef decltype(::DeleteAppContainerProfile) DeleteAppContainerProfileFunc;
+
+typedef decltype(::GetAppContainerFolderPath) GetAppContainerFolderPathFunc;
+
+typedef decltype(
+ ::GetAppContainerRegistryLocation) GetAppContainerRegistryLocationFunc;
+
+struct FreeSidDeleter {
+ inline void operator()(void* ptr) const { ::FreeSid(ptr); }
+};
+
+bool IsValidObjectType(SE_OBJECT_TYPE object_type) {
+ switch (object_type) {
+ case SE_FILE_OBJECT:
+ case SE_REGISTRY_KEY:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool GetGenericMappingForType(SE_OBJECT_TYPE object_type,
+ GENERIC_MAPPING* generic_mapping) {
+ if (!IsValidObjectType(object_type))
+ return false;
+ if (object_type == SE_FILE_OBJECT) {
+ generic_mapping->GenericRead = FILE_GENERIC_READ;
+ generic_mapping->GenericWrite = FILE_GENERIC_WRITE;
+ generic_mapping->GenericExecute = FILE_GENERIC_EXECUTE;
+ generic_mapping->GenericAll = FILE_ALL_ACCESS;
+ } else {
+ generic_mapping->GenericRead = KEY_READ;
+ generic_mapping->GenericWrite = KEY_WRITE;
+ generic_mapping->GenericExecute = KEY_EXECUTE;
+ generic_mapping->GenericAll = KEY_ALL_ACCESS;
+ }
+ return true;
+}
+
+class ScopedImpersonation {
+ public:
+ ScopedImpersonation(const base::win::ScopedHandle& token) {
+ BOOL result = ::ImpersonateLoggedOnUser(token.Get());
+ DCHECK(result);
+ }
+
+ ~ScopedImpersonation() {
+ BOOL result = ::RevertToSelf();
+ DCHECK(result);
+ }
+};
+
+} // namespace
+
+// static
+scoped_refptr<AppContainerProfile> AppContainerProfile::Create(
+ const wchar_t* package_name,
+ const wchar_t* display_name,
+ const wchar_t* description) {
+ static auto create_app_container_profile =
+ reinterpret_cast<CreateAppContainerProfileFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "CreateAppContainerProfile"));
+ if (!create_app_container_profile)
+ return nullptr;
+
+ PSID package_sid = nullptr;
+ HRESULT hr = create_app_container_profile(
+ package_name, display_name, description, nullptr, 0, &package_sid);
+ if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
+ return Open(package_name);
+
+ if (FAILED(hr))
+ return nullptr;
+ std::unique_ptr<void, FreeSidDeleter> sid_deleter(package_sid);
+ return new AppContainerProfile(Sid(package_sid));
+}
+
+// static
+scoped_refptr<AppContainerProfile> AppContainerProfile::Open(
+ const wchar_t* package_name) {
+ static auto derive_app_container_sid =
+ reinterpret_cast<DeriveAppContainerSidFromAppContainerNameFunc*>(
+ GetProcAddress(GetModuleHandle(L"userenv"),
+ "DeriveAppContainerSidFromAppContainerName"));
+ if (!derive_app_container_sid)
+ return nullptr;
+
+ PSID package_sid = nullptr;
+ HRESULT hr = derive_app_container_sid(package_name, &package_sid);
+ if (FAILED(hr))
+ return nullptr;
+
+ std::unique_ptr<void, FreeSidDeleter> sid_deleter(package_sid);
+ return new AppContainerProfile(Sid(package_sid));
+}
+
+// static
+bool AppContainerProfile::Delete(const wchar_t* package_name) {
+ static auto delete_app_container_profile =
+ reinterpret_cast<DeleteAppContainerProfileFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "DeleteAppContainerProfile"));
+ if (!delete_app_container_profile)
+ return false;
+
+ return SUCCEEDED(delete_app_container_profile(package_name));
+}
+
+AppContainerProfile::AppContainerProfile(const Sid& package_sid)
+ : ref_count_(0),
+ package_sid_(package_sid),
+ enable_low_privilege_app_container_(false) {}
+
+AppContainerProfile::~AppContainerProfile() {}
+
+void AppContainerProfile::AddRef() {
+ ::InterlockedIncrement(&ref_count_);
+}
+
+void AppContainerProfile::Release() {
+ LONG ref_count = ::InterlockedDecrement(&ref_count_);
+ if (ref_count == 0) {
+ delete this;
+ }
+}
+
+bool AppContainerProfile::GetRegistryLocation(REGSAM desired_access,
+ base::win::ScopedHandle* key) {
+ static GetAppContainerRegistryLocationFunc*
+ get_app_container_registry_location =
+ reinterpret_cast<GetAppContainerRegistryLocationFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "GetAppContainerRegistryLocation"));
+ if (!get_app_container_registry_location)
+ return false;
+
+ base::win::ScopedHandle token;
+ if (!BuildLowBoxToken(&token))
+ return false;
+
+ ScopedImpersonation impersonation(token);
+ HKEY key_handle;
+ if (FAILED(get_app_container_registry_location(desired_access, &key_handle)))
+ return false;
+ key->Set(key_handle);
+ return true;
+}
+
+bool AppContainerProfile::GetFolderPath(base::FilePath* file_path) {
+ static GetAppContainerFolderPathFunc* get_app_container_folder_path =
+ reinterpret_cast<GetAppContainerFolderPathFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "GetAppContainerFolderPath"));
+ if (!get_app_container_folder_path)
+ return false;
+ base::string16 sddl_str;
+ if (!package_sid_.ToSddlString(&sddl_str))
+ return false;
+ base::win::ScopedCoMem<wchar_t> path_str;
+ if (FAILED(get_app_container_folder_path(sddl_str.c_str(), &path_str)))
+ return false;
+ *file_path = base::FilePath(path_str.get());
+ return true;
+}
+
+bool AppContainerProfile::GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) {
+ base::string16 sddl_str;
+ if (!package_sid_.ToSddlString(&sddl_str))
+ return false;
+ *pipe_path = base::FilePath(base::StringPrintf(L"\\\\.\\pipe\\%ls\\%ls",
+ sddl_str.c_str(), pipe_name));
+ return true;
+}
+
+bool AppContainerProfile::AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status) {
+ GENERIC_MAPPING generic_mapping;
+ if (!GetGenericMappingForType(object_type, &generic_mapping))
+ return false;
+ PSECURITY_DESCRIPTOR sd = nullptr;
+ PACL dacl = nullptr;
+ if (GetNamedSecurityInfo(
+ object_name, object_type,
+ OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION |
+ DACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION,
+ nullptr, nullptr, &dacl, nullptr, &sd) != ERROR_SUCCESS) {
+ return false;
+ }
+
+ std::unique_ptr<void, LocalFreeDeleter> sd_ptr(sd);
+
+ if (enable_low_privilege_app_container_) {
+ Sid any_package_sid(::WinBuiltinAnyPackageSid);
+ // We can't create a LPAC token directly, so modify the DACL to simulate it.
+ // Set mask for ALL APPLICATION PACKAGE Sid to 0.
+ for (WORD index = 0; index < dacl->AceCount; ++index) {
+ PVOID temp_ace;
+ if (!GetAce(dacl, index, &temp_ace))
+ return false;
+ PACE_HEADER header = static_cast<PACE_HEADER>(temp_ace);
+ if ((header->AceType != ACCESS_ALLOWED_ACE_TYPE) &&
+ (header->AceType != ACCESS_DENIED_ACE_TYPE)) {
+ continue;
+ }
+ // Allowed and deny aces have the same underlying structure.
+ PACCESS_ALLOWED_ACE ace = static_cast<PACCESS_ALLOWED_ACE>(temp_ace);
+ if (!::IsValidSid(&ace->SidStart)) {
+ continue;
+ }
+ if (::EqualSid(&ace->SidStart, any_package_sid.GetPSID())) {
+ ace->Mask = 0;
+ }
+ }
+ }
+
+ PRIVILEGE_SET priv_set = {};
+ DWORD priv_set_length = sizeof(PRIVILEGE_SET);
+
+ base::win::ScopedHandle token;
+ if (!BuildLowBoxToken(&token))
+ return false;
+
+ return !!::AccessCheck(sd, token.Get(), desired_access, &generic_mapping,
+ &priv_set, &priv_set_length, granted_access,
+ access_status);
+}
+
+bool AppContainerProfile::AddCapability(const wchar_t* capability_name) {
+ return AddCapability(Sid::FromNamedCapability(capability_name), false);
+}
+
+bool AppContainerProfile::AddCapability(WellKnownCapabilities capability) {
+ return AddCapability(Sid::FromKnownCapability(capability), false);
+}
+
+bool AppContainerProfile::AddCapability(const Sid& capability_sid) {
+ return AddCapability(capability_sid, false);
+}
+
+bool AppContainerProfile::AddCapability(const Sid& capability_sid,
+ bool impersonation_only) {
+ if (!capability_sid.IsValid())
+ return false;
+ if (!impersonation_only)
+ capabilities_.push_back(capability_sid);
+ impersonation_capabilities_.push_back(capability_sid);
+ return true;
+}
+
+bool AppContainerProfile::AddImpersonationCapability(
+ const wchar_t* capability_name) {
+ return AddCapability(Sid::FromNamedCapability(capability_name), true);
+}
+
+bool AppContainerProfile::AddImpersonationCapability(
+ WellKnownCapabilities capability) {
+ return AddCapability(Sid::FromKnownCapability(capability), true);
+}
+
+bool AppContainerProfile::AddImpersonationCapability(
+ const Sid& capability_sid) {
+ return AddCapability(capability_sid, true);
+}
+
+const std::vector<Sid>& AppContainerProfile::GetCapabilities() {
+ return capabilities_;
+}
+
+const std::vector<Sid>& AppContainerProfile::GetImpersonationCapabilities() {
+ return impersonation_capabilities_;
+}
+
+Sid AppContainerProfile::GetPackageSid() const {
+ return package_sid_;
+}
+
+void AppContainerProfile::SetEnableLowPrivilegeAppContainer(bool enable) {
+ enable_low_privilege_app_container_ = enable;
+}
+
+bool AppContainerProfile::GetEnableLowPrivilegeAppContainer() {
+ return enable_low_privilege_app_container_;
+}
+
+std::unique_ptr<SecurityCapabilities>
+AppContainerProfile::GetSecurityCapabilities() {
+ return std::unique_ptr<SecurityCapabilities>(
+ new SecurityCapabilities(package_sid_, capabilities_));
+}
+
+bool AppContainerProfile::BuildLowBoxToken(base::win::ScopedHandle* token) {
+ return CreateLowBoxToken(nullptr, IMPERSONATION,
+ GetSecurityCapabilities().get(), nullptr, 0,
+ token) == ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/app_container_profile.h b/chromium/sandbox/win/src/app_container_profile.h
new file mode 100644
index 00000000000..33f79381db8
--- /dev/null
+++ b/chromium/sandbox/win/src/app_container_profile.h
@@ -0,0 +1,121 @@
+// 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.
+
+#ifndef SANDBOX_SRC_APP_CONTAINER_PROFILE_H_
+#define SANDBOX_SRC_APP_CONTAINER_PROFILE_H_
+
+#include <windows.h>
+
+#include <accctrl.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+class AppContainerProfile {
+ public:
+ // Increments the reference count of this object. The reference count must
+ // be incremented if this interface is given to another component.
+ void AddRef();
+
+ // Decrements the reference count of this object. When the reference count
+ // is zero the object is automatically destroyed.
+ // Indicates that the caller is done with this interface. After calling
+ // release no other method should be called.
+ void Release();
+
+ // Get a handle to a registry key for this package.
+ bool GetRegistryLocation(REGSAM desired_access, base::win::ScopedHandle* key);
+
+ // Get a folder path to a location for this package.
+ bool GetFolderPath(base::FilePath* file_path);
+
+ // Get a pipe name usable by this AC.
+ bool GetPipePath(const wchar_t* pipe_name, base::FilePath* pipe_path);
+
+ // Get the package SID for this AC.
+ Sid GetPackageSid() const;
+
+ // Do an access check based on this profile for a named object. If method
+ // returns true then access_status reflects whether access was granted and
+ // granted_access gives the final access rights. The object_type can be one of
+ // SE_FILE_OBJECT, SE_REGISTRY_KEY, SE_REGISTRY_WOW64_32KEY. See
+ // ::GetNamedSecurityInfo for more information about how the enumeration is
+ // used and what format object_name needs to be.
+ bool AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status);
+
+ // Adds a capability by name to this profile.
+ bool AddCapability(const wchar_t* capability_name);
+ // Adds a capability from a known list.
+ bool AddCapability(WellKnownCapabilities capability);
+ // Adds a capability from a SID
+ bool AddCapability(const Sid& capability_sid);
+
+ // Adds an impersonation capability by name to this profile.
+ bool AddImpersonationCapability(const wchar_t* capability_name);
+ // Adds an impersonation capability from a known list.
+ bool AddImpersonationCapability(WellKnownCapabilities capability);
+ // Adds an impersonation capability from a SID
+ bool AddImpersonationCapability(const Sid& capability_sid);
+
+ // Enable Low Privilege AC.
+ void SetEnableLowPrivilegeAppContainer(bool enable);
+ bool GetEnableLowPrivilegeAppContainer();
+
+ // Get an allocated SecurityCapabilities object for this App Container.
+ std::unique_ptr<SecurityCapabilities> GetSecurityCapabilities();
+
+ // Get a vector of capabilities.
+ const std::vector<Sid>& GetCapabilities();
+
+ // Get a vector of impersonation only capabilities. Used if the process needs
+ // a more privileged token to start.
+ const std::vector<Sid>& GetImpersonationCapabilities();
+
+ // Creates a new AppContainerProfile object. This will create a new profile
+ // if it doesn't already exist. The profile must be deleted manually using
+ // the Delete method if it's no longer required.
+ static scoped_refptr<AppContainerProfile> Create(const wchar_t* package_name,
+ const wchar_t* display_name,
+ const wchar_t* description);
+
+ // Opens an AppContainerProfile object. No checks will be made on
+ // whether the package exists or not.
+ static scoped_refptr<AppContainerProfile> Open(const wchar_t* package_name);
+
+ // Delete a profile based on name. Returns true if successful, or if the
+ // package doesn't already exist.
+ static bool Delete(const wchar_t* package_name);
+
+ private:
+ AppContainerProfile(const Sid& package_sid);
+ ~AppContainerProfile();
+
+ bool BuildLowBoxToken(base::win::ScopedHandle* token);
+ bool AddCapability(const Sid& capability_sid, bool impersonation_only);
+
+ // Standard object-lifetime reference counter.
+ volatile LONG ref_count_;
+ Sid package_sid_;
+ bool enable_low_privilege_app_container_;
+ std::vector<Sid> capabilities_;
+ std::vector<Sid> impersonation_capabilities_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppContainerProfile);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_APP_CONTAINER_PROFILE_H_
diff --git a/chromium/sandbox/win/src/app_container_unittest.cc b/chromium/sandbox/win/src/app_container_unittest.cc
new file mode 100644
index 00000000000..b61541f8aac
--- /dev/null
+++ b/chromium/sandbox/win/src/app_container_unittest.cc
@@ -0,0 +1,382 @@
+// 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 <windows.h>
+
+#include <Sddl.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/path_service.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sid.h"
+#include "sandbox/win/src/win_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool ValidSecurityCapabilities(PSECURITY_CAPABILITIES security_capabilities,
+ const Sid& package_sid,
+ const std::vector<Sid>& capabilities) {
+ if (!security_capabilities)
+ return false;
+
+ if (!::EqualSid(package_sid.GetPSID(),
+ security_capabilities->AppContainerSid)) {
+ return false;
+ }
+
+ // If empty then count and list of capabilities should be 0 and nullptr.
+ if (capabilities.empty() && !security_capabilities->CapabilityCount &&
+ !security_capabilities->Capabilities) {
+ return true;
+ }
+
+ if (!security_capabilities->Capabilities)
+ return false;
+
+ if (security_capabilities->CapabilityCount != capabilities.size())
+ return false;
+
+ for (DWORD index = 0; index < security_capabilities->CapabilityCount;
+ ++index) {
+ if (!::EqualSid(capabilities[index].GetPSID(),
+ security_capabilities->Capabilities[index].Sid)) {
+ return false;
+ }
+ if (security_capabilities->Capabilities[index].Attributes !=
+ SE_GROUP_ENABLED) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CompareSidVectors(const std::vector<Sid>& left,
+ const std::vector<Sid>& right) {
+ if (left.size() != right.size())
+ return false;
+ auto left_interator = left.cbegin();
+ auto right_interator = right.cbegin();
+ while (left_interator != left.cend()) {
+ if (!::EqualSid(left_interator->GetPSID(), right_interator->GetPSID()))
+ return false;
+ ++left_interator;
+ ++right_interator;
+ }
+ return true;
+}
+
+bool GetProfilePath(const std::wstring& package_name,
+ base::FilePath* profile_path) {
+ base::FilePath local_app_data;
+ if (!base::PathService::Get(base::DIR_LOCAL_APP_DATA, &local_app_data))
+ return false;
+ *profile_path = local_app_data.Append(L"Packages").Append(package_name);
+ return true;
+}
+
+bool ProfileExist(const std::wstring& package_name) {
+ base::FilePath profile_path;
+ if (!GetProfilePath(package_name, &profile_path))
+ return false;
+ return base::PathExists(profile_path);
+}
+
+std::wstring GenerateRandomPackageName() {
+ return base::StringPrintf(L"%016lX%016lX", base::RandUint64(),
+ base::RandUint64());
+}
+
+class SECURITY_ATTRIBUTES_SDDL : public SECURITY_ATTRIBUTES {
+ public:
+ SECURITY_ATTRIBUTES_SDDL(LPCWSTR sddl) : SECURITY_ATTRIBUTES() {
+ nLength = sizeof(SECURITY_ATTRIBUTES);
+ if (!::ConvertStringSecurityDescriptorToSecurityDescriptor(
+ sddl, SDDL_REVISION_1, &lpSecurityDescriptor, nullptr)) {
+ lpSecurityDescriptor = nullptr;
+ }
+ }
+
+ ~SECURITY_ATTRIBUTES_SDDL() {
+ if (lpSecurityDescriptor)
+ ::LocalFree(lpSecurityDescriptor);
+ }
+
+ bool IsValid() { return lpSecurityDescriptor != nullptr; }
+};
+
+std::wstring CreateSddlWithSid(const Sid& sid) {
+ base::string16 sddl_string;
+ if (!sid.ToSddlString(&sddl_string))
+ return L"";
+ std::wstring base_sddl = L"D:(A;;GA;;;WD)(A;;GA;;;";
+ return base_sddl + sddl_string + L")";
+}
+
+void AccessCheckFile(AppContainerProfile* profile,
+ const base::FilePath& path,
+ const Sid& sid,
+ DWORD desired_access,
+ DWORD expected_access,
+ BOOL expected_status) {
+ SECURITY_ATTRIBUTES_SDDL sa(CreateSddlWithSid(sid).c_str());
+ ASSERT_TRUE(sa.IsValid());
+ base::win::ScopedHandle file_handle(::CreateFile(
+ path.value().c_str(), DELETE, FILE_SHARE_READ | FILE_SHARE_DELETE, &sa,
+ CREATE_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr));
+
+ ASSERT_TRUE(file_handle.IsValid());
+ DWORD granted_access;
+ BOOL access_status;
+ ASSERT_TRUE(profile->AccessCheck(path.value().c_str(), SE_FILE_OBJECT,
+ FILE_READ_DATA, &granted_access,
+ &access_status));
+ ASSERT_EQ(expected_status, access_status);
+ if (access_status)
+ ASSERT_EQ(expected_access, granted_access);
+}
+
+} // namespace
+
+TEST(AppContainerTest, SecurityCapabilities) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ // This isn't a valid package SID but it doesn't matter for this test.
+ Sid package_sid(::WinNullSid);
+
+ std::vector<Sid> capabilities;
+ SecurityCapabilities no_capabilities(package_sid);
+ EXPECT_TRUE(
+ ValidSecurityCapabilities(&no_capabilities, package_sid, capabilities));
+
+ capabilities.push_back(::WinWorldSid);
+ SecurityCapabilities one_capability(package_sid, capabilities);
+ EXPECT_TRUE(
+ ValidSecurityCapabilities(&one_capability, package_sid, capabilities));
+
+ capabilities.push_back(::WinLocalSid);
+ SecurityCapabilities two_capabilities(package_sid, capabilities);
+ EXPECT_TRUE(
+ ValidSecurityCapabilities(&two_capabilities, package_sid, capabilities));
+}
+
+TEST(AppContainerTest, CreateAndDeleteAppContainerProfile) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ std::wstring package_name = GenerateRandomPackageName();
+ EXPECT_FALSE(ProfileExist(package_name));
+ scoped_refptr<AppContainerProfile> profile = AppContainerProfile::Create(
+ package_name.c_str(), L"Name", L"Description");
+ ASSERT_NE(nullptr, profile.get());
+ EXPECT_TRUE(ProfileExist(package_name));
+ EXPECT_TRUE(AppContainerProfile::Delete(package_name.c_str()));
+ EXPECT_FALSE(ProfileExist(package_name));
+}
+
+TEST(AppContainerTest, CreateAndOpenAppContainerProfile) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ std::wstring package_name = GenerateRandomPackageName();
+ EXPECT_FALSE(ProfileExist(package_name));
+ scoped_refptr<AppContainerProfile> profile = AppContainerProfile::Create(
+ package_name.c_str(), L"Name", L"Description");
+ ASSERT_NE(nullptr, profile.get());
+ EXPECT_TRUE(ProfileExist(package_name));
+ scoped_refptr<AppContainerProfile> open_profile =
+ AppContainerProfile::Open(package_name.c_str());
+ ASSERT_NE(nullptr, profile.get());
+ EXPECT_TRUE(::EqualSid(profile->GetPackageSid().GetPSID(),
+ open_profile->GetPackageSid().GetPSID()));
+ EXPECT_TRUE(AppContainerProfile::Delete(package_name.c_str()));
+ EXPECT_FALSE(ProfileExist(package_name));
+ scoped_refptr<AppContainerProfile> open_profile2 =
+ AppContainerProfile::Open(package_name.c_str());
+ EXPECT_FALSE(ProfileExist(package_name));
+}
+
+TEST(AppContainerTest, SetLowPrivilegeAppContainer) {
+ // LPAC first supported in RS1.
+ if (base::win::GetVersion() < base::win::VERSION_WIN10_RS1)
+ return;
+ std::wstring package_name = GenerateRandomPackageName();
+ scoped_refptr<AppContainerProfile> profile =
+ AppContainerProfile::Open(package_name.c_str());
+ ASSERT_NE(nullptr, profile.get());
+ profile->SetEnableLowPrivilegeAppContainer(true);
+ EXPECT_TRUE(profile->GetEnableLowPrivilegeAppContainer());
+}
+
+TEST(AppContainerTest, OpenAppContainerProfileAndGetSecurityCapabilities) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ std::wstring package_name = GenerateRandomPackageName();
+ scoped_refptr<AppContainerProfile> profile =
+ AppContainerProfile::Open(package_name.c_str());
+ ASSERT_NE(nullptr, profile.get());
+
+ std::vector<Sid> capabilities;
+ auto no_capabilities = profile->GetSecurityCapabilities();
+ ASSERT_TRUE(ValidSecurityCapabilities(
+ no_capabilities.get(), profile->GetPackageSid(), capabilities));
+
+ // No support for named capabilities prior to Win10.
+ if (base::win::GetVersion() >= base::win::VERSION_WIN10) {
+ ASSERT_TRUE(profile->AddCapability(L"FakeCapability"));
+ capabilities.push_back(Sid::FromNamedCapability(L"FakeCapability"));
+ }
+
+ ASSERT_TRUE(profile->AddCapability(kInternetClient));
+ capabilities.push_back(Sid::FromKnownCapability(kInternetClient));
+ Sid sid_sddl = Sid::FromSddlString(L"S-1-15-3-1");
+ ASSERT_TRUE(profile->AddCapability(sid_sddl));
+ capabilities.push_back(sid_sddl);
+ auto with_capabilities = profile->GetSecurityCapabilities();
+ ASSERT_TRUE(ValidSecurityCapabilities(
+ with_capabilities.get(), profile->GetPackageSid(), capabilities));
+}
+
+TEST(AppContainerTest, GetResources) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ std::wstring package_name = GenerateRandomPackageName();
+ scoped_refptr<AppContainerProfile> profile = AppContainerProfile::Create(
+ package_name.c_str(), L"Name", L"Description");
+ ASSERT_NE(nullptr, profile.get());
+ base::win::ScopedHandle key;
+ EXPECT_TRUE(profile->GetRegistryLocation(KEY_READ, &key));
+ EXPECT_TRUE(key.IsValid());
+ key.Close();
+ base::FilePath path;
+ EXPECT_TRUE(profile->GetFolderPath(&path));
+ EXPECT_TRUE(base::PathExists(path));
+ base::FilePath pipe_path;
+ EXPECT_TRUE(profile->GetPipePath(package_name.c_str(), &pipe_path));
+ base::win::ScopedHandle pipe_handle;
+ pipe_handle.Set(::CreateNamedPipe(
+ pipe_path.value().c_str(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE,
+ PIPE_UNLIMITED_INSTANCES, 0, 0, 0, nullptr));
+ EXPECT_TRUE(pipe_handle.IsValid());
+ EXPECT_TRUE(AppContainerProfile::Delete(package_name.c_str()));
+}
+
+TEST(AppContainerTest, AccessCheckFile) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ // We don't need a valid profile to do the access check tests.
+ std::wstring package_name = GenerateRandomPackageName();
+ scoped_refptr<AppContainerProfile> profile =
+ AppContainerProfile::Open(package_name.c_str());
+ profile->AddCapability(kInternetClient);
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ base::FilePath path = temp_dir.GetPath().Append(package_name);
+
+ AccessCheckFile(profile.get(), path, ::WinNullSid, FILE_READ_DATA, 0, FALSE);
+ AccessCheckFile(profile.get(), path, ::WinBuiltinAnyPackageSid,
+ FILE_READ_DATA, FILE_READ_DATA, TRUE);
+ AccessCheckFile(profile.get(), path, profile->GetPackageSid(), FILE_READ_DATA,
+ FILE_READ_DATA, TRUE);
+ AccessCheckFile(profile.get(), path,
+ Sid::FromKnownCapability(kInternetClient), FILE_READ_DATA,
+ FILE_READ_DATA, TRUE);
+
+ // No support for LPAC less than Win10 RS1.
+ if (base::win::GetVersion() < base::win::VERSION_WIN10_RS1)
+ return;
+ profile->SetEnableLowPrivilegeAppContainer(true);
+ AccessCheckFile(profile.get(), path, ::WinBuiltinAnyPackageSid,
+ FILE_READ_DATA, 0, FALSE);
+ AccessCheckFile(profile.get(), path, Sid::AllRestrictedApplicationPackages(),
+ FILE_READ_DATA, FILE_READ_DATA, TRUE);
+}
+
+TEST(AppContainerTest, AccessCheckRegistry) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ // We don't need a valid profile to do the access check tests.
+ std::wstring package_name = GenerateRandomPackageName();
+ scoped_refptr<AppContainerProfile> profile =
+ AppContainerProfile::Open(package_name.c_str());
+ // Ensure the key doesn't exist.
+ RegDeleteKey(HKEY_CURRENT_USER, package_name.c_str());
+ SECURITY_ATTRIBUTES_SDDL sa(
+ CreateSddlWithSid(::WinBuiltinAnyPackageSid).c_str());
+ HKEY key_handle;
+ ASSERT_EQ(ERROR_SUCCESS,
+ RegCreateKeyEx(HKEY_CURRENT_USER, package_name.c_str(), 0, nullptr,
+ REG_OPTION_VOLATILE, KEY_ALL_ACCESS, &sa,
+ &key_handle, nullptr));
+ base::win::ScopedHandle key(key_handle);
+ std::wstring key_name = L"CURRENT_USER\\";
+ key_name += package_name;
+ DWORD granted_access;
+ BOOL access_status;
+
+ ASSERT_TRUE(profile->AccessCheck(key_name.c_str(), SE_REGISTRY_KEY,
+ KEY_QUERY_VALUE, &granted_access,
+ &access_status));
+ ASSERT_TRUE(access_status);
+ ASSERT_EQ(DWORD{KEY_QUERY_VALUE}, granted_access);
+ RegDeleteKey(HKEY_CURRENT_USER, package_name.c_str());
+}
+
+TEST(AppContainerTest, ImpersonationCapabilities) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ std::wstring package_name = GenerateRandomPackageName();
+ scoped_refptr<AppContainerProfile> profile =
+ AppContainerProfile::Open(package_name.c_str());
+ ASSERT_NE(nullptr, profile.get());
+
+ std::vector<Sid> capabilities;
+ std::vector<Sid> impersonation_capabilities;
+
+ ASSERT_TRUE(profile->AddCapability(kInternetClient));
+ capabilities.push_back(Sid::FromKnownCapability(kInternetClient));
+ impersonation_capabilities.push_back(
+ Sid::FromKnownCapability(kInternetClient));
+
+ ASSERT_TRUE(CompareSidVectors(profile->GetCapabilities(), capabilities));
+ ASSERT_TRUE(CompareSidVectors(profile->GetImpersonationCapabilities(),
+ impersonation_capabilities));
+
+ ASSERT_TRUE(profile->AddImpersonationCapability(kPrivateNetworkClientServer));
+ impersonation_capabilities.push_back(
+ Sid::FromKnownCapability(kPrivateNetworkClientServer));
+ // No support for named capabilities prior to Win10.
+ if (base::win::GetVersion() >= base::win::VERSION_WIN10) {
+ ASSERT_TRUE(profile->AddImpersonationCapability(L"FakeCapability"));
+ impersonation_capabilities.push_back(
+ Sid::FromNamedCapability(L"FakeCapability"));
+ }
+ Sid sid_sddl = Sid::FromSddlString(L"S-1-15-3-1");
+ ASSERT_TRUE(profile->AddImpersonationCapability(sid_sddl));
+ impersonation_capabilities.push_back(sid_sddl);
+ ASSERT_TRUE(CompareSidVectors(profile->GetCapabilities(), capabilities));
+ ASSERT_TRUE(CompareSidVectors(profile->GetImpersonationCapabilities(),
+ impersonation_capabilities));
+}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/broker_services.h b/chromium/sandbox/win/src/broker_services.h
index 539dd3b83f0..14cfed5f947 100644
--- a/chromium/sandbox/win/src/broker_services.h
+++ b/chromium/sandbox/win/src/broker_services.h
@@ -13,6 +13,7 @@
#include "base/compiler_specific.h"
#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
#include "base/win/scoped_handle.h"
#include "sandbox/win/src/crosscall_server.h"
#include "sandbox/win/src/job.h"
diff --git a/chromium/sandbox/win/src/filesystem_policy.cc b/chromium/sandbox/win/src/filesystem_policy.cc
index dd504ab31f7..a21a0c3aacb 100644
--- a/chromium/sandbox/win/src/filesystem_policy.cc
+++ b/chromium/sandbox/win/src/filesystem_policy.cc
@@ -30,7 +30,7 @@ NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle,
ULONG create_disposition,
ULONG create_options,
PVOID ea_buffer,
- ULONG ea_lenght,
+ ULONG ea_length,
HANDLE target_process) {
NtCreateFileFunction NtCreateFile = nullptr;
ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile);
@@ -39,7 +39,7 @@ NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle,
NTSTATUS status =
NtCreateFile(&local_handle, desired_access, obj_attributes,
io_status_block, nullptr, file_attributes, share_access,
- create_disposition, create_options, ea_buffer, ea_lenght);
+ create_disposition, create_options, ea_buffer, ea_length);
if (!NT_SUCCESS(status)) {
return status;
}
diff --git a/chromium/sandbox/win/src/nt_internals.h b/chromium/sandbox/win/src/nt_internals.h
index d89c9b10654..2b2dc0f2889 100644
--- a/chromium/sandbox/win/src/nt_internals.h
+++ b/chromium/sandbox/win/src/nt_internals.h
@@ -794,6 +794,11 @@ struct PROCESS_ACCESS_TOKEN {
const unsigned int NtProcessInformationAccessToken = 9;
+typedef NTSTATUS(WINAPI* RtlDeriveCapabilitySidsFromNameFunction)(
+ PCUNICODE_STRING SourceString,
+ PSID CapabilityGroupSid,
+ PSID CapabilitySid);
+
// -----------------------------------------------------------------------
// GDI OPM API and Supported Calls
diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.cc b/chromium/sandbox/win/src/policy_engine_opcodes.cc
index 71a3ea030e2..4bac801f9bc 100644
--- a/chromium/sandbox/win/src/policy_engine_opcodes.cc
+++ b/chromium/sandbox/win/src/policy_engine_opcodes.cc
@@ -217,7 +217,7 @@ EvalResult OpcodeEval<OP_NUMBER_AND_MATCH>(PolicyOpcode* opcode,
// Opcode OpWStringMatch:
// Requires a wchar_t* in selected_param.
// Argument 0 is the byte displacement of the stored string.
-// Argument 1 is the lenght in chars of the stored string.
+// Argument 1 is the length in chars of the stored string.
// Argument 2 is the offset to apply on the input string. It has special values.
// as noted in the header file.
// Argument 3 is the string matching options.
@@ -232,7 +232,7 @@ PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16_t selected_param,
if ('\0' == match_str[0])
return nullptr;
- int lenght = lstrlenW(match_str);
+ int length = lstrlenW(match_str);
PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param);
if (!opcode)
@@ -241,7 +241,7 @@ PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16_t selected_param,
if (0 == delta_str)
return nullptr;
opcode->SetArgument(0, delta_str);
- opcode->SetArgument(1, lenght);
+ opcode->SetArgument(1, length);
opcode->SetArgument(2, start_position);
opcode->SetArgument(3, match_opts);
return opcode;
@@ -291,8 +291,8 @@ EvalResult OpcodeEval<OP_WSTRING_MATCH>(PolicyOpcode* opcode,
if (start_position >= 0) {
if (kSeekToEnd == start_position) {
start_position = source_len - match_len;
- } else if (match_opts & EXACT_LENGHT) {
- // A sub-case of case 3 is when the EXACT_LENGHT flag is on
+ } else if (match_opts & EXACT_LENGTH) {
+ // A sub-case of case 3 is when the EXACT_LENGTH flag is on
// the match needs to be not just substring but full match.
if ((match_len + start_position) != source_len) {
return EVAL_FALSE;
@@ -365,8 +365,8 @@ PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id,
ptrdiff_t OpcodeFactory::AllocRelative(void* start,
const wchar_t* str,
- size_t lenght) {
- size_t bytes = lenght * sizeof(wchar_t);
+ size_t length) {
+ size_t bytes = length * sizeof(wchar_t);
if (memory_size() < bytes)
return 0;
memory_bottom_ -= bytes;
diff --git a/chromium/sandbox/win/src/policy_engine_opcodes.h b/chromium/sandbox/win/src/policy_engine_opcodes.h
index 00a7299fb2f..53a5c5a5ce0 100644
--- a/chromium/sandbox/win/src/policy_engine_opcodes.h
+++ b/chromium/sandbox/win/src/policy_engine_opcodes.h
@@ -224,7 +224,7 @@ class PolicyOpcode {
enum StringMatchOptions {
CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by
CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API.
- EXACT_LENGHT = 2 // Don't do substring match. Do full string match.
+ EXACT_LENGTH = 2 // Don't do substring match. Do full string match.
};
// Opcodes that do string comparisons take a parameter that is the starting
@@ -362,7 +362,7 @@ class OpcodeFactory {
// Allocates (and copies) a string (of size length) inside the buffer and
// returns the displacement with respect to start.
- ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t lenght);
+ ptrdiff_t AllocRelative(void* start, const wchar_t* str, size_t length);
// Points to the lowest currently available address of the memory
// used to make the opcodes. This pointer increments as opcodes are made.
diff --git a/chromium/sandbox/win/src/policy_low_level.cc b/chromium/sandbox/win/src/policy_low_level.cc
index beb0e86bcd5..86e2a13dcad 100644
--- a/chromium/sandbox/win/src/policy_low_level.cc
+++ b/chromium/sandbox/win/src/policy_low_level.cc
@@ -206,7 +206,7 @@ bool PolicyRule::GenStringOpcode(RuleType rule_type,
*skip_count = 0;
} else {
if (last_call) {
- match_opts = static_cast<StringMatchOptions>(EXACT_LENGHT | match_opts);
+ match_opts = static_cast<StringMatchOptions>(EXACT_LENGTH | match_opts);
}
op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0,
match_opts, options);
diff --git a/chromium/sandbox/win/src/restricted_token_unittest.cc b/chromium/sandbox/win/src/restricted_token_unittest.cc
index 170aa5cc336..fad38cc8c20 100644
--- a/chromium/sandbox/win/src/restricted_token_unittest.cc
+++ b/chromium/sandbox/win/src/restricted_token_unittest.cc
@@ -13,6 +13,8 @@
#include <vector>
#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/security_capabilities.h"
#include "sandbox/win/src/sid.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -62,6 +64,106 @@ void TestDefaultDalc(bool restricted_required) {
ASSERT_FALSE(logon_sid_found);
}
+bool GetVariableTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS information_class,
+ std::vector<char>* information) {
+ DWORD return_length;
+ if (!::GetTokenInformation(token, information_class, nullptr, 0,
+ &return_length)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+ }
+
+ information->resize(return_length);
+ return !!::GetTokenInformation(token, information_class, information->data(),
+ return_length, &return_length);
+}
+
+bool GetVariableTokenInformation(const base::win::ScopedHandle& token,
+ TOKEN_INFORMATION_CLASS information_class,
+ std::vector<char>* information) {
+ return GetVariableTokenInformation(token.Get(), information_class,
+ information);
+}
+
+void CheckLowBoxToken(const base::win::ScopedHandle& token,
+ TOKEN_TYPE token_type,
+ PSECURITY_CAPABILITIES security_capabilities) {
+ DWORD appcontainer;
+ DWORD return_length;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenIsAppContainer,
+ &appcontainer, sizeof(appcontainer),
+ &return_length));
+ ASSERT_TRUE(appcontainer);
+ TOKEN_TYPE token_type_real;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenType, &token_type_real,
+ sizeof(token_type_real), &return_length));
+ ASSERT_EQ(token_type_real, token_type);
+ if (token_type == ::TokenImpersonation) {
+ SECURITY_IMPERSONATION_LEVEL imp_level;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenImpersonationLevel,
+ &imp_level, sizeof(imp_level),
+ &return_length));
+ ASSERT_EQ(imp_level, ::SecurityImpersonation);
+ }
+
+ std::vector<char> package_sid_buf;
+ ASSERT_TRUE(GetVariableTokenInformation(token, ::TokenAppContainerSid,
+ &package_sid_buf));
+ PTOKEN_APPCONTAINER_INFORMATION package_sid =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(package_sid_buf.data());
+ EXPECT_TRUE(::EqualSid(security_capabilities->AppContainerSid,
+ package_sid->TokenAppContainer));
+
+ std::vector<char> capabilities_buf;
+ ASSERT_TRUE(GetVariableTokenInformation(token, ::TokenCapabilities,
+ &capabilities_buf));
+ PTOKEN_GROUPS capabilities =
+ reinterpret_cast<PTOKEN_GROUPS>(capabilities_buf.data());
+ ASSERT_EQ(capabilities->GroupCount, security_capabilities->CapabilityCount);
+ for (DWORD index = 0; index < capabilities->GroupCount; ++index) {
+ EXPECT_EQ(capabilities->Groups[index].Attributes,
+ security_capabilities->Capabilities[index].Attributes);
+ EXPECT_TRUE(::EqualSid(capabilities->Groups[index].Sid,
+ security_capabilities->Capabilities[index].Sid));
+ }
+}
+
+// Checks if a sid is in the restricting list of the restricted token.
+// Asserts if it's not the case. If count is a positive number, the number of
+// elements in the restricting sids list has to be equal.
+void CheckRestrictingSid(HANDLE restricted_token, ATL::CSid sid, int count) {
+ std::vector<char> memory;
+ ASSERT_TRUE(GetVariableTokenInformation(restricted_token,
+ ::TokenRestrictedSids, &memory));
+ PTOKEN_GROUPS groups = reinterpret_cast<PTOKEN_GROUPS>(memory.data());
+ ATL::CTokenGroups atl_groups(*groups);
+
+ if (count >= 0)
+ ASSERT_EQ(static_cast<unsigned>(count), atl_groups.GetCount());
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ atl_groups.GetSidsAndAttributes(&sids, &attributes);
+
+ bool present = false;
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (sids[i] == sid) {
+ present = true;
+ break;
+ }
+ }
+
+ ASSERT_TRUE(present);
+}
+
+void CheckRestrictingSid(const ATL::CAccessToken& restricted_token,
+ ATL::CSid sid,
+ int count) {
+ CheckRestrictingSid(restricted_token.GetHandle(), sid, count);
+}
+
} // namespace
// Tests the initializatioin with an invalid token handle.
@@ -441,40 +543,6 @@ TEST(RestrictedTokenTest, DeletePrivilege) {
}
}
-// Checks if a sid is in the restricting list of the restricted token.
-// Asserts if it's not the case. If count is a positive number, the number of
-// elements in the restricting sids list has to be equal.
-void CheckRestrictingSid(const ATL::CAccessToken& restricted_token,
- ATL::CSid sid,
- int count) {
- DWORD length = 8192;
- BYTE* memory = new BYTE[length];
- TOKEN_GROUPS* groups = reinterpret_cast<TOKEN_GROUPS*>(memory);
- ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(),
- TokenRestrictedSids, groups, length,
- &length));
-
- ATL::CTokenGroups atl_groups(*groups);
- delete[] memory;
-
- if (count >= 0)
- ASSERT_EQ(static_cast<unsigned>(count), atl_groups.GetCount());
-
- ATL::CSid::CSidArray sids;
- ATL::CAtlArray<DWORD> attributes;
- atl_groups.GetSidsAndAttributes(&sids, &attributes);
-
- bool present = false;
- for (unsigned int i = 0; i < sids.GetCount(); ++i) {
- if (sids[i] == sid) {
- present = true;
- break;
- }
- }
-
- ASSERT_TRUE(present);
-}
-
// Tests the method AddRestrictingSid.
TEST(RestrictedTokenTest, AddRestrictingSid) {
RestrictedToken token;
@@ -579,16 +647,11 @@ TEST(RestrictedTokenTest, AddMultipleRestrictingSids) {
ATL::CSid session;
restricted_token.GetLogonSid(&session);
- DWORD length = 8192;
- BYTE* memory = new BYTE[length];
- TOKEN_GROUPS* groups = reinterpret_cast<TOKEN_GROUPS*>(memory);
- ASSERT_TRUE(::GetTokenInformation(restricted_token.GetHandle(),
- TokenRestrictedSids, groups, length,
- &length));
-
+ std::vector<char> memory;
+ ASSERT_TRUE(GetVariableTokenInformation(restricted_token.GetHandle(),
+ ::TokenRestrictedSids, &memory));
+ PTOKEN_GROUPS groups = reinterpret_cast<PTOKEN_GROUPS>(memory.data());
ATL::CTokenGroups atl_groups(*groups);
- delete[] memory;
-
ASSERT_EQ(3u, atl_groups.GetCount());
}
@@ -651,4 +714,71 @@ TEST(RestrictedTokenTest, LockdownDefaultDaclNoLogonSid) {
ASSERT_EQ(DWORD{ERROR_SUCCESS}, token.GetRestrictedToken(&handle));
}
+TEST(RestrictedTokenTest, LowBoxToken) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+ base::win::ScopedHandle token;
+
+ Sid package_sid = Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-7");
+ SecurityCapabilities caps_no_capabilities(package_sid);
+
+ ASSERT_EQ(DWORD{ERROR_INVALID_PARAMETER},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_no_capabilities, nullptr,
+ 0, nullptr));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_no_capabilities, nullptr,
+ 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_no_capabilities);
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, IMPERSONATION, &caps_no_capabilities,
+ nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenImpersonation, &caps_no_capabilities);
+
+ std::vector<Sid> capabilities;
+ capabilities.push_back(Sid::FromKnownCapability(kInternetClient));
+ capabilities.push_back(Sid::FromKnownCapability(kPrivateNetworkClientServer));
+ SecurityCapabilities caps_with_capabilities(package_sid, capabilities);
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_with_capabilities,
+ nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_with_capabilities);
+
+ RestrictedToken restricted_token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, restricted_token.Init(nullptr));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ restricted_token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ restricted_token.GetRestrictedToken(&token_handle));
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(token_handle.Get(), PRIMARY,
+ &caps_with_capabilities, nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_with_capabilities);
+ CheckRestrictingSid(token.Get(), ATL::Sids::World(), 1);
+
+ SecurityCapabilities caps_for_handles(
+ Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-8"));
+ base::win::ScopedHandle object_handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxObjectDirectory(caps_for_handles.AppContainerSid, true,
+ &object_handle));
+ HANDLE saved_handles[] = {object_handle.Get()};
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(token_handle.Get(), PRIMARY, &caps_for_handles,
+ saved_handles, 1, &token));
+ ASSERT_TRUE(token.IsValid());
+ object_handle.Close();
+ ASSERT_FALSE(object_handle.IsValid());
+ ASSERT_EQ(DWORD{ERROR_ALREADY_EXISTS},
+ CreateLowBoxObjectDirectory(caps_for_handles.AppContainerSid, false,
+ &object_handle));
+}
+
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/restricted_token_utils.cc b/chromium/sandbox/win/src/restricted_token_utils.cc
index 8a8e95812f7..104ae07caab 100644
--- a/chromium/sandbox/win/src/restricted_token_utils.cc
+++ b/chromium/sandbox/win/src/restricted_token_utils.cc
@@ -7,15 +7,19 @@
#include <aclapi.h>
#include <sddl.h>
+#include <memory>
#include <vector>
#include "base/logging.h"
+#include "base/strings/stringprintf.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/job.h"
#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/sandbox_utils.h"
#include "sandbox/win/src/security_level.h"
#include "sandbox/win/src/sid.h"
+#include "sandbox/win/src/win_utils.h"
namespace sandbox {
@@ -294,4 +298,103 @@ DWORD HardenProcessIntegrityLevelPolicy() {
return HardenTokenIntegrityLevelPolicy(token.Get());
}
+DWORD CreateLowBoxToken(HANDLE base_token,
+ TokenType token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ PHANDLE saved_handles,
+ DWORD saved_handles_count,
+ base::win::ScopedHandle* token) {
+ NtCreateLowBoxToken CreateLowBoxToken = nullptr;
+ ResolveNTFunctionPtr("NtCreateLowBoxToken", &CreateLowBoxToken);
+
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return ERROR_CALL_NOT_IMPLEMENTED;
+
+ if (token_type != PRIMARY && token_type != IMPERSONATION)
+ return ERROR_INVALID_PARAMETER;
+
+ if (!token)
+ return ERROR_INVALID_PARAMETER;
+
+ base::win::ScopedHandle base_token_handle;
+ if (!base_token) {
+ HANDLE process_token = nullptr;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &process_token)) {
+ return ::GetLastError();
+ }
+ base_token_handle.Set(process_token);
+ base_token = process_token;
+ }
+ OBJECT_ATTRIBUTES obj_attr;
+ InitializeObjectAttributes(&obj_attr, nullptr, 0, nullptr, nullptr);
+ HANDLE token_lowbox = nullptr;
+
+ NTSTATUS status = CreateLowBoxToken(
+ &token_lowbox, base_token, TOKEN_ALL_ACCESS, &obj_attr,
+ security_capabilities->AppContainerSid,
+ security_capabilities->CapabilityCount,
+ security_capabilities->Capabilities, saved_handles_count, saved_handles);
+ if (!NT_SUCCESS(status))
+ return GetLastErrorFromNtStatus(status);
+
+ base::win::ScopedHandle token_lowbox_handle(token_lowbox);
+ DCHECK(token_lowbox_handle.IsValid());
+
+ // Default from NtCreateLowBoxToken is a Primary token.
+ if (token_type == PRIMARY) {
+ token->Set(token_lowbox_handle.Take());
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE dup_handle = nullptr;
+ if (!::DuplicateTokenEx(token_lowbox_handle.Get(), TOKEN_ALL_ACCESS, nullptr,
+ ::SecurityImpersonation, ::TokenImpersonation,
+ &dup_handle)) {
+ return ::GetLastError();
+ }
+
+ token->Set(dup_handle);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD CreateLowBoxObjectDirectory(PSID lowbox_sid,
+ bool open_directory,
+ base::win::ScopedHandle* directory) {
+ DWORD session_id = 0;
+ if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id))
+ return ::GetLastError();
+
+ LPWSTR sid_string = nullptr;
+ if (!::ConvertSidToStringSid(lowbox_sid, &sid_string))
+ return ::GetLastError();
+
+ std::unique_ptr<wchar_t, LocalFreeDeleter> sid_string_ptr(sid_string);
+ base::string16 directory_path = base::StringPrintf(
+ L"\\Sessions\\%d\\AppContainerNamedObjects\\%ls", session_id, sid_string);
+
+ NtCreateDirectoryObjectFunction CreateObjectDirectory = nullptr;
+ ResolveNTFunctionPtr("NtCreateDirectoryObject", &CreateObjectDirectory);
+
+ OBJECT_ATTRIBUTES obj_attr;
+ UNICODE_STRING obj_name;
+ DWORD attributes = OBJ_CASE_INSENSITIVE;
+ if (open_directory)
+ attributes |= OBJ_OPENIF;
+
+ sandbox::InitObjectAttribs(directory_path, attributes, nullptr, &obj_attr,
+ &obj_name, nullptr);
+
+ HANDLE handle = nullptr;
+ NTSTATUS status =
+ CreateObjectDirectory(&handle, DIRECTORY_ALL_ACCESS, &obj_attr);
+
+ if (!NT_SUCCESS(status))
+ return GetLastErrorFromNtStatus(status);
+ directory->Set(handle);
+
+ return ERROR_SUCCESS;
+}
+
} // namespace sandbox
diff --git a/chromium/sandbox/win/src/restricted_token_utils.h b/chromium/sandbox/win/src/restricted_token_utils.h
index f181557742b..10815b00b89 100644
--- a/chromium/sandbox/win/src/restricted_token_utils.h
+++ b/chromium/sandbox/win/src/restricted_token_utils.h
@@ -72,6 +72,31 @@ DWORD HardenTokenIntegrityLevelPolicy(HANDLE token);
// holes.
DWORD HardenProcessIntegrityLevelPolicy();
+// Create a lowbox token. This is not valid prior to Windows 8.
+// |base_token| a base token to derive the lowbox token from. Can be nullptr.
+// |security_capabilities| list of LowBox capabilities to use when creating the
+// token.
+// |token| is the output value containing the handle of the newly created
+// restricted token.
+// |lockdown_default_dacl| indicates the token's default DACL should be locked
+// down to restrict what other process can open kernel resources created while
+// running under the token.
+DWORD CreateLowBoxToken(HANDLE base_token,
+ TokenType token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ PHANDLE saved_handles,
+ DWORD saved_handles_count,
+ base::win::ScopedHandle* token);
+
+// Create a lowbox object directory token. This is not valid prior to Windows 8.
+// This returns the Win32 error code from the operation.
+// |lowbox_sid| the SID for the LowBox.
+// |open_directory| open the directory if it already exists.
+// |directory| is the output value for the directory object.
+DWORD CreateLowBoxObjectDirectory(PSID lowbox_sid,
+ bool open_directory,
+ base::win::ScopedHandle* directory);
+
} // namespace sandbox
#endif // SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
diff --git a/chromium/sandbox/win/src/sandbox_policy.h b/chromium/sandbox/win/src/sandbox_policy.h
index dac9e8c297d..29f8705bff7 100644
--- a/chromium/sandbox/win/src/sandbox_policy.h
+++ b/chromium/sandbox/win/src/sandbox_policy.h
@@ -172,9 +172,6 @@ class TargetPolicy {
// than the current level, the sandbox will fail to start.
virtual ResultCode SetDelayedIntegrityLevel(IntegrityLevel level) = 0;
- // Sets a capability to be enabled for the sandboxed process' AppContainer.
- virtual ResultCode SetCapability(const wchar_t* sid) = 0;
-
// Sets the LowBox token for sandboxed process. This is mutually exclusive
// with SetAppContainer method.
virtual ResultCode SetLowBox(const wchar_t* sid) = 0;
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.cc b/chromium/sandbox/win/src/sandbox_policy_base.cc
index 5ded3e12a22..867a549651b 100644
--- a/chromium/sandbox/win/src/sandbox_policy_base.cc
+++ b/chromium/sandbox/win/src/sandbox_policy_base.cc
@@ -28,6 +28,7 @@
#include "sandbox/win/src/restricted_token_utils.h"
#include "sandbox/win/src/sandbox_policy.h"
#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/security_capabilities.h"
#include "sandbox/win/src/sync_policy.h"
#include "sandbox/win/src/target_process.h"
#include "sandbox/win/src/top_level_dispatcher.h"
@@ -64,39 +65,6 @@ bool IsInheritableHandle(HANDLE handle) {
return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
}
-HANDLE CreateLowBoxObjectDirectory(PSID lowbox_sid) {
- DWORD session_id = 0;
- if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id))
- return nullptr;
-
- LPWSTR sid_string = nullptr;
- if (!::ConvertSidToStringSid(lowbox_sid, &sid_string))
- return nullptr;
-
- base::string16 directory_path =
- base::StringPrintf(L"\\Sessions\\%d\\AppContainerNamedObjects\\%ls",
- session_id, sid_string)
- .c_str();
- ::LocalFree(sid_string);
-
- NtCreateDirectoryObjectFunction CreateObjectDirectory = nullptr;
- ResolveNTFunctionPtr("NtCreateDirectoryObject", &CreateObjectDirectory);
-
- OBJECT_ATTRIBUTES obj_attr;
- UNICODE_STRING obj_name;
- sandbox::InitObjectAttribs(directory_path, OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
- nullptr, &obj_attr, &obj_name, nullptr);
-
- HANDLE handle = nullptr;
- NTSTATUS status =
- CreateObjectDirectory(&handle, DIRECTORY_ALL_ACCESS, &obj_attr);
-
- if (!NT_SUCCESS(status))
- return nullptr;
-
- return handle;
-}
-
} // namespace
namespace sandbox {
@@ -313,11 +281,6 @@ ResultCode PolicyBase::SetDelayedIntegrityLevel(
return SBOX_ALL_OK;
}
-ResultCode PolicyBase::SetCapability(const wchar_t* sid) {
- capabilities_.push_back(sid);
- return SBOX_ALL_OK;
-}
-
ResultCode PolicyBase::SetLowBox(const wchar_t* sid) {
if (base::win::OSInfo::GetInstance()->version() < base::win::VERSION_WIN8)
return SBOX_ERROR_UNSUPPORTED;
@@ -475,29 +438,23 @@ ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
}
if (lowbox_sid_) {
- NtCreateLowBoxToken CreateLowBoxToken = nullptr;
- ResolveNTFunctionPtr("NtCreateLowBoxToken", &CreateLowBoxToken);
- OBJECT_ATTRIBUTES obj_attr;
- InitializeObjectAttributes(&obj_attr, nullptr, 0, nullptr, nullptr);
- HANDLE token_lowbox = nullptr;
-
- if (!lowbox_directory_.IsValid())
- lowbox_directory_.Set(CreateLowBoxObjectDirectory(lowbox_sid_));
- DCHECK(lowbox_directory_.IsValid());
+ if (!lowbox_directory_.IsValid()) {
+ result =
+ CreateLowBoxObjectDirectory(lowbox_sid_, true, &lowbox_directory_);
+ DCHECK(result == ERROR_SUCCESS);
+ }
// The order of handles isn't important in the CreateLowBoxToken call.
// The kernel will maintain a reference to the object directory handle.
HANDLE saved_handles[1] = {lowbox_directory_.Get()};
DWORD saved_handles_count = lowbox_directory_.IsValid() ? 1 : 0;
- NTSTATUS status = CreateLowBoxToken(
- &token_lowbox, lockdown->Get(), TOKEN_ALL_ACCESS, &obj_attr,
- lowbox_sid_, 0, nullptr, saved_handles_count, saved_handles);
- if (!NT_SUCCESS(status))
+ Sid package_sid(lowbox_sid_);
+ SecurityCapabilities caps(package_sid);
+ if (CreateLowBoxToken(lockdown->Get(), PRIMARY, &caps, saved_handles,
+ saved_handles_count, lowbox) != ERROR_SUCCESS) {
return SBOX_ERROR_GENERIC;
-
- DCHECK(token_lowbox);
- lowbox->Set(token_lowbox);
+ }
}
// Create the 'better' token. We use this token as the one that the main
diff --git a/chromium/sandbox/win/src/sandbox_policy_base.h b/chromium/sandbox/win/src/sandbox_policy_base.h
index 50ee61845db..61a66419ada 100644
--- a/chromium/sandbox/win/src/sandbox_policy_base.h
+++ b/chromium/sandbox/win/src/sandbox_policy_base.h
@@ -53,7 +53,6 @@ class PolicyBase final : public TargetPolicy {
ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) override;
IntegrityLevel GetIntegrityLevel() const override;
ResultCode SetDelayedIntegrityLevel(IntegrityLevel integrity_level) override;
- ResultCode SetCapability(const wchar_t* sid) override;
ResultCode SetLowBox(const wchar_t* sid) override;
ResultCode SetProcessMitigations(MitigationFlags flags) override;
MitigationFlags GetProcessMitigations() override;
@@ -153,7 +152,6 @@ class PolicyBase final : public TargetPolicy {
// target process. A null set means we need to close all handles of the
// given type.
HandleCloser handle_closer_;
- std::vector<base::string16> capabilities_;
PSID lowbox_sid_;
base::win::ScopedHandle lowbox_directory_;
std::unique_ptr<Dispatcher> dispatcher_;
diff --git a/chromium/sandbox/win/src/security_capabilities.cc b/chromium/sandbox/win/src/security_capabilities.cc
new file mode 100644
index 00000000000..0df446b5bee
--- /dev/null
+++ b/chromium/sandbox/win/src/security_capabilities.cc
@@ -0,0 +1,33 @@
+// 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 "sandbox/win/src/security_capabilities.h"
+#include "base/numerics/safe_conversions.h"
+
+namespace sandbox {
+
+SecurityCapabilities::SecurityCapabilities(const Sid& package_sid,
+ const std::vector<Sid>& capabilities)
+ : SECURITY_CAPABILITIES(),
+ capabilities_(capabilities),
+ package_sid_(package_sid) {
+ AppContainerSid = package_sid_.GetPSID();
+ if (capabilities_.empty())
+ return;
+
+ capability_sids_.resize(capabilities_.size());
+ for (size_t index = 0; index < capabilities_.size(); ++index) {
+ capability_sids_[index].Sid = capabilities_[index].GetPSID();
+ capability_sids_[index].Attributes = SE_GROUP_ENABLED;
+ }
+ CapabilityCount = base::checked_cast<DWORD>(capability_sids_.size());
+ Capabilities = capability_sids_.data();
+}
+
+SecurityCapabilities::SecurityCapabilities(const Sid& package_sid)
+ : SecurityCapabilities(package_sid, std::vector<Sid>()) {}
+
+SecurityCapabilities::~SecurityCapabilities() {}
+
+} // namespace sandbox
diff --git a/chromium/sandbox/win/src/security_capabilities.h b/chromium/sandbox/win/src/security_capabilities.h
new file mode 100644
index 00000000000..7a66e59743a
--- /dev/null
+++ b/chromium/sandbox/win/src/security_capabilities.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef SANDBOX_SRC_SECURITY_CAPABILITIES_H_
+#define SANDBOX_SRC_SECURITY_CAPABILITIES_H_
+
+#include <windows.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+class SecurityCapabilities final : public SECURITY_CAPABILITIES {
+ public:
+ explicit SecurityCapabilities(const Sid& package_sid);
+ SecurityCapabilities(const Sid& package_sid,
+ const std::vector<Sid>& capabilities);
+ ~SecurityCapabilities();
+
+ private:
+ std::vector<Sid> capabilities_;
+ std::vector<SID_AND_ATTRIBUTES> capability_sids_;
+ Sid package_sid_;
+
+ DISALLOW_COPY_AND_ASSIGN(SecurityCapabilities);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SECURITY_CAPABILITIES_H_ \ No newline at end of file
diff --git a/chromium/sandbox/win/src/sid.cc b/chromium/sandbox/win/src/sid.cc
index 5c7754817d8..3507ad7c346 100644
--- a/chromium/sandbox/win/src/sid.cc
+++ b/chromium/sandbox/win/src/sid.cc
@@ -16,49 +16,36 @@ namespace sandbox {
namespace {
-typedef decltype(
- ::DeriveCapabilitySidsFromName) DeriveCapabilitySidsFromNameFunc;
-
-class SidArray {
- public:
- SidArray() : count_(0), sids_(nullptr) {}
-
- ~SidArray() {
- if (sids_) {
- for (size_t index = 0; index < count_; ++index) {
- ::LocalFree(sids_[index]);
- }
- ::LocalFree(sids_);
- }
- }
-
- DWORD count() { return count_; }
- PSID* sids() { return sids_; }
- PDWORD count_ptr() { return &count_; }
- PSID** sids_ptr() { return &sids_; }
-
- private:
- DWORD count_;
- PSID* sids_;
-};
-
-const wchar_t* WellKnownCapabilityToName(WellKnownCapabilities capability) {
+DWORD WellKnownCapabilityToRid(WellKnownCapabilities capability) {
switch (capability) {
case kInternetClient:
- return L"internetClient";
+ return SECURITY_CAPABILITY_INTERNET_CLIENT;
case kInternetClientServer:
- return L"internetClientServer";
- case kRegistryRead:
- return L"registryRead";
- case kLpacCryptoServices:
- return L"lpacCryptoServices";
- case kEnterpriseAuthentication:
- return L"enterpriseAuthentication";
+ return SECURITY_CAPABILITY_INTERNET_CLIENT_SERVER;
case kPrivateNetworkClientServer:
- return L"privateNetworkClientServer";
+ return SECURITY_CAPABILITY_PRIVATE_NETWORK_CLIENT_SERVER;
+ case kPicturesLibrary:
+ return SECURITY_CAPABILITY_PICTURES_LIBRARY;
+ case kVideosLibrary:
+ return SECURITY_CAPABILITY_VIDEOS_LIBRARY;
+ case kMusicLibrary:
+ return SECURITY_CAPABILITY_MUSIC_LIBRARY;
+ case kDocumentsLibrary:
+ return SECURITY_CAPABILITY_DOCUMENTS_LIBRARY;
+ case kEnterpriseAuthentication:
+ return SECURITY_CAPABILITY_ENTERPRISE_AUTHENTICATION;
+ case kSharedUserCertificates:
+ return SECURITY_CAPABILITY_SHARED_USER_CERTIFICATES;
+ case kRemovableStorage:
+ return SECURITY_CAPABILITY_REMOVABLE_STORAGE;
+ case kAppointments:
+ return SECURITY_CAPABILITY_APPOINTMENTS;
+ case kContacts:
+ return SECURITY_CAPABILITY_CONTACTS;
default:
- return nullptr;
+ break;
}
+ return 0;
}
} // namespace
@@ -81,33 +68,39 @@ Sid::Sid(WELL_KNOWN_SID_TYPE type) {
}
Sid Sid::FromKnownCapability(WellKnownCapabilities capability) {
- return Sid::FromNamedCapability(WellKnownCapabilityToName(capability));
+ DWORD capability_rid = WellKnownCapabilityToRid(capability);
+ if (!capability_rid)
+ return Sid();
+ SID_IDENTIFIER_AUTHORITY capability_authority = {
+ SECURITY_APP_PACKAGE_AUTHORITY};
+ DWORD sub_authorities[] = {SECURITY_CAPABILITY_BASE_RID, capability_rid};
+ return FromSubAuthorities(&capability_authority, 2, sub_authorities);
}
Sid Sid::FromNamedCapability(const wchar_t* capability_name) {
- DeriveCapabilitySidsFromNameFunc* derive_capablity_sids =
- (DeriveCapabilitySidsFromNameFunc*)GetProcAddress(
- GetModuleHandle(L"kernelbase"), "DeriveCapabilitySidsFromName");
- if (!derive_capablity_sids)
+ RtlDeriveCapabilitySidsFromNameFunction derive_capability_sids = nullptr;
+ ResolveNTFunctionPtr("RtlDeriveCapabilitySidsFromName",
+ &derive_capability_sids);
+ RtlInitUnicodeStringFunction init_unicode_string = nullptr;
+ ResolveNTFunctionPtr("RtlInitUnicodeString", &init_unicode_string);
+
+ if (!derive_capability_sids || !init_unicode_string)
return Sid();
if (!capability_name || ::wcslen(capability_name) == 0)
return Sid();
- SidArray capability_group_sids;
- SidArray capability_sids;
-
- if (!derive_capablity_sids(capability_name, capability_group_sids.sids_ptr(),
- capability_group_sids.count_ptr(),
- capability_sids.sids_ptr(),
- capability_sids.count_ptr())) {
- return Sid();
- }
+ UNICODE_STRING name = {};
+ init_unicode_string(&name, capability_name);
+ Sid capability_sid;
+ Sid group_sid;
- if (capability_sids.count() < 1)
+ NTSTATUS status =
+ derive_capability_sids(&name, group_sid.sid_, capability_sid.sid_);
+ if (!NT_SUCCESS(status))
return Sid();
- return Sid(capability_sids.sids()[0]);
+ return capability_sid;
}
Sid Sid::FromSddlString(const wchar_t* sddl_sid) {
diff --git a/chromium/sandbox/win/src/sid.h b/chromium/sandbox/win/src/sid.h
index acaaa744ba7..4ef22e54602 100644
--- a/chromium/sandbox/win/src/sid.h
+++ b/chromium/sandbox/win/src/sid.h
@@ -11,13 +11,20 @@
namespace sandbox {
+// Known capabilities defined in Windows 8.
enum WellKnownCapabilities {
kInternetClient,
kInternetClientServer,
- kRegistryRead,
- kLpacCryptoServices,
- kEnterpriseAuthentication,
kPrivateNetworkClientServer,
+ kPicturesLibrary,
+ kVideosLibrary,
+ kMusicLibrary,
+ kDocumentsLibrary,
+ kEnterpriseAuthentication,
+ kSharedUserCertificates,
+ kRemovableStorage,
+ kAppointments,
+ kContacts,
kMaxWellKnownCapability
};
@@ -34,7 +41,8 @@ class Sid {
// Create a Sid from an AppContainer capability name. The name can be
// completely arbitrary.
static Sid FromNamedCapability(const wchar_t* capability_name);
- // Create a Sid from a known capability enumeration value.
+ // Create a Sid from a known capability enumeration value. The Sids
+ // match with the list defined in Windows 8.
static Sid FromKnownCapability(WellKnownCapabilities capability);
// Create a Sid from a SDDL format string, such as S-1-1-0.
static Sid FromSddlString(const wchar_t* sddl_sid);
diff --git a/chromium/sandbox/win/src/sid_unittest.cc b/chromium/sandbox/win/src/sid_unittest.cc
index 0f4fc9bb07f..5b01d592acc 100644
--- a/chromium/sandbox/win/src/sid_unittest.cc
+++ b/chromium/sandbox/win/src/sid_unittest.cc
@@ -35,10 +35,14 @@ bool EqualSid(const Sid& sid, const wchar_t* sddl_sid) {
return equal;
}
-struct CapabilityTestEntry {
- WellKnownCapabilities capability_;
- const wchar_t* capability_name_;
- const wchar_t* sddl_sid_;
+struct KnownCapabilityTestEntry {
+ WellKnownCapabilities capability;
+ const wchar_t* sddl_sid;
+};
+
+struct NamedCapabilityTestEntry {
+ const wchar_t* capability_name;
+ const wchar_t* sddl_sid;
};
} // namespace
@@ -95,22 +99,38 @@ TEST(SidTest, GetPSID) {
ASSERT_TRUE(EqualSid(Sid(::WinProxySid), ATL::Sids::Proxy()));
}
-TEST(SidTest, AppContainer) {
- const CapabilityTestEntry capabilities[] = {
- {kInternetClient, L"internetClient", L"S-1-15-3-1"},
- {kInternetClientServer, L"internetClientServer", L"S-1-15-3-2"},
- {kRegistryRead, L"registryRead",
- L"S-1-15-3-1024-1065365936-1281604716-3511738428-"
- "1654721687-432734479-3232135806-4053264122-3456934681"},
- {kLpacCryptoServices, L"lpacCryptoServices",
- L"S-1-15-3-1024-3203351429-2120443784-2872670797-"
- "1918958302-2829055647-4275794519-765664414-2751773334"},
- {kEnterpriseAuthentication, L"enterpriseAuthentication", L"S-1-15-3-8"},
- {kPrivateNetworkClientServer, L"privateNetworkClientServer",
- L"S-1-15-3-3"}};
+TEST(SidTest, KnownCapability) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN8)
+ return;
+
+ Sid sid_invalid_well_known =
+ Sid::FromKnownCapability(kMaxWellKnownCapability);
+ EXPECT_FALSE(sid_invalid_well_known.IsValid());
+
+ const KnownCapabilityTestEntry capabilities[] = {
+ {kInternetClient, L"S-1-15-3-1"},
+ {kInternetClientServer, L"S-1-15-3-2"},
+ {kPrivateNetworkClientServer, L"S-1-15-3-3"},
+ {kPicturesLibrary, L"S-1-15-3-4"},
+ {kVideosLibrary, L"S-1-15-3-5"},
+ {kMusicLibrary, L"S-1-15-3-6"},
+ {kDocumentsLibrary, L"S-1-15-3-7"},
+ {kEnterpriseAuthentication, L"S-1-15-3-8"},
+ {kSharedUserCertificates, L"S-1-15-3-9"},
+ {kRemovableStorage, L"S-1-15-3-10"},
+ {kAppointments, L"S-1-15-3-11"},
+ {kContacts, L"S-1-15-3-12"},
+ };
- // No support for AppContainer less than Win10 RS2.
- if (base::win::GetVersion() < base::win::VERSION_WIN10_RS2)
+ for (auto capability : capabilities) {
+ EXPECT_TRUE(EqualSid(Sid::FromKnownCapability(capability.capability),
+ capability.sddl_sid))
+ << "Known Capability: " << capability.sddl_sid;
+ }
+}
+
+TEST(SidTest, NamedCapability) {
+ if (base::win::GetVersion() < base::win::VERSION_WIN10)
return;
Sid sid_nullptr = Sid::FromNamedCapability(nullptr);
@@ -119,18 +139,22 @@ TEST(SidTest, AppContainer) {
Sid sid_empty = Sid::FromNamedCapability(L"");
EXPECT_FALSE(sid_empty.IsValid());
- WellKnownCapabilities invalid_well_known =
- static_cast<WellKnownCapabilities>(kMaxWellKnownCapability + 1);
- Sid sid_invalid_well_known = Sid::FromKnownCapability(invalid_well_known);
- EXPECT_FALSE(sid_invalid_well_known.IsValid());
+ const NamedCapabilityTestEntry capabilities[] = {
+ {L"internetClient", L"S-1-15-3-1"},
+ {L"internetClientServer", L"S-1-15-3-2"},
+ {L"registryRead",
+ L"S-1-15-3-1024-1065365936-1281604716-3511738428-"
+ "1654721687-432734479-3232135806-4053264122-3456934681"},
+ {L"lpacCryptoServices",
+ L"S-1-15-3-1024-3203351429-2120443784-2872670797-"
+ "1918958302-2829055647-4275794519-765664414-2751773334"},
+ {L"enterpriseAuthentication", L"S-1-15-3-8"},
+ {L"privateNetworkClientServer", L"S-1-15-3-3"}};
for (auto capability : capabilities) {
- EXPECT_TRUE(EqualSid(Sid::FromNamedCapability(capability.capability_name_),
- capability.sddl_sid_))
- << "Named Capability: " << capability.sddl_sid_;
- EXPECT_TRUE(EqualSid(Sid::FromKnownCapability(capability.capability_),
- capability.sddl_sid_))
- << "Known Capability: " << capability.sddl_sid_;
+ EXPECT_TRUE(EqualSid(Sid::FromNamedCapability(capability.capability_name),
+ capability.sddl_sid))
+ << "Named Capability: " << capability.sddl_sid;
}
}