summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/acl.cc171
-rw-r--r--security/sandbox/chromium/sandbox/win/src/acl.h64
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_profile.h74
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc337
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h94
-rw-r--r--security/sandbox/chromium/sandbox/win/src/app_container_test.cc342
-rw-r--r--security/sandbox/chromium/sandbox/win/src/broker_services.cc745
-rw-r--r--security/sandbox/chromium/sandbox/win/src/broker_services.h105
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_client.h509
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_params.h315
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_server.cc345
-rw-r--r--security/sandbox/chromium/sandbox/win/src/crosscall_server.h261
-rw-r--r--security/sandbox/chromium/sandbox/win/src/eat_resolver.cc88
-rw-r--r--security/sandbox/chromium/sandbox/win/src/eat_resolver.h49
-rw-r--r--security/sandbox/chromium/sandbox/win/src/file_policy_test.cc705
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc302
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h76
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc412
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_interception.h67
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc443
-rw-r--r--security/sandbox/chromium/sandbox/win/src/filesystem_policy.h112
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer.cc185
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer.h76
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc239
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc297
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc93
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h41
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc49
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_interception.cc48
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_interception.h24
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_policy.cc93
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_policy.h39
-rw-r--r--security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc114
-rw-r--r--security/sandbox/chromium/sandbox/win/src/heap_helper.cc124
-rw-r--r--security/sandbox/chromium/sandbox/win/src/heap_helper.h26
-rw-r--r--security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc118
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception.cc512
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception.h290
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_agent.cc234
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_agent.h87
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_internal.h77
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interception_unittest.cc263
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interceptors.h73
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interceptors_64.cc531
-rw-r--r--security/sandbox/chromium/sandbox/win/src/interceptors_64.h330
-rw-r--r--security/sandbox/chromium/sandbox/win/src/internal_types.h68
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_args.cc96
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_args.h24
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc58
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_tags.h59
-rw-r--r--security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc632
-rw-r--r--security/sandbox/chromium/sandbox/win/src/job.cc117
-rw-r--r--security/sandbox/chromium/sandbox/win/src/job.h66
-rw-r--r--security/sandbox/chromium/sandbox/win/src/job_unittest.cc197
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc95
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc80
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h41
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc89
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h43
-rw-r--r--security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc121
-rw-r--r--security/sandbox/chromium/sandbox/win/src/nt_internals.h983
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_broker.cc123
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_broker.h27
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc450
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h379
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_params.h190
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc103
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h143
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc103
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_low_level.cc355
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_low_level.h189
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc684
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc364
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_params.h70
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_target.cc138
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_target.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/policy_target_test.cc486
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations.cc622
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations.h56
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc592
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h89
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc523
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h151
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc410
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h91
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_policy_test.cc548
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc275
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h69
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc520
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_interception.h101
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc269
-rw-r--r--security/sandbox/chromium/sandbox/win/src/process_thread_policy.h91
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc167
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h51
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_interception.cc261
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_interception.h38
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_policy.cc230
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_policy.h56
-rw-r--r--security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc322
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver.cc63
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver.h107
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver_32.cc95
-rw-r--r--security/sandbox/chromium/sandbox/win/src/resolver_64.cc95
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token.cc432
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token.h207
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc829
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc480
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h105
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox.cc47
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox.h228
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox.vcproj648
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_factory.h52
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc18
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h47
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc755
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h220
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_policy.h296
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc832
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h198
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc22
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_rand.h15
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_types.h199
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc32
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sandbox_utils.h24
-rw-r--r--security/sandbox/chromium/sandbox/win/src/security_capabilities.cc33
-rw-r--r--security/sandbox/chromium/sandbox/win/src/security_capabilities.h34
-rw-r--r--security/sandbox/chromium/sandbox/win/src/security_level.h300
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver.cc47
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver.h158
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc476
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc290
-rw-r--r--security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc278
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc193
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h140
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc346
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h137
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sid.cc163
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sid.h74
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sid_unittest.cc182
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc68
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h37
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_interception.cc97
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_interception.h30
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_policy.cc102
-rw-r--r--security/sandbox/chromium/sandbox/win/src/signed_policy.h39
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc82
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h44
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_interception.cc177
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_interception.h46
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy.cc243
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy.h49
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc145
-rw-r--r--security/sandbox/chromium/sandbox/win/src/sync_policy_test.h18
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_interceptions.cc136
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_interceptions.h43
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_process.cc393
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_process.h143
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_services.cc264
-rw-r--r--security/sandbox/chromium/sandbox/win/src/target_services.h73
-rw-r--r--security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc97
-rw-r--r--security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc178
-rw-r--r--security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h54
-rw-r--r--security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc100
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc67
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h61
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win_utils.cc619
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win_utils.h156
-rw-r--r--security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc258
-rw-r--r--security/sandbox/chromium/sandbox/win/src/window.cc147
-rw-r--r--security/sandbox/chromium/sandbox/win/src/window.h37
172 files changed, 34722 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/acl.cc b/security/sandbox/chromium/sandbox/win/src/acl.cc
new file mode 100644
index 0000000000..bd0b181833
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/acl.cc
@@ -0,0 +1,171 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/acl.h"
+
+#include <aclapi.h>
+#include <sddl.h>
+
+#include "base/logging.h"
+#include "base/memory/free_deleter.h"
+
+namespace sandbox {
+
+bool GetDefaultDacl(
+ HANDLE token,
+ std::unique_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter>* default_dacl) {
+ if (!token)
+ return false;
+
+ DCHECK(default_dacl);
+
+ unsigned long length = 0;
+ ::GetTokenInformation(token, TokenDefaultDacl, nullptr, 0, &length);
+ if (length == 0) {
+ NOTREACHED();
+ return false;
+ }
+
+ TOKEN_DEFAULT_DACL* acl =
+ reinterpret_cast<TOKEN_DEFAULT_DACL*>(malloc(length));
+ default_dacl->reset(acl);
+
+ if (!::GetTokenInformation(token, TokenDefaultDacl, default_dacl->get(),
+ length, &length))
+ return false;
+
+ return true;
+}
+
+bool AddSidToDacl(const Sid& sid,
+ ACL* old_dacl,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access,
+ ACL** new_dacl) {
+ EXPLICIT_ACCESS new_access = {0};
+ new_access.grfAccessMode = access_mode;
+ new_access.grfAccessPermissions = access;
+ new_access.grfInheritance = NO_INHERITANCE;
+
+ new_access.Trustee.pMultipleTrustee = nullptr;
+ new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(sid.GetPSID());
+
+ if (ERROR_SUCCESS != ::SetEntriesInAcl(1, &new_access, old_dacl, new_dacl))
+ return false;
+
+ return true;
+}
+
+bool AddSidToDefaultDacl(HANDLE token,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access) {
+ if (!token)
+ return false;
+
+ std::unique_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter> default_dacl;
+ if (!GetDefaultDacl(token, &default_dacl))
+ return false;
+
+ ACL* new_dacl = nullptr;
+ if (!AddSidToDacl(sid, default_dacl->DefaultDacl, access_mode, access,
+ &new_dacl))
+ return false;
+
+ TOKEN_DEFAULT_DACL new_token_dacl = {0};
+ new_token_dacl.DefaultDacl = new_dacl;
+
+ bool ret = ::SetTokenInformation(token, TokenDefaultDacl, &new_token_dacl,
+ sizeof(new_token_dacl));
+ ::LocalFree(new_dacl);
+ return ret;
+}
+
+bool RevokeLogonSidFromDefaultDacl(HANDLE token) {
+ DWORD size = sizeof(TOKEN_GROUPS) + SECURITY_MAX_SID_SIZE;
+ TOKEN_GROUPS* logon_sid = reinterpret_cast<TOKEN_GROUPS*>(malloc(size));
+
+ std::unique_ptr<TOKEN_GROUPS, base::FreeDeleter> logon_sid_ptr(logon_sid);
+
+ if (!::GetTokenInformation(token, TokenLogonSid, logon_sid, size, &size)) {
+ // If no logon sid, there's nothing to revoke.
+ if (::GetLastError() == ERROR_NOT_FOUND)
+ return true;
+ return false;
+ }
+ if (logon_sid->GroupCount < 1) {
+ ::SetLastError(ERROR_INVALID_TOKEN);
+ return false;
+ }
+ return AddSidToDefaultDacl(token,
+ reinterpret_cast<SID*>(logon_sid->Groups[0].Sid),
+ REVOKE_ACCESS, 0);
+}
+
+bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access) {
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(malloc(size));
+
+ std::unique_ptr<TOKEN_USER, base::FreeDeleter> token_user_ptr(token_user);
+
+ if (!::GetTokenInformation(token, TokenUser, token_user, size, &size))
+ return false;
+
+ return AddSidToDefaultDacl(token,
+ reinterpret_cast<SID*>(token_user->User.Sid),
+ GRANT_ACCESS, access);
+}
+
+bool AddKnownSidToObject(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access) {
+ PSECURITY_DESCRIPTOR descriptor = nullptr;
+ PACL old_dacl = nullptr;
+ PACL new_dacl = nullptr;
+
+ if (ERROR_SUCCESS !=
+ ::GetSecurityInfo(object, object_type, DACL_SECURITY_INFORMATION, nullptr,
+ nullptr, &old_dacl, nullptr, &descriptor))
+ return false;
+
+ if (!AddSidToDacl(sid, old_dacl, access_mode, access, &new_dacl)) {
+ ::LocalFree(descriptor);
+ return false;
+ }
+
+ DWORD result =
+ ::SetSecurityInfo(object, object_type, DACL_SECURITY_INFORMATION, nullptr,
+ nullptr, new_dacl, nullptr);
+
+ ::LocalFree(new_dacl);
+ ::LocalFree(descriptor);
+
+ if (ERROR_SUCCESS != result)
+ return false;
+
+ return true;
+}
+
+bool ReplacePackageSidInDacl(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& package_sid,
+ ACCESS_MASK access) {
+ if (!AddKnownSidToObject(object, object_type, package_sid, REVOKE_ACCESS,
+ 0)) {
+ return false;
+ }
+
+ Sid any_package_sid(::WinBuiltinAnyPackageSid);
+ if (!AddKnownSidToObject(object, object_type, any_package_sid, GRANT_ACCESS,
+ access)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/acl.h b/security/sandbox/chromium/sandbox/win/src/acl.h
new file mode 100644
index 0000000000..194edb0988
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/acl.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_ACL_H_
+#define SANDBOX_SRC_ACL_H_
+
+#include <accctrl.h>
+#include <windows.h>
+
+#include <memory>
+
+#include "base/memory/free_deleter.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+// Returns the default dacl from the token passed in.
+bool GetDefaultDacl(
+ HANDLE token,
+ std::unique_ptr<TOKEN_DEFAULT_DACL, base::FreeDeleter>* default_dacl);
+
+// Appends an ACE represented by |sid|, |access_mode|, and |access| to
+// |old_dacl|. If the function succeeds, new_dacl contains the new dacl and
+// must be freed using LocalFree.
+bool AddSidToDacl(const Sid& sid,
+ ACL* old_dacl,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access,
+ ACL** new_dacl);
+
+// Adds an ACE represented by |sid| and |access| with |access_mode| to the
+// default dacl present in the token.
+bool AddSidToDefaultDacl(HANDLE token,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access);
+
+// Revokes access to the logon SID for the default dacl present in the token.
+bool RevokeLogonSidFromDefaultDacl(HANDLE token);
+
+// Adds an ACE represented by the user sid and |access| to the default dacl
+// present in the token.
+bool AddUserSidToDefaultDacl(HANDLE token, ACCESS_MASK access);
+
+// Adds an ACE represented by |known_sid|, |access_mode|, and |access| to
+// the dacl of the kernel object referenced by |object| and of |object_type|.
+bool AddKnownSidToObject(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access);
+
+// Replace package SID in DACL to the "any package" SID. It allows Low-IL
+// tokens to open the object which is important for warm up when using renderer
+// AppContainer.
+bool ReplacePackageSidInDacl(HANDLE object,
+ SE_OBJECT_TYPE object_type,
+ const Sid& package_sid,
+ ACCESS_MASK access);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_ACL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_profile.h b/security/sandbox/chromium/sandbox/win/src/app_container_profile.h
new file mode 100644
index 0000000000..c95c68e552
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_profile.h
@@ -0,0 +1,74 @@
+// 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 "base/files/file_path.h"
+#include "base/win/scoped_handle.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.
+ virtual void AddRef() = 0;
+
+ // 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.
+ virtual void Release() = 0;
+
+ // Get a handle to a registry key for this package.
+ virtual bool GetRegistryLocation(REGSAM desired_access,
+ base::win::ScopedHandle* key) = 0;
+
+ // Get a folder path to a location for this package.
+ virtual bool GetFolderPath(base::FilePath* file_path) = 0;
+
+ // Get a pipe name usable by this AC.
+ virtual bool GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) = 0;
+
+ // 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.
+ virtual bool AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status) = 0;
+
+ // Adds a capability by name to this profile.
+ virtual bool AddCapability(const wchar_t* capability_name) = 0;
+ // Adds a capability from a known list.
+ virtual bool AddCapability(WellKnownCapabilities capability) = 0;
+ // Adds a capability from a SID
+ virtual bool AddCapabilitySddl(const wchar_t* sddl_sid) = 0;
+
+ // Adds an impersonation capability by name to this profile.
+ virtual bool AddImpersonationCapability(const wchar_t* capability_name) = 0;
+ // Adds an impersonation capability from a known list.
+ virtual bool AddImpersonationCapability(WellKnownCapabilities capability) = 0;
+ // Adds an impersonation capability from a SID
+ virtual bool AddImpersonationCapabilitySddl(const wchar_t* sddl_sid) = 0;
+
+ // Enable Low Privilege AC.
+ virtual void SetEnableLowPrivilegeAppContainer(bool enable) = 0;
+ virtual bool GetEnableLowPrivilegeAppContainer() = 0;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_APP_CONTAINER_PROFILE_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc
new file mode 100644
index 0000000000..e388a9978e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.cc
@@ -0,0 +1,337 @@
+// 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"
+#if !defined(MOZ_SANDBOX)
+#include "base/win/scoped_co_mem.h"
+#endif
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/app_container_profile_base.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
+AppContainerProfileBase* AppContainerProfileBase::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 AppContainerProfileBase(Sid(package_sid));
+}
+
+// static
+AppContainerProfileBase* AppContainerProfileBase::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 AppContainerProfileBase(Sid(package_sid));
+}
+
+// static
+bool AppContainerProfileBase::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));
+}
+
+AppContainerProfileBase::AppContainerProfileBase(const Sid& package_sid)
+ : ref_count_(0),
+ package_sid_(package_sid),
+ enable_low_privilege_app_container_(false) {}
+
+AppContainerProfileBase::~AppContainerProfileBase() {}
+
+void AppContainerProfileBase::AddRef() {
+ ::InterlockedIncrement(&ref_count_);
+}
+
+void AppContainerProfileBase::Release() {
+ LONG ref_count = ::InterlockedDecrement(&ref_count_);
+ if (ref_count == 0) {
+ delete this;
+ }
+}
+
+bool AppContainerProfileBase::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 AppContainerProfileBase::GetFolderPath(base::FilePath* file_path) {
+#if defined(MOZ_SANDBOX)
+ IMMEDIATE_CRASH();
+#else
+ static GetAppContainerFolderPathFunc* get_app_container_folder_path =
+ reinterpret_cast<GetAppContainerFolderPathFunc*>(GetProcAddress(
+ GetModuleHandle(L"userenv"), "GetAppContainerFolderPath"));
+ if (!get_app_container_folder_path)
+ return false;
+ std::wstring 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;
+#endif
+}
+
+bool AppContainerProfileBase::GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) {
+#if defined(MOZ_SANDBOX)
+ IMMEDIATE_CRASH();
+#else
+ std::wstring 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;
+#endif
+}
+
+bool AppContainerProfileBase::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;
+ MapGenericMask(&desired_access, &generic_mapping);
+ 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 AppContainerProfileBase::AddCapability(const wchar_t* capability_name) {
+ return AddCapability(Sid::FromNamedCapability(capability_name), false);
+}
+
+bool AppContainerProfileBase::AddCapability(WellKnownCapabilities capability) {
+ return AddCapability(Sid::FromKnownCapability(capability), false);
+}
+
+bool AppContainerProfileBase::AddCapabilitySddl(const wchar_t* sddl_sid) {
+ return AddCapability(Sid::FromSddlString(sddl_sid), false);
+}
+
+bool AppContainerProfileBase::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 AppContainerProfileBase::AddImpersonationCapability(
+ const wchar_t* capability_name) {
+ return AddCapability(Sid::FromNamedCapability(capability_name), true);
+}
+
+bool AppContainerProfileBase::AddImpersonationCapability(
+ WellKnownCapabilities capability) {
+ return AddCapability(Sid::FromKnownCapability(capability), true);
+}
+
+bool AppContainerProfileBase::AddImpersonationCapabilitySddl(
+ const wchar_t* sddl_sid) {
+ return AddCapability(Sid::FromSddlString(sddl_sid), true);
+}
+
+const std::vector<Sid>& AppContainerProfileBase::GetCapabilities() {
+ return capabilities_;
+}
+
+const std::vector<Sid>&
+AppContainerProfileBase::GetImpersonationCapabilities() {
+ return impersonation_capabilities_;
+}
+
+Sid AppContainerProfileBase::GetPackageSid() const {
+ return package_sid_;
+}
+
+void AppContainerProfileBase::SetEnableLowPrivilegeAppContainer(bool enable) {
+ enable_low_privilege_app_container_ = enable;
+}
+
+bool AppContainerProfileBase::GetEnableLowPrivilegeAppContainer() {
+ return enable_low_privilege_app_container_;
+}
+
+std::unique_ptr<SecurityCapabilities>
+AppContainerProfileBase::GetSecurityCapabilities() {
+ return std::unique_ptr<SecurityCapabilities>(
+ new SecurityCapabilities(package_sid_, capabilities_));
+}
+
+bool AppContainerProfileBase::BuildLowBoxToken(base::win::ScopedHandle* token) {
+ return CreateLowBoxToken(nullptr, IMPERSONATION,
+ GetSecurityCapabilities().get(), nullptr, 0,
+ token) == ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h
new file mode 100644
index 0000000000..35fb4efdf5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_profile_base.h
@@ -0,0 +1,94 @@
+// 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_BASE_H_
+#define SANDBOX_SRC_APP_CONTAINER_PROFILE_BASE_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/app_container_profile.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sid.h"
+
+namespace sandbox {
+
+class AppContainerProfileBase final : public AppContainerProfile {
+ public:
+ void AddRef() override;
+ void Release() override;
+ bool GetRegistryLocation(REGSAM desired_access,
+ base::win::ScopedHandle* key) override;
+ bool GetFolderPath(base::FilePath* file_path) override;
+ bool GetPipePath(const wchar_t* pipe_name,
+ base::FilePath* pipe_path) override;
+ bool AccessCheck(const wchar_t* object_name,
+ SE_OBJECT_TYPE object_type,
+ DWORD desired_access,
+ DWORD* granted_access,
+ BOOL* access_status) override;
+ bool AddCapability(const wchar_t* capability_name) override;
+ bool AddCapability(WellKnownCapabilities capability) override;
+ bool AddCapabilitySddl(const wchar_t* sddl_sid) override;
+ bool AddImpersonationCapability(const wchar_t* capability_name) override;
+ bool AddImpersonationCapability(WellKnownCapabilities capability) override;
+ bool AddImpersonationCapabilitySddl(const wchar_t* sddl_sid) override;
+ void SetEnableLowPrivilegeAppContainer(bool enable) override;
+ bool GetEnableLowPrivilegeAppContainer() override;
+
+ // Get the package SID for this AC.
+ Sid GetPackageSid() const;
+
+ // 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 AppContainerProfileBase* 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 AppContainerProfileBase* 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:
+ AppContainerProfileBase(const Sid& package_sid);
+ ~AppContainerProfileBase();
+
+ 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(AppContainerProfileBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_APP_CONTAINER_PROFILE_BASE_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/app_container_test.cc b/security/sandbox/chromium/sandbox/win/src/app_container_test.cc
new file mode 100644
index 0000000000..a6c0948a94
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/app_container_test.cc
@@ -0,0 +1,342 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include <sddl.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile_base.h"
+#include "sandbox/win/src/sync_policy_test.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+const wchar_t kAppContainerSid[] =
+ L"S-1-15-2-3251537155-1984446955-2931258699-841473695-1938553385-"
+ L"924012148-2839372144";
+
+std::wstring GenerateRandomPackageName() {
+ return base::StringPrintf(L"%016lX%016lX", base::RandUint64(),
+ base::RandUint64());
+}
+
+const char* TokenTypeToName(TOKEN_TYPE token_type) {
+ return token_type == ::TokenPrimary ? "Primary Token" : "Impersonation Token";
+}
+
+void CheckToken(HANDLE token,
+ TOKEN_TYPE token_type,
+ PSECURITY_CAPABILITIES security_capabilities,
+ BOOL restricted) {
+ ASSERT_EQ(restricted, ::IsTokenRestricted(token))
+ << TokenTypeToName(token_type);
+
+ DWORD appcontainer;
+ DWORD return_length;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenIsAppContainer, &appcontainer,
+ sizeof(appcontainer), &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_TRUE(appcontainer) << TokenTypeToName(token_type);
+ TOKEN_TYPE token_type_real;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenType, &token_type_real,
+ sizeof(token_type_real), &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_EQ(token_type_real, token_type) << TokenTypeToName(token_type);
+ if (token_type == ::TokenImpersonation) {
+ SECURITY_IMPERSONATION_LEVEL imp_level;
+ ASSERT_TRUE(::GetTokenInformation(token, ::TokenImpersonationLevel,
+ &imp_level, sizeof(imp_level),
+ &return_length))
+ << TokenTypeToName(token_type);
+ ASSERT_EQ(imp_level, ::SecurityImpersonation)
+ << TokenTypeToName(token_type);
+ }
+
+ std::unique_ptr<Sid> package_sid;
+ ASSERT_TRUE(GetTokenAppContainerSid(token, &package_sid))
+ << TokenTypeToName(token_type);
+ EXPECT_TRUE(::EqualSid(security_capabilities->AppContainerSid,
+ package_sid->GetPSID()))
+ << TokenTypeToName(token_type);
+
+ std::vector<SidAndAttributes> capabilities;
+ ASSERT_TRUE(GetTokenGroups(token, ::TokenCapabilities, &capabilities))
+ << TokenTypeToName(token_type);
+
+ ASSERT_EQ(capabilities.size(), security_capabilities->CapabilityCount)
+ << TokenTypeToName(token_type);
+ for (size_t index = 0; index < capabilities.size(); ++index) {
+ EXPECT_EQ(capabilities[index].GetAttributes(),
+ security_capabilities->Capabilities[index].Attributes)
+ << TokenTypeToName(token_type);
+ EXPECT_TRUE(::EqualSid(capabilities[index].GetPSID(),
+ security_capabilities->Capabilities[index].Sid))
+ << TokenTypeToName(token_type);
+ }
+}
+
+void CheckProcessToken(HANDLE process,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool restricted) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ CheckToken(token_handle, ::TokenPrimary, security_capabilities, restricted);
+}
+
+void CheckThreadToken(HANDLE thread,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool restricted) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenThreadToken(thread, TOKEN_ALL_ACCESS, TRUE, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ CheckToken(token_handle, ::TokenImpersonation, security_capabilities,
+ restricted);
+}
+
+// Check for LPAC using an access check. We could query for a security attribute
+// but that's undocumented and has the potential to change.
+void CheckLpacToken(HANDLE process) {
+ HANDLE token_handle;
+ ASSERT_TRUE(::OpenProcessToken(process, TOKEN_ALL_ACCESS, &token_handle));
+ base::win::ScopedHandle token(token_handle);
+ ASSERT_TRUE(
+ ::DuplicateToken(token.Get(), ::SecurityImpersonation, &token_handle));
+ token.Set(token_handle);
+ PSECURITY_DESCRIPTOR security_desc_ptr;
+ // AC is AllPackages, S-1-15-2-2 is AllRestrictedPackages. An LPAC token
+ // will get granted access of 2, where as a normal AC token will get 3.
+ ASSERT_TRUE(::ConvertStringSecurityDescriptorToSecurityDescriptor(
+ L"O:SYG:SYD:(A;;0x3;;;WD)(A;;0x1;;;AC)(A;;0x2;;;S-1-15-2-2)",
+ SDDL_REVISION_1, &security_desc_ptr, nullptr));
+ std::unique_ptr<void, LocalFreeDeleter> security_desc(security_desc_ptr);
+ GENERIC_MAPPING generic_mapping = {};
+ PRIVILEGE_SET priv_set = {};
+ DWORD priv_set_length = sizeof(PRIVILEGE_SET);
+ DWORD granted_access;
+ BOOL access_status;
+ ASSERT_TRUE(::AccessCheck(security_desc_ptr, token.Get(), MAXIMUM_ALLOWED,
+ &generic_mapping, &priv_set, &priv_set_length,
+ &granted_access, &access_status));
+ ASSERT_TRUE(access_status);
+ ASSERT_EQ(DWORD{2}, granted_access);
+}
+
+class AppContainerProfileTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+ package_name_ = GenerateRandomPackageName();
+ broker_services_ = GetBroker();
+ policy_ = broker_services_->CreatePolicy();
+ ASSERT_EQ(SBOX_ALL_OK,
+ policy_->SetProcessMitigations(MITIGATION_HEAP_TERMINATE));
+ ASSERT_EQ(SBOX_ALL_OK,
+ policy_->AddAppContainerProfile(package_name_.c_str(), true));
+ // For testing purposes we known the base class so cast directly.
+ profile_ = static_cast<AppContainerProfileBase*>(
+ policy_->GetAppContainerProfile().get());
+ }
+
+ void TearDown() override {
+ if (scoped_process_info_.IsValid())
+ ::TerminateProcess(scoped_process_info_.process_handle(), 0);
+ if (profile_)
+ AppContainerProfileBase::Delete(package_name_.c_str());
+ }
+
+ protected:
+ void CreateProcess() {
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH] = {};
+ ASSERT_NE(DWORD{0}, ::GetModuleFileNameW(nullptr, prog_name, MAX_PATH));
+
+ PROCESS_INFORMATION process_info = {};
+ ResultCode last_warning = SBOX_ALL_OK;
+ DWORD last_error = 0;
+ ResultCode result = broker_services_->SpawnTarget(
+ prog_name, prog_name, policy_, &last_warning, &last_error,
+ &process_info);
+ ASSERT_EQ(SBOX_ALL_OK, result) << "Last Error: " << last_error;
+ scoped_process_info_.Set(process_info);
+ }
+
+ std::wstring package_name_;
+ BrokerServices* broker_services_;
+ scoped_refptr<AppContainerProfileBase> profile_;
+ scoped_refptr<TargetPolicy> policy_;
+ base::win::ScopedProcessInformation scoped_process_info_;
+};
+
+} // namespace
+
+
+TEST(AppContainerTest, DenyOpenEventForLowBox) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+
+ TestRunner runner(JOB_UNPROTECTED, USER_UNPROTECTED, USER_UNPROTECTED);
+
+ EXPECT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetLowBox(kAppContainerSid));
+ // Run test once, this ensures the app container directory exists, we
+ // ignore the result.
+ runner.RunTest(L"Event_Open f test");
+ std::wstring event_name = L"AppContainerNamedObjects\\";
+ event_name += kAppContainerSid;
+ event_name += L"\\test";
+
+ base::win::ScopedHandle event(
+ ::CreateEvent(nullptr, false, false, event_name.c_str()));
+ ASSERT_TRUE(event.IsValid());
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test"));
+}
+
+TEST_F(AppContainerProfileTest, CheckIncompatibleOptions) {
+ if (!profile_)
+ return;
+ EXPECT_EQ(SBOX_ERROR_BAD_PARAMS,
+ policy_->SetIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED));
+ EXPECT_EQ(SBOX_ERROR_BAD_PARAMS, policy_->SetLowBox(kAppContainerSid));
+
+ MitigationFlags expected_mitigations = 0;
+ MitigationFlags expected_delayed = MITIGATION_HEAP_TERMINATE;
+ sandbox::ResultCode expected_result = SBOX_ERROR_BAD_PARAMS;
+
+ if (base::win::GetVersion() >= base::win::Version::WIN10_RS5) {
+ expected_mitigations = MITIGATION_HEAP_TERMINATE;
+ expected_delayed = 0;
+ expected_result = SBOX_ALL_OK;
+ }
+
+ EXPECT_EQ(expected_mitigations, policy_->GetProcessMitigations());
+ EXPECT_EQ(expected_delayed, policy_->GetDelayedProcessMitigations());
+ EXPECT_EQ(expected_result,
+ policy_->SetProcessMitigations(MITIGATION_HEAP_TERMINATE));
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilities) {
+ if (!profile_)
+ return;
+
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilitiesRestricted) {
+ if (!profile_)
+ return;
+
+ policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), TRUE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), TRUE);
+}
+
+TEST_F(AppContainerProfileTest, WithCapabilities) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+}
+
+TEST_F(AppContainerProfileTest, WithCapabilitiesRestricted) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ policy_->SetTokenLevel(USER_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), TRUE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), TRUE);
+}
+
+TEST_F(AppContainerProfileTest, WithImpersonationCapabilities) {
+ if (!profile_)
+ return;
+
+ profile_->AddCapability(kInternetClient);
+ profile_->AddCapability(kInternetClientServer);
+ profile_->AddImpersonationCapability(kPrivateNetworkClientServer);
+ profile_->AddImpersonationCapability(kPicturesLibrary);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ SecurityCapabilities impersonation_security_capabilities(
+ profile_->GetPackageSid(), profile_->GetImpersonationCapabilities());
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ &impersonation_security_capabilities, FALSE);
+}
+
+TEST_F(AppContainerProfileTest, NoCapabilitiesLPAC) {
+ if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
+ return;
+
+ profile_->SetEnableLowPrivilegeAppContainer(true);
+ policy_->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ policy_->SetJobLevel(JOB_NONE, 0);
+
+ CreateProcess();
+ auto security_capabilities = profile_->GetSecurityCapabilities();
+
+ CheckProcessToken(scoped_process_info_.process_handle(),
+ security_capabilities.get(), FALSE);
+ CheckThreadToken(scoped_process_info_.thread_handle(),
+ security_capabilities.get(), FALSE);
+ CheckLpacToken(scoped_process_info_.process_handle());
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.cc b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
new file mode 100644
index 0000000000..15ed321071
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/broker_services.cc
@@ -0,0 +1,745 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/broker_services.h"
+
+#include <aclapi.h>
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/threading/platform_thread.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/app_container_profile.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+#include "sandbox/win/src/sandbox_policy_diagnostic.h"
+#include "sandbox/win/src/target_process.h"
+#include "sandbox/win/src/win2k_threadpool.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Utility function to associate a completion port to a job object.
+bool AssociateCompletionPort(HANDLE job, HANDLE port, void* key) {
+ JOBOBJECT_ASSOCIATE_COMPLETION_PORT job_acp = {key, port};
+ return ::SetInformationJobObject(job,
+ JobObjectAssociateCompletionPortInformation,
+ &job_acp, sizeof(job_acp))
+ ? true
+ : false;
+}
+
+// Utility function to do the cleanup necessary when something goes wrong
+// while in SpawnTarget and we must terminate the target process.
+sandbox::ResultCode SpawnCleanup(sandbox::TargetProcess* target) {
+ target->Terminate();
+ delete target;
+ return sandbox::SBOX_ERROR_GENERIC;
+}
+
+// the different commands that you can send to the worker thread that
+// executes TargetEventsThread().
+enum {
+ THREAD_CTRL_NONE,
+ THREAD_CTRL_NEW_JOB_TRACKER,
+ THREAD_CTRL_NEW_PROCESS_TRACKER,
+ THREAD_CTRL_PROCESS_SIGNALLED,
+ THREAD_CTRL_GET_POLICY_INFO,
+ THREAD_CTRL_QUIT,
+ THREAD_CTRL_LAST,
+};
+
+// Helper structure that allows the Broker to associate a job notification
+// with a job object and with a policy.
+struct JobTracker {
+ JobTracker(base::win::ScopedHandle job,
+ scoped_refptr<sandbox::PolicyBase> policy,
+ DWORD process_id)
+ : job(std::move(job)), policy(policy), process_id(process_id) {}
+ ~JobTracker() { FreeResources(); }
+
+ // Releases the Job and notifies the associated Policy object to release its
+ // resources as well.
+ void FreeResources();
+
+ base::win::ScopedHandle job;
+ scoped_refptr<sandbox::PolicyBase> policy;
+ DWORD process_id;
+};
+
+void JobTracker::FreeResources() {
+ if (policy) {
+ bool res = ::TerminateJobObject(job.Get(), sandbox::SBOX_ALL_OK);
+ DCHECK(res);
+ // Closing the job causes the target process to be destroyed so this needs
+ // to happen before calling OnJobEmpty().
+ HANDLE stale_job_handle = job.Get();
+ job.Close();
+
+ // In OnJobEmpty() we don't actually use the job handle directly.
+ policy->OnJobEmpty(stale_job_handle);
+ policy = nullptr;
+ }
+}
+
+// tracks processes that are not in jobs
+struct ProcessTracker {
+ ProcessTracker(scoped_refptr<sandbox::PolicyBase> policy,
+ DWORD process_id,
+ base::win::ScopedHandle process)
+ : policy(policy), process_id(process_id), process(std::move(process)) {}
+ ~ProcessTracker() { FreeResources(); }
+
+ void FreeResources();
+
+ scoped_refptr<sandbox::PolicyBase> policy;
+ DWORD process_id;
+ base::win::ScopedHandle process;
+ // Used to UnregisterWait. Not a real handle so cannot CloseHandle().
+ HANDLE wait_handle;
+ // IOCP that is tracking this non-job process
+ HANDLE iocp;
+};
+
+void ProcessTracker::FreeResources() {
+ if (policy) {
+ policy->OnJobEmpty(nullptr);
+ policy = nullptr;
+ }
+}
+
+// Helper redispatches process events to tracker thread.
+void WINAPI ProcessEventCallback(PVOID param, BOOLEAN ignored) {
+ // This callback should do very little, and must be threadpool safe.
+ ProcessTracker* tracker = reinterpret_cast<ProcessTracker*>(param);
+ // If this fails we can do nothing... we will leak the policy.
+ ::PostQueuedCompletionStatus(tracker->iocp, 0, THREAD_CTRL_PROCESS_SIGNALLED,
+ reinterpret_cast<LPOVERLAPPED>(tracker));
+}
+
+// Helper class to send policy lists
+class PolicyDiagnosticList final : public sandbox::PolicyList {
+ public:
+ PolicyDiagnosticList() {}
+ ~PolicyDiagnosticList() override {}
+ void push_back(std::unique_ptr<sandbox::PolicyInfo> info) {
+ internal_list_.push_back(std::move(info));
+ }
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator begin() override {
+ return internal_list_.begin();
+ }
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>>::iterator end() override {
+ return internal_list_.end();
+ }
+ size_t size() const override { return internal_list_.size(); }
+
+ private:
+ std::vector<std::unique_ptr<sandbox::PolicyInfo>> internal_list_;
+};
+
+} // namespace
+
+namespace sandbox {
+
+BrokerServicesBase::BrokerServicesBase() {}
+
+// The broker uses a dedicated worker thread that services the job completion
+// port to perform policy notifications and associated cleanup tasks.
+ResultCode BrokerServicesBase::Init() {
+ if (job_port_.IsValid() || thread_pool_)
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
+ ::InitializeCriticalSection(&lock_);
+
+ job_port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0));
+ if (!job_port_.IsValid())
+ return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
+
+ no_targets_.Set(::CreateEventW(nullptr, true, false, nullptr));
+
+ job_thread_.Set(::CreateThread(nullptr, 0, // Default security and stack.
+ TargetEventsThread, this, 0, nullptr));
+ if (!job_thread_.IsValid())
+ return SBOX_ERROR_CANNOT_INIT_BROKERSERVICES;
+
+ return SBOX_ALL_OK;
+}
+
+// The destructor should only be called when the Broker process is terminating.
+// Since BrokerServicesBase is a singleton, this is called from the CRT
+// termination handlers, if this code lives on a DLL it is called during
+// DLL_PROCESS_DETACH in other words, holding the loader lock, so we cannot
+// wait for threads here.
+BrokerServicesBase::~BrokerServicesBase() {
+ // If there is no port Init() was never called successfully.
+ if (!job_port_.IsValid())
+ return;
+
+ // Closing the port causes, that no more Job notifications are delivered to
+ // the worker thread and also causes the thread to exit. This is what we
+ // want to do since we are going to close all outstanding Jobs and notifying
+ // the policy objects ourselves.
+ ::PostQueuedCompletionStatus(job_port_.Get(), 0, THREAD_CTRL_QUIT, nullptr);
+
+ if (job_thread_.IsValid() &&
+ WAIT_TIMEOUT == ::WaitForSingleObject(job_thread_.Get(), 1000)) {
+ // Cannot clean broker services.
+ NOTREACHED();
+ return;
+ }
+ thread_pool_.reset();
+ ::DeleteCriticalSection(&lock_);
+}
+
+scoped_refptr<TargetPolicy> BrokerServicesBase::CreatePolicy() {
+ // If you change the type of the object being created here you must also
+ // change the downcast to it in SpawnTarget().
+ scoped_refptr<TargetPolicy> policy(new PolicyBase);
+ // PolicyBase starts with refcount 1.
+ policy->Release();
+ return policy;
+}
+
+// The worker thread stays in a loop waiting for asynchronous notifications
+// from the job objects. Right now we only care about knowing when the last
+// process on a job terminates, but in general this is the place to tell
+// the policy about events.
+DWORD WINAPI BrokerServicesBase::TargetEventsThread(PVOID param) {
+ if (!param)
+ return 1;
+
+ base::PlatformThread::SetName("BrokerEvent");
+
+ BrokerServicesBase* broker = reinterpret_cast<BrokerServicesBase*>(param);
+ HANDLE port = broker->job_port_.Get();
+ HANDLE no_targets = broker->no_targets_.Get();
+
+ std::set<DWORD> child_process_ids;
+ std::list<std::unique_ptr<JobTracker>> jobs;
+ std::list<std::unique_ptr<ProcessTracker>> processes;
+ int target_counter = 0;
+ int untracked_target_counter = 0;
+ ::ResetEvent(no_targets);
+
+ while (true) {
+ DWORD events = 0;
+ ULONG_PTR key = 0;
+ LPOVERLAPPED ovl = nullptr;
+
+ if (!::GetQueuedCompletionStatus(port, &events, &key, &ovl, INFINITE)) {
+ // this call fails if the port has been closed before we have a
+ // chance to service the last packet which is 'exit' anyway so
+ // this is not an error.
+ return 1;
+ }
+
+ if (key > THREAD_CTRL_LAST) {
+ // The notification comes from a job object. There are nine notifications
+ // that jobs can send and some of them depend on the job attributes set.
+ JobTracker* tracker = reinterpret_cast<JobTracker*>(key);
+
+ // Processes may be added to a job after the process count has
+ // reached zero, leading us to manipulate a freed JobTracker
+ // object or job handle (as the key is no longer valid). We
+ // therefore check if the tracker has already been deleted.
+ if (std::find_if(jobs.begin(), jobs.end(), [&](auto&& p) -> bool {
+ return p.get() == tracker;
+ }) == jobs.end()) {
+ CHECK(false);
+ }
+
+ switch (events) {
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: {
+ // The job object has signaled that the last process associated
+ // with it has terminated. It is safe to free the tracker
+ // and release its reference to the associated policy object
+ // which will Close the job handle.
+ HANDLE job_handle = tracker->job.Get();
+
+ // Erase by comparing with the job handle.
+ jobs.erase(std::remove_if(jobs.begin(), jobs.end(),
+ [&](auto&& p) -> bool {
+ return p->job.Get() == job_handle;
+ }),
+ jobs.end());
+ break;
+ }
+
+ case JOB_OBJECT_MSG_NEW_PROCESS: {
+ // Child process created from sandboxed process.
+ DWORD process_id =
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl));
+ size_t count = child_process_ids.count(process_id);
+ if (count == 0)
+ untracked_target_counter++;
+ ++target_counter;
+ if (1 == target_counter) {
+ ::ResetEvent(no_targets);
+ }
+ break;
+ }
+
+ case JOB_OBJECT_MSG_EXIT_PROCESS:
+ case JOB_OBJECT_MSG_ABNORMAL_EXIT_PROCESS: {
+ {
+ AutoLock lock(&broker->lock_);
+ broker->active_targets_.erase(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ }
+ size_t erase_result = child_process_ids.erase(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(ovl)));
+ if (erase_result != 1U) {
+ // The process was untracked e.g. a child process of the target.
+ --untracked_target_counter;
+ DCHECK(untracked_target_counter >= 0);
+ }
+ --target_counter;
+ if (0 == target_counter)
+ ::SetEvent(no_targets);
+
+ DCHECK(target_counter >= 0);
+ break;
+ }
+
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT: {
+ // A child process attempted and failed to create a child process.
+ // Windows does not reveal the process id.
+ untracked_target_counter++;
+ target_counter++;
+ break;
+ }
+
+ case JOB_OBJECT_MSG_PROCESS_MEMORY_LIMIT: {
+ bool res = ::TerminateJobObject(tracker->job.Get(),
+ SBOX_FATAL_MEMORY_EXCEEDED);
+ DCHECK(res);
+ break;
+ }
+
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ } else if (THREAD_CTRL_NEW_JOB_TRACKER == key) {
+ std::unique_ptr<JobTracker> tracker;
+ tracker.reset(reinterpret_cast<JobTracker*>(ovl));
+ DCHECK(tracker->job.IsValid());
+
+ child_process_ids.insert(tracker->process_id);
+ jobs.push_back(std::move(tracker));
+
+ } else if (THREAD_CTRL_NEW_PROCESS_TRACKER == key) {
+ std::unique_ptr<ProcessTracker> tracker;
+ tracker.reset(reinterpret_cast<ProcessTracker*>(ovl));
+
+ if (child_process_ids.empty()) {
+ ::SetEvent(broker->no_targets_.Get());
+ }
+
+ tracker->iocp = port;
+ if (!::RegisterWaitForSingleObject(&(tracker->wait_handle),
+ tracker->process.Get(),
+ ProcessEventCallback, tracker.get(),
+ INFINITE, WT_EXECUTEONLYONCE)) {
+ // Failed. Invalidate the wait_handle and store anyway.
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ }
+ processes.push_back(std::move(tracker));
+
+ } else if (THREAD_CTRL_PROCESS_SIGNALLED == key) {
+ ProcessTracker* tracker =
+ static_cast<ProcessTracker*>(reinterpret_cast<void*>(ovl));
+
+ {
+ AutoLock lock(&broker->lock_);
+ broker->active_targets_.erase(tracker->process_id);
+ }
+
+ ::UnregisterWait(tracker->wait_handle);
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ // Copy process_id so that we can legally reference it even after we have
+ // found the ProcessTracker object to delete.
+ const DWORD process_id = tracker->process_id;
+ // PID is unique until the process handle is closed in dtor.
+ processes.erase(std::remove_if(processes.begin(), processes.end(),
+ [&](auto&& p) -> bool {
+ return p->process_id == process_id;
+ }),
+ processes.end());
+ } else if (THREAD_CTRL_GET_POLICY_INFO == key) {
+ // Clone the policies for sandbox diagnostics.
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver;
+ receiver.reset(static_cast<PolicyDiagnosticsReceiver*>(
+ reinterpret_cast<void*>(ovl)));
+ // The PollicyInfo ctor copies essential information from the trackers.
+ auto policy_list = std::make_unique<PolicyDiagnosticList>();
+ for (auto&& process_tracker : processes) {
+ if (process_tracker->policy) {
+ policy_list->push_back(std::make_unique<PolicyDiagnostic>(
+ process_tracker->policy.get()));
+ }
+ }
+ for (auto&& job_tracker : jobs) {
+ if (job_tracker->policy) {
+ policy_list->push_back(
+ std::make_unique<PolicyDiagnostic>(job_tracker->policy.get()));
+ }
+ }
+ // Receiver should return quickly.
+ receiver->ReceiveDiagnostics(std::move(policy_list));
+
+ } else if (THREAD_CTRL_QUIT == key) {
+ // The broker object is being destroyed so the thread needs to exit.
+ for (auto&& tracker : processes) {
+ ::UnregisterWait(tracker->wait_handle);
+ tracker->wait_handle = INVALID_HANDLE_VALUE;
+ }
+ // After this point, so further calls to ProcessEventCallback can
+ // occur. Other tracked objects are destroyed as this thread ends.
+ return 0;
+ } else {
+ // We have not implemented more commands.
+ NOTREACHED();
+ }
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+// SpawnTarget does all the interesting sandbox setup and creates the target
+// process inside the sandbox.
+ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target_info) {
+ if (!exe_path)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!policy)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ // Even though the resources touched by SpawnTarget can be accessed in
+ // multiple threads, the method itself cannot be called from more than one
+ // thread. This is to protect the global variables used while setting up the
+ // child process, and to make sure launcher thread mitigations are applied
+ // correctly.
+ static DWORD thread_id = ::GetCurrentThreadId();
+ DCHECK(thread_id == ::GetCurrentThreadId());
+ *last_warning = SBOX_ALL_OK;
+
+ // Launcher thread only needs to be opted out of ACG once. Do this on the
+ // first child process being spawned.
+ static bool launcher_thread_opted_out = false;
+
+ if (!launcher_thread_opted_out) {
+ // Soft fail this call. It will fail if ACG is not enabled for this process.
+ sandbox::ApplyMitigationsToCurrentThread(
+ sandbox::MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD);
+ launcher_thread_opted_out = true;
+ }
+
+ // This downcast is safe as long as we control CreatePolicy()
+ scoped_refptr<PolicyBase> policy_base(static_cast<PolicyBase*>(policy.get()));
+
+ // Construct the tokens and the job object that we are going to associate
+ // with the soon to be created target process.
+ base::win::ScopedHandle initial_token;
+ base::win::ScopedHandle lockdown_token;
+ base::win::ScopedHandle lowbox_token;
+ ResultCode result = SBOX_ALL_OK;
+
+ result =
+ policy_base->MakeTokens(&initial_token, &lockdown_token, &lowbox_token);
+ if (SBOX_ALL_OK != result)
+ return result;
+ if (lowbox_token.IsValid() &&
+ base::win::GetVersion() < base::win::Version::WIN8) {
+ // We don't allow lowbox_token below Windows 8.
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ base::win::ScopedHandle job;
+ result = policy_base->MakeJobObject(&job);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Initialize the startup information from the policy.
+ base::win::StartupInformation startup_info;
+
+ // We don't want any child processes causing the IDC_APPSTARTING cursor.
+ startup_info.startup_info()->dwFlags |= STARTF_FORCEOFFFEEDBACK;
+
+ // The liftime of |mitigations|, |inherit_handle_list| and
+ // |child_process_creation| have to be at least as long as
+ // |startup_info| because |UpdateProcThreadAttribute| requires that
+ // its |lpValue| parameter persist until |DeleteProcThreadAttributeList| is
+ // called; StartupInformation's destructor makes such a call.
+ DWORD64 mitigations[2];
+ std::vector<HANDLE> inherited_handle_list;
+ DWORD child_process_creation = PROCESS_CREATION_CHILD_PROCESS_RESTRICTED;
+
+ std::wstring desktop = policy_base->GetAlternateDesktop();
+ if (!desktop.empty()) {
+ startup_info.startup_info()->lpDesktop =
+ const_cast<wchar_t*>(desktop.c_str());
+ }
+
+ bool inherit_handles = false;
+
+ int attribute_count = 0;
+
+ size_t mitigations_size;
+ ConvertProcessMitigationsToPolicy(policy_base->GetProcessMitigations(),
+ &mitigations[0], &mitigations_size);
+ if (mitigations[0] || mitigations[1])
+ ++attribute_count;
+
+ bool restrict_child_process_creation = false;
+ if (base::win::GetVersion() >= base::win::Version::WIN10_TH2 &&
+ policy_base->GetJobLevel() <= JOB_LIMITED_USER) {
+ restrict_child_process_creation = true;
+ ++attribute_count;
+ }
+
+ HANDLE stdout_handle = policy_base->GetStdoutHandle();
+ HANDLE stderr_handle = policy_base->GetStderrHandle();
+
+ if (stdout_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stdout_handle);
+
+ // Handles in the list must be unique.
+ if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE)
+ inherited_handle_list.push_back(stderr_handle);
+
+ const auto& policy_handle_list = policy_base->GetHandlesBeingShared();
+
+ for (HANDLE handle : policy_handle_list)
+ inherited_handle_list.push_back(handle);
+
+ if (inherited_handle_list.size())
+ ++attribute_count;
+
+ scoped_refptr<AppContainerProfileBase> profile =
+ policy_base->GetAppContainerProfileBase();
+ if (profile) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ // LPAC first supported in RS1.
+ if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
+ return SBOX_ERROR_BAD_PARAMS;
+ ++attribute_count;
+ }
+ }
+
+ if (!startup_info.InitializeProcThreadAttributeList(attribute_count))
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+
+ if (mitigations[0] || mitigations[1]) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &mitigations[0],
+ mitigations_size)) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+
+ if (restrict_child_process_creation) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY, &child_process_creation,
+ sizeof(child_process_creation))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+
+ if (inherited_handle_list.size()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &inherited_handle_list[0],
+ sizeof(HANDLE) * inherited_handle_list.size())) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES;
+ startup_info.startup_info()->hStdInput = INVALID_HANDLE_VALUE;
+ startup_info.startup_info()->hStdOutput = stdout_handle;
+ startup_info.startup_info()->hStdError = stderr_handle;
+ // Allowing inheritance of handles is only secure now that we
+ // have limited which handles will be inherited.
+ inherit_handles = true;
+ }
+
+ // Declared here to ensure they stay in scope until after process creation.
+ std::unique_ptr<SecurityCapabilities> security_capabilities;
+ DWORD all_applications_package_policy =
+ PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT;
+
+ if (profile) {
+ security_capabilities = profile->GetSecurityCapabilities();
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
+ security_capabilities.get(), sizeof(SECURITY_CAPABILITIES))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ if (profile->GetEnableLowPrivilegeAppContainer()) {
+ if (!startup_info.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY,
+ &all_applications_package_policy,
+ sizeof(all_applications_package_policy))) {
+ return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
+ }
+ }
+ }
+
+ // Construct the thread pool here in case it is expensive.
+ // The thread pool is shared by all the targets
+ if (!thread_pool_)
+ thread_pool_ = std::make_unique<Win2kThreadPool>();
+
+ // Create the TargetProcess object and spawn the target suspended. Note that
+ // Brokerservices does not own the target object. It is owned by the Policy.
+ base::win::ScopedProcessInformation process_info;
+ TargetProcess* target = new TargetProcess(
+ std::move(initial_token), std::move(lockdown_token), job.Get(),
+ thread_pool_.get(),
+ profile ? profile->GetImpersonationCapabilities() : std::vector<Sid>());
+
+ result = target->Create(exe_path, command_line, inherit_handles, startup_info,
+ &process_info, env_map, last_error);
+
+ if (result != SBOX_ALL_OK) {
+ SpawnCleanup(target);
+ return result;
+ }
+
+ if (lowbox_token.IsValid()) {
+ *last_warning = target->AssignLowBoxToken(lowbox_token);
+ // If this fails we continue, but report the error as a warning.
+ // This is due to certain configurations causing the setting of the
+ // token to fail post creation, and we'd rather continue if possible.
+ if (*last_warning != SBOX_ALL_OK)
+ *last_error = ::GetLastError();
+ }
+
+ // Now the policy is the owner of the target.
+ result = policy_base->AddTarget(target);
+
+ if (result != SBOX_ALL_OK) {
+ *last_error = ::GetLastError();
+ SpawnCleanup(target);
+ return result;
+ }
+
+ if (job.IsValid()) {
+ JobTracker* tracker =
+ new JobTracker(std::move(job), policy_base, process_info.process_id());
+
+ // Post the tracker to the tracking thread, then associate the job with
+ // the tracker. The worker thread takes ownership of these objects.
+ CHECK(::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_NEW_JOB_TRACKER,
+ reinterpret_cast<LPOVERLAPPED>(tracker)));
+ // There is no obvious recovery after failure here. Previous version with
+ // SpawnCleanup() caused deletion of TargetProcess twice. crbug.com/480639
+ CHECK(
+ AssociateCompletionPort(tracker->job.Get(), job_port_.Get(), tracker));
+
+ AutoLock lock(&lock_);
+ active_targets_.insert(process_info.process_id());
+ } else {
+ result = AddTargetPeerInternal(process_info.process_handle(),
+ process_info.process_id(),
+ policy_base, last_error);
+ if (result != SBOX_ALL_OK) {
+ // This may fail in the same way as Job associated processes.
+ // crbug.com/480639.
+ target->Terminate();
+ return result;
+ }
+ }
+
+ *target_info = process_info.Take();
+ return result;
+}
+
+ResultCode BrokerServicesBase::WaitForAllTargets() {
+ ::WaitForSingleObject(no_targets_.Get(), INFINITE);
+ return SBOX_ALL_OK;
+}
+
+bool BrokerServicesBase::IsSafeDuplicationTarget(DWORD process_id) {
+ AutoLock lock(&lock_);
+ return active_targets_.find(process_id) != active_targets_.end();
+}
+
+ResultCode BrokerServicesBase::AddTargetPeerInternal(
+ HANDLE peer_process_handle,
+ DWORD peer_process_id,
+ scoped_refptr<PolicyBase> policy_base,
+ DWORD* last_error) {
+ // Duplicate the process handle to give the tracking machinery
+ // something valid to wait on in the tracking thread.
+ HANDLE tmp_process_handle = INVALID_HANDLE_VALUE;
+ if (!::DuplicateHandle(::GetCurrentProcess(), peer_process_handle,
+ ::GetCurrentProcess(), &tmp_process_handle,
+ SYNCHRONIZE, false, 0 /*no options*/)) {
+ *last_error = ::GetLastError();
+ return SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE;
+ }
+ base::win::ScopedHandle dup_process_handle(tmp_process_handle);
+ ProcessTracker* tracker = new ProcessTracker(
+ policy_base, peer_process_id, std::move(dup_process_handle));
+ // The tracker and policy will leak if this call fails.
+ ::PostQueuedCompletionStatus(job_port_.Get(), 0,
+ THREAD_CTRL_NEW_PROCESS_TRACKER,
+ reinterpret_cast<LPOVERLAPPED>(tracker));
+
+ AutoLock lock(&lock_);
+ active_targets_.insert(peer_process_id);
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode BrokerServicesBase::AddTargetPeer(HANDLE peer_process) {
+ DWORD last_error;
+ return AddTargetPeerInternal(peer_process, ::GetProcessId(peer_process),
+ nullptr, &last_error);
+}
+
+ResultCode BrokerServicesBase::GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) {
+ CHECK(job_thread_.IsValid());
+ // Post to the job thread.
+ if (!::PostQueuedCompletionStatus(
+ job_port_.Get(), 0, THREAD_CTRL_GET_POLICY_INFO,
+ reinterpret_cast<LPOVERLAPPED>(receiver.get()))) {
+ receiver->OnError(SBOX_ERROR_GENERIC);
+ return SBOX_ERROR_GENERIC;
+ }
+
+ // Ownership has passed to tracker thread.
+ receiver.release();
+ return SBOX_ALL_OK;
+}
+
+bool BrokerServicesBase::DeriveCapabilitySidFromName(const wchar_t* name,
+ PSID derived_sid,
+ DWORD sid_buffer_length) {
+ return ::CopySid(sid_buffer_length, derived_sid,
+ Sid::FromNamedCapability(name).GetPSID());
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/broker_services.h b/security/sandbox/chromium/sandbox/win/src/broker_services.h
new file mode 100644
index 0000000000..64dc6d66e5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/broker_services.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_BROKER_SERVICES_H_
+#define SANDBOX_WIN_SRC_BROKER_SERVICES_H_
+
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/environment.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"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "sandbox/win/src/win2k_threadpool.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+// BrokerServicesBase ---------------------------------------------------------
+// Broker implementation version 0
+//
+// This is an implementation of the interface BrokerServices and
+// of the associated TargetProcess interface. In this implementation
+// TargetProcess is a friend of BrokerServices where the later manages a
+// collection of the former.
+class BrokerServicesBase final : public BrokerServices,
+ public SingletonBase<BrokerServicesBase> {
+ public:
+ BrokerServicesBase();
+
+ ~BrokerServicesBase();
+
+ // BrokerServices interface.
+ ResultCode Init() override;
+ scoped_refptr<TargetPolicy> CreatePolicy() override;
+ ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target) override;
+ ResultCode WaitForAllTargets() override;
+ ResultCode AddTargetPeer(HANDLE peer_process) override;
+
+ // Checks if the supplied process ID matches one of the broker's active
+ // target processes. We use this method for the specific purpose of
+ // checking if we can safely duplicate a handle to the supplied process
+ // in DuplicateHandleProxyAction.
+ bool IsSafeDuplicationTarget(DWORD process_id);
+
+ ResultCode GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) override;
+
+ bool DeriveCapabilitySidFromName(const wchar_t* name, PSID derived_sid,
+ DWORD sid_buffer_length) override;
+
+ private:
+ // The routine that the worker thread executes. It is in charge of
+ // notifications and cleanup-related tasks.
+ static DWORD WINAPI TargetEventsThread(PVOID param);
+
+ // The completion port used by the job objects to communicate events to
+ // the worker thread.
+ base::win::ScopedHandle job_port_;
+
+ // Handle to a manual-reset event that is signaled when the total target
+ // process count reaches zero.
+ base::win::ScopedHandle no_targets_;
+
+ // Handle to the worker thread that reacts to job notifications.
+ base::win::ScopedHandle job_thread_;
+
+ // Provides a pool of threads that are used to wait on the IPC calls.
+ std::unique_ptr<ThreadProvider> thread_pool_;
+
+ // The set representing the broker's active target processes including
+ // both sandboxed and unsandboxed peer processes.
+ std::set<DWORD> active_targets_;
+
+ // Lock used to protect active_targets_ from being simultaneously accessed
+ // by multiple threads.
+ CRITICAL_SECTION lock_;
+
+ ResultCode AddTargetPeerInternal(HANDLE peer_process_handle,
+ DWORD peer_process_id,
+ scoped_refptr<PolicyBase> policy_base,
+ DWORD* last_error);
+
+ DISALLOW_COPY_AND_ASSIGN(BrokerServicesBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_BROKER_SERVICES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_client.h b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h
new file mode 100644
index 0000000000..a346aeb753
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_client.h
@@ -0,0 +1,509 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_CLIENT_H_
+#define SANDBOX_SRC_CROSSCALL_CLIENT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/compiler_specific.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/sandbox.h"
+
+// This header defines the CrossCall(..) family of templated functions
+// Their purpose is to simulate the syntax of regular call but to generate
+// and IPC from the client-side.
+//
+// The basic pattern is to
+// 1) use template argument deduction to compute the size of each
+// parameter and the appropriate copy method
+// 2) pack the parameters in the appropriate ActualCallParams< > object
+// 3) call the IPC interface IPCProvider::DoCall( )
+//
+// The general interface of CrossCall is:
+// ResultCode CrossCall(IPCProvider& ipc_provider,
+// uint32_t tag,
+// const Par1& p1, const Par2& p2,...pn
+// CrossCallReturn* answer)
+//
+// where:
+// ipc_provider: is a specific implementation of the ipc transport see
+// sharedmem_ipc_server.h for an example.
+// tag : is the unique id for this IPC call. Is used to route the call to
+// the appropriate service.
+// p1, p2,.. pn : The input parameters of the IPC. Use only simple types
+// and wide strings (can add support for others).
+// answer : If the IPC was successful. The server-side answer is here. The
+// interpretation of the answer is private to client and server.
+//
+// The return value is ALL_OK if the IPC was delivered to the server, other
+// return codes indicate that the IPC transport failed to deliver it.
+namespace sandbox {
+
+enum class IpcTag;
+
+// The copy helper uses templates to deduce the appropriate copy function to
+// copy the input parameters in the buffer that is going to be send across the
+// IPC. These template facility can be made more sophisticated as need arises.
+
+// The default copy helper. It catches the general case where no other
+// specialized template matches better. We set the type to UINT32_TYPE, so this
+// only works with objects whose size is 32 bits.
+template <typename T>
+class CopyHelper {
+ public:
+ CopyHelper(const T& t) : t_(t) {}
+
+ // Returns the pointer to the start of the input.
+ const void* GetStart() const { return &t_; }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the input in bytes.
+ uint32_t GetSize() const { return sizeof(T); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ // Returns this object's type.
+ ArgType GetType() {
+ static_assert(sizeof(T) == sizeof(uint32_t), "specialization needed");
+ return UINT32_TYPE;
+ }
+
+ private:
+ const T& t_;
+};
+
+// This copy helper template specialization if for the void pointer
+// case both 32 and 64 bit.
+template <>
+class CopyHelper<void*> {
+ public:
+ CopyHelper(void* t) : t_(t) {}
+
+ // Returns the pointer to the start of the input.
+ const void* GetStart() const { return &t_; }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the input in bytes.
+ uint32_t GetSize() const { return sizeof(t_); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ // Returns this object's type.
+ ArgType GetType() { return VOIDPTR_TYPE; }
+
+ private:
+ const void* t_;
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a pointer to a string.
+template <>
+class CopyHelper<const wchar_t*> {
+ public:
+ CopyHelper(const wchar_t* t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const { return t_; }
+
+ // Update the stored value with the value in the buffer. This is not
+ // supported for this type.
+ bool Update(void* buffer) {
+ // Not supported;
+ return true;
+ }
+
+ // Returns the size of the string in bytes. We define a nullptr string to
+ // be of zero length.
+ uint32_t GetSize() const {
+ __try {
+ return (!t_) ? 0
+ : static_cast<uint32_t>(StringLength(t_) * sizeof(t_[0]));
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return UINT32_MAX;
+ }
+ }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ ArgType GetType() { return WCHAR_TYPE; }
+
+ private:
+ // We provide our not very optimized version of wcslen(), since we don't
+ // want to risk having the linker use the version in the CRT since the CRT
+ // might not be present when we do an early IPC call.
+ static size_t CDECL StringLength(const wchar_t* wcs) {
+ const wchar_t* eos = wcs;
+ while (*eos++)
+ ;
+ return static_cast<size_t>(eos - wcs - 1);
+ }
+
+ const wchar_t* t_;
+};
+
+// Specialization for non-const strings. We just reuse the implementation of the
+// const string specialization.
+template <>
+class CopyHelper<wchar_t*> : public CopyHelper<const wchar_t*> {
+ public:
+ typedef CopyHelper<const wchar_t*> Base;
+ CopyHelper(wchar_t* t) : Base(t) {}
+
+ const void* GetStart() const { return Base::GetStart(); }
+
+ bool Update(void* buffer) { return Base::Update(buffer); }
+
+ uint32_t GetSize() const { return Base::GetSize(); }
+
+ bool IsInOut() { return Base::IsInOut(); }
+
+ ArgType GetType() { return Base::GetType(); }
+};
+
+// Specialization for wchar_t arrays strings. We just reuse the implementation
+// of the const string specialization.
+template <size_t n>
+class CopyHelper<const wchar_t[n]> : public CopyHelper<const wchar_t*> {
+ public:
+ typedef const wchar_t array[n];
+ typedef CopyHelper<const wchar_t*> Base;
+ CopyHelper(array t) : Base(t) {}
+
+ const void* GetStart() const { return Base::GetStart(); }
+
+ bool Update(void* buffer) { return Base::Update(buffer); }
+
+ uint32_t GetSize() const { return Base::GetSize(); }
+
+ bool IsInOut() { return Base::IsInOut(); }
+
+ ArgType GetType() { return Base::GetType(); }
+};
+
+// Generic encapsulation class containing a pointer to a buffer and the
+// size of the buffer. It is used by the IPC to be able to pass in/out
+// parameters.
+class InOutCountedBuffer : public CountedBuffer {
+ public:
+ InOutCountedBuffer(void* buffer, uint32_t size)
+ : CountedBuffer(buffer, size) {}
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a an input buffer.
+template <>
+class CopyHelper<CountedBuffer> {
+ public:
+ CopyHelper(const CountedBuffer t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const { return t_.Buffer(); }
+
+ // Update not required so just return true;
+ bool Update(void* buffer) { return true; }
+
+ // Returns the size of the string in bytes. We define a nullptr string to
+ // be of zero length.
+ uint32_t GetSize() const { return t_.Size(); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return false; }
+
+ ArgType GetType() { return INPTR_TYPE; }
+
+ private:
+ const CountedBuffer t_;
+};
+
+// This copy helper template specialization catches the cases where the
+// parameter is a an input/output buffer.
+template <>
+class CopyHelper<InOutCountedBuffer> {
+ public:
+ CopyHelper(const InOutCountedBuffer t) : t_(t) {}
+
+ // Returns the pointer to the start of the string.
+ const void* GetStart() const { return t_.Buffer(); }
+
+ // Updates the buffer with the value from the new buffer in parameter.
+ bool Update(void* buffer) {
+ // We are touching user memory, this has to be done from inside a try
+ // except.
+ __try {
+ memcpy_wrapper(t_.Buffer(), buffer, t_.Size());
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+ }
+
+ // Returns the size of the string in bytes. We define a nullptr string to
+ // be of zero length.
+ uint32_t GetSize() const { return t_.Size(); }
+
+ // Returns true if the current type is used as an In or InOut parameter.
+ bool IsInOut() { return true; }
+
+ ArgType GetType() { return INOUTPTR_TYPE; }
+
+ private:
+ const InOutCountedBuffer t_;
+};
+
+// The following two macros make it less error prone the generation
+// of CrossCall functions with ever more input parameters.
+
+#define XCALL_GEN_PARAMS_OBJ(num, params) \
+ typedef ActualCallParams<num, kIPCChannelSize> ActualParams; \
+ void* raw_mem = ipc_provider.GetBuffer(); \
+ if (!raw_mem) \
+ return SBOX_ERROR_NO_SPACE; \
+ ActualParams* params = new (raw_mem) ActualParams(tag);
+
+#define XCALL_GEN_COPY_PARAM(num, params) \
+ static_assert(kMaxIpcParams >= num, "too many parameters"); \
+ CopyHelper<Par##num> ch##num(p##num); \
+ if (!params->CopyParamIn(num - 1, ch##num.GetStart(), ch##num.GetSize(), \
+ ch##num.IsInOut(), ch##num.GetType())) \
+ return SBOX_ERROR_NO_SPACE;
+
+#define XCALL_GEN_UPDATE_PARAM(num, params) \
+ if (!ch##num.Update(params->GetParamPtr(num - 1))) { \
+ ipc_provider.FreeBuffer(raw_mem); \
+ return SBOX_ERROR_BAD_PARAMS; \
+ }
+
+#define XCALL_GEN_FREE_CHANNEL() ipc_provider.FreeBuffer(raw_mem);
+
+// CrossCall template with one input parameter
+template <typename IPCProvider, typename Par1>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(1, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+
+ return result;
+}
+
+// CrossCall template with two input parameters.
+template <typename IPCProvider, typename Par1, typename Par2>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(2, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with three input parameters.
+template <typename IPCProvider, typename Par1, typename Par2, typename Par3>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(3, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with four input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(4, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with five input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4,
+ typename Par5>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ const Par5& p5,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(5, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with six input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4,
+ typename Par5,
+ typename Par6>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ const Par5& p5,
+ const Par6& p6,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(6, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+ XCALL_GEN_COPY_PARAM(6, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_UPDATE_PARAM(6, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+
+// CrossCall template with seven input parameters.
+template <typename IPCProvider,
+ typename Par1,
+ typename Par2,
+ typename Par3,
+ typename Par4,
+ typename Par5,
+ typename Par6,
+ typename Par7>
+ResultCode CrossCall(IPCProvider& ipc_provider,
+ IpcTag tag,
+ const Par1& p1,
+ const Par2& p2,
+ const Par3& p3,
+ const Par4& p4,
+ const Par5& p5,
+ const Par6& p6,
+ const Par7& p7,
+ CrossCallReturn* answer) {
+ XCALL_GEN_PARAMS_OBJ(7, call_params);
+ XCALL_GEN_COPY_PARAM(1, call_params);
+ XCALL_GEN_COPY_PARAM(2, call_params);
+ XCALL_GEN_COPY_PARAM(3, call_params);
+ XCALL_GEN_COPY_PARAM(4, call_params);
+ XCALL_GEN_COPY_PARAM(5, call_params);
+ XCALL_GEN_COPY_PARAM(6, call_params);
+ XCALL_GEN_COPY_PARAM(7, call_params);
+
+ ResultCode result = ipc_provider.DoCall(call_params, answer);
+
+ if (SBOX_ERROR_CHANNEL_ERROR != result) {
+ XCALL_GEN_UPDATE_PARAM(1, call_params);
+ XCALL_GEN_UPDATE_PARAM(2, call_params);
+ XCALL_GEN_UPDATE_PARAM(3, call_params);
+ XCALL_GEN_UPDATE_PARAM(4, call_params);
+ XCALL_GEN_UPDATE_PARAM(5, call_params);
+ XCALL_GEN_UPDATE_PARAM(6, call_params);
+ XCALL_GEN_UPDATE_PARAM(7, call_params);
+ XCALL_GEN_FREE_CHANNEL();
+ }
+ return result;
+}
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_CLIENT_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_params.h b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
new file mode 100644
index 0000000000..6971b55cb7
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_params.h
@@ -0,0 +1,315 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_PARAMS_H__
+#define SANDBOX_SRC_CROSSCALL_PARAMS_H__
+
+#if !defined(SANDBOX_FUZZ_TARGET)
+#include <windows.h>
+
+#include <lmaccess.h>
+#else
+#include "sandbox/win/fuzzer/fuzzer_types.h"
+#endif
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/win/src/internal_types.h"
+#if !defined(SANDBOX_FUZZ_TARGET)
+#include "sandbox/win/src/sandbox_nt_types.h"
+#endif
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+// This header is part of CrossCall: the sandbox inter-process communication.
+// This header defines the basic types used both in the client IPC and in the
+// server IPC code. CrossCallParams and ActualCallParams model the input
+// parameters of an IPC call and CrossCallReturn models the output params and
+// the return value.
+//
+// An IPC call is defined by its 'tag' which is a (uint32_t) unique identifier
+// that is used to route the IPC call to the proper server. Every tag implies
+// a complete call signature including the order and type of each parameter.
+//
+// Like most IPC systems. CrossCall is designed to take as inputs 'simple'
+// types such as integers and strings. Classes, generic arrays or pointers to
+// them are not supported.
+//
+// Another limitation of CrossCall is that the return value and output
+// parameters can only be uint32_t integers. Returning complex structures or
+// strings is not supported.
+
+namespace sandbox {
+
+// this is the assumed channel size. This can be overridden in a given
+// IPC implementation.
+const uint32_t kIPCChannelSize = 1024;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+namespace {
+
+// Increases |value| until there is no need for padding given an int64_t
+// alignment. Returns the increased value.
+inline uint32_t Align(uint32_t value) {
+ uint32_t alignment = sizeof(int64_t);
+ return ((value + alignment - 1) / alignment) * alignment;
+}
+
+inline void* memcpy_wrapper(void* dest, const void* src, size_t count) {
+ if (g_nt.memcpy)
+ return g_nt.memcpy(dest, src, count);
+ return memcpy(dest, src, count);
+}
+
+} // namespace
+
+// max number of extended return parameters. See CrossCallReturn
+const size_t kExtendedReturnCount = 8;
+
+// Union of multiple types to be used as extended results
+// in the CrossCallReturn.
+union MultiType {
+ uint32_t unsigned_int;
+ void* pointer;
+ HANDLE handle;
+ ULONG_PTR ulong_ptr;
+};
+
+// Maximum number of IPC parameters currently supported.
+// To increase this value, we have to:
+// - Add another Callback typedef to Dispatcher.
+// - Add another case to the switch on SharedMemIPCServer::InvokeCallback.
+// - Add another case to the switch in GetActualAndMaxBufferSize
+// - Add another case to the switch in GetMinDeclaredActualCallParamsSize
+const int kMaxIpcParams = 9;
+
+// Contains the information about a parameter in the ipc buffer.
+struct ParamInfo {
+ ArgType type_;
+ uint32_t offset_;
+ uint32_t size_;
+};
+
+// Models the return value and the return parameters of an IPC call
+// currently limited to one status code and eight generic return values
+// which cannot be pointers to other data. For x64 ports this structure
+// might have to use other integer types.
+struct CrossCallReturn {
+ // the IPC tag. It should match the original IPC tag.
+ uint32_t tag;
+ // The result of the IPC operation itself.
+ ResultCode call_outcome;
+ // the result of the IPC call as executed in the server. The interpretation
+ // of this value depends on the specific service.
+ union {
+ NTSTATUS nt_status;
+ DWORD win32_result;
+ };
+ // Number of extended return values.
+ uint32_t extended_count;
+ // for calls that should return a windows handle. It is found here.
+ HANDLE handle;
+ // The array of extended values.
+ MultiType extended[kExtendedReturnCount];
+};
+
+// CrossCallParams base class that models the input params all packed in a
+// single compact memory blob. The representation can vary but in general a
+// given child of this class is meant to represent all input parameters
+// necessary to make a IPC call.
+//
+// This class cannot have virtual members because its assumed the IPC
+// parameters start from the 'this' pointer to the end, which is defined by
+// one of the subclasses
+//
+// Objects of this class cannot be constructed directly. Only derived
+// classes have the proper knowledge to construct it.
+class CrossCallParams {
+ public:
+ // Returns the tag (ipc unique id) associated with this IPC.
+ IpcTag GetTag() const { return tag_; }
+
+ // Returns the beggining of the buffer where the IPC params can be stored.
+ // prior to an IPC call
+ const void* GetBuffer() const { return this; }
+
+ // Returns how many parameter this IPC call should have.
+ uint32_t GetParamsCount() const { return params_count_; }
+
+ // Returns a pointer to the CrossCallReturn structure.
+ CrossCallReturn* GetCallReturn() { return &call_return; }
+
+ // Returns true if this call contains InOut parameters.
+ bool IsInOut() const { return (1 == is_in_out_); }
+
+ // Tells the CrossCall object if it contains InOut parameters.
+ void SetIsInOut(bool value) {
+ if (value)
+ is_in_out_ = 1;
+ else
+ is_in_out_ = 0;
+ }
+
+ protected:
+ // constructs the IPC call params. Called only from the derived classes
+ CrossCallParams(IpcTag tag, uint32_t params_count)
+ : tag_(tag), is_in_out_(0), params_count_(params_count) {}
+
+ private:
+ IpcTag tag_;
+ uint32_t is_in_out_;
+ CrossCallReturn call_return;
+ const uint32_t params_count_;
+ DISALLOW_COPY_AND_ASSIGN(CrossCallParams);
+};
+
+// ActualCallParams models an specific IPC call parameters with respect to the
+// storage allocation that the packed parameters should need.
+// NUMBER_PARAMS: the number of parameters, valid from 1 to N
+// BLOCK_SIZE: the total storage that the NUMBER_PARAMS parameters can take,
+// typically the block size is defined by the channel size of the underlying
+// ipc mechanism.
+// In practice this class is used to levergage C++ capacity to properly
+// calculate sizes and displacements given the possibility of the packed params
+// blob to be complex.
+//
+// As is, this class assumes that the layout of the blob is as follows. Assume
+// that NUMBER_PARAMS = 2 and a 32-bit build:
+//
+// [ tag 4 bytes]
+// [ IsOnOut 4 bytes]
+// [ call return 52 bytes]
+// [ params count 4 bytes]
+// [ parameter 0 type 4 bytes]
+// [ parameter 0 offset 4 bytes] ---delta to ---\
+// [ parameter 0 size 4 bytes] |
+// [ parameter 1 type 4 bytes] |
+// [ parameter 1 offset 4 bytes] ---------------|--\
+// [ parameter 1 size 4 bytes] | |
+// [ parameter 2 type 4 bytes] | |
+// [ parameter 2 offset 4 bytes] ----------------------\
+// [ parameter 2 size 4 bytes] | | |
+// |---------------------------| | | |
+// | value 0 (x bytes) | <--------------/ | |
+// | value 1 (y bytes) | <-----------------/ |
+// | | |
+// | end of buffer | <---------------------/
+// |---------------------------|
+//
+// Note that the actual number of params is NUMBER_PARAMS + 1
+// so that the size of each actual param can be computed from the difference
+// between one parameter and the next down. The offset of the last param
+// points to the end of the buffer and the type and size are undefined.
+//
+template <size_t NUMBER_PARAMS, size_t BLOCK_SIZE>
+class ActualCallParams : public CrossCallParams {
+ public:
+ // constructor. Pass the ipc unique tag as input
+ explicit ActualCallParams(IpcTag tag) : CrossCallParams(tag, NUMBER_PARAMS) {
+ param_info_[0].offset_ =
+ static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
+ }
+
+ // Testing-only constructor. Allows setting the |number_params| to a
+ // wrong value.
+ ActualCallParams(IpcTag tag, uint32_t number_params)
+ : CrossCallParams(tag, number_params) {
+ param_info_[0].offset_ =
+ static_cast<uint32_t>(parameters_ - reinterpret_cast<char*>(this));
+ }
+
+ static constexpr size_t MaxParamsSize() {
+ return sizeof(
+ ActualCallParams<NUMBER_PARAMS, kIPCChannelSize>::parameters_);
+ }
+
+ // Testing-only method. Allows setting the apparent size to a wrong value.
+ // returns the previous size.
+ uint32_t OverrideSize(uint32_t new_size) {
+ uint32_t previous_size = param_info_[NUMBER_PARAMS].offset_;
+ param_info_[NUMBER_PARAMS].offset_ = new_size;
+ return previous_size;
+ }
+
+ // Copies each paramter into the internal buffer. For each you must supply:
+ // index: 0 for the first param, 1 for the next an so on
+ bool CopyParamIn(uint32_t index,
+ const void* parameter_address,
+ uint32_t size,
+ bool is_in_out,
+ ArgType type) {
+ if (index >= NUMBER_PARAMS) {
+ return false;
+ }
+
+ if (UINT32_MAX == size) {
+ // Memory error while getting the size.
+ return false;
+ }
+
+ if (size && !parameter_address) {
+ return false;
+ }
+
+ if ((size > sizeof(*this)) ||
+ (param_info_[index].offset_ > (sizeof(*this) - size))) {
+ // It does not fit, abort copy.
+ return false;
+ }
+
+ char* dest = reinterpret_cast<char*>(this) + param_info_[index].offset_;
+
+ // We might be touching user memory, this has to be done from inside a try
+ // except.
+ __try {
+ memcpy_wrapper(dest, parameter_address, size);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+
+ // Set the flag to tell the broker to update the buffer once the call is
+ // made.
+ if (is_in_out)
+ SetIsInOut(true);
+
+ param_info_[index + 1].offset_ = Align(param_info_[index].offset_ + size);
+ param_info_[index].size_ = size;
+ param_info_[index].type_ = type;
+ return true;
+ }
+
+ // Returns a pointer to a parameter in the memory section.
+ void* GetParamPtr(size_t index) {
+ return reinterpret_cast<char*>(this) + param_info_[index].offset_;
+ }
+
+ // Returns the total size of the buffer. Only valid once all the paramters
+ // have been copied in with CopyParamIn.
+ uint32_t GetSize() const { return param_info_[NUMBER_PARAMS].offset_; }
+
+ protected:
+ ActualCallParams() : CrossCallParams(IpcTag::UNUSED, NUMBER_PARAMS) {}
+
+ private:
+ ParamInfo param_info_[NUMBER_PARAMS + 1];
+ char parameters_[BLOCK_SIZE - sizeof(CrossCallParams) -
+ sizeof(ParamInfo) * (NUMBER_PARAMS + 1)];
+ DISALLOW_COPY_AND_ASSIGN(ActualCallParams);
+
+ friend uint32_t GetMinDeclaredActualCallParamsSize(uint32_t param_count);
+};
+
+static_assert(sizeof(ActualCallParams<1, 1024>) == 1024, "bad size buffer");
+static_assert(sizeof(ActualCallParams<2, 1024>) == 1024, "bad size buffer");
+static_assert(sizeof(ActualCallParams<3, 1024>) == 1024, "bad size buffer");
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_PARAMS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
new file mode 100644
index 0000000000..27bfdfbd65
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/crosscall_server.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <atomic>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_params.h"
+
+// See comment in atomicops.h. This is needed any time windows.h is included
+// after atomicops.h.
+#undef MemoryBarrier
+
+// This code performs the ipc message validation. Potential security flaws
+// on the ipc are likelier to be found in this code than in the rest of
+// the ipc code.
+
+namespace {
+
+// The buffer for a message must match the max channel size.
+const size_t kMaxBufferSize = sandbox::kIPCChannelSize;
+
+} // namespace
+
+namespace sandbox {
+
+// The template types are used to calculate the maximum expected size.
+typedef ActualCallParams<0, kMaxBufferSize> ActualCP0;
+typedef ActualCallParams<1, kMaxBufferSize> ActualCP1;
+typedef ActualCallParams<2, kMaxBufferSize> ActualCP2;
+typedef ActualCallParams<3, kMaxBufferSize> ActualCP3;
+typedef ActualCallParams<4, kMaxBufferSize> ActualCP4;
+typedef ActualCallParams<5, kMaxBufferSize> ActualCP5;
+typedef ActualCallParams<6, kMaxBufferSize> ActualCP6;
+typedef ActualCallParams<7, kMaxBufferSize> ActualCP7;
+typedef ActualCallParams<8, kMaxBufferSize> ActualCP8;
+typedef ActualCallParams<9, kMaxBufferSize> ActualCP9;
+
+// Returns the actual size for the parameters in an IPC buffer. Returns
+// zero if the |param_count| is zero or too big.
+uint32_t GetActualBufferSize(uint32_t param_count, void* buffer_base) {
+ // Retrieve the actual size and the maximum size of the params buffer.
+ switch (param_count) {
+ case 0:
+ return 0;
+ case 1:
+ return reinterpret_cast<ActualCP1*>(buffer_base)->GetSize();
+ case 2:
+ return reinterpret_cast<ActualCP2*>(buffer_base)->GetSize();
+ case 3:
+ return reinterpret_cast<ActualCP3*>(buffer_base)->GetSize();
+ case 4:
+ return reinterpret_cast<ActualCP4*>(buffer_base)->GetSize();
+ case 5:
+ return reinterpret_cast<ActualCP5*>(buffer_base)->GetSize();
+ case 6:
+ return reinterpret_cast<ActualCP6*>(buffer_base)->GetSize();
+ case 7:
+ return reinterpret_cast<ActualCP7*>(buffer_base)->GetSize();
+ case 8:
+ return reinterpret_cast<ActualCP8*>(buffer_base)->GetSize();
+ case 9:
+ return reinterpret_cast<ActualCP9*>(buffer_base)->GetSize();
+ default:
+ return 0;
+ }
+}
+
+// Returns the minimum size for the parameters in an IPC buffer. Returns
+// zero if the |param_count| is less than zero or too big.
+uint32_t GetMinDeclaredActualCallParamsSize(uint32_t param_count) {
+ switch (param_count) {
+ case 0:
+ return offsetof(ActualCP0, parameters_);
+ case 1:
+ return offsetof(ActualCP1, parameters_);
+ case 2:
+ return offsetof(ActualCP2, parameters_);
+ case 3:
+ return offsetof(ActualCP3, parameters_);
+ case 4:
+ return offsetof(ActualCP4, parameters_);
+ case 5:
+ return offsetof(ActualCP5, parameters_);
+ case 6:
+ return offsetof(ActualCP6, parameters_);
+ case 7:
+ return offsetof(ActualCP7, parameters_);
+ case 8:
+ return offsetof(ActualCP8, parameters_);
+ case 9:
+ return offsetof(ActualCP9, parameters_);
+ default:
+ return 0;
+ }
+}
+
+// Verifies that the declared sizes of an IPC buffer are within range.
+bool IsSizeWithinRange(uint32_t buffer_size,
+ uint32_t min_declared_size,
+ uint32_t declared_size) {
+ if ((buffer_size < min_declared_size) ||
+ (sizeof(CrossCallParamsEx) > min_declared_size)) {
+ // Minimal computed size bigger than existing buffer or param_count
+ // integer overflow.
+ return false;
+ }
+
+ if ((declared_size > buffer_size) || (declared_size < min_declared_size)) {
+ // Declared size is bigger than buffer or smaller than computed size
+ // or param_count is equal to 0 or bigger than 9.
+ return false;
+ }
+
+ return true;
+}
+
+CrossCallParamsEx::CrossCallParamsEx() : CrossCallParams(IpcTag::UNUSED, 0) {}
+
+// We override the delete operator because the object's backing memory
+// is hand allocated in CreateFromBuffer. We don't override the new operator
+// because the constructors are private so there is no way to mismatch
+// new & delete.
+void CrossCallParamsEx::operator delete(void* raw_memory) throw() {
+ if (!raw_memory) {
+ // C++ standard allows 'delete 0' behavior.
+ return;
+ }
+ delete[] reinterpret_cast<char*>(raw_memory);
+}
+
+// This function uses a SEH try block so cannot use C++ objects that
+// have destructors or else you get Compiler Error C2712. So no DCHECKs
+// inside this function.
+CrossCallParamsEx* CrossCallParamsEx::CreateFromBuffer(void* buffer_base,
+ uint32_t buffer_size,
+ uint32_t* output_size) {
+ // IMPORTANT: Everything inside buffer_base and derived from it such
+ // as param_count and declared_size is untrusted.
+ if (!buffer_base)
+ return nullptr;
+ if (buffer_size < sizeof(CrossCallParams))
+ return nullptr;
+ if (buffer_size > kMaxBufferSize)
+ return nullptr;
+
+ char* backing_mem = nullptr;
+ uint32_t param_count = 0;
+ uint32_t declared_size;
+ uint32_t min_declared_size;
+ CrossCallParamsEx* copied_params = nullptr;
+
+ // Touching the untrusted buffer is done under a SEH try block. This
+ // will catch memory access violations so we don't crash.
+ __try {
+ CrossCallParams* call_params =
+ reinterpret_cast<CrossCallParams*>(buffer_base);
+
+ // Check against the minimum size given the number of stated params
+ // if too small we bail out.
+ param_count = call_params->GetParamsCount();
+ min_declared_size = GetMinDeclaredActualCallParamsSize(param_count);
+
+ // Initial check for the buffer being big enough to determine the actual
+ // buffer size.
+ if (buffer_size < min_declared_size)
+ return nullptr;
+
+ // Retrieve the declared size which if it fails returns 0.
+ declared_size = GetActualBufferSize(param_count, buffer_base);
+
+ if (!IsSizeWithinRange(buffer_size, min_declared_size, declared_size))
+ return nullptr;
+
+ // Now we copy the actual amount of the message.
+ *output_size = declared_size;
+ backing_mem = new char[declared_size];
+ copied_params = reinterpret_cast<CrossCallParamsEx*>(backing_mem);
+ memcpy(backing_mem, call_params, declared_size);
+
+ // Avoid compiler optimizations across this point. Any value stored in
+ // memory should be stored for real, and values previously read from memory
+ // should be actually read.
+ std::atomic_thread_fence(std::memory_order_seq_cst);
+
+ min_declared_size = GetMinDeclaredActualCallParamsSize(param_count);
+
+ // Check that the copied buffer is still valid.
+ if (copied_params->GetParamsCount() != param_count ||
+ GetActualBufferSize(param_count, backing_mem) != declared_size ||
+ !IsSizeWithinRange(buffer_size, min_declared_size, declared_size)) {
+ delete[] backing_mem;
+ return nullptr;
+ }
+
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ // In case of a windows exception we know it occurred while touching the
+ // untrusted buffer so we bail out as is.
+ delete[] backing_mem;
+ return nullptr;
+ }
+
+ // Here and below we're making use of uintptr_t to have well-defined integer
+ // overflow when doing pointer arithmetic.
+ auto backing_mem_ptr = reinterpret_cast<uintptr_t>(backing_mem);
+ auto last_byte = reinterpret_cast<uintptr_t>(&backing_mem[declared_size]);
+ auto first_byte =
+ reinterpret_cast<uintptr_t>(&backing_mem[min_declared_size]);
+
+ // Verify here that all and each parameters make sense. This is done in the
+ // local copy.
+ for (uint32_t ix = 0; ix != param_count; ++ix) {
+ uint32_t size = 0;
+ ArgType type;
+ auto address = reinterpret_cast<uintptr_t>(
+ copied_params->GetRawParameter(ix, &size, &type));
+ if ((!address) || // No null params.
+ (INVALID_TYPE >= type) || (LAST_TYPE <= type) || // Unknown type.
+ (address < backing_mem_ptr) || // Start cannot point before buffer.
+ (address < first_byte) || // Start cannot point too low.
+ (address > last_byte) || // Start cannot point past buffer.
+ ((address + size) < address) || // Invalid size.
+ ((address + size) > last_byte)) { // End cannot point past buffer.
+ // Malformed.
+ delete[] backing_mem;
+ return nullptr;
+ }
+ }
+ // The parameter buffer looks good.
+ return copied_params;
+}
+
+// Accessors to the parameters in the raw buffer.
+void* CrossCallParamsEx::GetRawParameter(uint32_t index,
+ uint32_t* size,
+ ArgType* type) {
+ if (index >= GetParamsCount())
+ return nullptr;
+ // The size is always computed from the parameter minus the next
+ // parameter, this works because the message has an extra parameter slot
+ *size = param_info_[index].size_;
+ *type = param_info_[index].type_;
+
+ return param_info_[index].offset_ + reinterpret_cast<char*>(this);
+}
+
+// Covers common case for 32 bit integers.
+bool CrossCallParamsEx::GetParameter32(uint32_t index, uint32_t* param) {
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if (!start || (4 != size) || (UINT32_TYPE != type))
+ return false;
+ // Copy the 4 bytes.
+ *(reinterpret_cast<uint32_t*>(param)) = *(reinterpret_cast<uint32_t*>(start));
+ return true;
+}
+
+bool CrossCallParamsEx::GetParameterVoidPtr(uint32_t index, void** param) {
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if (!start || (sizeof(void*) != size) || (VOIDPTR_TYPE != type))
+ return false;
+ *param = *(reinterpret_cast<void**>(start));
+ return true;
+}
+
+// Covers the common case of reading a string. Note that the string is not
+// scanned for invalid characters.
+bool CrossCallParamsEx::GetParameterStr(uint32_t index, std::wstring* string) {
+ DCHECK(string->empty());
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+ if (WCHAR_TYPE != type)
+ return false;
+
+ // Check if this is an empty string.
+ if (size == 0) {
+ *string = std::wstring();
+ return true;
+ }
+
+ if (!start || ((size % sizeof(wchar_t)) != 0))
+ return false;
+
+ string->assign(reinterpret_cast<const wchar_t*>(start),
+ size / sizeof(wchar_t));
+ return true;
+}
+
+bool CrossCallParamsEx::GetParameterPtr(uint32_t index,
+ uint32_t expected_size,
+ void** pointer) {
+ uint32_t size = 0;
+ ArgType type;
+ void* start = GetRawParameter(index, &size, &type);
+
+ if ((size != expected_size) || (INOUTPTR_TYPE != type && INPTR_TYPE != type))
+ return false;
+
+ if (!start)
+ return false;
+
+ *pointer = start;
+ return true;
+}
+
+void SetCallError(ResultCode error, CrossCallReturn* call_return) {
+ call_return->call_outcome = error;
+ call_return->extended_count = 0;
+}
+
+void SetCallSuccess(CrossCallReturn* call_return) {
+ call_return->call_outcome = SBOX_ALL_OK;
+}
+
+Dispatcher* Dispatcher::OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) {
+ DCHECK(callback);
+ std::vector<IPCCall>::iterator it = ipc_calls_.begin();
+ for (; it != ipc_calls_.end(); ++it) {
+ if (it->params.Matches(ipc)) {
+ *callback = it->callback;
+ return this;
+ }
+ }
+ return nullptr;
+}
+
+Dispatcher::Dispatcher() {}
+
+Dispatcher::~Dispatcher() {}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/crosscall_server.h b/security/sandbox/chromium/sandbox/win/src/crosscall_server.h
new file mode 100644
index 0000000000..aed7f99aae
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/crosscall_server.h
@@ -0,0 +1,261 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_CROSSCALL_SERVER_H_
+#define SANDBOX_SRC_CROSSCALL_SERVER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/ipc_tags.h"
+
+// This is the IPC server interface for CrossCall: The IPC for the Sandbox
+// On the server, CrossCall needs two things:
+// 1) threads: Or better said, someone to provide them, that is what the
+// ThreadProvider interface is defined for. These thread(s) are
+// the ones that will actually execute the IPC data retrieval.
+//
+// 2) a dispatcher: This interface represents the way to route and process
+// an IPC call given the IPC tag.
+//
+// The other class included here CrossCallParamsEx is the server side version
+// of the CrossCallParams class of /sandbox/crosscall_params.h The difference
+// is that the sever version is paranoid about the correctness of the IPC
+// message and will do all sorts of verifications.
+//
+// A general diagram of the interaction is as follows:
+//
+// ------------
+// | |
+// ThreadProvider <--(1)Register--| IPC |
+// | | Implemen |
+// | | -tation |
+// (2) | | OnMessage
+// IPC fired --callback ------>| |--(3)---> Dispatcher
+// | |
+// ------------
+//
+// The IPC implementation sits as a middleman between the handling of the
+// specifics of scheduling a thread to service the IPC and the multiple
+// entities that can potentially serve each particular IPC.
+namespace sandbox {
+
+class InterceptionManager;
+
+// This function signature is required as the callback when an IPC call fires.
+// context: a user-defined pointer that was set using ThreadProvider
+// reason: 0 if the callback was fired because of a timeout.
+// 1 if the callback was fired because of an event.
+typedef void(__stdcall* CrossCallIPCCallback)(void* context,
+ unsigned char reason);
+
+// ThreadProvider models a thread factory. The idea is to decouple thread
+// creation and lifetime from the inner guts of the IPC. The contract is
+// simple:
+// - the IPC implementation calls RegisterWait with a waitable object that
+// becomes signaled when an IPC arrives and needs to be serviced.
+// - when the waitable object becomes signaled, the thread provider conjures
+// a thread that calls the callback (CrossCallIPCCallback) function
+// - the callback function tries its best not to block and return quickly
+// and should not assume that the next callback will use the same thread
+// - when the callback returns the ThreadProvider owns again the thread
+// and can destroy it or keep it around.
+class ThreadProvider {
+ public:
+ // Registers a waitable object with the thread provider.
+ // client: A number to associate with all the RegisterWait calls, typically
+ // this is the address of the caller object. This parameter cannot
+ // be zero.
+ // waitable_object : a kernel object that can be waited on
+ // callback: a function pointer which is the function that will be called
+ // when the waitable object fires
+ // context: a user-provider pointer that is passed back to the callback
+ // when its called
+ virtual bool RegisterWait(const void* client,
+ HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) = 0;
+
+ // Removes all the registrations done with the same cookie parameter.
+ // This frees internal thread pool resources.
+ virtual bool UnRegisterWaits(void* cookie) = 0;
+ virtual ~ThreadProvider() {}
+};
+
+// Models the server-side of the original input parameters.
+// Provides IPC buffer validation and it is capable of reading the parameters
+// out of the IPC buffer.
+class CrossCallParamsEx : public CrossCallParams {
+ public:
+ // Factory constructor. Pass an IPCbuffer (and buffer size) that contains a
+ // pending IPCcall. This constructor will:
+ // 1) validate the IPC buffer. returns nullptr is the IPCbuffer is malformed.
+ // 2) make a copy of the IPCbuffer (parameter capture)
+ static CrossCallParamsEx* CreateFromBuffer(void* buffer_base,
+ uint32_t buffer_size,
+ uint32_t* output_size);
+
+ // Provides IPCinput parameter raw access:
+ // index : the parameter to read; 0 is the first parameter
+ // returns nullptr if the parameter is non-existent. If it exists it also
+ // returns the size in *size
+ void* GetRawParameter(uint32_t index, uint32_t* size, ArgType* type);
+
+ // Gets a parameter that is four bytes in size.
+ // Returns false if the parameter does not exist or is not 32 bits wide.
+ bool GetParameter32(uint32_t index, uint32_t* param);
+
+ // Gets a parameter that is void pointer in size.
+ // Returns false if the parameter does not exist or is not void pointer sized.
+ bool GetParameterVoidPtr(uint32_t index, void** param);
+
+ // Gets a parameter that is a string. Returns false if the parameter does not
+ // exist.
+ bool GetParameterStr(uint32_t index, std::wstring* string);
+
+ // Gets a parameter that is an in/out buffer. Returns false is the parameter
+ // does not exist or if the size of the actual parameter is not equal to the
+ // expected size.
+ bool GetParameterPtr(uint32_t index, uint32_t expected_size, void** pointer);
+
+ // Frees the memory associated with the IPC parameters.
+ static void operator delete(void* raw_memory) throw();
+
+ private:
+ // Only the factory method CreateFromBuffer can construct these objects.
+ CrossCallParamsEx();
+
+ ParamInfo param_info_[1];
+ DISALLOW_COPY_AND_ASSIGN(CrossCallParamsEx);
+};
+
+// Simple helper function that sets the members of CrossCallReturn
+// to the proper state to signal a basic error.
+void SetCallError(ResultCode error, CrossCallReturn* call_return);
+
+// Sets the internal status of call_return to signify the that IPC call
+// completed successfully.
+void SetCallSuccess(CrossCallReturn* call_return);
+
+// Represents the client process that initiated the IPC which boils down to the
+// process handle and the job object handle that contains the client process.
+struct ClientInfo {
+ HANDLE process;
+ DWORD process_id;
+};
+
+// All IPC-related information to be passed to the IPC handler.
+struct IPCInfo {
+ IpcTag ipc_tag;
+ const ClientInfo* client_info;
+ CrossCallReturn return_info;
+};
+
+// This structure identifies IPC signatures.
+struct IPCParams {
+ IpcTag ipc_tag;
+ ArgType args[kMaxIpcParams];
+
+ bool Matches(IPCParams* other) const {
+ return !memcmp(this, other, sizeof(*other));
+ }
+};
+
+// Models an entity that can process an IPC message or it can route to another
+// one that could handle it. When an IPC arrives the IPC implementation will:
+// 1) call OnMessageReady() with the tag of the pending IPC. If the dispatcher
+// returns nullptr it means that it cannot handle this IPC but if it returns
+// non-null, it must be the pointer to a dispatcher that can handle it.
+// 2) When the IPC finally obtains a valid Dispatcher the IPC
+// implementation creates a CrossCallParamsEx from the raw IPC buffer.
+// 3) It calls the returned callback, with the IPC info and arguments.
+class Dispatcher {
+ public:
+ // Called from the IPC implementation to handle a specific IPC message.
+ typedef bool (Dispatcher::*CallbackGeneric)();
+ typedef bool (Dispatcher::*Callback0)(IPCInfo* ipc);
+ typedef bool (Dispatcher::*Callback1)(IPCInfo* ipc, void* p1);
+ typedef bool (Dispatcher::*Callback2)(IPCInfo* ipc, void* p1, void* p2);
+ typedef bool (Dispatcher::*Callback3)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3);
+ typedef bool (Dispatcher::*Callback4)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4);
+ typedef bool (Dispatcher::*Callback5)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5);
+ typedef bool (Dispatcher::*Callback6)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6);
+ typedef bool (Dispatcher::*Callback7)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6,
+ void* p7);
+ typedef bool (Dispatcher::*Callback8)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6,
+ void* p7,
+ void* p8);
+ typedef bool (Dispatcher::*Callback9)(IPCInfo* ipc,
+ void* p1,
+ void* p2,
+ void* p3,
+ void* p4,
+ void* p5,
+ void* p6,
+ void* p7,
+ void* p8,
+ void* p9);
+
+ // Called from the IPC implementation when an IPC message is ready override
+ // on a derived class to handle a set of IPC messages. Return nullptr if your
+ // subclass does not handle the message or return the pointer to the subclass
+ // that can handle it.
+ virtual Dispatcher* OnMessageReady(IPCParams* ipc, CallbackGeneric* callback);
+
+ // Called when a target proces is created, to setup the interceptions related
+ // with the given service (IPC).
+ virtual bool SetupService(InterceptionManager* manager, IpcTag service) = 0;
+
+ Dispatcher();
+ virtual ~Dispatcher();
+
+ protected:
+ // Structure that defines an IPC Call with all the parameters and the handler.
+ struct IPCCall {
+ IPCParams params;
+ CallbackGeneric callback;
+ };
+
+ // List of IPC Calls supported by the class.
+ std::vector<IPCCall> ipc_calls_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_CROSSCALL_SERVER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc b/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc
new file mode 100644
index 0000000000..f9551ed4b2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/eat_resolver.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2006-2010 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/eat_resolver.h"
+
+#include <stddef.h>
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS EatResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret =
+ Init(target_module, interceptor_module, target_name, interceptor_name,
+ interceptor_entry_point, thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ if (!eat_entry_)
+ return STATUS_INVALID_PARAMETER;
+
+#if defined(_WIN64)
+ // We have two thunks, in order: the return path and the forward path.
+ if (!SetInternalThunk(thunk_storage, storage_bytes, nullptr, target_))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ size_t thunk_bytes = GetInternalThunkSize();
+ storage_bytes -= thunk_bytes;
+ thunk_storage = reinterpret_cast<char*>(thunk_storage) + thunk_bytes;
+#endif
+
+ if (!SetInternalThunk(thunk_storage, storage_bytes, target_, interceptor_))
+ return STATUS_BUFFER_TOO_SMALL;
+
+ AutoProtectMemory memory;
+ ret = memory.ChangeProtection(eat_entry_, sizeof(DWORD), PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ // Perform the patch.
+ *eat_entry_ = static_cast<DWORD>(reinterpret_cast<uintptr_t>(thunk_storage)) -
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(target_module));
+
+ if (storage_used)
+ *storage_used = GetThunkSize();
+
+ return ret;
+}
+
+NTSTATUS EatResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ DCHECK_NT(address);
+ if (!module)
+ return STATUS_INVALID_PARAMETER;
+
+ base::win::PEImage pe(module);
+ if (!pe.VerifyMagic())
+ return STATUS_INVALID_IMAGE_FORMAT;
+
+ eat_entry_ = pe.GetExportEntry(function_name);
+
+ if (!eat_entry_)
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ *address = pe.RVAToAddr(*eat_entry_);
+
+ return STATUS_SUCCESS;
+}
+
+size_t EatResolverThunk::GetThunkSize() const {
+#if defined(_WIN64)
+ return GetInternalThunkSize() * 2;
+#else
+ return GetInternalThunkSize();
+#endif
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/eat_resolver.h b/security/sandbox/chromium/sandbox/win/src/eat_resolver.h
new file mode 100644
index 0000000000..cbc5516f8c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/eat_resolver.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2010 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_EAT_RESOLVER_H__
+#define SANDBOX_SRC_EAT_RESOLVER_H__
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform exports table interceptions.
+class EatResolverThunk : public ResolverThunk {
+ public:
+ EatResolverThunk() : eat_entry_(nullptr) {}
+ ~EatResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) override;
+
+ // Implementation of Resolver::ResolveTarget.
+ NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ private:
+ // The entry to patch.
+ DWORD* eat_entry_;
+
+ DISALLOW_COPY_AND_ASSIGN(EatResolverThunk);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_EAT_RESOLVER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc
new file mode 100644
index 0000000000..4989ab308f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/file_policy_test.cc
@@ -0,0 +1,705 @@
+// Copyright (c) 2011 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 <algorithm>
+#include <cctype>
+
+#include <windows.h>
+#include <winioctl.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define BINDNTDLL(name) \
+ name##Function name = reinterpret_cast<name##Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+namespace sandbox {
+
+const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+
+// Creates a file using different desired access. Returns if the call succeeded
+// or not. The first argument in argv is the filename. The second argument
+// determines the type of access and the dispositino of the file.
+SBOX_TESTS_COMMAND int File_Create(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring operation(argv[0]);
+
+ if (operation == L"Read") {
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_READ, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
+ base::win::ScopedHandle file2(CreateFile(
+ argv[1], FILE_EXECUTE, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
+
+ if (file1.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+
+ } else if (operation == L"Write") {
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_ALL, kSharing, nullptr, OPEN_EXISTING, 0, nullptr));
+ base::win::ScopedHandle file2(
+ CreateFile(argv[1], GENERIC_READ | FILE_WRITE_DATA, kSharing, nullptr,
+ OPEN_EXISTING, 0, nullptr));
+
+ if (file1.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+
+ } else if (operation == L"ReadCreate") {
+ base::win::ScopedHandle file2(CreateFile(argv[1], GENERIC_READ, kSharing,
+ nullptr, CREATE_NEW, 0, nullptr));
+ base::win::ScopedHandle file1(CreateFile(
+ argv[1], GENERIC_READ, kSharing, nullptr, CREATE_ALWAYS, 0, nullptr));
+
+ if (file1.IsValid() == file2.IsValid())
+ return file1.IsValid() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+ return file1.IsValid() ? SBOX_TEST_FIRST_ERROR : SBOX_TEST_SECOND_ERROR;
+ }
+
+ return SBOX_TEST_INVALID_PARAMETER;
+}
+
+SBOX_TESTS_COMMAND int File_Win32Create(int argc, wchar_t** argv) {
+ if (argc != 1) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ std::wstring full_path = MakePathToSys(argv[0], false);
+ if (full_path.empty()) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+
+ HANDLE file =
+ ::CreateFileW(full_path.c_str(), GENERIC_READ, kSharing, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
+
+ if (INVALID_HANDLE_VALUE != file) {
+ ::CloseHandle(file);
+ return SBOX_TEST_SUCCEEDED;
+ } else {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ } else {
+ return SBOX_TEST_FAILED;
+ }
+ }
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Creates the file in parameter using the NtCreateFile api and returns if the
+// call succeeded or not.
+SBOX_TESTS_COMMAND int File_CreateSys32(int argc, wchar_t** argv) {
+ BINDNTDLL(NtCreateFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtCreateFile || !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring file(argv[0]);
+ if (0 != _wcsnicmp(file.c_str(), kNTDevicePrefix, kNTDevicePrefixLen))
+ file = MakePathToSys(argv[0], true);
+
+ UNICODE_STRING object_name;
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+ HANDLE handle;
+ IO_STATUS_BLOCK io_block = {};
+ NTSTATUS status =
+ NtCreateFile(&handle, FILE_READ_DATA, &obj_attributes, &io_block, nullptr,
+ 0, kSharing, FILE_OPEN, 0, nullptr, 0);
+ if (NT_SUCCESS(status)) {
+ ::CloseHandle(handle);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+ return SBOX_TEST_FAILED;
+}
+
+// Opens the file in parameter using the NtOpenFile api and returns if the
+// call succeeded or not.
+SBOX_TESTS_COMMAND int File_OpenSys32(int argc, wchar_t** argv) {
+ BINDNTDLL(NtOpenFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtOpenFile || !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring file = MakePathToSys(argv[0], true);
+ UNICODE_STRING object_name;
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+ HANDLE handle;
+ IO_STATUS_BLOCK io_block = {};
+ NTSTATUS status = NtOpenFile(&handle, FILE_READ_DATA, &obj_attributes,
+ &io_block, kSharing, 0);
+ if (NT_SUCCESS(status)) {
+ ::CloseHandle(handle);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int File_GetDiskSpace(int argc, wchar_t** argv) {
+ std::wstring sys_path = MakePathToSys(L"", false);
+ if (sys_path.empty()) {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ ULARGE_INTEGER free_user = {};
+ ULARGE_INTEGER total = {};
+ ULARGE_INTEGER free_total = {};
+ if (::GetDiskFreeSpaceExW(sys_path.c_str(), &free_user, &total,
+ &free_total)) {
+ if ((total.QuadPart != 0) && (free_total.QuadPart != 0)) {
+ return SBOX_TEST_SUCCEEDED;
+ }
+ } else {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ } else {
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ }
+ }
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Move a file using the MoveFileEx api and returns if the call succeeded or
+// not.
+SBOX_TESTS_COMMAND int File_Rename(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (::MoveFileEx(argv[0], argv[1], 0))
+ return SBOX_TEST_SUCCEEDED;
+
+ if (::GetLastError() != ERROR_ACCESS_DENIED)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_DENIED;
+}
+
+// Query the attributes of file in parameter using the NtQueryAttributesFile api
+// and NtQueryFullAttributesFile and returns if the call succeeded or not. The
+// second argument in argv is "d" or "f" telling if we expect the attributes to
+// specify a file or a directory. The expected attribute has to match the real
+// attributes for the call to be successful.
+SBOX_TESTS_COMMAND int File_QueryAttributes(int argc, wchar_t** argv) {
+ BINDNTDLL(NtQueryAttributesFile);
+ BINDNTDLL(NtQueryFullAttributesFile);
+ BINDNTDLL(RtlInitUnicodeString);
+ if (!NtQueryAttributesFile || !NtQueryFullAttributesFile ||
+ !RtlInitUnicodeString)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ bool expect_directory = (L'd' == argv[1][0]);
+
+ UNICODE_STRING object_name;
+ std::wstring file = MakePathToSys(argv[0], true);
+ RtlInitUnicodeString(&object_name, file.c_str());
+
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ InitializeObjectAttributes(&obj_attributes, &object_name,
+ OBJ_CASE_INSENSITIVE, nullptr, nullptr);
+
+ FILE_BASIC_INFORMATION info = {};
+ FILE_NETWORK_OPEN_INFORMATION full_info = {};
+ NTSTATUS status1 = NtQueryAttributesFile(&obj_attributes, &info);
+ NTSTATUS status2 = NtQueryFullAttributesFile(&obj_attributes, &full_info);
+
+ if (status1 != status2)
+ return SBOX_TEST_FAILED;
+
+ if (NT_SUCCESS(status1)) {
+ if (info.FileAttributes != full_info.FileAttributes)
+ return SBOX_TEST_FAILED;
+
+ bool is_directory1 = (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ if (expect_directory == is_directory1)
+ return SBOX_TEST_SUCCEEDED;
+ } else if (STATUS_ACCESS_DENIED == status1) {
+ return SBOX_TEST_DENIED;
+ } else if (STATUS_OBJECT_NAME_NOT_FOUND == status1) {
+ return SBOX_TEST_NOT_FOUND;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+// Tries to create a backup of calc.exe in system32 folder. This should fail
+// with ERROR_ACCESS_DENIED if everything is working as expected.
+SBOX_TESTS_COMMAND int File_CopyFile(int argc, wchar_t** argv) {
+ std::wstring calc_path = MakePathToSys(L"calc.exe", false);
+ std::wstring calc_backup_path = MakePathToSys(L"calc.exe.bak", false);
+
+ if (::CopyFile(calc_path.c_str(), calc_backup_path.c_str(), FALSE))
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (::GetLastError() != ERROR_ACCESS_DENIED)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(FilePolicyTest, DenyNtCreateCalc) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+}
+
+TEST(FilePolicyTest, AllowNtCreateCalc) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CreateSys32 calc.exe"));
+}
+
+TEST(FilePolicyTest, AllowNtCreateWithNativePath) {
+ std::wstring calc = MakePathToSys(L"calc.exe", false);
+ std::wstring nt_path;
+ ASSERT_TRUE(GetNtPathFromWin32Path(calc, &nt_path));
+ TestRunner runner;
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, nt_path.c_str());
+
+ wchar_t buff[MAX_PATH];
+ ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
+
+ for (wchar_t& c : nt_path)
+ c = std::tolower(c);
+ ::wsprintfW(buff, L"File_CreateSys32 %s", nt_path.c_str());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(buff));
+}
+
+TEST(FilePolicyTest, AllowReadOnly) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ EXPECT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name));
+
+ wchar_t command_read[MAX_PATH + 20] = {};
+ wsprintf(command_read, L"File_Create Read \"%ls\"", temp_file_name);
+ wchar_t command_read_create[MAX_PATH + 20] = {};
+ wsprintf(command_read_create, L"File_Create ReadCreate \"%ls\"",
+ temp_file_name);
+ wchar_t command_write[MAX_PATH + 20] = {};
+ wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
+
+ // Verify that we cannot create the file after revert.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_read_create));
+
+ // Verify that we don't have write access after revert.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write));
+
+ // Verify that we have read access after revert.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_read));
+
+ // Verify that we really have write access to the file.
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
+
+ DeleteFile(temp_file_name);
+}
+
+// Tests support of "\\\\.\\DeviceName" kind of paths.
+TEST(FilePolicyTest, AllowImplicitDeviceName) {
+ TestRunner runner;
+
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ std::wstring path(temp_file_name);
+ EXPECT_TRUE(ConvertToLongPath(&path));
+ EXPECT_TRUE(GetNtPathFromWin32Path(path, &path));
+ path = path.substr(sandbox::kNTDevicePrefixLen);
+
+ wchar_t command[MAX_PATH + 20] = {};
+ wsprintf(command, L"File_Create Read \"\\\\.\\%ls\"", path.c_str());
+ path = std::wstring(kNTPrefix) + path;
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, path.c_str()));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
+
+ DeleteFile(temp_file_name);
+}
+
+TEST(FilePolicyTest, AllowWildcard) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ wcscat_s(temp_directory, MAX_PATH, L"*");
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_directory));
+
+ wchar_t command_write[MAX_PATH + 20] = {};
+ wsprintf(command_write, L"File_Create Write \"%ls\"", temp_file_name);
+
+ // Verify that we have write access after revert.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write));
+
+ DeleteFile(temp_file_name);
+}
+
+TEST(FilePolicyTest, AllowNtCreatePatternRule) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"App*.dll"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_OpenSys32 apphelp.dll"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_OpenSys32 apphelp.dll"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_OpenSys32 appwiz.cpl"));
+}
+
+TEST(FilePolicyTest, CheckNotFound) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"n*.dll"));
+
+ EXPECT_EQ(SBOX_TEST_NOT_FOUND,
+ runner.RunTest(L"File_OpenSys32 notfound.dll"));
+}
+
+TEST(FilePolicyTest, CheckNoLeak) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_CreateSys32 notfound.exe"));
+}
+
+TEST(FilePolicyTest, TestQueryAttributesFile) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"apphelp.dll"));
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notfound.exe"));
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"drivers"));
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_QUERY, L"ipconfig.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes drivers d"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes apphelp.dll f"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_QueryAttributes ipconfig.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes ftp.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_NOT_FOUND,
+ runner.RunTest(L"File_QueryAttributes notfound.exe f"));
+}
+
+// Makes sure that we don't leak information when there is not policy to allow
+// a path.
+TEST(FilePolicyTest, TestQueryAttributesFileNoPolicy) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes ftp.exe f"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"File_QueryAttributes notfound.exe f"));
+}
+
+TEST(FilePolicyTest, TestRename) {
+ TestRunner runner;
+
+ // Give access to the temp directory.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name1[MAX_PATH];
+ wchar_t temp_file_name2[MAX_PATH];
+ wchar_t temp_file_name3[MAX_PATH];
+ wchar_t temp_file_name4[MAX_PATH];
+ wchar_t temp_file_name5[MAX_PATH];
+ wchar_t temp_file_name6[MAX_PATH];
+ wchar_t temp_file_name7[MAX_PATH];
+ wchar_t temp_file_name8[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name1), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name2), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name3), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name4), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name5), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name6), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name7), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name8), 0u);
+
+ // Add rules to make file1->file2 succeed.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name1));
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name2));
+
+ // Add rules to make file3->file4 fail.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name3));
+ ASSERT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name4));
+
+ // Add rules to make file5->file6 fail.
+ ASSERT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_READONLY, temp_file_name5));
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name6));
+
+ // Add rules to make file7->no_pol_file fail.
+ ASSERT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY, temp_file_name7));
+
+ // Delete the files where the files are going to be renamed to.
+ ::DeleteFile(temp_file_name2);
+ ::DeleteFile(temp_file_name4);
+ ::DeleteFile(temp_file_name6);
+ ::DeleteFile(temp_file_name8);
+
+ wchar_t command[MAX_PATH * 2 + 20] = {};
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name1,
+ temp_file_name2);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name3,
+ temp_file_name4);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name5,
+ temp_file_name6);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ wsprintf(command, L"File_Rename \"%ls\" \"%ls\"", temp_file_name7,
+ temp_file_name8);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command));
+
+ // Delete all the files in case they are still there.
+ ::DeleteFile(temp_file_name1);
+ ::DeleteFile(temp_file_name2);
+ ::DeleteFile(temp_file_name3);
+ ::DeleteFile(temp_file_name4);
+ ::DeleteFile(temp_file_name5);
+ ::DeleteFile(temp_file_name6);
+ ::DeleteFile(temp_file_name7);
+ ::DeleteFile(temp_file_name8);
+}
+
+TEST(FilePolicyTest, OpenSys32FilesDenyBecauseOfDir) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L"notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+}
+
+TEST(FilePolicyTest, OpenSys32FilesAllowNotepad) {
+ TestRunner runner;
+ EXPECT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_ANY, L"notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create calc.exe"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"File_Win32Create notepad.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_Win32Create calc.exe"));
+}
+
+TEST(FilePolicyTest, FileGetDiskSpace) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_GetDiskSpace"));
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+
+ // Add an 'allow' rule in the windows\system32 such that GetDiskFreeSpaceEx
+ // succeeds (it does an NtOpenFile) but windows\system32\notepad.exe is
+ // denied since there is no wild card in the rule.
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L""));
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_GetDiskSpace"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"File_Win32Create notepad.exe"));
+}
+
+TEST(FilePolicyTest, TestReparsePoint) {
+ TestRunner runner;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t temp_file_name[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, temp_file_name), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(temp_file_name));
+ ASSERT_TRUE(::CreateDirectory(temp_file_name, nullptr));
+
+ // Create a temporary file in the subfolder.
+ std::wstring subfolder = temp_file_name;
+ std::wstring temp_file_title = subfolder.substr(subfolder.rfind(L"\\") + 1);
+ std::wstring temp_file = subfolder + L"\\file_" + temp_file_title;
+
+ HANDLE file = ::CreateFile(temp_file.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ CREATE_ALWAYS, 0, nullptr);
+ ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Create a temporary file in the temp directory.
+ std::wstring temp_dir = temp_directory;
+ std::wstring temp_file_in_temp = temp_dir + L"file_" + temp_file_title;
+ file = ::CreateFile(temp_file_in_temp.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ CREATE_ALWAYS, 0, nullptr);
+ ASSERT_TRUE(INVALID_HANDLE_VALUE != file);
+ ASSERT_TRUE(::CloseHandle(file));
+
+ // Give write access to the temp directory.
+ std::wstring temp_dir_wildcard = temp_dir + L"*";
+ EXPECT_TRUE(runner.AddFsRule(TargetPolicy::FILES_ALLOW_ANY,
+ temp_dir_wildcard.c_str()));
+
+ // Prepare the command to execute.
+ std::wstring command_write;
+ command_write += L"File_Create Write \"";
+ command_write += temp_file;
+ command_write += L"\"";
+
+ // Verify that we have write access to the original file
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command_write.c_str()));
+
+ // Replace the subfolder by a reparse point to %temp%.
+ ::DeleteFile(temp_file.c_str());
+ HANDLE dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
+
+ std::wstring temp_dir_nt;
+ temp_dir_nt += L"\\??\\";
+ temp_dir_nt += temp_dir;
+ EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
+ EXPECT_TRUE(::CloseHandle(dir));
+
+ // Try to open the file again.
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(command_write.c_str()));
+
+ // Remove the reparse point.
+ dir = ::CreateFile(subfolder.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
+ nullptr);
+ EXPECT_TRUE(INVALID_HANDLE_VALUE != dir);
+ EXPECT_TRUE(DeleteReparsePoint(dir));
+ EXPECT_TRUE(::CloseHandle(dir));
+
+ // Cleanup.
+ EXPECT_TRUE(::DeleteFile(temp_file_in_temp.c_str()));
+ EXPECT_TRUE(::RemoveDirectory(subfolder.c_str()));
+}
+
+TEST(FilePolicyTest, CheckExistingNTPrefixEscape) {
+ std::wstring name = L"\\??\\NAME";
+
+ std::wstring result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), L"\\/?/?\\NAME");
+}
+
+TEST(FilePolicyTest, CheckEscapedNTPrefixNoEscape) {
+ std::wstring name = L"\\/?/?\\NAME";
+
+ std::wstring result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), name.c_str());
+}
+
+TEST(FilePolicyTest, CheckMissingNTPrefixEscape) {
+ std::wstring name = L"C:\\NAME";
+
+ std::wstring result = FixNTPrefixForMatch(name);
+
+ EXPECT_STREQ(result.c_str(), L"\\/?/?\\C:\\NAME");
+}
+
+TEST(FilePolicyTest, TestCopyFile) {
+ // Check if the test is running Win8 or newer since
+ // MITIGATION_STRICT_HANDLE_CHECKS is not supported on older systems.
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+
+ TestRunner runner;
+ runner.SetTimeout(2000);
+
+ // Allow read access to calc.exe, this should be on all Windows versions.
+ ASSERT_TRUE(
+ runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_READONLY, L"calc.exe"));
+
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ // Set proper mitigation.
+ EXPECT_EQ(
+ policy->SetDelayedProcessMitigations(MITIGATION_STRICT_HANDLE_CHECKS),
+ SBOX_ALL_OK);
+
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"File_CopyFile"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
new file mode 100644
index 0000000000..c0f1de07fb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
@@ -0,0 +1,302 @@
+// Copyright (c) 2006-2010 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/filesystem_dispatcher.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/filesystem_interception.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+FilesystemDispatcher::FilesystemDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::NTCREATEFILE,
+ {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtCreateFile)};
+
+ static const IPCCall open_file = {
+ {IpcTag::NTOPENFILE,
+ {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&FilesystemDispatcher::NtOpenFile)};
+
+ static const IPCCall attribs = {
+ {IpcTag::NTQUERYATTRIBUTESFILE, {WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryAttributesFile)};
+
+ static const IPCCall full_attribs = {
+ {IpcTag::NTQUERYFULLATTRIBUTESFILE,
+ {WCHAR_TYPE, UINT32_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtQueryFullAttributesFile)};
+
+ static const IPCCall set_info = {
+ {IpcTag::NTSETINFO_RENAME,
+ {VOIDPTR_TYPE, INOUTPTR_TYPE, INOUTPTR_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &FilesystemDispatcher::NtSetInformationFile)};
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_file);
+ ipc_calls_.push_back(attribs);
+ ipc_calls_.push_back(full_attribs);
+ ipc_calls_.push_back(set_info);
+}
+
+bool FilesystemDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ switch (service) {
+ case IpcTag::NTCREATEFILE:
+ return INTERCEPT_NT(manager, NtCreateFile, CREATE_FILE_ID, 48);
+
+ case IpcTag::NTOPENFILE:
+ return INTERCEPT_NT(manager, NtOpenFile, OPEN_FILE_ID, 28);
+
+ case IpcTag::NTQUERYATTRIBUTESFILE:
+ return INTERCEPT_NT(manager, NtQueryAttributesFile, QUERY_ATTRIB_FILE_ID,
+ 12);
+
+ case IpcTag::NTQUERYFULLATTRIBUTESFILE:
+ return INTERCEPT_NT(manager, NtQueryFullAttributesFile,
+ QUERY_FULL_ATTRIB_FILE_ID, 12);
+
+ case IpcTag::NTSETINFO_RENAME:
+ return INTERCEPT_NT(manager, NtSetInformationFile, SET_INFO_FILE_ID, 24);
+
+ default:
+ return false;
+ }
+}
+
+bool FilesystemDispatcher::NtCreateFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options) {
+ if (!PreProcessName(name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ uint32_t broker = BROKER_TRUE;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition);
+ params[OpenFile::OPTIONS] = ParamPickerMake(create_options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTCREATEFILE, params.GetBase());
+ HANDLE handle;
+ ULONG_PTR io_information = 0;
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::CreateFileAction(
+ result, *ipc->client_info, *name, attributes, desired_access,
+ file_attributes, share_access, create_disposition, create_options,
+ &handle, &nt_status, &io_information)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].ulong_ptr = io_information;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool FilesystemDispatcher::NtOpenFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t share_access,
+ uint32_t open_options) {
+ if (!PreProcessName(name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ const wchar_t* filename = name->c_str();
+
+ uint32_t broker = BROKER_TRUE;
+ uint32_t create_disposition = FILE_OPEN;
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(filename);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(create_disposition);
+ params[OpenFile::OPTIONS] = ParamPickerMake(open_options);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTOPENFILE, params.GetBase());
+ HANDLE handle;
+ ULONG_PTR io_information = 0;
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::OpenFileAction(
+ result, *ipc->client_info, *name, attributes, desired_access,
+ share_access, open_options, &handle, &nt_status, &io_information)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].ulong_ptr = io_information;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool FilesystemDispatcher::NtQueryAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info) {
+ if (sizeof(FILE_BASIC_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTQUERYATTRIBUTESFILE, params.GetBase());
+
+ FILE_BASIC_INFORMATION* information =
+ reinterpret_cast<FILE_BASIC_INFORMATION*>(info->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::QueryAttributesFileAction(result, *ipc->client_info,
+ *name, attributes,
+ information, &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+bool FilesystemDispatcher::NtQueryFullAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info) {
+ if (sizeof(FILE_NETWORK_OPEN_INFORMATION) != info->Size())
+ return false;
+
+ if (!PreProcessName(name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name->c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result = policy_base_->EvalPolicy(
+ IpcTag::NTQUERYFULLATTRIBUTESFILE, params.GetBase());
+
+ FILE_NETWORK_OPEN_INFORMATION* information =
+ reinterpret_cast<FILE_NETWORK_OPEN_INFORMATION*>(info->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::QueryFullAttributesFileAction(
+ result, *ipc->client_info, *name, attributes, information,
+ &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+bool FilesystemDispatcher::NtSetInformationFile(IPCInfo* ipc,
+ HANDLE handle,
+ CountedBuffer* status,
+ CountedBuffer* info,
+ uint32_t length,
+ uint32_t info_class) {
+ if (sizeof(IO_STATUS_BLOCK) != status->Size())
+ return false;
+ if (length != info->Size())
+ return false;
+
+ FILE_RENAME_INFORMATION* rename_info =
+ reinterpret_cast<FILE_RENAME_INFORMATION*>(info->Buffer());
+
+ if (!IsSupportedRenameCall(rename_info, length, info_class))
+ return false;
+
+ std::wstring name;
+ name.assign(rename_info->FileName,
+ rename_info->FileNameLength / sizeof(rename_info->FileName[0]));
+ if (!PreProcessName(&name)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ uint32_t broker = BROKER_TRUE;
+ const wchar_t* filename = name.c_str();
+ CountedParameterSet<FileName> params;
+ params[FileName::NAME] = ParamPickerMake(filename);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ // To evaluate the policy we need to call back to the policy object. We
+ // are just middlemen in the operation since is the FileSystemPolicy which
+ // knows what to do.
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTSETINFO_RENAME, params.GetBase());
+
+ IO_STATUS_BLOCK* io_status =
+ reinterpret_cast<IO_STATUS_BLOCK*>(status->Buffer());
+ NTSTATUS nt_status;
+ if (!FileSystemPolicy::SetInformationFileAction(
+ result, *ipc->client_info, handle, rename_info, length, info_class,
+ io_status, &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h
new file mode 100644
index 0000000000..5551656e62
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 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_FILESYSTEM_DISPATCHER_H__
+#define SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles file system-related IPC calls.
+class FilesystemDispatcher : public Dispatcher {
+ public:
+ explicit FilesystemDispatcher(PolicyBase* policy_base);
+ ~FilesystemDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateFile in the target.
+ bool NtCreateFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options);
+
+ // Processes IPC requests coming from calls to NtOpenFile in the target.
+ bool NtOpenFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t share_access,
+ uint32_t create_options);
+
+ // Processes IPC requests coming from calls to NtQueryAttributesFile in the
+ // target.
+ bool NtQueryAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to NtQueryFullAttributesFile in
+ // the target.
+ bool NtQueryFullAttributesFile(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to NtSetInformationFile with the
+ // rename information class.
+ bool NtSetInformationFile(IPCInfo* ipc,
+ HANDLE handle,
+ CountedBuffer* status,
+ CountedBuffer* info,
+ uint32_t length,
+ uint32_t info_class);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(FilesystemDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_DISPATCHER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
new file mode 100644
index 0000000000..200dd1f63d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
@@ -0,0 +1,412 @@
+// Copyright (c) 2006-2008 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/filesystem_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+// This status occurs when trying to access a network share on the machine from
+// which it is shared.
+#define STATUS_NETWORK_OPEN_RESTRICTION ((NTSTATUS)0xC0000201L)
+
+namespace sandbox {
+
+NTSTATUS WINAPI TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_CreateFile(
+ file, desired_access, object_attributes, io_status, allocation_size,
+ file_attributes, sharing, disposition, options, ea_buffer, ea_length);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtCreateFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ uint32_t options_uint32 = options;
+ uint32_t disposition_uint32 = disposition;
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<OpenFile> params;
+ const wchar_t* name_ptr = name.get();
+ params[OpenFile::NAME] = ParamPickerMake(name_ptr);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access_uint32);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(disposition_uint32);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options_uint32);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTCREATEFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ // The following call must match in the parameters with
+ // FilesystemDispatcher::ProcessNtCreateFile.
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTCREATEFILE, name.get(), attributes,
+ desired_access_uint32, file_attributes, sharing, disposition,
+ options_uint32, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtCreateFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenFile(NtOpenFileFunction orig_OpenFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenFile(file, desired_access, object_attributes,
+ io_status, sharing, options);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file, sizeof(HANDLE), WRITE))
+ break;
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ uint32_t options_uint32 = options;
+ uint32_t disposition_uint32 = FILE_OPEN;
+ uint32_t broker = BROKER_FALSE;
+ const wchar_t* name_ptr = name.get();
+ CountedParameterSet<OpenFile> params;
+ params[OpenFile::NAME] = ParamPickerMake(name_ptr);
+ params[OpenFile::ACCESS] = ParamPickerMake(desired_access_uint32);
+ params[OpenFile::DISPOSITION] = ParamPickerMake(disposition_uint32);
+ params[OpenFile::OPTIONS] = ParamPickerMake(options_uint32);
+ params[OpenFile::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTOPENFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTOPENFILE, name.get(), attributes,
+ desired_access_uint32, sharing, options_uint32, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *file = answer.handle;
+ io_status->Status = answer.nt_status;
+ io_status->Information = answer.extended[0].ulong_ptr;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtOpenFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtQueryAttributesFile(NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status = orig_QueryAttributes(object_attributes, file_attributes);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtQueryAttributesFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_BASIC_INFORMATION), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ InOutCountedBuffer file_info(file_attributes,
+ sizeof(FILE_BASIC_INFORMATION));
+
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<FileName> params;
+ const wchar_t* name_ptr = name.get();
+ params[FileName::NAME] = ParamPickerMake(name_ptr);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTQUERYATTRIBUTESFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTQUERYATTRIBUTESFILE, name.get(),
+ attributes, file_info, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ mozilla::sandboxing::LogAllowed("NtQueryAttributesFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryFullAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ // Check if the process can query it first.
+ NTSTATUS status =
+ orig_QueryFullAttributes(object_attributes, file_attributes);
+ if (STATUS_ACCESS_DENIED != status &&
+ STATUS_NETWORK_OPEN_RESTRICTION != status)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtQueryFullAttributesFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(file_attributes, sizeof(FILE_NETWORK_OPEN_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(object_attributes, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ InOutCountedBuffer file_info(file_attributes,
+ sizeof(FILE_NETWORK_OPEN_INFORMATION));
+
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<FileName> params;
+ const wchar_t* name_ptr = name.get();
+ params[FileName::NAME] = ParamPickerMake(name_ptr);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTQUERYFULLATTRIBUTESFILE, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTQUERYFULLATTRIBUTESFILE,
+ name.get(), attributes, file_info, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+
+ mozilla::sandboxing::LogAllowed("NtQueryFullAttributesFile",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtSetInformationFile(NtSetInformationFileFunction orig_SetInformationFile,
+ HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_info,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_info_class) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_SetInformationFile(file, io_status, file_info, length,
+ file_info_class);
+ if (STATUS_ACCESS_DENIED != status)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtSetInformationFile");
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ if (!ValidParameter(io_status, sizeof(IO_STATUS_BLOCK), WRITE))
+ break;
+
+ if (!ValidParameter(file_info, length, READ))
+ break;
+
+ FILE_RENAME_INFORMATION* file_rename_info =
+ reinterpret_cast<FILE_RENAME_INFORMATION*>(file_info);
+ OBJECT_ATTRIBUTES object_attributes;
+ UNICODE_STRING object_name;
+ InitializeObjectAttributes(&object_attributes, &object_name, 0, nullptr,
+ nullptr);
+
+ __try {
+ if (!IsSupportedRenameCall(file_rename_info, length, file_info_class))
+ break;
+
+ object_attributes.RootDirectory = file_rename_info->RootDirectory;
+ object_name.Buffer = file_rename_info->FileName;
+ object_name.Length = object_name.MaximumLength =
+ static_cast<USHORT>(file_rename_info->FileNameLength);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ NTSTATUS ret =
+ AllocAndCopyName(&object_attributes, &name, nullptr, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t broker = BROKER_FALSE;
+ CountedParameterSet<FileName> params;
+ const wchar_t* name_ptr = name.get();
+ params[FileName::NAME] = ParamPickerMake(name_ptr);
+ params[FileName::BROKER] = ParamPickerMake(broker);
+
+ if (!QueryBroker(IpcTag::NTSETINFO_RENAME, params.GetBase()))
+ break;
+
+ InOutCountedBuffer io_status_buffer(io_status, sizeof(IO_STATUS_BLOCK));
+ // This is actually not an InOut buffer, only In, but using InOut facility
+ // really helps to simplify the code.
+ InOutCountedBuffer file_info_buffer(file_info, length);
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTSETINFO_RENAME, file, io_status_buffer,
+ file_info_buffer, length, file_info_class, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ status = answer.nt_status;
+ mozilla::sandboxing::LogAllowed("NtSetInformationFile");
+ } while (false);
+
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.h b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.h
new file mode 100644
index 0000000000..d9d92f7fa4
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.h
@@ -0,0 +1,67 @@
+// Copyright (c) 2006-2008 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_WIN_SRC_FILESYSTEM_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_FILESYSTEM_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtCreateFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateFile(NtCreateFileFunction orig_CreateFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length);
+
+// Interception of NtOpenFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenFile(NtOpenFileFunction orig_OpenFile,
+ PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options);
+
+// Interception of NtQueryAtttributesFile on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtQueryAttributesFile(NtQueryAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes);
+
+// Interception of NtQueryFullAtttributesFile on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile(
+ NtQueryFullAttributesFileFunction orig_QueryAttributes,
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes);
+
+// Interception of NtSetInformationFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationFile(NtSetInformationFileFunction orig_SetInformationFile,
+ HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_information,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_information_class);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_FILESYSTEM_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
new file mode 100644
index 0000000000..3018cad0cb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.cc
@@ -0,0 +1,443 @@
+// Copyright (c) 2011 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/filesystem_policy.h"
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <string>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ IO_STATUS_BLOCK* io_status_block,
+ ULONG file_attributes,
+ ULONG share_access,
+ ULONG create_disposition,
+ ULONG create_options,
+ PVOID ea_buffer,
+ ULONG ea_length,
+ HANDLE target_process) {
+ NtCreateFileFunction NtCreateFile = nullptr;
+ ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile);
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status =
+ NtCreateFile(&local_handle, desired_access, obj_attributes,
+ io_status_block, nullptr, file_attributes, share_access,
+ create_disposition, create_options, ea_buffer, ea_length);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process,
+ target_file_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+// Get an initialized anonymous level Security QOS.
+SECURITY_QUALITY_OF_SERVICE GetAnonymousQOS() {
+ SECURITY_QUALITY_OF_SERVICE security_qos = {0};
+ security_qos.Length = sizeof(security_qos);
+ security_qos.ImpersonationLevel = SecurityAnonymous;
+ // Set dynamic tracking so that a pipe doesn't capture the broker's token
+ security_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ security_qos.EffectiveOnly = true;
+
+ return security_qos;
+}
+
+} // namespace.
+
+namespace sandbox {
+
+bool FileSystemPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+ // Don't pre-process the path name and check for reparse points if it is the
+ // special case of allowing read access to all paths.
+ if (!(semantics == TargetPolicy::FILES_ALLOW_READONLY
+ && mod_name.compare(L"*") == 0)
+ && !PreProcessName(&mod_name)) {
+ // The path to be added might contain a reparse point.
+ NOTREACHED();
+ return false;
+ }
+
+ // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
+ // infrastructure to normalize names. In any case we need to escape the
+ // question marks.
+ if (_wcsnicmp(mod_name.c_str(), kNTDevicePrefix, kNTDevicePrefixLen)) {
+ mod_name = FixNTPrefixForMatch(mod_name);
+ name = mod_name.c_str();
+ }
+
+ EvalResult result = ASK_BROKER;
+
+ // List of supported calls for the filesystem.
+ const unsigned kCallNtCreateFile = 0x1;
+ const unsigned kCallNtOpenFile = 0x2;
+ const unsigned kCallNtQueryAttributesFile = 0x4;
+ const unsigned kCallNtQueryFullAttributesFile = 0x8;
+ const unsigned kCallNtSetInfoRename = 0x10;
+
+ DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile |
+ kCallNtQueryAttributesFile |
+ kCallNtQueryFullAttributesFile | kCallNtSetInfoRename;
+
+ PolicyRule create(result);
+ PolicyRule open(result);
+ PolicyRule query(result);
+ PolicyRule query_full(result);
+ PolicyRule rename(result);
+
+ switch (semantics) {
+ case TargetPolicy::FILES_ALLOW_DIR_ANY: {
+ open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
+ create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_READONLY: {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write.
+ DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES |
+ FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE |
+ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL;
+ DWORD restricted_flags = ~allowed_flags;
+ open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
+ open.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
+ create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
+ create.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
+
+ // Read only access don't work for rename.
+ rule_to_add &= ~kCallNtSetInfoRename;
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_QUERY: {
+ // Here we don't want to add policy for the open or the create.
+ rule_to_add &=
+ ~(kCallNtOpenFile | kCallNtCreateFile | kCallNtSetInfoRename);
+ break;
+ }
+ case TargetPolicy::FILES_ALLOW_ANY: {
+ break;
+ }
+ default: {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if ((rule_to_add & kCallNtCreateFile) &&
+ (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTCREATEFILE, &create))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtOpenFile) &&
+ (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTOPENFILE, &open))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtQueryAttributesFile) &&
+ (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTQUERYATTRIBUTESFILE, &query))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtQueryFullAttributesFile) &&
+ (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTQUERYFULLATTRIBUTESFILE, &query_full))) {
+ return false;
+ }
+
+ if ((rule_to_add & kCallNtSetInfoRename) &&
+ (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTSETINFO_RENAME, &rename))) {
+ return false;
+ }
+
+ return true;
+}
+
+// Right now we insert two rules, to be evaluated before any user supplied rule:
+// - go to the broker if the path doesn't look like the paths that we push on
+// the policy (namely \??\something).
+// - go to the broker if it looks like this is a short-name path.
+//
+// It is possible to add a rule to go to the broker in any case; it would look
+// something like:
+// rule = new PolicyRule(ASK_BROKER);
+// rule->AddNumberMatch(IF_NOT, FileName::BROKER, true, AND);
+// policy->AddRule(service, rule);
+bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) {
+ PolicyRule format(ASK_BROKER);
+ PolicyRule short_name(ASK_BROKER);
+
+ bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, BROKER_TRUE, AND);
+ rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*",
+ CASE_SENSITIVE);
+
+ rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, BROKER_TRUE, AND);
+ rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE);
+
+ if (!rv || !policy->AddRule(IpcTag::NTCREATEFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTCREATEFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTOPENFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTOPENFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYATTRIBUTESFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYATTRIBUTESFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYFULLATTRIBUTESFILE, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTQUERYFULLATTRIBUTESFILE, &short_name))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTSETINFO_RENAME, &format))
+ return false;
+
+ if (!policy->AddRule(IpcTag::NTSETINFO_RENAME, &short_name))
+ return false;
+
+ return true;
+}
+
+bool FileSystemPolicy::CreateFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information) {
+ *handle = nullptr;
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+ IO_STATUS_BLOCK io_block = {};
+ UNICODE_STRING uni_name = {};
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *nt_status =
+ NtCreateFileInTarget(handle, desired_access, &obj_attributes, &io_block,
+ file_attributes, share_access, create_disposition,
+ create_options, nullptr, 0, client_info.process);
+
+ *io_information = io_block.Information;
+ return true;
+}
+
+bool FileSystemPolicy::OpenFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t share_access,
+ uint32_t open_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information) {
+ *handle = nullptr;
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+ // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and
+ // CreateDisposition = FILE_OPEN.
+ IO_STATUS_BLOCK io_block = {};
+ UNICODE_STRING uni_name = {};
+ OBJECT_ATTRIBUTES obj_attributes = {};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *nt_status = NtCreateFileInTarget(
+ handle, desired_access, &obj_attributes, &io_block, 0, share_access,
+ FILE_OPEN, open_options, nullptr, 0, client_info.process);
+
+ *io_information = io_block.Information;
+ return true;
+}
+
+bool FileSystemPolicy::QueryAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ FILE_BASIC_INFORMATION* file_info,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means query the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ NtQueryAttributesFileFunction NtQueryAttributesFile = nullptr;
+ ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *nt_status = NtQueryAttributesFile(&obj_attributes, file_info);
+
+ return true;
+}
+
+bool FileSystemPolicy::QueryFullAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ FILE_NETWORK_OPEN_INFORMATION* file_info,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means query the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = nullptr;
+ ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile);
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
+
+ InitObjectAttribs(file, attributes, nullptr, &obj_attributes, &uni_name,
+ IsPipe(file) ? &security_qos : nullptr);
+ *nt_status = NtQueryFullAttributesFile(&obj_attributes, file_info);
+
+ return true;
+}
+
+bool FileSystemPolicy::SetInformationFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ HANDLE target_file_handle,
+ void* file_info,
+ uint32_t length,
+ uint32_t info_class,
+ IO_STATUS_BLOCK* io_block,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ NtSetInformationFileFunction NtSetInformationFile = nullptr;
+ ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile);
+
+ HANDLE local_handle = nullptr;
+ if (!::DuplicateHandle(client_info.process, target_file_handle,
+ ::GetCurrentProcess(), &local_handle, 0, false,
+ DUPLICATE_SAME_ACCESS)) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ base::win::ScopedHandle handle(local_handle);
+
+ FILE_INFORMATION_CLASS file_info_class =
+ static_cast<FILE_INFORMATION_CLASS>(info_class);
+ *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length,
+ file_info_class);
+
+ return true;
+}
+
+bool PreProcessName(std::wstring* path) {
+ // We now allow symbolic links to be opened via the broker, so we can no
+ // longer rely on the same object check where we checked the path of the
+ // opened file against the original. We don't specify a root when creating
+ // OBJECT_ATTRIBUTES from file names for brokering so they must be fully
+ // qualified and we can just check for the parent directory double dot between
+ // two backslashes. NtCreateFile doesn't seem to allow it anyway, but this is
+ // just an extra precaution. It also doesn't seem to allow the forward slash,
+ // but this is also used for checking policy rules, so we just replace forward
+ // slashes with backslashes.
+ std::replace(path->begin(), path->end(), L'/', L'\\');
+ if (path->find(L"\\..\\") != std::wstring::npos) {
+ return false;
+ }
+
+ ConvertToLongPath(path);
+ return true;
+}
+
+std::wstring FixNTPrefixForMatch(const std::wstring& name) {
+ std::wstring mod_name = name;
+
+ // NT prefix escaped for rule matcher
+ const wchar_t kNTPrefixEscaped[] = L"\\/?/?\\";
+ const int kNTPrefixEscapedLen = base::size(kNTPrefixEscaped) - 1;
+
+ if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) {
+ if (0 != mod_name.compare(0, kNTPrefixEscapedLen, kNTPrefixEscaped)) {
+ // TODO(nsylvain): Find a better way to do name resolution. Right now we
+ // take the name and we expand it.
+ mod_name.insert(0, kNTPrefixEscaped);
+ }
+ } else {
+ // Start of name matches NT prefix, replace with escaped format
+ // Fixes bug: 334882
+ mod_name.replace(0, kNTPrefixLen, kNTPrefixEscaped);
+ }
+
+ return mod_name;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h
new file mode 100644
index 0000000000..81dd1d0d1d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_policy.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2006-2008 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_FILESYSTEM_POLICY_H__
+#define SANDBOX_SRC_FILESYSTEM_POLICY_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum IsBroker { BROKER_FALSE, BROKER_TRUE };
+
+// This class centralizes most of the knowledge related to file system policy
+class FileSystemPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for File IO, in particular open or create actions.
+ // 'name' is the file or directory name.
+ // 'semantics' is the desired semantics for the open or create.
+ // 'policy' is the policy generator to which the rules are going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Add basic file system rules.
+ static bool SetInitialRules(LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a create request with an
+ // API that is compatible with the IPC-received parameters.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'file' : The target file or directory.
+ static bool CreateFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t file_attributes,
+ uint32_t share_access,
+ uint32_t create_disposition,
+ uint32_t create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information);
+
+ // Performs the desired policy action on an open request with an
+ // API that is compatible with the IPC-received parameters.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'file' : The target file or directory.
+ static bool OpenFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ uint32_t desired_access,
+ uint32_t share_access,
+ uint32_t open_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG_PTR* io_information);
+
+ // Performs the desired policy action on a query request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool QueryAttributesFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ FILE_BASIC_INFORMATION* file_info,
+ NTSTATUS* nt_status);
+
+ // Performs the desired policy action on a query request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool QueryFullAttributesFileAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& file,
+ uint32_t attributes,
+ FILE_NETWORK_OPEN_INFORMATION* file_info,
+ NTSTATUS* nt_status);
+
+ // Performs the desired policy action on a set_info request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool SetInformationFileAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ HANDLE target_file_handle,
+ void* file_info,
+ uint32_t length,
+ uint32_t info_class,
+ IO_STATUS_BLOCK* io_block,
+ NTSTATUS* nt_status);
+};
+
+// Expands the path and check if it's a reparse point. Returns false if the path
+// cannot be trusted.
+bool PreProcessName(std::wstring* path);
+
+// Corrects global paths to have a correctly escaped NT prefix at the
+// beginning. If the name has no NT prefix (either normal or escaped)
+// add the escaped form to the string
+std::wstring FixNTPrefixForMatch(const std::wstring& name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_FILESYSTEM_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer.cc
new file mode 100644
index 0000000000..6751151dcb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer.cc
@@ -0,0 +1,185 @@
+// Copyright (c) 2011 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/handle_closer.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/memory/free_deleter.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+template <typename T>
+T RoundUpToWordSize(T v) {
+ if (size_t mod = v % sizeof(size_t))
+ v += sizeof(size_t) - mod;
+ return v;
+}
+
+template <typename T>
+T* RoundUpToWordSize(T* v) {
+ return reinterpret_cast<T*>(RoundUpToWordSize(reinterpret_cast<size_t>(v)));
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close;
+
+HandleCloser::HandleCloser() {}
+
+HandleCloser::~HandleCloser() {}
+
+ResultCode HandleCloser::AddHandle(const wchar_t* handle_type,
+ const wchar_t* handle_name) {
+ if (!handle_type)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ std::wstring resolved_name;
+ if (handle_name) {
+ resolved_name = handle_name;
+ if (handle_type == std::wstring(L"Key"))
+ if (!ResolveRegistryName(resolved_name, &resolved_name))
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ HandleMap::iterator names = handles_to_close_.find(handle_type);
+ if (names == handles_to_close_.end()) { // We have no entries for this type.
+ std::pair<HandleMap::iterator, bool> result = handles_to_close_.insert(
+ HandleMap::value_type(handle_type, HandleMap::mapped_type()));
+ names = result.first;
+ if (handle_name)
+ names->second.insert(resolved_name);
+ } else if (!handle_name) { // Now we need to close all handles of this type.
+ names->second.clear();
+ } else if (!names->second.empty()) { // Add another name for this type.
+ names->second.insert(resolved_name);
+ } // If we're already closing all handles of type then we're done.
+
+ return SBOX_ALL_OK;
+}
+
+size_t HandleCloser::GetBufferSize() {
+ size_t bytes_total = offsetof(HandleCloserInfo, handle_entries);
+
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ size_t bytes_entry = offsetof(HandleListEntry, handle_type) +
+ (i->first.size() + 1) * sizeof(wchar_t);
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ bytes_entry += ((*j).size() + 1) * sizeof(wchar_t);
+ }
+
+ // Round up to the nearest multiple of word size.
+ bytes_entry = RoundUpToWordSize(bytes_entry);
+ bytes_total += bytes_entry;
+ }
+
+ return bytes_total;
+}
+
+bool HandleCloser::InitializeTargetHandles(TargetProcess* target) {
+ // Do nothing on an empty list (global pointer already initialized to
+ // nullptr).
+ if (handles_to_close_.empty())
+ return true;
+
+ size_t bytes_needed = GetBufferSize();
+ std::unique_ptr<size_t[]> local_buffer(
+ new size_t[bytes_needed / sizeof(size_t)]);
+
+ if (!SetupHandleList(local_buffer.get(), bytes_needed))
+ return false;
+
+ void* remote_data;
+ if (!CopyToChildMemory(target->Process(), local_buffer.get(), bytes_needed,
+ &remote_data))
+ return false;
+
+ g_handles_to_close = reinterpret_cast<HandleCloserInfo*>(remote_data);
+
+ ResultCode rc = target->TransferVariable(
+ "g_handles_to_close", &g_handles_to_close, sizeof(g_handles_to_close));
+
+ return (SBOX_ALL_OK == rc);
+}
+
+bool HandleCloser::SetupHandleList(void* buffer, size_t buffer_bytes) {
+ ::ZeroMemory(buffer, buffer_bytes);
+ HandleCloserInfo* handle_info = reinterpret_cast<HandleCloserInfo*>(buffer);
+ handle_info->record_bytes = buffer_bytes;
+ handle_info->num_handle_types = handles_to_close_.size();
+
+ wchar_t* output = reinterpret_cast<wchar_t*>(&handle_info->handle_entries[0]);
+ wchar_t* end = reinterpret_cast<wchar_t*>(reinterpret_cast<char*>(buffer) +
+ buffer_bytes);
+ for (HandleMap::iterator i = handles_to_close_.begin();
+ i != handles_to_close_.end(); ++i) {
+ if (output >= end)
+ return false;
+ HandleListEntry* list_entry = reinterpret_cast<HandleListEntry*>(output);
+ output = &list_entry->handle_type[0];
+
+ // Copy the typename and set the offset and count.
+ i->first.copy(output, i->first.size());
+ *(output += i->first.size()) = L'\0';
+ output++;
+ list_entry->offset_to_names =
+ reinterpret_cast<char*>(output) - reinterpret_cast<char*>(list_entry);
+ list_entry->name_count = i->second.size();
+
+ // Copy the handle names.
+ for (HandleMap::mapped_type::iterator j = i->second.begin();
+ j != i->second.end(); ++j) {
+ output = std::copy((*j).begin(), (*j).end(), output) + 1;
+ }
+
+ // Round up to the nearest multiple of sizeof(size_t).
+ output = RoundUpToWordSize(output);
+ list_entry->record_bytes =
+ reinterpret_cast<char*>(output) - reinterpret_cast<char*>(list_entry);
+ }
+
+ DCHECK_EQ(reinterpret_cast<size_t>(output), reinterpret_cast<size_t>(end));
+ return output <= end;
+}
+
+bool GetHandleName(HANDLE handle, std::wstring* handle_name) {
+ static NtQueryObject QueryObject = nullptr;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ ULONG size = MAX_PATH;
+ std::unique_ptr<UNICODE_STRING, base::FreeDeleter> name;
+ NTSTATUS result;
+
+ do {
+ name.reset(static_cast<UNICODE_STRING*>(malloc(size)));
+ DCHECK(name.get());
+ result =
+ QueryObject(handle, ObjectNameInformation, name.get(), size, &size);
+ } while (result == STATUS_INFO_LENGTH_MISMATCH ||
+ result == STATUS_BUFFER_OVERFLOW);
+
+ if (NT_SUCCESS(result) && name->Buffer && name->Length)
+ handle_name->assign(name->Buffer, name->Length / sizeof(wchar_t));
+ else
+ handle_name->clear();
+
+ return NT_SUCCESS(result);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer.h b/security/sandbox/chromium/sandbox/win/src/handle_closer.h
new file mode 100644
index 0000000000..4f023b27b6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_CLOSER_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_H_
+
+#include <stddef.h>
+
+#include <map>
+#include <set>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/target_process.h"
+
+namespace sandbox {
+
+// This is a map of handle-types to names that we need to close in the
+// target process. A null set means we need to close all handles of the
+// given type.
+typedef std::map<const std::wstring, std::set<std::wstring>> HandleMap;
+
+// Type and set of corresponding handle names to close.
+struct HandleListEntry {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t offset_to_names; // Nul terminated strings of name_count names.
+ size_t name_count;
+ wchar_t handle_type[1];
+};
+
+// Global parameters and a pointer to the list of entries.
+struct HandleCloserInfo {
+ size_t record_bytes; // Rounded to sizeof(size_t) bytes.
+ size_t num_handle_types;
+ struct HandleListEntry handle_entries[1];
+};
+
+SANDBOX_INTERCEPT HandleCloserInfo* g_handle_closer_info;
+
+// Adds handles to close after lockdown.
+class HandleCloser {
+ public:
+ HandleCloser();
+ ~HandleCloser();
+
+ // Adds a handle that will be closed in the target process after lockdown.
+ // A nullptr value for handle_name indicates all handles of the specified
+ // type. An empty string for handle_name indicates the handle is unnamed.
+ ResultCode AddHandle(const wchar_t* handle_type, const wchar_t* handle_name);
+
+ // Serializes and copies the closer table into the target process.
+ bool InitializeTargetHandles(TargetProcess* target);
+
+ private:
+ // Calculates the memory needed to copy the serialized handles list (rounded
+ // to the nearest machine-word size).
+ size_t GetBufferSize();
+
+ // Serializes the handle list into the target process.
+ bool SetupHandleList(void* buffer, size_t buffer_bytes);
+
+ HandleMap handles_to_close_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloser);
+};
+
+// Returns the object manager's name associated with a handle
+bool GetHandleName(HANDLE handle, std::wstring* handle_name);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc
new file mode 100644
index 0000000000..55fe2d4689
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_closer_agent.h"
+
+#include <limits.h>
+#include <stddef.h>
+
+#include "base/logging.h"
+#include "base/win/static_constants.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Returns type infomation for an NT object. This routine is expected to be
+// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
+// that can be generated when handle tracing is enabled.
+NTSTATUS QueryObjectTypeInformation(HANDLE handle, void* buffer, ULONG* size) {
+ static NtQueryObject QueryObject = nullptr;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ __try {
+ status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
+ } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
+ ? EXCEPTION_EXECUTE_HANDLER
+ : EXCEPTION_CONTINUE_SEARCH) {
+ status = STATUS_INVALID_HANDLE;
+ }
+ return status;
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Memory buffer mapped from the parent, with the list of handles.
+SANDBOX_INTERCEPT HandleCloserInfo* g_handles_to_close = nullptr;
+
+bool HandleCloserAgent::NeedsHandlesClosed() {
+ return !!g_handles_to_close;
+}
+
+HandleCloserAgent::HandleCloserAgent()
+ : dummy_handle_(::CreateEvent(nullptr, false, false, nullptr)) {}
+
+HandleCloserAgent::~HandleCloserAgent() {}
+
+// Attempts to stuff |closed_handle| with a duplicated handle for a dummy Event
+// with no access. This should allow the handle to be closed, to avoid
+// generating EXCEPTION_INVALID_HANDLE on shutdown, but nothing else. For now
+// the only supported |type| is Event or File.
+bool HandleCloserAgent::AttemptToStuffHandleSlot(HANDLE closed_handle,
+ const std::wstring& type) {
+ // Only attempt to stuff Files and Events at the moment.
+ if (type != L"Event" && type != L"File") {
+ return true;
+ }
+
+ if (!dummy_handle_.IsValid())
+ return false;
+
+ // This should never happen, as g_dummy is created before closing to_stuff.
+ DCHECK(dummy_handle_.Get() != closed_handle);
+
+ std::vector<HANDLE> to_close;
+
+ const DWORD original_proc_num = GetCurrentProcessorNumber();
+ DWORD proc_num = original_proc_num;
+ DWORD_PTR original_affinity_mask =
+ SetThreadAffinityMask(GetCurrentThread(), DWORD_PTR{1} << proc_num);
+ bool found_handle = false;
+ BOOL result = FALSE;
+
+ // There is per-processor based free list of handles entries. The free handle
+ // from current processor's freelist is preferred for reusing, so cycling
+ // through all possible processors to find closed_handle.
+ // Start searching from current processor which covers usual cases.
+
+ do {
+ DWORD_PTR current_mask = DWORD_PTR{1} << proc_num;
+
+ if (original_affinity_mask & current_mask) {
+ if (proc_num != original_proc_num) {
+ SetThreadAffinityMask(GetCurrentThread(), current_mask);
+ }
+
+ HANDLE dup_dummy = nullptr;
+ size_t count = 16;
+
+ do {
+ result =
+ ::DuplicateHandle(::GetCurrentProcess(), dummy_handle_.Get(),
+ ::GetCurrentProcess(), &dup_dummy, 0, false, 0);
+ if (!result) {
+ break;
+ }
+ if (dup_dummy != closed_handle) {
+ to_close.push_back(dup_dummy);
+ } else {
+ found_handle = true;
+ }
+ } while (count-- && reinterpret_cast<uintptr_t>(dup_dummy) <
+ reinterpret_cast<uintptr_t>(closed_handle));
+ }
+
+ proc_num++;
+ if (proc_num == sizeof(DWORD_PTR) * 8) {
+ proc_num = 0;
+ }
+ if (proc_num == original_proc_num) {
+ break;
+ }
+ } while (result && !found_handle);
+
+ SetThreadAffinityMask(GetCurrentThread(), original_affinity_mask);
+
+ for (HANDLE h : to_close)
+ ::CloseHandle(h);
+
+ return found_handle;
+}
+
+// Reads g_handles_to_close and creates the lookup map.
+void HandleCloserAgent::InitializeHandlesToClose(bool* is_csrss_connected) {
+ CHECK(g_handles_to_close);
+
+ // Default to connected state
+ *is_csrss_connected = true;
+
+ // Grab the header.
+ HandleListEntry* entry = g_handles_to_close->handle_entries;
+ for (size_t i = 0; i < g_handles_to_close->num_handle_types; ++i) {
+ // Set the type name.
+ wchar_t* input = entry->handle_type;
+ if (!wcscmp(input, L"ALPC Port")) {
+ *is_csrss_connected = false;
+ }
+ HandleMap::mapped_type& handle_names = handles_to_close_[input];
+ input = reinterpret_cast<wchar_t*>(reinterpret_cast<char*>(entry) +
+ entry->offset_to_names);
+ // Grab all the handle names.
+ for (size_t j = 0; j < entry->name_count; ++j) {
+ std::pair<HandleMap::mapped_type::iterator, bool> name =
+ handle_names.insert(input);
+ CHECK(name.second);
+ input += name.first->size() + 1;
+ }
+
+ // Move on to the next entry.
+ entry = reinterpret_cast<HandleListEntry*>(reinterpret_cast<char*>(entry) +
+ entry->record_bytes);
+
+ DCHECK(reinterpret_cast<wchar_t*>(entry) >= input);
+ DCHECK(reinterpret_cast<wchar_t*>(entry) - input <
+ static_cast<ptrdiff_t>(sizeof(size_t) / sizeof(wchar_t)));
+ }
+
+ // Clean up the memory we copied over.
+ ::VirtualFree(g_handles_to_close, 0, MEM_RELEASE);
+ g_handles_to_close = nullptr;
+}
+
+bool HandleCloserAgent::CloseHandles() {
+ DWORD handle_count = UINT_MAX;
+ const int kInvalidHandleThreshold = 100;
+ const size_t kHandleOffset = 4; // Handles are always a multiple of 4.
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return false;
+
+ // Skip closing these handles when Application Verifier is in use in order to
+ // avoid invalid-handle exceptions.
+ if (GetModuleHandleA(base::win::kApplicationVerifierDllName))
+ return true;
+
+ // Set up buffers for the type info and the name.
+ std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+ 32 * sizeof(wchar_t));
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ std::wstring handle_name;
+ HANDLE handle = nullptr;
+ int invalid_count = 0;
+
+ // Keep incrementing until we hit the number of handles reported by
+ // GetProcessHandleCount(). If we hit a very long sequence of invalid
+ // handles we assume that we've run past the end of the table.
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ NTSTATUS rc;
+
+ // Get the type name, reusing the buffer.
+ ULONG size = static_cast<ULONG>(type_info_buffer.size());
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ while (rc == STATUS_INFO_LENGTH_MISMATCH || rc == STATUS_BUFFER_OVERFLOW) {
+ type_info_buffer.resize(size + sizeof(wchar_t));
+ type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ // Leave padding for the nul terminator.
+ if (NT_SUCCESS(rc) && size == type_info_buffer.size())
+ rc = STATUS_INFO_LENGTH_MISMATCH;
+ }
+ if (!NT_SUCCESS(rc) || !type_info->Name.Buffer) {
+ ++invalid_count;
+ continue;
+ }
+
+ --handle_count;
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
+
+ // Check if we're looking for this type of handle.
+ HandleMap::iterator result = handles_to_close_.find(type_info->Name.Buffer);
+ if (result != handles_to_close_.end()) {
+ HandleMap::mapped_type& names = result->second;
+ // Empty set means close all handles of this type; otherwise check name.
+ if (!names.empty()) {
+ // Move on to the next handle if this name doesn't match.
+ if (!GetHandleName(handle, &handle_name) || !names.count(handle_name))
+ continue;
+ }
+
+ if (!::SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, 0))
+ return false;
+ if (!::CloseHandle(handle))
+ return false;
+ // Attempt to stuff this handle with a new dummy Event.
+ AttemptToStuffHandleSlot(handle, result->first);
+ }
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h
new file mode 100644
index 0000000000..91f8e74c7e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_agent.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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_HANDLE_CLOSER_AGENT_H_
+#define SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// Target process code to close the handle list copied over from the broker.
+class HandleCloserAgent {
+ public:
+ HandleCloserAgent();
+ ~HandleCloserAgent();
+
+ // Reads the serialized list from the broker and creates the lookup map.
+ // Updates is_csrss_connected based on type of handles closed.
+ void InitializeHandlesToClose(bool* is_csrss_connected);
+
+ // Closes any handles matching those in the lookup map.
+ bool CloseHandles();
+
+ // True if we have handles waiting to be closed.
+ static bool NeedsHandlesClosed();
+
+ private:
+ // Attempt to stuff a closed handle with a dummy Event.
+ bool AttemptToStuffHandleSlot(HANDLE closed_handle, const std::wstring& type);
+
+ HandleMap handles_to_close_;
+ base::win::ScopedHandle dummy_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(HandleCloserAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_CLOSER_AGENT_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc
new file mode 100644
index 0000000000..7406f77592
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_closer_test.cc
@@ -0,0 +1,297 @@
+// Copyright (c) 2011 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 <limits.h>
+#include <stddef.h>
+
+#include "base/strings/stringprintf.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/handle_closer_agent.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const wchar_t* kFileExtensions[] = {L".1", L".2", L".3", L".4"};
+
+// Returns a handle to a unique marker file that can be retrieved between runs.
+HANDLE GetMarkerFile(const wchar_t* extension) {
+ wchar_t path_buffer[MAX_PATH + 1];
+ CHECK(::GetTempPath(MAX_PATH, path_buffer));
+ std::wstring marker_path = path_buffer;
+ marker_path += L"\\sbox_marker_";
+
+ // Generate a unique value from the exe's size and timestamp.
+ CHECK(::GetModuleFileName(nullptr, path_buffer, MAX_PATH));
+ base::win::ScopedHandle module(
+ ::CreateFile(path_buffer, FILE_READ_ATTRIBUTES, FILE_SHARE_READ, nullptr,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
+ CHECK(module.IsValid());
+ FILETIME timestamp;
+ CHECK(::GetFileTime(module.Get(), &timestamp, nullptr, nullptr));
+ marker_path +=
+ base::StringPrintf(L"%08x%08x%08x", ::GetFileSize(module.Get(), nullptr),
+ timestamp.dwLowDateTime, timestamp.dwHighDateTime);
+ marker_path += extension;
+
+ // Make the file delete-on-close so cleanup is automatic.
+ return CreateFile(marker_path.c_str(), FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, nullptr);
+}
+
+// Returns type infomation for an NT object. This routine is expected to be
+// called for invalid handles so it catches STATUS_INVALID_HANDLE exceptions
+// that can be generated when handle tracing is enabled.
+NTSTATUS QueryObjectTypeInformation(HANDLE handle, void* buffer, ULONG* size) {
+ static NtQueryObject QueryObject = nullptr;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ __try {
+ status = QueryObject(handle, ObjectTypeInformation, buffer, *size, size);
+ } __except (GetExceptionCode() == STATUS_INVALID_HANDLE
+ ? EXCEPTION_EXECUTE_HANDLER
+ : EXCEPTION_CONTINUE_SEARCH) {
+ status = STATUS_INVALID_HANDLE;
+ }
+ return status;
+}
+
+// Used by the thread pool tests.
+HANDLE finish_event;
+const int kWaitCount = 20;
+
+} // namespace
+
+namespace sandbox {
+
+// Checks for the presence of a list of files (in object path form).
+// Format: CheckForFileHandle (Y|N) \path\to\file1 [\path\to\file2 ...]
+// - Y or N depending if the file should exist or not.
+SBOX_TESTS_COMMAND int CheckForFileHandles(int argc, wchar_t** argv) {
+ if (argc < 2)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ bool should_find = argv[0][0] == L'Y';
+ if (argv[0][1] != L'\0' || (!should_find && argv[0][0] != L'N'))
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ static int state = BEFORE_INIT;
+ switch (state++) {
+ case BEFORE_INIT:
+ // Create a unique marker file that is open while the test is running.
+ // The handles leak, but it will be closed by the test or on exit.
+ for (const wchar_t* kExtension : kFileExtensions)
+ CHECK_NE(GetMarkerFile(kExtension), INVALID_HANDLE_VALUE);
+ return SBOX_TEST_SUCCEEDED;
+
+ case AFTER_REVERT: {
+ // Brute force the handle table to find what we're looking for.
+ DWORD handle_count = UINT_MAX;
+ const int kInvalidHandleThreshold = 100;
+ const size_t kHandleOffset = 4; // Handles are always a multiple of 4.
+ HANDLE handle = nullptr;
+ int invalid_count = 0;
+ std::wstring handle_name;
+
+ if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count))
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ while (handle_count && invalid_count < kInvalidHandleThreshold) {
+ reinterpret_cast<size_t&>(handle) += kHandleOffset;
+ if (GetHandleName(handle, &handle_name)) {
+ for (int i = 1; i < argc; ++i) {
+ if (handle_name == argv[i])
+ return should_find ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+ }
+ --handle_count;
+ } else {
+ ++invalid_count;
+ }
+ }
+
+ return should_find ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
+ }
+
+ default: // Do nothing.
+ break;
+ }
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Checks that supplied handle is an Event and it's not waitable.
+// Format: CheckForEventHandles
+SBOX_TESTS_COMMAND int CheckForEventHandles(int argc, wchar_t** argv) {
+ static int state = BEFORE_INIT;
+ static std::vector<HANDLE> to_check;
+
+ switch (state++) {
+ case BEFORE_INIT:
+ // Create a unique marker file that is open while the test is running.
+ for (const wchar_t* kExtension : kFileExtensions) {
+ HANDLE handle = GetMarkerFile(kExtension);
+ CHECK_NE(handle, INVALID_HANDLE_VALUE);
+ to_check.push_back(handle);
+ }
+ return SBOX_TEST_SUCCEEDED;
+
+ case AFTER_REVERT:
+ for (HANDLE handle : to_check) {
+ // Set up buffers for the type info and the name.
+ std::vector<BYTE> type_info_buffer(sizeof(OBJECT_TYPE_INFORMATION) +
+ 32 * sizeof(wchar_t));
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(&(type_info_buffer[0]));
+ NTSTATUS rc;
+
+ // Get the type name, reusing the buffer.
+ ULONG size = static_cast<ULONG>(type_info_buffer.size());
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ while (rc == STATUS_INFO_LENGTH_MISMATCH ||
+ rc == STATUS_BUFFER_OVERFLOW) {
+ type_info_buffer.resize(size + sizeof(wchar_t));
+ type_info = reinterpret_cast<OBJECT_TYPE_INFORMATION*>(
+ &(type_info_buffer[0]));
+ rc = QueryObjectTypeInformation(handle, type_info, &size);
+ // Leave padding for the nul terminator.
+ if (NT_SUCCESS(rc) && size == type_info_buffer.size())
+ rc = STATUS_INFO_LENGTH_MISMATCH;
+ }
+
+ CHECK(NT_SUCCESS(rc));
+ CHECK(type_info->Name.Buffer);
+
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] =
+ L'\0';
+
+ // Should be an Event now.
+ CHECK_EQ(wcslen(type_info->Name.Buffer), 5U);
+ CHECK_EQ(wcscmp(L"Event", type_info->Name.Buffer), 0);
+
+ // Should not be able to wait.
+ CHECK_EQ(WaitForSingleObject(handle, INFINITE), WAIT_FAILED);
+
+ // Should be able to close.
+ CHECK(::CloseHandle(handle));
+ }
+ return SBOX_TEST_SUCCEEDED;
+
+ default: // Do nothing.
+ break;
+ }
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleCloserTest, CheckForMarkerFiles) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+
+ std::wstring command = std::wstring(L"CheckForFileHandles Y");
+ for (const wchar_t* kExtension : kFileExtensions) {
+ std::wstring handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kExtension));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+ command += (L" ");
+ command += handle_name;
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str()))
+ << "Failed: " << command;
+}
+
+TEST(HandleCloserTest, CloseMarkerFiles) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ std::wstring command = std::wstring(L"CheckForFileHandles N");
+ for (const wchar_t* kExtension : kFileExtensions) {
+ std::wstring handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kExtension));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+ CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
+ SBOX_ALL_OK);
+ command += (L" ");
+ command += handle_name;
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str()))
+ << "Failed: " << command;
+}
+
+TEST(HandleCloserTest, CheckStuffedHandle) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+
+ for (const wchar_t* kExtension : kFileExtensions) {
+ std::wstring handle_name;
+ base::win::ScopedHandle marker(GetMarkerFile(kExtension));
+ CHECK(marker.IsValid());
+ CHECK(sandbox::GetHandleName(marker.Get(), &handle_name));
+ CHECK_EQ(policy->AddKernelObjectToClose(L"File", handle_name.c_str()),
+ SBOX_ALL_OK);
+ }
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckForEventHandles"));
+}
+
+void WINAPI ThreadPoolTask(void* event, BOOLEAN timeout) {
+ static volatile LONG waiters_remaining = kWaitCount;
+ CHECK(!timeout);
+ CHECK(::CloseHandle(event));
+ if (::InterlockedDecrement(&waiters_remaining) == 0)
+ CHECK(::SetEvent(finish_event));
+}
+
+// Run a thread pool inside a sandbox without a CSRSS connection.
+SBOX_TESTS_COMMAND int RunThreadPool(int argc, wchar_t** argv) {
+ HANDLE wait_list[20];
+ finish_event = ::CreateEvent(nullptr, true, false, nullptr);
+ CHECK(finish_event);
+
+ // Set up a bunch of waiters.
+ HANDLE pool = nullptr;
+ for (int i = 0; i < kWaitCount; ++i) {
+ HANDLE event = ::CreateEvent(nullptr, true, false, nullptr);
+ CHECK(event);
+ CHECK(::RegisterWaitForSingleObject(&pool, event, ThreadPoolTask, event,
+ INFINITE, WT_EXECUTEONLYONCE));
+ wait_list[i] = event;
+ }
+
+ // Signal all the waiters.
+ for (int i = 0; i < kWaitCount; ++i)
+ CHECK(::SetEvent(wait_list[i]));
+
+ CHECK_EQ(::WaitForSingleObject(finish_event, INFINITE), WAIT_OBJECT_0);
+ CHECK(::CloseHandle(finish_event));
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleCloserTest, RunThreadPool) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(AFTER_REVERT);
+
+ // Sandbox policy will determine which platforms to disconnect CSRSS and when
+ // to close the CSRSS handle.
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"RunThreadPool"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc
new file mode 100644
index 0000000000..611e33d2a6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/handle_interception.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+
+namespace sandbox {
+
+HandleDispatcher::HandleDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall duplicate_handle_proxy = {
+ {IpcTag::DUPLICATEHANDLEPROXY,
+ {VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &HandleDispatcher::DuplicateHandleProxy)};
+
+ ipc_calls_.push_back(duplicate_handle_proxy);
+}
+
+bool HandleDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ // We perform no interceptions for handles right now.
+ switch (service) {
+ case IpcTag::DUPLICATEHANDLEPROXY:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool HandleDispatcher::DuplicateHandleProxy(IPCInfo* ipc,
+ HANDLE source_handle,
+ uint32_t target_process_id,
+ uint32_t desired_access,
+ uint32_t options) {
+ static NtQueryObject QueryObject = NULL;
+ if (!QueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &QueryObject);
+
+ // Get a copy of the handle for use in the broker process.
+ HANDLE handle_temp;
+ if (!::DuplicateHandle(ipc->client_info->process, source_handle,
+ ::GetCurrentProcess(), &handle_temp,
+ 0, FALSE, DUPLICATE_SAME_ACCESS | options)) {
+ ipc->return_info.win32_result = ::GetLastError();
+ return false;
+ }
+ options &= ~DUPLICATE_CLOSE_SOURCE;
+ base::win::ScopedHandle handle(handle_temp);
+
+ // Get the object type (32 characters is safe; current max is 14).
+ BYTE buffer[sizeof(OBJECT_TYPE_INFORMATION) + 32 * sizeof(wchar_t)];
+ OBJECT_TYPE_INFORMATION* type_info =
+ reinterpret_cast<OBJECT_TYPE_INFORMATION*>(buffer);
+ ULONG size = sizeof(buffer) - sizeof(wchar_t);
+ NTSTATUS error =
+ QueryObject(handle.Get(), ObjectTypeInformation, type_info, size, &size);
+ if (!NT_SUCCESS(error)) {
+ ipc->return_info.nt_status = error;
+ return false;
+ }
+ type_info->Name.Buffer[type_info->Name.Length / sizeof(wchar_t)] = L'\0';
+
+ CountedParameterSet<HandleTarget> params;
+ params[HandleTarget::NAME] = ParamPickerMake(type_info->Name.Buffer);
+ params[HandleTarget::TARGET] = ParamPickerMake(target_process_id);
+
+ EvalResult eval = policy_base_->EvalPolicy(IpcTag::DUPLICATEHANDLEPROXY,
+ params.GetBase());
+ ipc->return_info.win32_result =
+ HandlePolicy::DuplicateHandleProxyAction(eval, handle.Get(),
+ target_process_id,
+ &ipc->return_info.handle,
+ desired_access, options);
+ return true;
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h
new file mode 100644
index 0000000000..6f9adbc10b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_dispatcher.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_DISPATCHER_H_
+#define SANDBOX_SRC_HANDLE_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles handle-related IPC calls.
+class HandleDispatcher : public Dispatcher {
+ public:
+ explicit HandleDispatcher(PolicyBase* policy_base);
+ ~HandleDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to
+ // TargetServices::DuplicateHandle() in the target.
+ bool DuplicateHandleProxy(IPCInfo* ipc,
+ HANDLE source_handle,
+ uint32_t target_process_id,
+ uint32_t desired_access,
+ uint32_t options);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(HandleDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_DISPATCHER_H_
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc
new file mode 100644
index 0000000000..c91903c809
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_inheritance_test.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int HandleInheritanceTests_PrintToStdout(int argc,
+ wchar_t** argv) {
+ printf("Example output to stdout\n");
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(HandleInheritanceTests, TestStdoutInheritance) {
+ base::ScopedTempDir temp_directory;
+ base::FilePath temp_file_name;
+ ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
+ ASSERT_TRUE(
+ CreateTemporaryFileInDir(temp_directory.GetPath(), &temp_file_name));
+
+ SECURITY_ATTRIBUTES attrs = {};
+ attrs.nLength = sizeof(attrs);
+ attrs.bInheritHandle = true;
+ base::win::ScopedHandle tmp_handle(
+ CreateFile(temp_file_name.value().c_str(), GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, &attrs,
+ OPEN_EXISTING, 0, nullptr));
+ ASSERT_TRUE(tmp_handle.IsValid());
+
+ TestRunner runner;
+ ASSERT_EQ(SBOX_ALL_OK, runner.GetPolicy()->SetStdoutHandle(tmp_handle.Get()));
+ int result = runner.RunTest(L"HandleInheritanceTests_PrintToStdout");
+ ASSERT_EQ(SBOX_TEST_SUCCEEDED, result);
+
+ std::string data;
+ ASSERT_TRUE(base::ReadFileToString(base::FilePath(temp_file_name), &data));
+ // Redirection uses a feature that was added in Windows Vista.
+ ASSERT_EQ("Example output to stdout\r\n", data);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.cc b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
new file mode 100644
index 0000000000..53db4a8b27
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+ResultCode DuplicateHandleProxy(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ *target_handle = NULL;
+
+ void* memory = GetGlobalIPCMemory();
+ if (NULL == memory)
+ return SBOX_ERROR_NO_SPACE;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::DUPLICATEHANDLEPROXY,
+ source_handle, target_process_id,
+ desired_access, options, &answer);
+ if (SBOX_ALL_OK != code)
+ return code;
+
+ if (answer.win32_result) {
+ ::SetLastError(answer.win32_result);
+ mozilla::sandboxing::LogBlocked("DuplicateHandle");
+ return SBOX_ERROR_GENERIC;
+ }
+
+ *target_handle = answer.handle;
+ mozilla::sandboxing::LogAllowed("DuplicateHandle");
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_interception.h b/security/sandbox/chromium/sandbox/win/src/handle_interception.h
new file mode 100644
index 0000000000..6f60811f17
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_interception.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+#ifndef SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+#define SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+
+namespace sandbox {
+
+// TODO(jschuh) Add an interception to catch dangerous DuplicateHandle calls.
+
+ResultCode DuplicateHandleProxy(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_INTERCEPTION_H_
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy.cc b/security/sandbox/chromium/sandbox/win/src/handle_policy.cc
new file mode 100644
index 0000000000..fa3295ae3f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_policy.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/handle_policy.h"
+
+#include <string>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/broker_services.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+
+namespace sandbox {
+
+bool HandlePolicy::GenerateRules(const wchar_t* type_name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ PolicyRule duplicate_rule(ASK_BROKER);
+
+ switch (semantics) {
+ case TargetPolicy::HANDLES_DUP_ANY: {
+ if (!duplicate_rule.AddNumberMatch(IF_NOT, HandleTarget::TARGET,
+ ::GetCurrentProcessId(), EQUAL)) {
+ return false;
+ }
+ break;
+ }
+
+ case TargetPolicy::HANDLES_DUP_BROKER: {
+ if (!duplicate_rule.AddNumberMatch(IF, HandleTarget::TARGET,
+ ::GetCurrentProcessId(), EQUAL)) {
+ return false;
+ }
+ break;
+ }
+
+ default:
+ return false;
+ }
+ if (!duplicate_rule.AddStringMatch(IF, HandleTarget::NAME, type_name,
+ CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::DUPLICATEHANDLEPROXY, &duplicate_rule)) {
+ return false;
+ }
+ return true;
+}
+
+DWORD HandlePolicy::DuplicateHandleProxyAction(EvalResult eval_result,
+ HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ // The only action supported is ASK_BROKER which means duplicate the handle.
+ if (ASK_BROKER != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ base::win::ScopedHandle remote_target_process;
+ if (target_process_id != ::GetCurrentProcessId()) {
+ // Sandboxed children are dynamic, so we check that manually.
+ if (!BrokerServicesBase::GetInstance()->IsSafeDuplicationTarget(
+ target_process_id)) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ remote_target_process.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE,
+ target_process_id));
+ if (!remote_target_process.IsValid())
+ return ::GetLastError();
+ }
+
+ // If the policy didn't block us and we have no valid target, then the broker
+ // (this process) is the valid target.
+ HANDLE target_process = remote_target_process.IsValid() ?
+ remote_target_process.Get() : ::GetCurrentProcess();
+ if (!::DuplicateHandle(::GetCurrentProcess(), source_handle, target_process,
+ target_handle, desired_access, FALSE,
+ options)) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy.h b/security/sandbox/chromium/sandbox/win/src/handle_policy.h
new file mode 100644
index 0000000000..29ce5ab666
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_policy.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_HANDLE_POLICY_H_
+#define SANDBOX_SRC_HANDLE_POLICY_H_
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+enum EvalResult;
+
+// This class centralizes most of the knowledge related to handle policy.
+class HandlePolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for handles, in particular duplicate action.
+ static bool GenerateRules(const wchar_t* type_name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Processes a 'TargetPolicy::DuplicateHandle()' request from the target.
+ static DWORD DuplicateHandleProxyAction(EvalResult eval_result,
+ HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_HANDLE_POLICY_H_
+
diff --git a/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc
new file mode 100644
index 0000000000..11382da811
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/handle_policy_test.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/stringprintf.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Just waits for the supplied number of milliseconds.
+SBOX_TESTS_COMMAND int Handle_WaitProcess(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ ::Sleep(::wcstoul(argv[0], NULL, 10));
+ return SBOX_TEST_TIMED_OUT;
+}
+
+// Attempts to duplicate an event handle into the target process.
+SBOX_TESTS_COMMAND int Handle_DuplicateEvent(int argc, wchar_t **argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // Create a test event to use as a handle.
+ base::win::ScopedHandle test_event;
+ test_event.Set(::CreateEvent(NULL, TRUE, TRUE, NULL));
+ if (!test_event.IsValid())
+ return SBOX_TEST_FIRST_ERROR;
+
+ // Get the target process ID.
+ DWORD target_process_id = ::wcstoul(argv[0], NULL, 10);
+
+ HANDLE handle = NULL;
+ ResultCode result = SandboxFactory::GetTargetServices()->DuplicateHandle(
+ test_event.Get(), target_process_id, &handle, 0, DUPLICATE_SAME_ACCESS);
+
+ return (result == SBOX_ALL_OK) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_DENIED;
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicateHandle) {
+ TestRunner target;
+ TestRunner runner;
+
+ // Kick off an asynchronous target process for testing.
+ target.SetAsynchronous(true);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
+
+ // First test that we fail to open the event.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicatePeerHandle) {
+ TestRunner target;
+ TestRunner runner;
+
+ // Kick off an asynchronous target process for testing.
+ target.SetAsynchronous(true);
+ target.SetUnsandboxed(true);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, target.RunTest(L"Handle_WaitProcess 30000"));
+
+ // First test that we fail to open the event.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ target.process_id());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Now successfully open the event after adding a duplicate handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+// Tests that duplicating an object works only when the policy allows it.
+TEST(HandlePolicyTest, DuplicateBrokerHandle) {
+ TestRunner runner;
+
+ // First test that we fail to open the event.
+ base::string16 cmd_line = base::StringPrintf(L"Handle_DuplicateEvent %d",
+ ::GetCurrentProcessId());
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+ // Add the peer rule and make sure we fail again.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_ANY,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(cmd_line.c_str()));
+
+
+ // Now successfully open the event after adding a broker handle rule.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_HANDLES,
+ TargetPolicy::HANDLES_DUP_BROKER,
+ L"Event"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(cmd_line.c_str()));
+}
+
+} // namespace sandbox
+
diff --git a/security/sandbox/chromium/sandbox/win/src/heap_helper.cc b/security/sandbox/chromium/sandbox/win/src/heap_helper.cc
new file mode 100644
index 0000000000..b0f4498fea
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/heap_helper.cc
@@ -0,0 +1,124 @@
+// 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/heap_helper.h"
+
+#include <windows.h>
+
+#include "base/memory/ref_counted.h"
+#include "base/win/windows_version.h"
+
+namespace sandbox {
+namespace {
+#pragma pack(1)
+
+// These are undocumented, but readily found on the internet.
+constexpr DWORD kHeapClass8 = 0x00008000; // CSR port heap
+constexpr DWORD kHeapClassMask = 0x0000f000;
+
+constexpr DWORD kHeapSegmentSignature = 0xffeeffee;
+constexpr DWORD kHeapSignature = 0xeeffeeff;
+
+typedef struct _HEAP_ENTRY {
+ PVOID Data1;
+ PVOID Data2;
+} HEAP_ENTRY, *PHEAP_ENTRY;
+
+// The _HEAP struct is not documented, so char arrays are used to space out the
+// struct of the fields that are not relevant. However, this spacing is
+// different because of the different pointer widths between 32 and 64-bit.
+// So 32 and 64 bit structs are defined.
+struct _HEAP_32 {
+ HEAP_ENTRY HeapEntry;
+ DWORD SegmentSignature;
+ DWORD SegmentFlags;
+ LIST_ENTRY SegmentListEntry;
+ struct _HEAP_32* Heap;
+ char Unknown0[0x24];
+ // Offset 0x40
+ DWORD Flags;
+ // Offset 0x60
+ char Unknown1[0x1c];
+ DWORD Signature;
+ // Other stuff that is not relevant.
+};
+
+struct _HEAP_64 {
+ HEAP_ENTRY HeapEntry;
+ DWORD SegmentSignature;
+ DWORD SegmentFlags;
+ LIST_ENTRY SegmentListEntry;
+ struct _HEAP_64* Heap;
+ char Unknown0[0x40];
+ // Offset 0x70
+ DWORD Flags;
+ // Offset 0x98
+ char Unknown1[0x24];
+ DWORD Signature;
+ // Other stuff that is not relevant.
+};
+
+#if defined(_WIN64)
+using _HEAP = _HEAP_64;
+#else // defined(_WIN64)
+using _HEAP = _HEAP_32;
+#endif // defined(_WIN64)
+
+bool ValidateHeap(_HEAP* heap) {
+ if (heap->SegmentSignature != kHeapSegmentSignature)
+ return false;
+ if (heap->Heap != heap)
+ return false;
+ if (heap->Signature != kHeapSignature)
+ return false;
+ return true;
+}
+
+} // namespace
+
+bool HeapFlags(HANDLE handle, DWORD* flags) {
+ if (!handle || !flags) {
+ // This is an error.
+ return false;
+ }
+ _HEAP* heap = reinterpret_cast<_HEAP*>(handle);
+ if (!ValidateHeap(heap)) {
+ DLOG(ERROR) << "unable to validate heap";
+ return false;
+ }
+ *flags = heap->Flags;
+ return true;
+}
+
+HANDLE FindCsrPortHeap() {
+ if (base::win::GetVersion() < base::win::Version::WIN10) {
+ // This functionality has not been verified on versions before Win10.
+ return nullptr;
+ }
+ DWORD number_of_heaps = ::GetProcessHeaps(0, nullptr);
+ std::unique_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]);
+ if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps)
+ return nullptr;
+
+ // Search for the CSR port heap handle, identified purely based on flags.
+ HANDLE csr_port_heap = nullptr;
+ for (size_t i = 0; i < number_of_heaps; ++i) {
+ HANDLE handle = all_heaps[i];
+ DWORD flags = 0;
+ if (!HeapFlags(handle, &flags)) {
+ DLOG(ERROR) << "Unable to get flags for this heap";
+ continue;
+ }
+ if ((flags & kHeapClassMask) == kHeapClass8) {
+ if (csr_port_heap) {
+ DLOG(ERROR) << "Found multiple suitable CSR Port heaps";
+ return nullptr;
+ }
+ csr_port_heap = handle;
+ }
+ }
+ return csr_port_heap;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/heap_helper.h b/security/sandbox/chromium/sandbox/win/src/heap_helper.h
new file mode 100644
index 0000000000..302a3c4e29
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/heap_helper.h
@@ -0,0 +1,26 @@
+// 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_WIN_SRC_HEAP_HELPER_H_
+#define SANDBOX_WIN_SRC_HEAP_HELPER_H_
+
+#include <windows.h>
+
+#include "base/win/windows_version.h"
+
+namespace sandbox {
+// These helper functions are not expected to be used generally, but are exposed
+// only to allow direct testing of this functionality.
+
+// Return the flags for this heap handle. Limited verification of the handle is
+// performed. No verification of the flags is performed.
+bool HeapFlags(HANDLE handle, DWORD* flags);
+
+// Return the handle to the CSR Port Heap, return nullptr if none or more than
+// one candidate was found.
+HANDLE FindCsrPortHeap();
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_HEAP_HELPER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc b/security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc
new file mode 100644
index 0000000000..a4788af028
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/integrity_level_test.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2011 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 <atlsecurity.h>
+
+#include "base/process/process_info.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int CheckUntrustedIntegrityLevel(int argc, wchar_t** argv) {
+ return (base::GetCurrentProcessIntegrityLevel() == base::UNTRUSTED_INTEGRITY)
+ ? SBOX_TEST_SUCCEEDED
+ : SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int CheckLowIntegrityLevel(int argc, wchar_t** argv) {
+ return (base::GetCurrentProcessIntegrityLevel() == base::LOW_INTEGRITY)
+ ? SBOX_TEST_SUCCEEDED
+ : SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int CheckIntegrityLevel(int argc, wchar_t** argv) {
+ ATL::CAccessToken token;
+ if (!token.GetEffectiveToken(TOKEN_READ))
+ return SBOX_TEST_FAILED;
+
+ char* buffer[100];
+ DWORD buf_size = 100;
+ if (!::GetTokenInformation(token.GetHandle(), TokenIntegrityLevel,
+ reinterpret_cast<void*>(buffer), buf_size,
+ &buf_size))
+ return SBOX_TEST_FAILED;
+
+ TOKEN_MANDATORY_LABEL* label =
+ reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buffer);
+
+ PSID sid_low = nullptr;
+ if (!::ConvertStringSidToSid(L"S-1-16-4096", &sid_low))
+ return SBOX_TEST_FAILED;
+
+ bool is_low_sid = ::EqualSid(label->Label.Sid, sid_low);
+
+ ::LocalFree(sid_low);
+
+ if (is_low_sid)
+ return SBOX_TEST_SUCCEEDED;
+
+ return SBOX_TEST_DENIED;
+}
+
+TEST(IntegrityLevelTest, TestLowILReal) {
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ runner.GetPolicy()->SetAlternateDesktop(true);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(DelayedIntegrityLevelTest, TestLowILDelayed) {
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckIntegrityLevel"));
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(IntegrityLevelTest, TestNoILChange) {
+ TestRunner runner(JOB_LOCKDOWN, USER_INTERACTIVE, USER_INTERACTIVE);
+
+ runner.SetTimeout(INFINITE);
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"CheckIntegrityLevel"));
+}
+
+TEST(IntegrityLevelTest, TestUntrustedIL) {
+ TestRunner runner(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_UNTRUSTED);
+ runner.GetPolicy()->SetLockdownDefaultDacl();
+
+ runner.SetTimeout(INFINITE);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"CheckUntrustedIntegrityLevel"));
+}
+
+TEST(IntegrityLevelTest, TestLowIL) {
+ TestRunner runner(JOB_LOCKDOWN, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.GetPolicy()->SetIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ runner.GetPolicy()->SetDelayedIntegrityLevel(INTEGRITY_LEVEL_LOW);
+ runner.GetPolicy()->SetLockdownDefaultDacl();
+
+ runner.SetTimeout(INFINITE);
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"CheckLowIntegrityLevel"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interception.cc b/security/sandbox/chromium/sandbox/win/src/interception.cc
new file mode 100644
index 0000000000..b2c0bc47ea
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception.cc
@@ -0,0 +1,512 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// For information about interceptions as a whole see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include "sandbox/win/src/interception.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/logging.h"
+#include "base/scoped_native_library.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_rand.h"
+#include "sandbox/win/src/service_resolver.h"
+#include "sandbox/win/src/target_interceptions.h"
+#include "sandbox/win/src/target_process.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+// Standard allocation granularity and page size for Windows.
+const size_t kAllocGranularity = 65536;
+const size_t kPageSize = 4096;
+
+} // namespace
+
+namespace internal {
+
+// Find a random offset within 64k and aligned to ceil(log2(size)).
+size_t GetGranularAlignedRandomOffset(size_t size) {
+ CHECK_LE(size, kAllocGranularity);
+ unsigned int offset;
+
+ do {
+ GetRandom(&offset);
+ offset &= (kAllocGranularity - 1);
+ } while (offset > (kAllocGranularity - size));
+
+ // Find an alignment between 64 and the page size (4096).
+ size_t align_size = kPageSize;
+ for (size_t new_size = align_size / 2; new_size >= size; new_size /= 2) {
+ align_size = new_size;
+ }
+ return offset & ~(align_size - 1);
+}
+
+} // namespace internal
+
+SANDBOX_INTERCEPT SharedMemory* g_interceptions;
+
+// Table of the unpatched functions that we intercept. Mapped from the parent.
+SANDBOX_INTERCEPT OriginalFunctions g_originals = {nullptr};
+
+// Magic constant that identifies that this function is not to be patched.
+const char kUnloadDLLDummyFunction[] = "@";
+
+InterceptionManager::InterceptionData::InterceptionData() {}
+
+InterceptionManager::InterceptionData::InterceptionData(
+ const InterceptionData& other) = default;
+
+InterceptionManager::InterceptionData::~InterceptionData() {}
+
+InterceptionManager::InterceptionManager(TargetProcess* child_process,
+ bool relaxed)
+ : child_(child_process), names_used_(false), relaxed_(relaxed) {
+ child_->AddRef();
+}
+InterceptionManager::~InterceptionManager() {
+ child_->Release();
+}
+
+bool InterceptionManager::AddToPatchedFunctions(
+ const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const void* replacement_code_address,
+ InterceptorId id) {
+ InterceptionData function;
+ function.type = interception_type;
+ function.id = id;
+ function.dll = dll_name;
+ function.function = function_name;
+ function.interceptor_address = replacement_code_address;
+
+ interceptions_.push_back(function);
+ return true;
+}
+
+bool InterceptionManager::AddToPatchedFunctions(
+ const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const char* replacement_function_name,
+ InterceptorId id) {
+ InterceptionData function;
+ function.type = interception_type;
+ function.id = id;
+ function.dll = dll_name;
+ function.function = function_name;
+ function.interceptor = replacement_function_name;
+ function.interceptor_address = nullptr;
+
+ interceptions_.push_back(function);
+ names_used_ = true;
+ return true;
+}
+
+bool InterceptionManager::AddToUnloadModules(const wchar_t* dll_name) {
+ InterceptionData module_to_unload;
+ module_to_unload.type = INTERCEPTION_UNLOAD_MODULE;
+ module_to_unload.dll = dll_name;
+ // The next two are dummy values that make the structures regular, instead
+ // of having special cases. They should not be used.
+ module_to_unload.function = kUnloadDLLDummyFunction;
+ module_to_unload.interceptor_address = reinterpret_cast<void*>(1);
+
+ interceptions_.push_back(module_to_unload);
+ return true;
+}
+
+ResultCode InterceptionManager::InitializeInterceptions() {
+ if (interceptions_.empty())
+ return SBOX_ALL_OK; // Nothing to do here
+
+ size_t buffer_bytes = GetBufferSize();
+ std::unique_ptr<char[]> local_buffer(new char[buffer_bytes]);
+
+ if (!SetupConfigBuffer(local_buffer.get(), buffer_bytes))
+ return SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_CONFIG_BUFFER;
+
+ void* remote_buffer;
+ if (!CopyToChildMemory(child_->Process(), local_buffer.get(), buffer_bytes,
+ &remote_buffer))
+ return SBOX_ERROR_CANNOT_COPY_DATA_TO_CHILD;
+
+ bool hot_patch_needed = (0 != buffer_bytes);
+ ResultCode rc = PatchNtdll(hot_patch_needed);
+
+ if (rc != SBOX_ALL_OK)
+ return rc;
+
+ g_interceptions = reinterpret_cast<SharedMemory*>(remote_buffer);
+ rc = child_->TransferVariable("g_interceptions", &g_interceptions,
+ sizeof(g_interceptions));
+ return rc;
+}
+
+size_t InterceptionManager::GetBufferSize() const {
+ std::set<std::wstring> dlls;
+ size_t buffer_bytes = 0;
+
+ for (const auto& interception : interceptions_) {
+ // skip interceptions that are performed from the parent
+ if (!IsInterceptionPerformedByChild(interception))
+ continue;
+
+ if (!dlls.count(interception.dll)) {
+ // NULL terminate the dll name on the structure
+ size_t dll_name_bytes = (interception.dll.size() + 1) * sizeof(wchar_t);
+
+ // include the dll related size
+ buffer_bytes += RoundUpToMultiple(
+ offsetof(DllPatchInfo, dll_name) + dll_name_bytes, sizeof(size_t));
+ dlls.insert(interception.dll);
+ }
+
+ // we have to NULL terminate the strings on the structure
+ size_t strings_chars =
+ interception.function.size() + interception.interceptor.size() + 2;
+
+ // a new FunctionInfo is required per function
+ size_t record_bytes = offsetof(FunctionInfo, function) + strings_chars;
+ record_bytes = RoundUpToMultiple(record_bytes, sizeof(size_t));
+ buffer_bytes += record_bytes;
+ }
+
+ if (0 != buffer_bytes)
+ // add the part of SharedMemory that we have not counted yet
+ buffer_bytes += offsetof(SharedMemory, dll_list);
+
+ return buffer_bytes;
+}
+
+// Basically, walk the list of interceptions moving them to the config buffer,
+// but keeping together all interceptions that belong to the same dll.
+// The config buffer is a local buffer, not the one allocated on the child.
+bool InterceptionManager::SetupConfigBuffer(void* buffer, size_t buffer_bytes) {
+ if (0 == buffer_bytes)
+ return true;
+
+ DCHECK(buffer_bytes > sizeof(SharedMemory));
+
+ SharedMemory* shared_memory = reinterpret_cast<SharedMemory*>(buffer);
+ DllPatchInfo* dll_info = shared_memory->dll_list;
+ int num_dlls = 0;
+
+ shared_memory->interceptor_base =
+ names_used_ ? child_->MainModule() : nullptr;
+
+ buffer_bytes -= offsetof(SharedMemory, dll_list);
+ buffer = dll_info;
+
+ std::list<InterceptionData>::iterator it = interceptions_.begin();
+ for (; it != interceptions_.end();) {
+ // skip interceptions that are performed from the parent
+ if (!IsInterceptionPerformedByChild(*it)) {
+ ++it;
+ continue;
+ }
+
+ const std::wstring dll = it->dll;
+ if (!SetupDllInfo(*it, &buffer, &buffer_bytes))
+ return false;
+
+ // walk the interceptions from this point, saving the ones that are
+ // performed on this dll, and removing the entry from the list.
+ // advance the iterator before removing the element from the list
+ std::list<InterceptionData>::iterator rest = it;
+ for (; rest != interceptions_.end();) {
+ if (rest->dll == dll) {
+ if (!SetupInterceptionInfo(*rest, &buffer, &buffer_bytes, dll_info))
+ return false;
+ if (it == rest)
+ ++it;
+ rest = interceptions_.erase(rest);
+ } else {
+ ++rest;
+ }
+ }
+ dll_info = reinterpret_cast<DllPatchInfo*>(buffer);
+ ++num_dlls;
+ }
+
+ shared_memory->num_intercepted_dlls = num_dlls;
+ return true;
+}
+
+// Fills up just the part that depends on the dll, not the info that depends on
+// the actual interception.
+bool InterceptionManager::SetupDllInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes) const {
+ DCHECK(buffer_bytes);
+ DCHECK(buffer);
+ DCHECK(*buffer);
+
+ DllPatchInfo* dll_info = reinterpret_cast<DllPatchInfo*>(*buffer);
+
+ // the strings have to be zero terminated
+ size_t required = offsetof(DllPatchInfo, dll_name) +
+ (data.dll.size() + 1) * sizeof(wchar_t);
+ required = RoundUpToMultiple(required, sizeof(size_t));
+ if (*buffer_bytes < required)
+ return false;
+
+ *buffer_bytes -= required;
+ *buffer = reinterpret_cast<char*>(*buffer) + required;
+
+ // set up the dll info to be what we know about it at this time
+ dll_info->unload_module = (data.type == INTERCEPTION_UNLOAD_MODULE);
+ dll_info->record_bytes = required;
+ dll_info->offset_to_functions = required;
+ dll_info->num_functions = 0;
+ data.dll.copy(dll_info->dll_name, data.dll.size());
+ dll_info->dll_name[data.dll.size()] = L'\0';
+
+ return true;
+}
+
+bool InterceptionManager::SetupInterceptionInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes,
+ DllPatchInfo* dll_info) const {
+ DCHECK(buffer_bytes);
+ DCHECK(buffer);
+ DCHECK(*buffer);
+
+ if ((dll_info->unload_module) && (data.function != kUnloadDLLDummyFunction)) {
+ // Can't specify a dll for both patch and unload.
+ NOTREACHED();
+ }
+
+ FunctionInfo* function = reinterpret_cast<FunctionInfo*>(*buffer);
+
+ size_t name_bytes = data.function.size();
+ size_t interceptor_bytes = data.interceptor.size();
+
+ // the strings at the end of the structure are zero terminated
+ size_t required =
+ offsetof(FunctionInfo, function) + name_bytes + interceptor_bytes + 2;
+ required = RoundUpToMultiple(required, sizeof(size_t));
+ if (*buffer_bytes < required)
+ return false;
+
+ // update the caller's values
+ *buffer_bytes -= required;
+ *buffer = reinterpret_cast<char*>(*buffer) + required;
+
+ function->record_bytes = required;
+ function->type = data.type;
+ function->id = data.id;
+ function->interceptor_address = data.interceptor_address;
+ char* names = function->function;
+
+ data.function.copy(names, name_bytes);
+ names += name_bytes;
+ *names++ = '\0';
+
+ // interceptor follows the function_name
+ data.interceptor.copy(names, interceptor_bytes);
+ names += interceptor_bytes;
+ *names++ = '\0';
+
+ // update the dll table
+ dll_info->num_functions++;
+ dll_info->record_bytes += required;
+
+ return true;
+}
+
+// Only return true if the child should be able to perform this interception.
+bool InterceptionManager::IsInterceptionPerformedByChild(
+ const InterceptionData& data) const {
+ if (INTERCEPTION_INVALID == data.type)
+ return false;
+
+ if (INTERCEPTION_SERVICE_CALL == data.type)
+ return false;
+
+ if (data.type >= INTERCEPTION_LAST)
+ return false;
+
+ std::wstring ntdll(kNtdllName);
+ if (ntdll == data.dll)
+ return false; // ntdll has to be intercepted from the parent
+
+ return true;
+}
+
+ResultCode InterceptionManager::PatchNtdll(bool hot_patch_needed) {
+ // Maybe there is nothing to do
+ if (!hot_patch_needed && interceptions_.empty())
+ return SBOX_ALL_OK;
+
+ if (hot_patch_needed) {
+#if defined(SANDBOX_EXPORTS)
+// Make sure the functions are not excluded by the linker.
+#if defined(_WIN64)
+#pragma comment(linker, "/include:TargetNtMapViewOfSection64")
+#pragma comment(linker, "/include:TargetNtUnmapViewOfSection64")
+#else
+#pragma comment(linker, "/include:_TargetNtMapViewOfSection@44")
+#pragma comment(linker, "/include:_TargetNtUnmapViewOfSection@12")
+#endif
+#endif // defined(SANDBOX_EXPORTS)
+ ADD_NT_INTERCEPTION(NtMapViewOfSection, MAP_VIEW_OF_SECTION_ID, 44);
+ ADD_NT_INTERCEPTION(NtUnmapViewOfSection, UNMAP_VIEW_OF_SECTION_ID, 12);
+ }
+
+ // Reserve a full 64k memory range in the child process.
+ HANDLE child = child_->Process();
+ BYTE* thunk_base = reinterpret_cast<BYTE*>(::VirtualAllocEx(
+ child, nullptr, kAllocGranularity, MEM_RESERVE, PAGE_NOACCESS));
+
+ // Find an aligned, random location within the reserved range.
+ size_t thunk_bytes =
+ interceptions_.size() * sizeof(ThunkData) + sizeof(DllInterceptionData);
+ size_t thunk_offset = internal::GetGranularAlignedRandomOffset(thunk_bytes);
+
+ // Split the base and offset along page boundaries.
+ thunk_base += thunk_offset & ~(kPageSize - 1);
+ thunk_offset &= kPageSize - 1;
+
+ // Make an aligned, padded allocation, and move the pointer to our chunk.
+ size_t thunk_bytes_padded = (thunk_bytes + kPageSize - 1) & ~(kPageSize - 1);
+ thunk_base = reinterpret_cast<BYTE*>(
+ ::VirtualAllocEx(child, thunk_base, thunk_bytes_padded, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE));
+ CHECK(thunk_base); // If this fails we'd crash anyway on an invalid access.
+ DllInterceptionData* thunks =
+ reinterpret_cast<DllInterceptionData*>(thunk_base + thunk_offset);
+
+ DllInterceptionData dll_data;
+ dll_data.data_bytes = thunk_bytes;
+ dll_data.num_thunks = 0;
+ dll_data.used_bytes = offsetof(DllInterceptionData, thunks);
+
+ // Reset all helpers for a new child.
+ memset(g_originals, 0, sizeof(g_originals));
+
+ // this should write all the individual thunks to the child's memory
+ ResultCode rc = PatchClientFunctions(thunks, thunk_bytes, &dll_data);
+
+ if (rc != SBOX_ALL_OK)
+ return rc;
+
+ // and now write the first part of the table to the child's memory
+ SIZE_T written;
+ bool ok =
+ !!::WriteProcessMemory(child, thunks, &dll_data,
+ offsetof(DllInterceptionData, thunks), &written);
+
+ if (!ok || (offsetof(DllInterceptionData, thunks) != written))
+ return SBOX_ERROR_CANNOT_WRITE_INTERCEPTION_THUNK;
+
+ // Attempt to protect all the thunks, but ignore failure
+ DWORD old_protection;
+ ::VirtualProtectEx(child, thunks, thunk_bytes, PAGE_EXECUTE_READ,
+ &old_protection);
+
+ ResultCode ret =
+ child_->TransferVariable("g_originals", g_originals, sizeof(g_originals));
+ return ret;
+}
+
+ResultCode InterceptionManager::PatchClientFunctions(
+ DllInterceptionData* thunks,
+ size_t thunk_bytes,
+ DllInterceptionData* dll_data) {
+ DCHECK(thunks);
+ DCHECK(dll_data);
+
+ HMODULE ntdll_base = ::GetModuleHandle(kNtdllName);
+ if (!ntdll_base)
+ return SBOX_ERROR_NO_HANDLE;
+
+ char* interceptor_base = nullptr;
+
+#if defined(SANDBOX_EXPORTS)
+ interceptor_base = reinterpret_cast<char*>(child_->MainModule());
+ base::ScopedNativeLibrary local_interceptor(::LoadLibrary(child_->Name()));
+#endif // defined(SANDBOX_EXPORTS)
+
+ std::unique_ptr<ServiceResolverThunk> thunk;
+#if defined(_WIN64)
+ thunk.reset(new ServiceResolverThunk(child_->Process(), relaxed_));
+#else
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
+ if (os_info->version() >= base::win::Version::WIN10)
+ thunk.reset(new Wow64W10ResolverThunk(child_->Process(), relaxed_));
+ else if (os_info->version() >= base::win::Version::WIN8)
+ thunk.reset(new Wow64W8ResolverThunk(child_->Process(), relaxed_));
+ else
+ thunk.reset(new Wow64ResolverThunk(child_->Process(), relaxed_));
+ } else if (os_info->version() >= base::win::Version::WIN8) {
+ thunk.reset(new Win8ResolverThunk(child_->Process(), relaxed_));
+ } else {
+ thunk.reset(new ServiceResolverThunk(child_->Process(), relaxed_));
+ }
+#endif
+
+ for (auto interception : interceptions_) {
+ const std::wstring ntdll(kNtdllName);
+ if (interception.dll != ntdll)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (INTERCEPTION_SERVICE_CALL != interception.type)
+ return SBOX_ERROR_BAD_PARAMS;
+
+#if defined(SANDBOX_EXPORTS)
+ // We may be trying to patch by function name.
+ if (!interception.interceptor_address) {
+ const char* address;
+ NTSTATUS ret = thunk->ResolveInterceptor(
+ local_interceptor.get(), interception.interceptor.c_str(),
+ reinterpret_cast<const void**>(&address));
+ if (!NT_SUCCESS(ret)) {
+ ::SetLastError(GetLastErrorFromNtStatus(ret));
+ return SBOX_ERROR_CANNOT_RESOLVE_INTERCEPTION_THUNK;
+ }
+
+ // Translate the local address to an address on the child.
+ interception.interceptor_address =
+ interceptor_base +
+ (address - reinterpret_cast<char*>(local_interceptor.get()));
+ }
+#endif // defined(SANDBOX_EXPORTS)
+ NTSTATUS ret = thunk->Setup(
+ ntdll_base, interceptor_base, interception.function.c_str(),
+ interception.interceptor.c_str(), interception.interceptor_address,
+ &thunks->thunks[dll_data->num_thunks],
+ thunk_bytes - dll_data->used_bytes, nullptr);
+ if (!NT_SUCCESS(ret)) {
+ ::SetLastError(GetLastErrorFromNtStatus(ret));
+ return SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_THUNK;
+ }
+
+ DCHECK(!g_originals[interception.id]);
+ g_originals[interception.id] = &thunks->thunks[dll_data->num_thunks];
+
+ dll_data->num_thunks++;
+ dll_data->used_bytes += sizeof(ThunkData);
+ }
+
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interception.h b/security/sandbox/chromium/sandbox/win/src/interception.h
new file mode 100644
index 0000000000..6b4612fb6c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception.h
@@ -0,0 +1,290 @@
+// Copyright (c) 2011 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.
+
+// Defines InterceptionManager, the class in charge of setting up interceptions
+// for the sandboxed process. For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_H_
+#define SANDBOX_SRC_INTERCEPTION_H_
+
+#include <stddef.h>
+
+#include <list>
+#include <string>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+class TargetProcess;
+
+// Internal structures used for communication between the broker and the target.
+struct DllPatchInfo;
+struct DllInterceptionData;
+
+// The InterceptionManager executes on the parent application, and it is in
+// charge of setting up the desired interceptions, and placing the Interception
+// Agent into the child application.
+//
+// The exposed API consists of two methods: AddToPatchedFunctions to set up a
+// particular interception, and InitializeInterceptions to actually go ahead and
+// perform all interceptions and transfer data to the child application.
+//
+// The typical usage is something like this:
+//
+// InterceptionManager interception_manager(child);
+// if (!interception_manager.AddToPatchedFunctions(
+// L"ntdll.dll", "NtCreateFile",
+// sandbox::INTERCEPTION_SERVICE_CALL, &MyNtCreateFile, MY_ID_1))
+// return false;
+//
+// if (!interception_manager.AddToPatchedFunctions(
+// L"kernel32.dll", "CreateDirectoryW",
+// sandbox::INTERCEPTION_EAT, L"MyCreateDirectoryW@12", MY_ID_2))
+// return false;
+//
+// sandbox::ResultCode rc = interception_manager.InitializeInterceptions();
+// if (rc != sandbox::SBOX_ALL_OK) {
+// DWORD error = ::GetLastError();
+// return rc;
+// }
+//
+// Any required syncronization must be performed outside this class. Also, it is
+// not possible to perform further interceptions after InitializeInterceptions
+// is called.
+//
+class InterceptionManager {
+ // The unit test will access private members.
+ // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
+ // do not work with sandbox tests.
+ FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout1);
+ FRIEND_TEST_ALL_PREFIXES(InterceptionManagerTest, BufferLayout2);
+
+ public:
+ // An interception manager performs interceptions on a given child process.
+ // If we are allowed to intercept functions that have been patched by somebody
+ // else, relaxed should be set to true.
+ // Note: We increase the child's reference count internally.
+ InterceptionManager(TargetProcess* child_process, bool relaxed);
+ ~InterceptionManager();
+
+ // Patches function_name inside dll_name to point to replacement_code_address.
+ // function_name has to be an exported symbol of dll_name.
+ // Returns true on success.
+ //
+ // The new function should match the prototype and calling convention of the
+ // function to intercept except for one extra argument (the first one) that
+ // contains a pointer to the original function, to simplify the development
+ // of interceptors (for IA32). In x64, there is no extra argument to the
+ // interceptor, so the provided InterceptorId is used to keep a table of
+ // intercepted functions so that the interceptor can index that table to get
+ // the pointer that would have been the first argument (g_originals[id]).
+ //
+ // For example, to intercept NtClose, the following code could be used:
+ //
+ // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
+ // NTSTATUS WINAPI MyNtCose(IN NtCloseFunction OriginalClose,
+ // IN HANDLE Handle) {
+ // // do something
+ // // call the original function
+ // return OriginalClose(Handle);
+ // }
+ //
+ // And in x64:
+ //
+ // typedef NTSTATUS (WINAPI *NtCloseFunction) (IN HANDLE Handle);
+ // NTSTATUS WINAPI MyNtCose64(IN HANDLE Handle) {
+ // // do something
+ // // call the original function
+ // NtCloseFunction OriginalClose = g_originals[NT_CLOSE_ID];
+ // return OriginalClose(Handle);
+ // }
+ bool AddToPatchedFunctions(const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const void* replacement_code_address,
+ InterceptorId id);
+
+ // Patches function_name inside dll_name to point to
+ // replacement_function_name.
+ bool AddToPatchedFunctions(const wchar_t* dll_name,
+ const char* function_name,
+ InterceptionType interception_type,
+ const char* replacement_function_name,
+ InterceptorId id);
+
+ // The interception agent will unload the dll with dll_name.
+ bool AddToUnloadModules(const wchar_t* dll_name);
+
+ // Initializes all interceptions on the client.
+ // Returns SBOX_ALL_OK on success, or an appropriate error code.
+ //
+ // The child process must be created suspended, and cannot be resumed until
+ // after this method returns. In addition, no action should be performed on
+ // the child that may cause it to resume momentarily, such as injecting
+ // threads or APCs.
+ //
+ // This function must be called only once, after all interceptions have been
+ // set up using AddToPatchedFunctions.
+ ResultCode InitializeInterceptions();
+
+ private:
+ // Used to store the interception information until the actual set-up.
+ struct InterceptionData {
+ InterceptionData();
+ InterceptionData(const InterceptionData& other);
+ ~InterceptionData();
+
+ InterceptionType type; // Interception type.
+ InterceptorId id; // Interceptor id.
+ std::wstring dll; // Name of dll to intercept.
+ std::string function; // Name of function to intercept.
+ std::string interceptor; // Name of interceptor function.
+ const void* interceptor_address; // Interceptor's entry point.
+ };
+
+ // Calculates the size of the required configuration buffer.
+ size_t GetBufferSize() const;
+
+ // Rounds up the size of a given buffer, considering alignment (padding).
+ // value is the current size of the buffer, and alignment is specified in
+ // bytes.
+ static inline size_t RoundUpToMultiple(size_t value, size_t alignment) {
+ return ((value + alignment - 1) / alignment) * alignment;
+ }
+
+ // Sets up a given buffer with all the information that has to be transfered
+ // to the child.
+ // Returns true on success.
+ //
+ // The buffer size should be at least the value returned by GetBufferSize
+ bool SetupConfigBuffer(void* buffer, size_t buffer_bytes);
+
+ // Fills up the part of the transfer buffer that corresponds to information
+ // about one dll to patch.
+ // data is the first recorded interception for this dll.
+ // Returns true on success.
+ //
+ // On successful return, buffer will be advanced from it's current position
+ // to the point where the next block of configuration data should be written
+ // (the actual interception info), and the current size of the buffer will
+ // decrease to account the space used by this method.
+ bool SetupDllInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes) const;
+
+ // Fills up the part of the transfer buffer that corresponds to a single
+ // function to patch.
+ // dll_info points to the dll being updated with the interception stored on
+ // data. The buffer pointer and remaining size are updated by this call.
+ // Returns true on success.
+ bool SetupInterceptionInfo(const InterceptionData& data,
+ void** buffer,
+ size_t* buffer_bytes,
+ DllPatchInfo* dll_info) const;
+
+ // Returns true if this interception is to be performed by the child
+ // as opposed to from the parent.
+ bool IsInterceptionPerformedByChild(const InterceptionData& data) const;
+
+ // Allocates a buffer on the child's address space (returned on
+ // remote_buffer), and fills it with the contents of a local buffer.
+ // Returns SBOX_ALL_OK on success.
+ ResultCode CopyDataToChild(const void* local_buffer,
+ size_t buffer_bytes,
+ void** remote_buffer) const;
+
+ // Performs the cold patch (from the parent) of ntdll.
+ // Returns SBOX_ALL_OK on success.
+ //
+ // This method will insert additional interceptions to launch the interceptor
+ // agent on the child process, if there are additional interceptions to do.
+ ResultCode PatchNtdll(bool hot_patch_needed);
+
+ // Peforms the actual interceptions on ntdll.
+ // thunks is the memory to store all the thunks for this dll (on the child),
+ // and dll_data is a local buffer to hold global dll interception info.
+ // Returns SBOX_ALL_OK on success.
+ ResultCode PatchClientFunctions(DllInterceptionData* thunks,
+ size_t thunk_bytes,
+ DllInterceptionData* dll_data);
+
+ // The process to intercept.
+ TargetProcess* child_;
+ // Holds all interception info until the call to initialize (perform the
+ // actual patch).
+ std::list<InterceptionData> interceptions_;
+
+ // Keep track of patches added by name.
+ bool names_used_;
+
+ // true if we are allowed to patch already-patched functions.
+ bool relaxed_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterceptionManager);
+};
+
+// This macro simply calls interception_manager.AddToPatchedFunctions with
+// the given service to intercept (INTERCEPTION_SERVICE_CALL), and assumes that
+// the interceptor is called "TargetXXX", where XXX is the name of the service.
+// Note that num_params is the number of bytes to pop out of the stack for
+// the exported interceptor, following the calling convention of a service call
+// (WINAPI = with the "C" underscore).
+#if SANDBOX_EXPORTS
+#if defined(_WIN64)
+#define MAKE_SERVICE_NAME(service, params) "Target" #service "64"
+#else
+#define MAKE_SERVICE_NAME(service, params) "_Target" #service "@" #params
+#endif
+
+#define ADD_NT_INTERCEPTION(service, id, num_params) \
+ AddToPatchedFunctions(kNtdllName, #service, \
+ sandbox::INTERCEPTION_SERVICE_CALL, \
+ MAKE_SERVICE_NAME(service, num_params), id)
+
+#define INTERCEPT_NT(manager, service, id, num_params) \
+ ((&Target##service) ? manager->ADD_NT_INTERCEPTION(service, id, num_params) \
+ : false)
+
+// When intercepting the EAT it is important that the patched version of the
+// function not call any functions imported from system libraries unless
+// |TargetServices::InitCalled()| returns true, because it is only then that
+// we are guaranteed that our IAT has been initialized.
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ ((&Target##function) ? manager->AddToPatchedFunctions( \
+ dll, #function, sandbox::INTERCEPTION_EAT, \
+ MAKE_SERVICE_NAME(function, num_params), id) \
+ : false)
+#else // SANDBOX_EXPORTS
+#if defined(_WIN64)
+#define MAKE_SERVICE_NAME(service) &Target##service##64
+#else
+#define MAKE_SERVICE_NAME(service) &Target##service
+#endif
+
+#define ADD_NT_INTERCEPTION(service, id, num_params) \
+ AddToPatchedFunctions( \
+ kNtdllName, #service, sandbox::INTERCEPTION_SERVICE_CALL, \
+ reinterpret_cast<void*>(MAKE_SERVICE_NAME(service)), id)
+
+#define INTERCEPT_NT(manager, service, id, num_params) \
+ manager->ADD_NT_INTERCEPTION(service, id, num_params)
+
+// When intercepting the EAT it is important that the patched version of the
+// function not call any functions imported from system libraries unless
+// |TargetServices::InitCalled()| returns true, because it is only then that
+// we are guaranteed that our IAT has been initialized.
+#define INTERCEPT_EAT(manager, dll, function, id, num_params) \
+ manager->AddToPatchedFunctions( \
+ dll, #function, sandbox::INTERCEPTION_EAT, \
+ reinterpret_cast<void*>(MAKE_SERVICE_NAME(function)), id)
+#endif // SANDBOX_EXPORTS
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_agent.cc b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc
new file mode 100644
index 0000000000..e095328a71
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_agent.cc
@@ -0,0 +1,234 @@
+// Copyright (c) 2006-2010 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.
+
+// For information about interceptions as a whole see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include "sandbox/win/src/interception_agent.h"
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include "sandbox/win/src/eat_resolver.h"
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sidestep_resolver.h"
+
+namespace {
+
+// Returns true if target lies between base and base + range.
+bool IsWithinRange(const void* base, size_t range, const void* target) {
+ const char* end = reinterpret_cast<const char*>(base) + range;
+ return reinterpret_cast<const char*>(target) < end;
+}
+
+} // namespace
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// The list of intercepted functions back-pointers.
+SANDBOX_INTERCEPT OriginalFunctions g_originals;
+
+// Memory buffer mapped from the parent, with the list of interceptions.
+SANDBOX_INTERCEPT SharedMemory* g_interceptions = nullptr;
+
+InterceptionAgent* InterceptionAgent::GetInterceptionAgent() {
+ static InterceptionAgent* s_singleton = nullptr;
+ if (!s_singleton) {
+ if (!g_interceptions)
+ return nullptr;
+
+ size_t array_bytes = g_interceptions->num_intercepted_dlls * sizeof(void*);
+ s_singleton = reinterpret_cast<InterceptionAgent*>(
+ new (NT_ALLOC) char[array_bytes + sizeof(InterceptionAgent)]);
+
+ bool success = s_singleton->Init(g_interceptions);
+ if (!success) {
+ operator delete(s_singleton, NT_ALLOC);
+ s_singleton = nullptr;
+ }
+ }
+ return s_singleton;
+}
+
+bool InterceptionAgent::Init(SharedMemory* shared_memory) {
+ interceptions_ = shared_memory;
+ for (int i = 0; i < shared_memory->num_intercepted_dlls; i++)
+ dlls_[i] = nullptr;
+ return true;
+}
+
+bool InterceptionAgent::DllMatch(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ const DllPatchInfo* dll_info) {
+ UNICODE_STRING current_name;
+ current_name.Length =
+ static_cast<USHORT>(g_nt.wcslen(dll_info->dll_name) * sizeof(wchar_t));
+ current_name.MaximumLength = current_name.Length;
+ current_name.Buffer = const_cast<wchar_t*>(dll_info->dll_name);
+
+ BOOLEAN case_insensitive = TRUE;
+ if (full_path &&
+ !g_nt.RtlCompareUnicodeString(&current_name, full_path, case_insensitive))
+ return true;
+
+ if (name &&
+ !g_nt.RtlCompareUnicodeString(&current_name, name, case_insensitive))
+ return true;
+
+ return false;
+}
+
+bool InterceptionAgent::OnDllLoad(const UNICODE_STRING* full_path,
+ const UNICODE_STRING* name,
+ void* base_address) {
+ DllPatchInfo* dll_info = interceptions_->dll_list;
+ int i = 0;
+ for (; i < interceptions_->num_intercepted_dlls; i++) {
+ if (DllMatch(full_path, name, dll_info))
+ break;
+
+ dll_info = reinterpret_cast<DllPatchInfo*>(
+ reinterpret_cast<char*>(dll_info) + dll_info->record_bytes);
+ }
+
+ // Return now if the dll is not in our list of interest.
+ if (i == interceptions_->num_intercepted_dlls)
+ return true;
+
+ // The dll must be unloaded.
+ if (dll_info->unload_module)
+ return false;
+
+ // Purify causes this condition to trigger.
+ if (dlls_[i])
+ return true;
+
+ size_t buffer_bytes = offsetof(DllInterceptionData, thunks) +
+ dll_info->num_functions * sizeof(ThunkData);
+ dlls_[i] = reinterpret_cast<DllInterceptionData*>(
+ new (NT_PAGE, base_address) char[buffer_bytes]);
+
+ DCHECK_NT(dlls_[i]);
+ if (!dlls_[i])
+ return true;
+
+ dlls_[i]->data_bytes = buffer_bytes;
+ dlls_[i]->num_thunks = 0;
+ dlls_[i]->base = base_address;
+ dlls_[i]->used_bytes = offsetof(DllInterceptionData, thunks);
+
+ VERIFY(PatchDll(dll_info, dlls_[i]));
+
+ ULONG old_protect;
+ SIZE_T real_size = buffer_bytes;
+ void* to_protect = dlls_[i];
+ VERIFY_SUCCESS(g_nt.ProtectVirtualMemory(NtCurrentProcess, &to_protect,
+ &real_size, PAGE_EXECUTE_READ,
+ &old_protect));
+ return true;
+}
+
+void InterceptionAgent::OnDllUnload(void* base_address) {
+ for (int i = 0; i < interceptions_->num_intercepted_dlls; i++) {
+ if (dlls_[i] && dlls_[i]->base == base_address) {
+ operator delete(dlls_[i], NT_PAGE);
+ dlls_[i] = nullptr;
+ break;
+ }
+ }
+}
+
+// TODO(rvargas): We have to deal with prebinded dlls. I see two options: change
+// the timestamp of the patched dll, or modify the info on the prebinded dll.
+// the first approach messes matching of debug symbols, the second one is more
+// complicated.
+bool InterceptionAgent::PatchDll(const DllPatchInfo* dll_info,
+ DllInterceptionData* thunks) {
+ DCHECK_NT(thunks);
+ DCHECK_NT(dll_info);
+
+ const FunctionInfo* function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(dll_info) + dll_info->offset_to_functions);
+
+ for (int i = 0; i < dll_info->num_functions; i++) {
+ if (!IsWithinRange(dll_info, dll_info->record_bytes, function->function)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ ResolverThunk* resolver = GetResolver(function->type);
+ if (!resolver)
+ return false;
+
+ const char* interceptor =
+ function->function + g_nt.strlen(function->function) + 1;
+
+ if (!IsWithinRange(function, function->record_bytes, interceptor) ||
+ !IsWithinRange(dll_info, dll_info->record_bytes, interceptor)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ NTSTATUS ret = resolver->Setup(
+ thunks->base, interceptions_->interceptor_base, function->function,
+ interceptor, function->interceptor_address, &thunks->thunks[i],
+ sizeof(ThunkData), nullptr);
+ if (!NT_SUCCESS(ret)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ DCHECK_NT(!g_originals[function->id] ||
+ g_originals[function->id] == &thunks->thunks[i]);
+ g_originals[function->id] = &thunks->thunks[i];
+
+ thunks->num_thunks++;
+ thunks->used_bytes += sizeof(ThunkData);
+
+ function = reinterpret_cast<const FunctionInfo*>(
+ reinterpret_cast<const char*>(function) + function->record_bytes);
+ }
+
+ return true;
+}
+
+// This method is called from within the loader lock
+ResolverThunk* InterceptionAgent::GetResolver(InterceptionType type) {
+ static EatResolverThunk* eat_resolver = nullptr;
+ static SidestepResolverThunk* sidestep_resolver = nullptr;
+ static SmartSidestepResolverThunk* smart_sidestep_resolver = nullptr;
+
+ if (!eat_resolver)
+ eat_resolver = new (NT_ALLOC) EatResolverThunk;
+
+#if !defined(_WIN64)
+ // Sidestep is not supported for x64.
+ if (!sidestep_resolver)
+ sidestep_resolver = new (NT_ALLOC) SidestepResolverThunk;
+
+ if (!smart_sidestep_resolver)
+ smart_sidestep_resolver = new (NT_ALLOC) SmartSidestepResolverThunk;
+#endif
+
+ switch (type) {
+ case INTERCEPTION_EAT:
+ return eat_resolver;
+ case INTERCEPTION_SIDESTEP:
+ return sidestep_resolver;
+ case INTERCEPTION_SMART_SIDESTEP:
+ return smart_sidestep_resolver;
+ default:
+ NOTREACHED_NT();
+ }
+
+ return nullptr;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_agent.h b/security/sandbox/chromium/sandbox/win/src/interception_agent.h
new file mode 100644
index 0000000000..b2bce08b09
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_agent.h
@@ -0,0 +1,87 @@
+// Copyright (c) 2006-2008 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.
+
+// Defines InterceptionAgent, the class in charge of setting up interceptions
+// from the inside of the sandboxed process. For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_AGENT_H__
+#define SANDBOX_SRC_INTERCEPTION_AGENT_H__
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// Internal structures used for communication between the broker and the target.
+struct DllInterceptionData;
+struct SharedMemory;
+struct DllPatchInfo;
+
+class ResolverThunk;
+
+// The InterceptionAgent executes on the target application, and it is in charge
+// of setting up the desired interceptions or indicating what module needs to
+// be unloaded.
+//
+// The exposed API consists of three methods: GetInterceptionAgent to retrieve
+// the single class instance, OnDllLoad and OnDllUnload to process a dll being
+// loaded and unloaded respectively.
+//
+// This class assumes that it will get called for every dll being loaded,
+// starting with kernel32, so the singleton will be instantiated from within the
+// loader lock.
+class InterceptionAgent {
+ public:
+ // Returns the single InterceptionAgent object for this process.
+ static InterceptionAgent* GetInterceptionAgent();
+
+ // This method should be invoked whenever a new dll is loaded to perform the
+ // required patches. If the return value is false, this dll should not be
+ // allowed to load.
+ //
+ // full_path is the (optional) full name of the module being loaded and name
+ // is the internal module name. If full_path is provided, it will be used
+ // before the internal name to determine if we care about this dll.
+ bool OnDllLoad(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
+ void* base_address);
+
+ // Performs cleanup when a dll is unloaded.
+ void OnDllUnload(void* base_address);
+
+ private:
+ ~InterceptionAgent() {}
+
+ // Performs initialization of the singleton.
+ bool Init(SharedMemory* shared_memory);
+
+ // Returns true if we are interested on this dll. dll_info is an entry of the
+ // list of intercepted dlls.
+ bool DllMatch(const UNICODE_STRING* full_path, const UNICODE_STRING* name,
+ const DllPatchInfo* dll_info);
+
+ // Performs the patching of the dll loaded at base_address.
+ // The patches to perform are described on dll_info, and thunks is the thunk
+ // storage for the whole dll.
+ // Returns true on success.
+ bool PatchDll(const DllPatchInfo* dll_info, DllInterceptionData* thunks);
+
+ // Returns a resolver for a given interception type.
+ ResolverThunk* GetResolver(InterceptionType type);
+
+ // Shared memory containing the list of functions to intercept.
+ SharedMemory* interceptions_;
+
+ // Array of thunk data buffers for the intercepted dlls. This object singleton
+ // is allocated with a placement new with enough space to hold the complete
+ // array of pointers, not just the first element.
+ DllInterceptionData* dlls_[1];
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(InterceptionAgent);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_AGENT_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_internal.h b/security/sandbox/chromium/sandbox/win/src/interception_internal.h
new file mode 100644
index 0000000000..bf452e049e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_internal.h
@@ -0,0 +1,77 @@
+// Copyright (c) 2006-2010 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.
+
+// Defines InterceptionManager, the class in charge of setting up interceptions
+// for the sandboxed process. For more details see:
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#ifndef SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
+#define SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
+
+#include <stddef.h>
+
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+const int kMaxThunkDataBytes = 64;
+
+// The following structures contain variable size fields at the end, and will be
+// used to transfer information between two processes. In order to guarantee
+// our ability to follow the chain of structures, the alignment should be fixed,
+// hence this pragma.
+#pragma pack(push, 4)
+
+// Structures for the shared memory that contains patching information
+// for the InterceptionAgent.
+// A single interception:
+struct FunctionInfo {
+ size_t record_bytes; // rounded to sizeof(size_t) bytes
+ InterceptionType type;
+ InterceptorId id;
+ const void* interceptor_address;
+ char function[1]; // placeholder for null terminated name
+ // char interceptor[] // followed by the interceptor function
+};
+
+// A single dll:
+struct DllPatchInfo {
+ size_t record_bytes; // rounded to sizeof(size_t) bytes
+ size_t offset_to_functions;
+ int num_functions;
+ bool unload_module;
+ wchar_t dll_name[1]; // placeholder for null terminated name
+ // FunctionInfo function_info[] // followed by the functions to intercept
+};
+
+// All interceptions:
+struct SharedMemory {
+ int num_intercepted_dlls;
+ void* interceptor_base;
+ DllPatchInfo dll_list[1]; // placeholder for the list of dlls
+};
+
+// Dummy single thunk:
+struct ThunkData {
+ char data[kMaxThunkDataBytes];
+};
+
+// In-memory representation of the interceptions for a given dll:
+struct DllInterceptionData {
+ size_t data_bytes;
+ size_t used_bytes;
+ void* base;
+ int num_thunks;
+#if defined(_WIN64)
+ int dummy; // Improve alignment.
+#endif
+ ThunkData thunks[1];
+};
+
+#pragma pack(pop)
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTION_INTERNAL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc b/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc
new file mode 100644
index 0000000000..0120682bcb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interception_unittest.cc
@@ -0,0 +1,263 @@
+// Copyright (c) 2011 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.
+
+// This file contains unit tests for InterceptionManager.
+// The tests require private information so the whole interception.cc file is
+// included from this file.
+
+#include "sandbox/win/src/interception.h"
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+
+#include "base/bits.h"
+#include "sandbox/win/src/interception_internal.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/target_process.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace internal {
+size_t GetGranularAlignedRandomOffset(size_t size);
+}
+
+// Walks the settings buffer, verifying that the values make sense and counting
+// objects.
+// Arguments:
+// buffer (in): the buffer to walk.
+// size (in): buffer size
+// num_dlls (out): count of the dlls on the buffer.
+// num_function (out): count of intercepted functions.
+// num_names (out): count of named interceptor functions.
+void WalkBuffer(void* buffer,
+ size_t size,
+ int* num_dlls,
+ int* num_functions,
+ int* num_names) {
+ ASSERT_TRUE(buffer);
+ ASSERT_TRUE(num_functions);
+ ASSERT_TRUE(num_names);
+ *num_dlls = *num_functions = *num_names = 0;
+ SharedMemory* memory = reinterpret_cast<SharedMemory*>(buffer);
+
+ ASSERT_GT(size, sizeof(SharedMemory));
+ DllPatchInfo* dll = &memory->dll_list[0];
+
+ for (int i = 0; i < memory->num_intercepted_dlls; i++) {
+ ASSERT_NE(0u, wcslen(dll->dll_name));
+ ASSERT_EQ(0u, dll->record_bytes % sizeof(size_t));
+ ASSERT_EQ(0u, dll->offset_to_functions % sizeof(size_t));
+ ASSERT_NE(0, dll->num_functions);
+
+ FunctionInfo* function = reinterpret_cast<FunctionInfo*>(
+ reinterpret_cast<char*>(dll) + dll->offset_to_functions);
+
+ for (int j = 0; j < dll->num_functions; j++) {
+ ASSERT_EQ(0u, function->record_bytes % sizeof(size_t));
+
+ char* name = function->function;
+ size_t length = strlen(name);
+ ASSERT_NE(0u, length);
+ name += length + 1;
+
+ // look for overflows
+ ASSERT_GT(reinterpret_cast<char*>(buffer) + size, name + strlen(name));
+
+ // look for a named interceptor
+ if (strlen(name)) {
+ (*num_names)++;
+ EXPECT_TRUE(!function->interceptor_address);
+ } else {
+ EXPECT_TRUE(function->interceptor_address);
+ }
+
+ (*num_functions)++;
+ function = reinterpret_cast<FunctionInfo*>(
+ reinterpret_cast<char*>(function) + function->record_bytes);
+ }
+
+ (*num_dlls)++;
+ dll = reinterpret_cast<DllPatchInfo*>(reinterpret_cast<char*>(dll) +
+ dll->record_bytes);
+ }
+}
+
+TEST(InterceptionManagerTest, GetGranularAlignedRandomOffset) {
+ std::set<size_t> sizes;
+
+ // 544 is current value of interceptions_.size() * sizeof(ThunkData) +
+ // sizeof(DllInterceptionData).
+ const size_t kThunkBytes = 544;
+
+ // ciel(log2(544)) = 10.
+ // Alignment must be 2^10 = 1024.
+ const size_t kAlignmentBits = base::bits::Log2Ceiling(kThunkBytes);
+ const size_t kAlignment = static_cast<size_t>(1) << kAlignmentBits;
+
+ const size_t kAllocGranularity = 65536;
+
+ // Generate enough sample data to ensure there is at least one value in each
+ // potential bucket.
+ for (size_t i = 0; i < 1000000; i++)
+ sizes.insert(internal::GetGranularAlignedRandomOffset(kThunkBytes));
+
+ size_t prev_val = 0;
+ size_t min_val = kAllocGranularity;
+ size_t min_nonzero_val = kAllocGranularity;
+ size_t max_val = 0;
+
+ for (size_t val : sizes) {
+ ASSERT_LT(val, kAllocGranularity);
+ if (prev_val)
+ ASSERT_EQ(val - prev_val, kAlignment);
+ if (val)
+ min_nonzero_val = std::min(val, min_nonzero_val);
+ min_val = std::min(val, min_val);
+ prev_val = val;
+ max_val = std::max(val, max_val);
+ }
+ ASSERT_EQ(max_val, kAllocGranularity - kAlignment);
+ ASSERT_EQ(0u, min_val);
+ ASSERT_EQ(min_nonzero_val, kAlignment);
+}
+
+TEST(InterceptionManagerTest, BufferLayout1) {
+ wchar_t exe_name[MAX_PATH];
+ ASSERT_NE(0u, GetModuleFileName(nullptr, exe_name, MAX_PATH - 1));
+
+ TargetProcess* target =
+ MakeTestTargetProcess(::GetCurrentProcess(), ::GetModuleHandle(exe_name));
+
+ InterceptionManager interceptions(target, true);
+
+ // Any pointer will do for a function pointer.
+ void* function = &interceptions;
+
+ // We don't care about the interceptor id.
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx",
+ INTERCEPTION_SMART_SIDESTEP, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "FindWindow",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateMutex",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"user32.dll", "PostMsg",
+ INTERCEPTION_EAT, "replacement",
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtClose",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtOpenFile",
+ INTERCEPTION_SIDESTEP, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"some.dll", "Superfn", INTERCEPTION_EAT,
+ function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, "a", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_SIDESTEP, "ab", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"comctl.dll", "SaveAsDlg",
+ INTERCEPTION_EAT, "abc", OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "p", INTERCEPTION_EAT, function,
+ OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"b.dll",
+ "TheIncredibleCallToSaveTheWorld",
+ INTERCEPTION_EAT, function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "BIsLame", INTERCEPTION_EAT,
+ function, OPEN_KEY_ID);
+ interceptions.AddToPatchedFunctions(L"a.dll", "ARules", INTERCEPTION_EAT,
+ function, OPEN_KEY_ID);
+
+ // Verify that all interceptions were added
+ ASSERT_EQ(18u, interceptions.interceptions_.size());
+
+ size_t buffer_size = interceptions.GetBufferSize();
+ std::unique_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]);
+
+ ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), buffer_size));
+
+ // At this point, the interceptions should have been separated into two
+ // groups: one group with the local ("cold") interceptions, consisting of
+ // everything from ntdll and stuff set as INTRECEPTION_SERVICE_CALL, and
+ // another group with the interceptions belonging to dlls that will be "hot"
+ // patched on the client. The second group lives on local_buffer, and the
+ // first group remains on the list of interceptions (inside the object
+ // "interceptions"). There are 3 local interceptions (of ntdll); the
+ // other 15 have to be sent to the child to be performed "hot".
+ EXPECT_EQ(3u, interceptions.interceptions_.size());
+
+ int num_dlls, num_functions, num_names;
+ WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions,
+ &num_names);
+
+ // The 15 interceptions on the buffer (to the child) should be grouped on 6
+ // dlls. Only four interceptions are using an explicit name for the
+ // interceptor function.
+ EXPECT_EQ(6, num_dlls);
+ EXPECT_EQ(15, num_functions);
+ EXPECT_EQ(4, num_names);
+}
+
+TEST(InterceptionManagerTest, BufferLayout2) {
+ wchar_t exe_name[MAX_PATH];
+ ASSERT_NE(0u, GetModuleFileName(nullptr, exe_name, MAX_PATH - 1));
+
+ TargetProcess* target =
+ MakeTestTargetProcess(::GetCurrentProcess(), ::GetModuleHandle(exe_name));
+
+ InterceptionManager interceptions(target, true);
+
+ // Any pointer will do for a function pointer.
+ void* function = &interceptions;
+ interceptions.AddToUnloadModules(L"some01.dll");
+ // We don't care about the interceptor id.
+ interceptions.AddToPatchedFunctions(L"ntdll.dll", "NtCreateFile",
+ INTERCEPTION_SERVICE_CALL, function,
+ OPEN_FILE_ID);
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "CreateFileEx",
+ INTERCEPTION_EAT, function, OPEN_FILE_ID);
+ interceptions.AddToUnloadModules(L"some02.dll");
+ interceptions.AddToPatchedFunctions(L"kernel32.dll", "SomeFileEx",
+ INTERCEPTION_SMART_SIDESTEP, function,
+ OPEN_FILE_ID);
+ // Verify that all interceptions were added
+ ASSERT_EQ(5u, interceptions.interceptions_.size());
+
+ size_t buffer_size = interceptions.GetBufferSize();
+ std::unique_ptr<BYTE[]> local_buffer(new BYTE[buffer_size]);
+
+ ASSERT_TRUE(interceptions.SetupConfigBuffer(local_buffer.get(), buffer_size));
+
+ // At this point, the interceptions should have been separated into two
+ // groups: one group with the local ("cold") interceptions, and another
+ // group with the interceptions belonging to dlls that will be "hot"
+ // patched on the client. The second group lives on local_buffer, and the
+ // first group remains on the list of interceptions, in this case just one.
+ EXPECT_EQ(1u, interceptions.interceptions_.size());
+
+ int num_dlls, num_functions, num_names;
+ WalkBuffer(local_buffer.get(), buffer_size, &num_dlls, &num_functions,
+ &num_names);
+
+ EXPECT_EQ(3, num_dlls);
+ EXPECT_EQ(4, num_functions);
+ EXPECT_EQ(0, num_names);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interceptors.h b/security/sandbox/chromium/sandbox/win/src/interceptors.h
new file mode 100644
index 0000000000..5788614707
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interceptors.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 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_INTERCEPTORS_H_
+#define SANDBOX_SRC_INTERCEPTORS_H_
+
+#if defined(_WIN64)
+#include "sandbox/win/src/interceptors_64.h"
+#endif
+
+namespace sandbox {
+
+enum InterceptorId {
+ // Internal use:
+ MAP_VIEW_OF_SECTION_ID = 0,
+ UNMAP_VIEW_OF_SECTION_ID,
+ // Policy broker:
+ SET_INFORMATION_THREAD_ID,
+ OPEN_THREAD_TOKEN_ID,
+ OPEN_THREAD_TOKEN_EX_ID,
+ OPEN_THREAD_ID,
+ OPEN_PROCESS_ID,
+ OPEN_PROCESS_TOKEN_ID,
+ OPEN_PROCESS_TOKEN_EX_ID,
+ // Filesystem dispatcher:
+ CREATE_FILE_ID,
+ OPEN_FILE_ID,
+ QUERY_ATTRIB_FILE_ID,
+ QUERY_FULL_ATTRIB_FILE_ID,
+ SET_INFO_FILE_ID,
+ // Named pipe dispatcher:
+ CREATE_NAMED_PIPE_ID,
+ // Process-thread dispatcher:
+ CREATE_PROCESSW_ID,
+ CREATE_PROCESSA_ID,
+ CREATE_THREAD_ID,
+ // Registry dispatcher:
+ CREATE_KEY_ID,
+ OPEN_KEY_ID,
+ OPEN_KEY_EX_ID,
+ // Sync dispatcher:
+ CREATE_EVENT_ID,
+ OPEN_EVENT_ID,
+ // Process mitigations Win32k dispatcher:
+ GDIINITIALIZE_ID,
+ GETSTOCKOBJECT_ID,
+ REGISTERCLASSW_ID,
+ ENUMDISPLAYMONITORS_ID,
+ ENUMDISPLAYDEVICESA_ID,
+ GETMONITORINFOA_ID,
+ GETMONITORINFOW_ID,
+ CREATEOPMPROTECTEDOUTPUTS_ID,
+ GETCERTIFICATE_ID,
+ GETCERTIFICATESIZE_ID,
+ GETCERTIFICATEBYHANDLE_ID,
+ GETCERTIFICATESIZEBYHANDLE_ID,
+ DESTROYOPMPROTECTEDOUTPUT_ID,
+ CONFIGUREOPMPROTECTEDOUTPUT_ID,
+ GETOPMINFORMATION_ID,
+ GETOPMRANDOMNUMBER_ID,
+ GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_ID,
+ SETOPMSIGNINGKEYANDSEQUENCENUMBERS_ID,
+ // Signed dispatcher:
+ CREATE_SECTION_ID,
+ INTERCEPTOR_MAX_ID
+};
+
+typedef void* OriginalFunctions[INTERCEPTOR_MAX_ID];
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_INTERCEPTORS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/interceptors_64.cc b/security/sandbox/chromium/sandbox/win/src/interceptors_64.cc
new file mode 100644
index 0000000000..0ac2671fcc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interceptors_64.cc
@@ -0,0 +1,531 @@
+// Copyright (c) 2011 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/interceptors_64.h"
+
+#include "sandbox/win/src/filesystem_interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/named_pipe_interception.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/registry_interception.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/signed_interception.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/target_interceptions.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+SANDBOX_INTERCEPT OriginalFunctions g_originals;
+
+NTSTATUS WINAPI TargetNtMapViewOfSection64(HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect) {
+ NtMapViewOfSectionFunction orig_fn =
+ reinterpret_cast<NtMapViewOfSectionFunction>(
+ g_originals[MAP_VIEW_OF_SECTION_ID]);
+
+ return TargetNtMapViewOfSection(orig_fn, section, process, base, zero_bits,
+ commit_size, offset, view_size, inherit,
+ allocation_type, protect);
+}
+
+NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process, PVOID base) {
+ NtUnmapViewOfSectionFunction orig_fn =
+ reinterpret_cast<NtUnmapViewOfSectionFunction>(
+ g_originals[UNMAP_VIEW_OF_SECTION_ID]);
+ return TargetNtUnmapViewOfSection(orig_fn, process, base);
+}
+
+// -----------------------------------------------------------------------
+
+NTSTATUS WINAPI
+TargetNtSetInformationThread64(HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information,
+ ULONG thread_information_bytes) {
+ NtSetInformationThreadFunction orig_fn =
+ reinterpret_cast<NtSetInformationThreadFunction>(
+ g_originals[SET_INFORMATION_THREAD_ID]);
+ return TargetNtSetInformationThread(orig_fn, thread, thread_info_class,
+ thread_information,
+ thread_information_bytes);
+}
+
+NTSTATUS WINAPI TargetNtOpenThreadToken64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ PHANDLE token) {
+ NtOpenThreadTokenFunction orig_fn =
+ reinterpret_cast<NtOpenThreadTokenFunction>(
+ g_originals[OPEN_THREAD_TOKEN_ID]);
+ return TargetNtOpenThreadToken(orig_fn, thread, desired_access, open_as_self,
+ token);
+}
+
+NTSTATUS WINAPI TargetNtOpenThreadTokenEx64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ NtOpenThreadTokenExFunction orig_fn =
+ reinterpret_cast<NtOpenThreadTokenExFunction>(
+ g_originals[OPEN_THREAD_TOKEN_EX_ID]);
+ return TargetNtOpenThreadTokenEx(orig_fn, thread, desired_access,
+ open_as_self, handle_attributes, token);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length) {
+ NtCreateFileFunction orig_fn =
+ reinterpret_cast<NtCreateFileFunction>(g_originals[CREATE_FILE_ID]);
+ return TargetNtCreateFile(orig_fn, file, desired_access, object_attributes,
+ io_status, allocation_size, file_attributes,
+ sharing, disposition, options, ea_buffer,
+ ea_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options) {
+ NtOpenFileFunction orig_fn =
+ reinterpret_cast<NtOpenFileFunction>(g_originals[OPEN_FILE_ID]);
+ return TargetNtOpenFile(orig_fn, file, desired_access, object_attributes,
+ io_status, sharing, options);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtQueryAttributesFile64(POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes) {
+ NtQueryAttributesFileFunction orig_fn =
+ reinterpret_cast<NtQueryAttributesFileFunction>(
+ g_originals[QUERY_ATTRIB_FILE_ID]);
+ return TargetNtQueryAttributesFile(orig_fn, object_attributes,
+ file_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes) {
+ NtQueryFullAttributesFileFunction orig_fn =
+ reinterpret_cast<NtQueryFullAttributesFileFunction>(
+ g_originals[QUERY_FULL_ATTRIB_FILE_ID]);
+ return TargetNtQueryFullAttributesFile(orig_fn, object_attributes,
+ file_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationFile64(HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_information,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_information_class) {
+ NtSetInformationFileFunction orig_fn =
+ reinterpret_cast<NtSetInformationFileFunction>(
+ g_originals[SET_INFO_FILE_ID]);
+ return TargetNtSetInformationFile(orig_fn, file, io_status, file_information,
+ length, file_information_class);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateNamedPipeW64(LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ CreateNamedPipeWFunction orig_fn = reinterpret_cast<CreateNamedPipeWFunction>(
+ g_originals[CREATE_NAMED_PIPE_ID]);
+ return TargetCreateNamedPipeW(orig_fn, pipe_name, open_mode, pipe_mode,
+ max_instance, out_buffer_size, in_buffer_size,
+ default_timeout, security_attributes);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThread64(PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NtOpenThreadFunction orig_fn =
+ reinterpret_cast<NtOpenThreadFunction>(g_originals[OPEN_THREAD_ID]);
+ return TargetNtOpenThread(orig_fn, thread, desired_access, object_attributes,
+ client_id);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcess64(PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NtOpenProcessFunction orig_fn =
+ reinterpret_cast<NtOpenProcessFunction>(g_originals[OPEN_PROCESS_ID]);
+ return TargetNtOpenProcess(orig_fn, process, desired_access,
+ object_attributes, client_id);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessToken64(HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token) {
+ NtOpenProcessTokenFunction orig_fn =
+ reinterpret_cast<NtOpenProcessTokenFunction>(
+ g_originals[OPEN_PROCESS_TOKEN_ID]);
+ return TargetNtOpenProcessToken(orig_fn, process, desired_access, token);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx64(HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ NtOpenProcessTokenExFunction orig_fn =
+ reinterpret_cast<NtOpenProcessTokenExFunction>(
+ g_originals[OPEN_PROCESS_TOKEN_EX_ID]);
+ return TargetNtOpenProcessTokenEx(orig_fn, process, desired_access,
+ handle_attributes, token);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessW64(LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ CreateProcessWFunction orig_fn =
+ reinterpret_cast<CreateProcessWFunction>(g_originals[CREATE_PROCESSW_ID]);
+ return TargetCreateProcessW(
+ orig_fn, application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags, environment, current_directory,
+ startup_info, process_information);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessA64(LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ CreateProcessAFunction orig_fn =
+ reinterpret_cast<CreateProcessAFunction>(g_originals[CREATE_PROCESSA_ID]);
+ return TargetCreateProcessA(
+ orig_fn, application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags, environment, current_directory,
+ startup_info, process_information);
+}
+
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateThread64(LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ PVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id) {
+ CreateThreadFunction orig_fn =
+ reinterpret_cast<CreateThreadFunction>(g_originals[CREATE_THREAD_ID]);
+ return TargetCreateThread(orig_fn, thread_attributes, stack_size,
+ start_address, parameter, creation_flags,
+ thread_id);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index,
+ PUNICODE_STRING class_name,
+ ULONG create_options,
+ PULONG disposition) {
+ NtCreateKeyFunction orig_fn =
+ reinterpret_cast<NtCreateKeyFunction>(g_originals[CREATE_KEY_ID]);
+ return TargetNtCreateKey(orig_fn, key, desired_access, object_attributes,
+ title_index, class_name, create_options,
+ disposition);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NtOpenKeyFunction orig_fn =
+ reinterpret_cast<NtOpenKeyFunction>(g_originals[OPEN_KEY_ID]);
+ return TargetNtOpenKey(orig_fn, key, desired_access, object_attributes);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKeyEx64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options) {
+ NtOpenKeyExFunction orig_fn =
+ reinterpret_cast<NtOpenKeyExFunction>(g_originals[OPEN_KEY_EX_ID]);
+ return TargetNtOpenKeyEx(orig_fn, key, desired_access, object_attributes,
+ open_options);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state) {
+ NtCreateEventFunction orig_fn =
+ reinterpret_cast<NtCreateEventFunction>(g_originals[CREATE_EVENT_ID]);
+ return TargetNtCreateEvent(orig_fn, event_handle, desired_access,
+ object_attributes, event_type, initial_state);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NtOpenEventFunction orig_fn =
+ reinterpret_cast<NtOpenEventFunction>(g_originals[OPEN_EVENT_ID]);
+ return TargetNtOpenEvent(orig_fn, event_handle, desired_access,
+ object_attributes);
+}
+
+// -----------------------------------------------------------------------
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(HANDLE dll,
+ DWORD reason) {
+ GdiDllInitializeFunction orig_fn =
+ reinterpret_cast<GdiDllInitializeFunction>(g_originals[GDIINITIALIZE_ID]);
+ return TargetGdiDllInitialize(orig_fn, dll, reason);
+}
+
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object) {
+ GetStockObjectFunction orig_fn =
+ reinterpret_cast<GetStockObjectFunction>(g_originals[GETSTOCKOBJECT_ID]);
+ return TargetGetStockObject(orig_fn, object);
+}
+
+SANDBOX_INTERCEPT ATOM WINAPI
+TargetRegisterClassW64(const WNDCLASS* wnd_class) {
+ RegisterClassWFunction orig_fn =
+ reinterpret_cast<RegisterClassWFunction>(g_originals[REGISTERCLASSW_ID]);
+ return TargetRegisterClassW(orig_fn, wnd_class);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayMonitors64(HDC hdc,
+ LPCRECT clip_rect,
+ MONITORENUMPROC enum_function,
+ LPARAM data_pointer) {
+ EnumDisplayMonitorsFunction orig_fn =
+ reinterpret_cast<EnumDisplayMonitorsFunction>(
+ g_originals[ENUMDISPLAYMONITORS_ID]);
+ return TargetEnumDisplayMonitors(orig_fn, hdc, clip_rect, enum_function,
+ data_pointer);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayDevicesA64(LPCSTR device,
+ DWORD device_num,
+ PDISPLAY_DEVICEA display_device,
+ DWORD flags) {
+ EnumDisplayDevicesAFunction orig_fn =
+ reinterpret_cast<EnumDisplayDevicesAFunction>(
+ g_originals[ENUMDISPLAYDEVICESA_ID]);
+ return TargetEnumDisplayDevicesA(orig_fn, device, device_num, display_device,
+ flags);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoA64(HMONITOR monitor, LPMONITORINFO monitor_info) {
+ GetMonitorInfoAFunction orig_fn = reinterpret_cast<GetMonitorInfoAFunction>(
+ g_originals[GETMONITORINFOA_ID]);
+ return TargetGetMonitorInfoA(orig_fn, monitor, monitor_info);
+}
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoW64(HMONITOR monitor, LPMONITORINFO monitor_info) {
+ GetMonitorInfoWFunction orig_fn = reinterpret_cast<GetMonitorInfoWFunction>(
+ g_originals[GETMONITORINFOW_ID]);
+ return TargetGetMonitorInfoW(orig_fn, monitor, monitor_info);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetSuggestedOPMProtectedOutputArraySize64(
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size) {
+ GetSuggestedOPMProtectedOutputArraySizeFunction orig_fn =
+ reinterpret_cast<GetSuggestedOPMProtectedOutputArraySizeFunction>(
+ g_originals[GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_ID]);
+ return TargetGetSuggestedOPMProtectedOutputArraySize(
+ orig_fn, device_name, suggested_output_array_size);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetCreateOPMProtectedOutputs64(
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD protected_output_array_size,
+ DWORD* num_output_handles,
+ OPM_PROTECTED_OUTPUT_HANDLE* protected_output_array) {
+ CreateOPMProtectedOutputsFunction orig_fn =
+ reinterpret_cast<CreateOPMProtectedOutputsFunction>(
+ g_originals[CREATEOPMPROTECTEDOUTPUTS_ID]);
+ return TargetCreateOPMProtectedOutputs(
+ orig_fn, device_name, vos, protected_output_array_size,
+ num_output_handles, protected_output_array);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificate64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length) {
+ GetCertificateFunction orig_fn =
+ reinterpret_cast<GetCertificateFunction>(g_originals[GETCERTIFICATE_ID]);
+ return TargetGetCertificate(orig_fn, device_name, certificate_type,
+ certificate, certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSize64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ GetCertificateSizeFunction orig_fn =
+ reinterpret_cast<GetCertificateSizeFunction>(
+ g_originals[GETCERTIFICATESIZE_ID]);
+ return TargetGetCertificateSize(orig_fn, device_name, certificate_type,
+ certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length) {
+ GetCertificateByHandleFunction orig_fn =
+ reinterpret_cast<GetCertificateByHandleFunction>(
+ g_originals[GETCERTIFICATE_ID]);
+ return TargetGetCertificateByHandle(orig_fn, protected_output,
+ certificate_type, certificate,
+ certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSizeByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ GetCertificateSizeByHandleFunction orig_fn =
+ reinterpret_cast<GetCertificateSizeByHandleFunction>(
+ g_originals[GETCERTIFICATESIZE_ID]);
+ return TargetGetCertificateSizeByHandle(orig_fn, protected_output,
+ certificate_type, certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetDestroyOPMProtectedOutput64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output) {
+ DestroyOPMProtectedOutputFunction orig_fn =
+ reinterpret_cast<DestroyOPMProtectedOutputFunction>(
+ g_originals[DESTROYOPMPROTECTEDOUTPUT_ID]);
+ return TargetDestroyOPMProtectedOutput(orig_fn, protected_output);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMInformation64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_info) {
+ GetOPMInformationFunction orig_fn =
+ reinterpret_cast<GetOPMInformationFunction>(
+ g_originals[GETOPMINFORMATION_ID]);
+ return TargetGetOPMInformation(orig_fn, protected_output, parameters,
+ requested_info);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMRandomNumber64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number) {
+ GetOPMRandomNumberFunction orig_fn =
+ reinterpret_cast<GetOPMRandomNumberFunction>(
+ g_originals[GETOPMRANDOMNUMBER_ID]);
+ return TargetGetOPMRandomNumber(orig_fn, protected_output, random_number);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters) {
+ SetOPMSigningKeyAndSequenceNumbersFunction orig_fn =
+ reinterpret_cast<SetOPMSigningKeyAndSequenceNumbersFunction>(
+ g_originals[SETOPMSIGNINGKEYANDSEQUENCENUMBERS_ID]);
+ return TargetSetOPMSigningKeyAndSequenceNumbers(orig_fn, protected_output,
+ parameters);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetConfigureOPMProtectedOutput64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters) {
+ ConfigureOPMProtectedOutputFunction orig_fn =
+ reinterpret_cast<ConfigureOPMProtectedOutputFunction>(
+ g_originals[CONFIGUREOPMPROTECTEDOUTPUT_ID]);
+ return TargetConfigureOPMProtectedOutput(
+ orig_fn, protected_output, parameters, additional_parameters_size,
+ additional_parameters);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateSection64(PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle) {
+ NtCreateSectionFunction orig_fn =
+ reinterpret_cast<NtCreateSectionFunction>(g_originals[CREATE_SECTION_ID]);
+ return TargetNtCreateSection(
+ orig_fn, section_handle, desired_access, object_attributes, maximum_size,
+ section_page_protection, allocation_attributes, file_handle);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/interceptors_64.h b/security/sandbox/chromium/sandbox/win/src/interceptors_64.h
new file mode 100644
index 0000000000..f34627dcd2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/interceptors_64.h
@@ -0,0 +1,330 @@
+// Copyright (c) 2011 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_WIN_SRC_INTERCEPTORS_64_H_
+#define SANDBOX_WIN_SRC_INTERCEPTORS_64_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtMapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being loaded, so we can patch them if needed.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtMapViewOfSection64(HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect);
+
+// Interception of NtUnmapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being unloaded, so we can clean up our interceptions.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtUnmapViewOfSection64(HANDLE process,
+ PVOID base);
+
+// -----------------------------------------------------------------------
+// Interceptors without IPC.
+
+// Interception of NtSetInformationThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationThread64(HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information,
+ ULONG thread_information_bytes);
+
+// Interception of NtOpenThreadToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThreadToken64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ PHANDLE token);
+
+// Interception of NtOpenThreadTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThreadTokenEx64(HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ ULONG handle_attributes,
+ PHANDLE token);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the file system dispatcher.
+
+// Interception of NtCreateFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ PLARGE_INTEGER allocation_size,
+ ULONG file_attributes,
+ ULONG sharing,
+ ULONG disposition,
+ ULONG options,
+ PVOID ea_buffer,
+ ULONG ea_length);
+
+// Interception of NtOpenFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenFile64(PHANDLE file,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PIO_STATUS_BLOCK io_status,
+ ULONG sharing,
+ ULONG options);
+
+// Interception of NtQueryAtttributesFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtQueryAttributesFile64(POBJECT_ATTRIBUTES object_attributes,
+ PFILE_BASIC_INFORMATION file_attributes);
+
+// Interception of NtQueryFullAtttributesFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtQueryFullAttributesFile64(
+ POBJECT_ATTRIBUTES object_attributes,
+ PFILE_NETWORK_OPEN_INFORMATION file_attributes);
+
+// Interception of NtSetInformationFile on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtSetInformationFile64(HANDLE file,
+ PIO_STATUS_BLOCK io_status,
+ PVOID file_information,
+ ULONG length,
+ FILE_INFORMATION_CLASS file_information_class);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the named pipe dispatcher.
+
+// Interception of CreateNamedPipeW in kernel32.dll
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateNamedPipeW64(LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the process-thread dispatcher.
+
+// Interception of NtOpenThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThread64(PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcess on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcess64(PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcessToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessToken64(HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token);
+
+// Interception of NtOpenProcessTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx64(HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token);
+
+// Interception of CreateProcessW in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessW64(LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// Interception of CreateProcessA in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessA64(LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// Interception of CreateThread in kernel32.dll.
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateThread64(LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ PVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the registry dispatcher.
+
+// Interception of NtCreateKey on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index,
+ PUNICODE_STRING class_name,
+ ULONG create_options,
+ PULONG disposition);
+
+// Interception of NtOpenKey on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKey64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// Interception of NtOpenKeyEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenKeyEx64(PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the sync dispatcher.
+
+// Interception of NtCreateEvent/NtOpenEvent on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenEvent64(PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the process mitigations win32k lockdown code.
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI TargetGetStockObject64(int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI TargetRegisterClassW64(const WNDCLASS* wnd_class);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayMonitors64(HDC hdc,
+ LPCRECT lprcClip,
+ MONITORENUMPROC lpfnEnum,
+ LPARAM dwData);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetEnumDisplayDevicesA64(LPCSTR lpDevice,
+ DWORD iDevNum,
+ PDISPLAY_DEVICEA lpDisplayDevice,
+ DWORD dwFlags);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGetMonitorInfoA64(HMONITOR hMonitor,
+ LPMONITORINFO lpmi);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetGetMonitorInfoW64(HMONITOR hMonitor,
+ LPMONITORINFO lpmi);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetSuggestedOPMProtectedOutputArraySize64(
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetCreateOPMProtectedOutputs64(
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD protected_output_array_size,
+ DWORD* num_output_handles,
+ OPM_PROTECTED_OUTPUT_HANDLE* protected_output_array);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificate64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSize64(PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificateSizeByHandle64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetDestroyOPMProtectedOutput64(OPM_PROTECTED_OUTPUT_HANDLE protected_output);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMInformation64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_info);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetOPMRandomNumber64(OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetConfigureOPMProtectedOutput64(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters);
+
+// -----------------------------------------------------------------------
+// Interceptors handled by the signed process code.
+
+// Interception of NtCreateSection on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateSection64(PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_INTERCEPTORS_64_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/internal_types.h b/security/sandbox/chromium/sandbox/win/src/internal_types.h
new file mode 100644
index 0000000000..9971da81d0
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/internal_types.h
@@ -0,0 +1,68 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_INTERNAL_TYPES_H_
+#define SANDBOX_WIN_SRC_INTERNAL_TYPES_H_
+
+#include <stdint.h>
+
+namespace sandbox {
+
+const wchar_t kNtdllName[] = L"ntdll.dll";
+const wchar_t kKerneldllName[] = L"kernel32.dll";
+const wchar_t kKernelBasedllName[] = L"kernelbase.dll";
+
+// Defines the supported C++ types encoding to numeric id. Like a simplified
+// RTTI. Note that true C++ RTTI will not work because the types are not
+// polymorphic anyway.
+enum ArgType {
+ INVALID_TYPE = 0,
+ WCHAR_TYPE,
+ UINT32_TYPE,
+ UNISTR_TYPE,
+ VOIDPTR_TYPE,
+ INPTR_TYPE,
+ INOUTPTR_TYPE,
+ LAST_TYPE
+};
+
+// Encapsulates a pointer to a buffer and the size of the buffer.
+class CountedBuffer {
+ public:
+ CountedBuffer(void* buffer, uint32_t size) : size_(size), buffer_(buffer) {}
+
+ uint32_t Size() const { return size_; }
+
+ void* Buffer() const { return buffer_; }
+
+ private:
+ uint32_t size_;
+ void* buffer_;
+};
+
+// Helper class to convert void-pointer packed ints for both
+// 32 and 64 bit builds. This construct is non-portable.
+class IPCInt {
+ public:
+ explicit IPCInt(void* buffer) { buffer_.vp = buffer; }
+
+ explicit IPCInt(uint32_t i32) {
+ buffer_.vp = nullptr;
+ buffer_.i32 = i32;
+ }
+
+ uint32_t As32Bit() const { return buffer_.i32; }
+
+ void* AsVoidPtr() const { return buffer_.vp; }
+
+ private:
+ union U {
+ void* vp;
+ uint32_t i32;
+ } buffer_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_INTERNAL_TYPES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_args.cc b/security/sandbox/chromium/sandbox/win/src/ipc_args.cc
new file mode 100644
index 0000000000..c0c64f1864
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_args.cc
@@ -0,0 +1,96 @@
+// 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/ipc_args.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+// Releases memory allocated for IPC arguments, if needed.
+void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]) {
+ for (size_t i = 0; i < kMaxIpcParams; i++) {
+ switch (ipc_params->args[i]) {
+ case WCHAR_TYPE: {
+ delete reinterpret_cast<std::wstring*>(args[i]);
+ args[i] = nullptr;
+ break;
+ }
+ case INPTR_TYPE:
+ case INOUTPTR_TYPE: {
+ delete reinterpret_cast<CountedBuffer*>(args[i]);
+ args[i] = nullptr;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+// Fills up the list of arguments (args and ipc_params) for an IPC call.
+bool GetArgs(CrossCallParamsEx* params,
+ IPCParams* ipc_params,
+ void* args[kMaxIpcParams]) {
+ if (kMaxIpcParams < params->GetParamsCount())
+ return false;
+
+ for (uint32_t i = 0; i < params->GetParamsCount(); i++) {
+ uint32_t size;
+ ArgType type;
+ args[i] = params->GetRawParameter(i, &size, &type);
+ if (args[i]) {
+ ipc_params->args[i] = type;
+ switch (type) {
+ case WCHAR_TYPE: {
+ std::unique_ptr<std::wstring> data(new std::wstring);
+ if (!params->GetParameterStr(i, data.get())) {
+ args[i] = 0;
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data.release();
+ break;
+ }
+ case UINT32_TYPE: {
+ uint32_t data;
+ if (!params->GetParameter32(i, &data)) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ IPCInt ipc_int(data);
+ args[i] = ipc_int.AsVoidPtr();
+ break;
+ }
+ case VOIDPTR_TYPE: {
+ void* data;
+ if (!params->GetParameterVoidPtr(i, &data)) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ args[i] = data;
+ break;
+ }
+ case INPTR_TYPE:
+ case INOUTPTR_TYPE: {
+ if (!args[i]) {
+ ReleaseArgs(ipc_params, args);
+ return false;
+ }
+ CountedBuffer* buffer = new CountedBuffer(args[i], size);
+ args[i] = buffer;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_args.h b/security/sandbox/chromium/sandbox/win/src/ipc_args.h
new file mode 100644
index 0000000000..58b7507dee
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_args.h
@@ -0,0 +1,24 @@
+// 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_WIN_SRC_IPC_ARGS_H_
+#define SANDBOX_WIN_SRC_IPC_ARGS_H_
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+// Releases memory allocated for IPC arguments.
+void ReleaseArgs(const IPCParams* ipc_params, void* args[kMaxIpcParams]);
+
+// Fills up the list of arguments (args and ipc_params) for an IPC call.
+// Call ReleaseArgs on |ipc_params| and |args| after calling this.
+bool GetArgs(CrossCallParamsEx* params,
+ IPCParams* ipc_params,
+ void* args[kMaxIpcParams]);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_IPC_ARGS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc b/security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc
new file mode 100644
index 0000000000..44f6be433d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_ping_test.cc
@@ -0,0 +1,58 @@
+// Copyright (c) 2006-2008 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/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Tests that the IPC is working by issuing a special IPC that is not exposed
+// in the public API.
+SBOX_TESTS_COMMAND int IPC_Ping(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED;
+
+ TargetServices* ts = SandboxFactory::GetTargetServices();
+ if (!ts)
+ return SBOX_TEST_FAILED;
+
+ // Downcast because we have internal knowledge of the object returned.
+ TargetServicesBase* ts_base = reinterpret_cast<TargetServicesBase*>(ts);
+
+ int version = 0;
+ if (L'1' == argv[0][0])
+ version = 1;
+ else
+ version = 2;
+
+ if (!ts_base->TestIPCPing(version))
+ return SBOX_TEST_FAILED;
+
+ ::Sleep(1);
+ if (!ts_base->TestIPCPing(version))
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// The IPC ping test should work before and after the token drop.
+TEST(IPCTest, IPCPingTestSimple) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 1"));
+}
+
+TEST(IPCTest, IPCPingTestWithOutput) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"IPC_Ping 2"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_tags.h b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
new file mode 100644
index 0000000000..ec6de4a66a
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_tags.h
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_IPC_TAGS_H__
+#define SANDBOX_SRC_IPC_TAGS_H__
+
+namespace sandbox {
+
+enum class IpcTag {
+ UNUSED = 0,
+ PING1, // Takes a cookie in parameters and returns the cookie
+ // multiplied by 2 and the tick_count. Used for testing only.
+ PING2, // Takes an in/out cookie in parameters and modify the cookie
+ // to be multiplied by 3. Used for testing only.
+ NTCREATEFILE,
+ NTOPENFILE,
+ NTQUERYATTRIBUTESFILE,
+ NTQUERYFULLATTRIBUTESFILE,
+ NTSETINFO_RENAME,
+ CREATENAMEDPIPEW,
+ NTOPENTHREAD,
+ NTOPENPROCESS,
+ NTOPENPROCESSTOKEN,
+ NTOPENPROCESSTOKENEX,
+ CREATEPROCESSW,
+ CREATEEVENT,
+ OPENEVENT,
+ NTCREATEKEY,
+ NTOPENKEY,
+ DUPLICATEHANDLEPROXY,
+ GDI_GDIDLLINITIALIZE,
+ GDI_GETSTOCKOBJECT,
+ USER_REGISTERCLASSW,
+ CREATETHREAD,
+ USER_ENUMDISPLAYMONITORS,
+ USER_ENUMDISPLAYDEVICES,
+ USER_GETMONITORINFO,
+ GDI_CREATEOPMPROTECTEDOUTPUTS,
+ GDI_GETCERTIFICATE,
+ GDI_GETCERTIFICATESIZE,
+ GDI_DESTROYOPMPROTECTEDOUTPUT,
+ GDI_CONFIGUREOPMPROTECTEDOUTPUT,
+ GDI_GETOPMINFORMATION,
+ GDI_GETOPMRANDOMNUMBER,
+ GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
+ GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS,
+ NTCREATESECTION,
+ GETCOMPLEXLINEBREAKS,
+ LAST
+};
+
+constexpr size_t kMaxServiceCount = 64;
+constexpr size_t kMaxIpcTag = static_cast<size_t>(IpcTag::LAST);
+static_assert(kMaxIpcTag <= kMaxServiceCount, "kMaxServiceCount is too low");
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_IPC_TAGS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc b/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc
new file mode 100644
index 0000000000..71f7aff4cc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/ipc_unittest.cc
@@ -0,0 +1,632 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Helper function to make the fake shared memory with some
+// basic elements initialized.
+IPCControl* MakeChannels(size_t channel_size,
+ size_t total_shared_size,
+ size_t* base_start) {
+ // Allocate memory
+ char* mem = new char[total_shared_size];
+ memset(mem, 0, total_shared_size);
+ // Calculate how many channels we can fit in the shared memory.
+ total_shared_size -= offsetof(IPCControl, channels);
+ size_t channel_count =
+ total_shared_size / (sizeof(ChannelControl) + channel_size);
+ // Calculate the start of the first channel.
+ *base_start =
+ (sizeof(ChannelControl) * channel_count) + offsetof(IPCControl, channels);
+ // Setup client structure.
+ IPCControl* client_control = reinterpret_cast<IPCControl*>(mem);
+ client_control->channels_count = channel_count;
+ return client_control;
+}
+
+enum TestFixMode { FIX_NO_EVENTS, FIX_PONG_READY, FIX_PONG_NOT_READY };
+
+void FixChannels(IPCControl* client_control,
+ size_t base_start,
+ size_t channel_size,
+ TestFixMode mode) {
+ for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
+ ChannelControl& channel = client_control->channels[ix];
+ channel.channel_base = base_start;
+ channel.state = kFreeChannel;
+ if (mode != FIX_NO_EVENTS) {
+ bool signaled = (FIX_PONG_READY == mode) ? true : false;
+ channel.ping_event = ::CreateEventW(nullptr, false, false, nullptr);
+ channel.pong_event = ::CreateEventW(nullptr, false, signaled, nullptr);
+ }
+ base_start += channel_size;
+ }
+}
+
+void CloseChannelEvents(IPCControl* client_control) {
+ for (size_t ix = 0; ix != client_control->channels_count; ++ix) {
+ ChannelControl& channel = client_control->channels[ix];
+ ::CloseHandle(channel.ping_event);
+ ::CloseHandle(channel.pong_event);
+ }
+}
+
+TEST(IPCTest, ChannelMaker) {
+ // Test that our testing rig is computing offsets properly. We should have
+ // 5 channnels and the offset to the first channel is 108 bytes in 32 bits
+ // and 216 in 64 bits.
+ size_t channel_start = 0;
+ IPCControl* client_control = MakeChannels(12 * 64, 4096, &channel_start);
+ ASSERT_TRUE(client_control);
+ EXPECT_EQ(5u, client_control->channels_count);
+#if defined(_WIN64)
+ EXPECT_EQ(216u, channel_start);
+#else
+ EXPECT_EQ(108u, channel_start);
+#endif
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, ClientLockUnlock) {
+ // Make 7 channels of kIPCChannelSize (1kb) each. Test that we lock and
+ // unlock channels properly.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_NO_EVENTS);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ // Test that we lock the first 3 channels in sequence.
+ void* buff0 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff1 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff2 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[2].channel_base == buff2);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ // Test that we unlock and re-lock the right channel.
+ client.FreeBuffer(buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ void* buff2b = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff2b);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ client.FreeBuffer(buff0);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[2].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[3].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[4].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[5].state);
+
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallStrPacking) {
+ // This test tries the CrossCall object with null and non-null string
+ // combination of parameters, integer types and verifies that the unpacker
+ // can read them properly.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ IpcTag tag1 = IpcTag::PING1;
+ const wchar_t* text = L"98765 - 43210";
+ std::wstring copied_text;
+ CrossCallParamsEx* actual_params;
+
+ CrossCall(client, tag1, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag1, actual_params->GetTag());
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+ EXPECT_STREQ(text, copied_text.c_str());
+ copied_text.clear();
+
+ // Check with an empty string.
+ IpcTag tag2 = IpcTag::PING2;
+ const wchar_t* null_text = nullptr;
+ CrossCall(client, tag2, null_text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ uint32_t param_size = 1;
+ ArgType type = INVALID_TYPE;
+ void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ EXPECT_TRUE(param_addr);
+ EXPECT_EQ(0u, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+ EXPECT_TRUE(copied_text.empty());
+
+ IpcTag tag3 = IpcTag::PING1;
+ param_size = 1;
+ copied_text.clear();
+
+ // Check with an empty string and a non-empty string.
+ CrossCall(client, tag3, null_text, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(2u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag3, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ EXPECT_TRUE(param_addr);
+ EXPECT_EQ(0u, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text));
+ EXPECT_TRUE(copied_text.empty());
+ EXPECT_TRUE(actual_params->GetParameterStr(1, &copied_text));
+ EXPECT_STREQ(text, copied_text.c_str());
+
+ param_size = 1;
+ std::wstring copied_text_p0, copied_text_p2;
+
+ const wchar_t* text2 = L"AeFG";
+ CrossCall(client, tag1, text2, null_text, text, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(3u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag1, actual_params->GetTag());
+ EXPECT_TRUE(actual_params->GetParameterStr(0, &copied_text_p0));
+ EXPECT_STREQ(text2, copied_text_p0.c_str());
+ EXPECT_TRUE(actual_params->GetParameterStr(2, &copied_text_p2));
+ EXPECT_STREQ(text, copied_text_p2.c_str());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ EXPECT_TRUE(param_addr);
+ EXPECT_EQ(0u, param_size);
+ EXPECT_EQ(WCHAR_TYPE, type);
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallIntPacking) {
+ // Check handling for regular 32 bit integers used in Windows.
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 4, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ IpcTag tag1 = IpcTag::PING1;
+ IpcTag tag2 = IpcTag::PING2;
+ const wchar_t* text = L"godzilla";
+ CrossCallParamsEx* actual_params;
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ DWORD dw = 0xE6578;
+ CrossCall(client, tag2, dw, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(1u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ ArgType type = INVALID_TYPE;
+ uint32_t param_size = 1;
+ void* param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ ASSERT_EQ(sizeof(dw), param_size);
+ EXPECT_EQ(UINT32_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
+
+ // Check handling for windows HANDLES.
+ HANDLE h = HANDLE(0x70000500);
+ CrossCall(client, tag1, text, h, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(2u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag1, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+
+ // Check combination of 32 and 64 bits.
+ CrossCall(client, tag2, h, dw, h, &answer);
+ actual_params = reinterpret_cast<CrossCallParamsEx*>(client.GetBuffer());
+ EXPECT_EQ(3u, actual_params->GetParamsCount());
+ EXPECT_EQ(tag2, actual_params->GetTag());
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(0, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(1, &param_size, &type);
+ ASSERT_EQ(sizeof(dw), param_size);
+ EXPECT_EQ(UINT32_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&dw, param_addr, param_size));
+ type = INVALID_TYPE;
+ param_addr = actual_params->GetRawParameter(2, &param_size, &type);
+ ASSERT_EQ(sizeof(h), param_size);
+ EXPECT_EQ(VOIDPTR_TYPE, type);
+ ASSERT_TRUE(param_addr);
+ EXPECT_EQ(0, memcmp(&h, param_addr, param_size));
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+TEST(IPCTest, CrossCallValidation) {
+ // First a sanity test with a well formed parameter object.
+ unsigned long value = 124816;
+ IpcTag kTag = IpcTag::PING1;
+ const uint32_t kBufferSize = 256;
+ ActualCallParams<1, kBufferSize> params_1(kTag);
+ params_1.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE);
+ void* buffer = const_cast<void*>(params_1.GetBuffer());
+
+ uint32_t out_size = 0;
+ CrossCallParamsEx* ccp = 0;
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
+ &out_size);
+ ASSERT_TRUE(ccp);
+ EXPECT_TRUE(ccp->GetBuffer() != buffer);
+ EXPECT_EQ(kTag, ccp->GetTag());
+ EXPECT_EQ(1u, ccp->GetParamsCount());
+ delete[](reinterpret_cast<char*>(ccp));
+
+ // Test that we handle integer overflow on the number of params
+ // correctly. We use a test-only ctor for ActualCallParams that
+ // allows to create malformed cross-call buffers.
+ const int32_t kPtrDiffSz = sizeof(ptrdiff_t);
+ for (int32_t ix = -1; ix != 3; ++ix) {
+ uint32_t fake_num_params = (UINT32_MAX / kPtrDiffSz) + ix;
+ ActualCallParams<1, kBufferSize> params_2(kTag, fake_num_params);
+ params_2.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE);
+ buffer = const_cast<void*>(params_2.GetBuffer());
+
+ EXPECT_TRUE(buffer);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, params_1.GetSize(),
+ &out_size);
+ // If the buffer is malformed the return is nullptr.
+ EXPECT_TRUE(!ccp);
+ }
+
+ ActualCallParams<1, kBufferSize> params_3(kTag, 1);
+ params_3.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE);
+ buffer = const_cast<void*>(params_3.GetBuffer());
+ EXPECT_TRUE(buffer);
+
+ uint32_t correct_size = params_3.OverrideSize(1);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(!ccp);
+
+ // The correct_size is 8 bytes aligned.
+ params_3.OverrideSize(correct_size - 7);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(!ccp);
+
+ params_3.OverrideSize(correct_size);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(ccp);
+
+ // Make sure that two parameters work as expected.
+ ActualCallParams<2, kBufferSize> params_4(kTag, 2);
+ params_4.CopyParamIn(0, &value, sizeof(value), false, UINT32_TYPE);
+ params_4.CopyParamIn(1, buffer, sizeof(buffer), false, VOIDPTR_TYPE);
+ buffer = const_cast<void*>(params_4.GetBuffer());
+ EXPECT_TRUE(buffer);
+
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(ccp);
+
+#if defined(_WIN64)
+ correct_size = params_4.OverrideSize(1);
+ params_4.OverrideSize(correct_size - 1);
+ ccp = CrossCallParamsEx::CreateFromBuffer(buffer, kBufferSize, &out_size);
+ EXPECT_TRUE(!ccp);
+#endif
+}
+
+// This structure is passed to the mock server threads to simulate
+// the server side IPC so it has the required kernel objects.
+struct ServerEvents {
+ HANDLE ping;
+ HANDLE pong;
+ volatile LONG* state;
+ HANDLE mutex;
+};
+
+// This is the server thread that quicky answers an IPC and exits.
+DWORD WINAPI QuickResponseServer(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->ping, INFINITE);
+ ::InterlockedExchange(events->state, kAckChannel);
+ ::SetEvent(events->pong);
+ return wait_result;
+}
+
+class CrossCallParamsMock : public CrossCallParams {
+ public:
+ CrossCallParamsMock(IpcTag tag, uint32_t params_count)
+ : CrossCallParams(tag, params_count) {}
+};
+
+void FakeOkAnswerInChannel(void* channel) {
+ CrossCallReturn* answer = reinterpret_cast<CrossCallReturn*>(channel);
+ answer->call_outcome = SBOX_ALL_OK;
+}
+
+// Create two threads that will quickly answer IPCs; the first one
+// using channel 1 (channel 0 is busy) and one using channel 0. No time-out
+// should occur.
+TEST(IPCTest, ClientFastServer) {
+ const size_t channel_size = kIPCChannelSize;
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(channel_size, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
+ client_control->server_alive = ::CreateMutex(nullptr, false, nullptr);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ ServerEvents events = {0};
+ events.ping = client_control->channels[1].ping_event;
+ events.pong = client_control->channels[1].pong_event;
+ events.state = &client_control->channels[1].state;
+
+ HANDLE t1 =
+ ::CreateThread(nullptr, 0, QuickResponseServer, &events, 0, nullptr);
+ ASSERT_TRUE(t1);
+ ::CloseHandle(t1);
+
+ void* buff0 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[0].channel_base == buff0);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ void* buff1 = client.GetBuffer();
+ EXPECT_TRUE(mem + client_control->channels[1].channel_base == buff1);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kBusyChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ EXPECT_EQ(IpcTag::UNUSED, client_control->channels[1].ipc_tag);
+
+ IpcTag tag = IpcTag::PING1;
+ CrossCallReturn answer;
+ CrossCallParamsMock* params1 = new (buff1) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff1);
+
+ ResultCode result = client.DoCall(params1, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff1);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[1].ipc_tag);
+ EXPECT_EQ(kBusyChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ HANDLE t2 =
+ ::CreateThread(nullptr, 0, QuickResponseServer, &events, 0, nullptr);
+ ASSERT_TRUE(t2);
+ ::CloseHandle(t2);
+
+ client.FreeBuffer(buff0);
+ events.ping = client_control->channels[0].ping_event;
+ events.pong = client_control->channels[0].pong_event;
+ events.state = &client_control->channels[0].state;
+
+ tag = IpcTag::PING2;
+ CrossCallParamsMock* params2 = new (buff0) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff0);
+
+ result = client.DoCall(params2, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff0);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[1].state);
+ EXPECT_EQ(kFreeChannel, client_control->channels[2].state);
+
+ CloseChannelEvents(client_control);
+ ::CloseHandle(client_control->server_alive);
+
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+// This is the server thread that very slowly answers an IPC and exits. Note
+// that the pong event needs to be signaled twice.
+DWORD WINAPI SlowResponseServer(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->ping, INFINITE);
+ ::Sleep(kIPCWaitTimeOut1 + kIPCWaitTimeOut2 + 200);
+ ::InterlockedExchange(events->state, kAckChannel);
+ ::SetEvent(events->pong);
+ return wait_result;
+}
+
+// This thread's job is to keep the mutex locked.
+DWORD WINAPI MainServerThread(PVOID param) {
+ ServerEvents* events = reinterpret_cast<ServerEvents*>(param);
+ DWORD wait_result = 0;
+ wait_result = ::WaitForSingleObject(events->mutex, INFINITE);
+ Sleep(kIPCWaitTimeOut1 * 20);
+ return wait_result;
+}
+
+// Creates a server thread that answers the IPC so slow that is guaranteed to
+// trigger the time-out code path in the client. A second thread is created
+// to hold locked the server_alive mutex: this signals the client that the
+// server is not dead and it retries the wait.
+TEST(IPCTest, ClientSlowServer) {
+ size_t base_start = 0;
+ IPCControl* client_control =
+ MakeChannels(kIPCChannelSize, 4096 * 2, &base_start);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_NOT_READY);
+ client_control->server_alive = ::CreateMutex(nullptr, false, nullptr);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ ServerEvents events = {0};
+ events.ping = client_control->channels[0].ping_event;
+ events.pong = client_control->channels[0].pong_event;
+ events.state = &client_control->channels[0].state;
+
+ HANDLE t1 =
+ ::CreateThread(nullptr, 0, SlowResponseServer, &events, 0, nullptr);
+ ASSERT_TRUE(t1);
+ ::CloseHandle(t1);
+
+ ServerEvents events2 = {0};
+ events2.pong = events.pong;
+ events2.mutex = client_control->server_alive;
+
+ HANDLE t2 =
+ ::CreateThread(nullptr, 0, MainServerThread, &events2, 0, nullptr);
+ ASSERT_TRUE(t2);
+ ::CloseHandle(t2);
+
+ ::Sleep(1);
+
+ void* buff0 = client.GetBuffer();
+ IpcTag tag = IpcTag::PING1;
+ CrossCallReturn answer;
+ CrossCallParamsMock* params1 = new (buff0) CrossCallParamsMock(tag, 1);
+ FakeOkAnswerInChannel(buff0);
+
+ ResultCode result = client.DoCall(params1, &answer);
+ if (SBOX_ERROR_CHANNEL_ERROR != result)
+ client.FreeBuffer(buff0);
+
+ EXPECT_TRUE(SBOX_ALL_OK == result);
+ EXPECT_EQ(tag, client_control->channels[0].ipc_tag);
+ EXPECT_EQ(kFreeChannel, client_control->channels[0].state);
+
+ CloseChannelEvents(client_control);
+ ::CloseHandle(client_control->server_alive);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+// This test-only IPC dispatcher has two handlers with the same signature
+// but only CallOneHandler should be used.
+class UnitTestIPCDispatcher : public Dispatcher {
+ public:
+ UnitTestIPCDispatcher();
+ ~UnitTestIPCDispatcher() override {}
+
+ bool SetupService(InterceptionManager* manager, IpcTag service) override {
+ return true;
+ }
+
+ private:
+ bool CallOneHandler(IPCInfo* ipc, HANDLE p1, uint32_t p2) {
+ ipc->return_info.extended[0].handle = p1;
+ ipc->return_info.extended[1].unsigned_int = p2;
+ return true;
+ }
+
+ bool CallTwoHandler(IPCInfo* ipc, HANDLE p1, uint32_t p2) { return true; }
+};
+
+UnitTestIPCDispatcher::UnitTestIPCDispatcher() {
+ static const IPCCall call_one = {{IpcTag::PING1, {VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &UnitTestIPCDispatcher::CallOneHandler)};
+ static const IPCCall call_two = {{IpcTag::PING2, {VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &UnitTestIPCDispatcher::CallTwoHandler)};
+ ipc_calls_.push_back(call_one);
+ ipc_calls_.push_back(call_two);
+}
+
+// This test does most of the shared memory IPC client-server roundtrip
+// and tests the packing, unpacking and call dispatching.
+TEST(IPCTest, SharedMemServerTests) {
+ size_t base_start = 0;
+ IPCControl* client_control = MakeChannels(kIPCChannelSize, 4096, &base_start);
+ client_control->server_alive = HANDLE(1);
+ FixChannels(client_control, base_start, kIPCChannelSize, FIX_PONG_READY);
+
+ char* mem = reinterpret_cast<char*>(client_control);
+ SharedMemIPCClient client(mem);
+
+ CrossCallReturn answer;
+ HANDLE bar = HANDLE(191919);
+ DWORD foo = 6767676;
+ CrossCall(client, IpcTag::PING1, bar, foo, &answer);
+ void* buff = client.GetBuffer();
+ ASSERT_TRUE(buff);
+
+ UnitTestIPCDispatcher dispatcher;
+ // Since we are directly calling InvokeCallback, most of this structure
+ // can be set to nullptr.
+ sandbox::SharedMemIPCServer::ServerControl srv_control = {};
+ srv_control.channel_size = kIPCChannelSize;
+ srv_control.shared_base = reinterpret_cast<char*>(client_control);
+ srv_control.dispatcher = &dispatcher;
+
+ sandbox::CrossCallReturn call_return = {0};
+ EXPECT_TRUE(
+ SharedMemIPCServer::InvokeCallback(&srv_control, buff, &call_return));
+ EXPECT_EQ(SBOX_ALL_OK, call_return.call_outcome);
+ EXPECT_TRUE(bar == call_return.extended[0].handle);
+ EXPECT_EQ(foo, call_return.extended[1].unsigned_int);
+
+ CloseChannelEvents(client_control);
+ delete[] reinterpret_cast<char*>(client_control);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/job.cc b/security/sandbox/chromium/sandbox/win/src/job.cc
new file mode 100644
index 0000000000..39baffb193
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/job.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2011 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/job.h"
+
+#include <stddef.h>
+#include <utility>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/restricted_token.h"
+
+namespace sandbox {
+
+Job::Job() : job_handle_(nullptr) {}
+
+Job::~Job() {}
+
+DWORD Job::Init(JobLevel security_level,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit) {
+ if (job_handle_.IsValid())
+ return ERROR_ALREADY_INITIALIZED;
+
+ job_handle_.Set(::CreateJobObject(nullptr, // No security attribute
+ job_name));
+ if (!job_handle_.IsValid())
+ return ::GetLastError();
+
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {};
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {};
+
+ // Set the settings for the different security levels. Note: The higher levels
+ // inherit from the lower levels.
+ switch (security_level) {
+ case JOB_LOCKDOWN: {
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION;
+ FALLTHROUGH;
+ }
+ case JOB_RESTRICTED: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_READCLIPBOARD;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_GLOBALATOMS;
+ FALLTHROUGH;
+ }
+ case JOB_LIMITED_USER: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DISPLAYSETTINGS;
+ jeli.BasicLimitInformation.LimitFlags |= JOB_OBJECT_LIMIT_ACTIVE_PROCESS;
+ jeli.BasicLimitInformation.ActiveProcessLimit = 1;
+ FALLTHROUGH;
+ }
+ case JOB_INTERACTIVE: {
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_DESKTOP;
+ jbur.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;
+ FALLTHROUGH;
+ }
+ case JOB_UNPROTECTED: {
+ if (memory_limit) {
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_PROCESS_MEMORY;
+ jeli.ProcessMemoryLimit = memory_limit;
+ }
+
+ jeli.BasicLimitInformation.LimitFlags |=
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ break;
+ }
+ default: { return ERROR_BAD_ARGUMENTS; }
+ }
+
+ if (!::SetInformationJobObject(job_handle_.Get(),
+ JobObjectExtendedLimitInformation, &jeli,
+ sizeof(jeli))) {
+ return ::GetLastError();
+ }
+
+ jbur.UIRestrictionsClass = jbur.UIRestrictionsClass & (~ui_exceptions);
+ if (!::SetInformationJobObject(job_handle_.Get(),
+ JobObjectBasicUIRestrictions, &jbur,
+ sizeof(jbur))) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD Job::UserHandleGrantAccess(HANDLE handle) {
+ if (!job_handle_.IsValid())
+ return ERROR_NO_DATA;
+
+ if (!::UserHandleGrantAccess(handle, job_handle_.Get(),
+ true)) { // Access allowed.
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+base::win::ScopedHandle Job::Take() {
+ return std::move(job_handle_);
+}
+
+DWORD Job::AssignProcessToJob(HANDLE process_handle) {
+ if (!job_handle_.IsValid())
+ return ERROR_NO_DATA;
+
+ if (!::AssignProcessToJobObject(job_handle_.Get(), process_handle))
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/job.h b/security/sandbox/chromium/sandbox/win/src/job.h
new file mode 100644
index 0000000000..153080bf6b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/job.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2010 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_JOB_H_
+#define SANDBOX_SRC_JOB_H_
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+
+namespace sandbox {
+
+// Handles the creation of job objects based on a security profile.
+// Sample usage:
+// Job job;
+// job.Init(JOB_LOCKDOWN, nullptr); //no job name
+// job.AssignProcessToJob(process_handle);
+class Job {
+ public:
+ Job();
+
+ ~Job();
+
+ // Initializes and creates the job object. The security of the job is based
+ // on the security_level parameter.
+ // job_name can be nullptr if the job is unnamed.
+ // If the chosen profile has too many ui restrictions, you can disable some
+ // by specifying them in the ui_exceptions parameters.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD Init(JobLevel security_level,
+ const wchar_t* job_name,
+ DWORD ui_exceptions,
+ size_t memory_limit);
+
+ // Assigns the process referenced by process_handle to the job.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD AssignProcessToJob(HANDLE process_handle);
+
+ // Grants access to "handle" to the job. All processes in the job can
+ // subsequently recognize and use the handle.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD UserHandleGrantAccess(HANDLE handle);
+
+ // Revokes ownership to the job handle and returns it.
+ // If the object is not yet initialized, it returns an invalid handle.
+ base::win::ScopedHandle Take();
+
+ private:
+ // Handle to the job referenced by the object.
+ base::win::ScopedHandle job_handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(Job);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_JOB_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/job_unittest.cc b/security/sandbox/chromium/sandbox/win/src/job_unittest.cc
new file mode 100644
index 0000000000..536900dc40
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/job_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for the job object.
+
+#include "sandbox/win/src/job.h"
+
+#include "base/win/scoped_process_information.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Tests the creation and destruction of the job.
+TEST(JobTest, TestCreation) {
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+
+ // check if the job exists.
+ HANDLE job_handle =
+ ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(job_handle);
+
+ if (job_handle)
+ CloseHandle(job_handle);
+ }
+
+ // Check if the job is destroyed when the object goes out of scope.
+ HANDLE job_handle = ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(!job_handle);
+ ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError());
+}
+
+// Tests the method "Take".
+TEST(JobTest, Take) {
+ base::win::ScopedHandle job_handle;
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+
+ job_handle = job.Take();
+ ASSERT_TRUE(job_handle.IsValid());
+ }
+
+ // Check to be sure that the job is still alive even after the object is gone
+ // out of scope.
+ HANDLE job_handle_dup =
+ ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(job_handle_dup);
+
+ // Remove all references.
+ if (job_handle_dup)
+ ::CloseHandle(job_handle_dup);
+
+ job_handle.Close();
+
+ // Check if the jbo is really dead.
+ job_handle_dup = ::OpenJobObjectW(GENERIC_ALL, false, L"my_test_job_name");
+ ASSERT_TRUE(!job_handle_dup);
+ ASSERT_EQ(static_cast<DWORD>(ERROR_FILE_NOT_FOUND), ::GetLastError());
+}
+
+// Tests the ui exceptions
+TEST(JobTest, TestExceptions) {
+ base::win::ScopedHandle job_handle;
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name",
+ JOB_OBJECT_UILIMIT_READCLIPBOARD, 0));
+
+ job_handle = job.Take();
+ ASSERT_TRUE(job_handle.IsValid());
+
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
+ DWORD size = sizeof(jbur);
+ ASSERT_TRUE(::QueryInformationJobObject(
+ job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size));
+
+ ASSERT_EQ(0u, jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD);
+ job_handle.Close();
+ }
+
+ // Scope the creation of Job.
+ {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+
+ job_handle = job.Take();
+ ASSERT_TRUE(job_handle.IsValid());
+
+ JOBOBJECT_BASIC_UI_RESTRICTIONS jbur = {0};
+ DWORD size = sizeof(jbur);
+ ASSERT_TRUE(::QueryInformationJobObject(
+ job_handle.Get(), JobObjectBasicUIRestrictions, &jbur, size, &size));
+
+ ASSERT_EQ(static_cast<DWORD>(JOB_OBJECT_UILIMIT_READCLIPBOARD),
+ jbur.UIRestrictionsClass & JOB_OBJECT_UILIMIT_READCLIPBOARD);
+ }
+}
+
+// Tests the error case when the job is initialized twice.
+TEST(JobTest, DoubleInit) {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_LOCKDOWN, L"my_test_job_name", 0, 0));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_ALREADY_INITIALIZED),
+ job.Init(JOB_LOCKDOWN, L"test", 0, 0));
+}
+
+// Tests the error case when we use a method and the object is not yet
+// initialized.
+TEST(JobTest, NoInit) {
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_NO_DATA),
+ job.UserHandleGrantAccess(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_NO_DATA), job.AssignProcessToJob(nullptr));
+ ASSERT_FALSE(job.Take().IsValid());
+}
+
+// Tests the initialization of the job with different security level.
+TEST(JobTest, SecurityLevel) {
+ Job job1;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job1.Init(JOB_LOCKDOWN, L"job1", 0, 0));
+
+ Job job2;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job2.Init(JOB_RESTRICTED, L"job2", 0, 0));
+
+ Job job3;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job3.Init(JOB_LIMITED_USER, L"job3", 0, 0));
+
+ Job job4;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job4.Init(JOB_INTERACTIVE, L"job4", 0, 0));
+
+ Job job5;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job5.Init(JOB_UNPROTECTED, L"job5", 0, 0));
+
+ // JOB_NONE means we run without a job object so Init should fail.
+ Job job6;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_BAD_ARGUMENTS),
+ job6.Init(JOB_NONE, L"job6", 0, 0));
+
+ Job job7;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_BAD_ARGUMENTS),
+ job7.Init(static_cast<JobLevel>(JOB_NONE + 1), L"job7", 0, 0));
+}
+
+// Tests the method "AssignProcessToJob".
+TEST(JobTest, ProcessInJob) {
+ // Create the job.
+ Job job;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.Init(JOB_UNPROTECTED, L"job_test_process", 0, 0));
+
+ wchar_t notepad[] = L"notepad";
+ STARTUPINFO si = {sizeof(si)};
+ PROCESS_INFORMATION temp_process_info = {};
+ ASSERT_TRUE(::CreateProcess(nullptr, notepad, nullptr, nullptr, false, 0,
+ nullptr, nullptr, &si, &temp_process_info));
+ base::win::ScopedProcessInformation pi(temp_process_info);
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ job.AssignProcessToJob(pi.process_handle()));
+
+ // Get the job handle.
+ base::win::ScopedHandle job_handle = job.Take();
+
+ // Check if the process is in the job.
+ JOBOBJECT_BASIC_PROCESS_ID_LIST jbpidl = {0};
+ DWORD size = sizeof(jbpidl);
+ EXPECT_TRUE(::QueryInformationJobObject(
+ job_handle.Get(), JobObjectBasicProcessIdList, &jbpidl, size, &size));
+
+ EXPECT_EQ(1u, jbpidl.NumberOfAssignedProcesses);
+ EXPECT_EQ(1u, jbpidl.NumberOfProcessIdsInList);
+ EXPECT_EQ(pi.process_id(), jbpidl.ProcessIdList[0]);
+
+ EXPECT_TRUE(::TerminateProcess(pi.process_handle(), 0));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc
new file mode 100644
index 0000000000..85ffebe6e6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/named_pipe_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/named_pipe_interception.h"
+#include "sandbox/win/src/named_pipe_policy.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+
+namespace sandbox {
+
+NamedPipeDispatcher::NamedPipeDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::CREATENAMEDPIPEW,
+ {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&NamedPipeDispatcher::CreateNamedPipe)};
+
+ ipc_calls_.push_back(create_params);
+}
+
+bool NamedPipeDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (IpcTag::CREATENAMEDPIPEW == service)
+ return INTERCEPT_EAT(manager, kKerneldllName, CreateNamedPipeW,
+ CREATE_NAMED_PIPE_ID, 36);
+
+ return false;
+}
+
+bool NamedPipeDispatcher::CreateNamedPipe(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t open_mode,
+ uint32_t pipe_mode,
+ uint32_t max_instances,
+ uint32_t out_buffer_size,
+ uint32_t in_buffer_size,
+ uint32_t default_timeout) {
+ ipc->return_info.win32_result = ERROR_ACCESS_DENIED;
+ ipc->return_info.handle = INVALID_HANDLE_VALUE;
+
+ base::StringPiece16 dotdot(STRING16_LITERAL(".."));
+
+ for (const base::StringPiece16& path : base::SplitStringPiece(
+ base::AsStringPiece16(*name), STRING16_LITERAL("/"),
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ for (const base::StringPiece16& inner :
+ base::SplitStringPiece(path, STRING16_LITERAL("\\"),
+ base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
+ if (inner == dotdot)
+ return true;
+ }
+ }
+
+ const wchar_t* pipe_name = name->c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(pipe_name);
+
+ EvalResult eval =
+ policy_base_->EvalPolicy(IpcTag::CREATENAMEDPIPEW, params.GetBase());
+
+ // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to
+ // disable all string parsing and to send the string that follows it straight
+ // to the file system."
+ // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ // This ensures even if there is a path traversal in the pipe name, and it is
+ // able to get past the checks above, it will still not be allowed to escape
+ // our allowed namespace.
+ if (name->compare(0, 4, L"\\\\.\\") == 0)
+ name->replace(0, 4, L"\\\\\?\\");
+
+ HANDLE pipe;
+ DWORD ret = NamedPipePolicy::CreateNamedPipeAction(
+ eval, *ipc->client_info, *name, open_mode, pipe_mode, max_instances,
+ out_buffer_size, in_buffer_size, default_timeout, &pipe);
+
+ ipc->return_info.win32_result = ret;
+ ipc->return_info.handle = pipe;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h
new file mode 100644
index 0000000000..a14f658506
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_dispatcher.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2010 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_NAMED_PIPE_DISPATCHER_H__
+#define SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles named pipe related IPC calls.
+class NamedPipeDispatcher : public Dispatcher {
+ public:
+ explicit NamedPipeDispatcher(PolicyBase* policy_base);
+ ~NamedPipeDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to CreateNamedPipeW() in the
+ // target.
+ bool CreateNamedPipe(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t open_mode,
+ uint32_t pipe_mode,
+ uint32_t max_instances,
+ uint32_t out_buffer_size,
+ uint32_t in_buffer_size,
+ uint32_t default_timeout);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(NamedPipeDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_NAMED_PIPE_DISPATCHER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc
new file mode 100644
index 0000000000..aa3d5dc358
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2006-2008 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/named_pipe_interception.h"
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+HANDLE WINAPI
+TargetCreateNamedPipeW(CreateNamedPipeWFunction orig_CreateNamedPipeW,
+ LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ HANDLE pipe = orig_CreateNamedPipeW(
+ pipe_name, open_mode, pipe_mode, max_instance, out_buffer_size,
+ in_buffer_size, default_timeout, security_attributes);
+ if (INVALID_HANDLE_VALUE != pipe)
+ return pipe;
+
+ mozilla::sandboxing::LogBlocked("CreateNamedPipeW", pipe_name);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return INVALID_HANDLE_VALUE;
+
+ DWORD original_error = ::GetLastError();
+
+ // We don't support specific Security Attributes.
+ if (security_attributes)
+ return INVALID_HANDLE_VALUE;
+
+ do {
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(pipe_name);
+
+ if (!QueryBroker(IpcTag::CREATENAMEDPIPEW, params.GetBase()))
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code =
+ CrossCall(ipc, IpcTag::CREATENAMEDPIPEW, pipe_name, open_mode,
+ pipe_mode, max_instance, out_buffer_size, in_buffer_size,
+ default_timeout, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+
+ if (ERROR_SUCCESS != answer.win32_result)
+ return INVALID_HANDLE_VALUE;
+
+ mozilla::sandboxing::LogAllowed("CreateNamedPipeW", pipe_name);
+ return answer.handle;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return INVALID_HANDLE_VALUE;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h
new file mode 100644
index 0000000000..8eedd14b31
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_interception.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2006-2008 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_WIN_SRC_NAMED_PIPE_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_NAMED_PIPE_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+typedef HANDLE(WINAPI* CreateNamedPipeWFunction)(
+ LPCWSTR lpName,
+ DWORD dwOpenMode,
+ DWORD dwPipeMode,
+ DWORD nMaxInstances,
+ DWORD nOutBufferSize,
+ DWORD nInBufferSize,
+ DWORD nDefaultTimeOut,
+ LPSECURITY_ATTRIBUTES lpSecurityAttributes);
+
+// Interception of CreateNamedPipeW in kernel32.dll
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateNamedPipeW(CreateNamedPipeWFunction orig_CreateNamedPipeW,
+ LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instance,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_NAMED_PIPE_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc
new file mode 100644
index 0000000000..bfad75852d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2006-2008 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/named_pipe_policy.h"
+
+#include <string>
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace {
+
+// Creates a named pipe and duplicates the handle to 'target_process'. The
+// remaining parameters are the same as CreateNamedPipeW().
+HANDLE CreateNamedPipeHelper(HANDLE target_process,
+ LPCWSTR pipe_name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ LPSECURITY_ATTRIBUTES security_attributes) {
+ HANDLE pipe = ::CreateNamedPipeW(
+ pipe_name, open_mode, pipe_mode, max_instances, out_buffer_size,
+ in_buffer_size, default_timeout, security_attributes);
+ if (INVALID_HANDLE_VALUE == pipe)
+ return pipe;
+
+ HANDLE new_pipe;
+ if (!::DuplicateHandle(::GetCurrentProcess(), pipe, target_process, &new_pipe,
+ 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return INVALID_HANDLE_VALUE;
+ }
+
+ return new_pipe;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool NamedPipePolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ if (TargetPolicy::NAMEDPIPES_ALLOW_ANY != semantics) {
+ return false;
+ }
+ PolicyRule pipe(ASK_BROKER);
+ if (!pipe.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::CREATENAMEDPIPEW, &pipe)) {
+ return false;
+ }
+ return true;
+}
+
+DWORD NamedPipePolicy::CreateNamedPipeAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ HANDLE* pipe) {
+ *pipe = INVALID_HANDLE_VALUE;
+ // The only action supported is ASK_BROKER which means create the pipe.
+ if (ASK_BROKER != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ *pipe = CreateNamedPipeHelper(client_info.process, name.c_str(), open_mode,
+ pipe_mode, max_instances, out_buffer_size,
+ in_buffer_size, default_timeout, nullptr);
+
+ if (INVALID_HANDLE_VALUE == *pipe)
+ return ERROR_ACCESS_DENIED;
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h
new file mode 100644
index 0000000000..4ad272f16f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2006-2008 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_NAMED_PIPE_POLICY_H__
+#define SANDBOX_SRC_NAMED_PIPE_POLICY_H__
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to named pipe creation.
+class NamedPipePolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level.
+ // policy rule for named pipe creation
+ // 'name' is the named pipe to be created
+ // 'semantics' is the desired semantics.
+ // 'policy' is the policy generator to which the rules are going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Processes a 'CreateNamedPipeW()' request from the target.
+ static DWORD CreateNamedPipeAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& name,
+ DWORD open_mode,
+ DWORD pipe_mode,
+ DWORD max_instances,
+ DWORD out_buffer_size,
+ DWORD in_buffer_size,
+ DWORD default_timeout,
+ HANDLE* pipe);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_NAMED_PIPE_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc
new file mode 100644
index 0000000000..db532d618d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/named_pipe_policy_test.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int NamedPipe_Create(int argc, wchar_t** argv) {
+ if (argc < 1 || argc > 2 || !argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ HANDLE pipe = ::CreateNamedPipeW(
+ argv[0], PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, 4096, 4096, 2000, nullptr);
+ if (INVALID_HANDLE_VALUE == pipe)
+ return SBOX_TEST_DENIED;
+
+ // The second parameter allows us to enforce an allowlist for where the
+ // pipe should be in the object namespace after creation.
+ if (argc == 2) {
+ std::wstring handle_name;
+ if (GetHandleName(pipe, &handle_name)) {
+ if (handle_name.compare(0, wcslen(argv[1]), argv[1]) != 0)
+ return SBOX_TEST_FAILED;
+ } else {
+ return SBOX_TEST_FAILED;
+ }
+ }
+
+ OVERLAPPED overlapped = {0};
+ overlapped.hEvent = ::CreateEvent(nullptr, true, true, nullptr);
+ bool result = ::ConnectNamedPipe(pipe, &overlapped);
+
+ if (!result) {
+ DWORD error = ::GetLastError();
+ if (ERROR_PIPE_CONNECTED != error && ERROR_IO_PENDING != error) {
+ return SBOX_TEST_FAILED;
+ }
+ }
+
+ if (!::CloseHandle(pipe))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(overlapped.hEvent);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests if we can create a pipe in the sandbox.
+TEST(NamedPipePolicyTest, CreatePipe) {
+ TestRunner runner;
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh"));
+}
+
+// Tests if we can create a pipe with a path traversal in the sandbox.
+TEST(NamedPipePolicyTest, CreatePipeTraversal) {
+ TestRunner runner;
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\..\\bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/../bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test\\../bleh"));
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\test/..\\bleh"));
+}
+
+// This tests that path canonicalization is actually disabled if we use \\?\
+// syntax.
+TEST(NamedPipePolicyTest, CreatePipeCanonicalization) {
+ // "For file I/O, the "\\?\" prefix to a path string tells the Windows APIs to
+ // disable all string parsing and to send the string that follows it straight
+ // to the file system."
+ // http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ const wchar_t* argv[2] = {L"\\\\?\\pipe\\test\\..\\bleh",
+ L"\\Device\\NamedPipe\\test"};
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ NamedPipe_Create(2, const_cast<wchar_t**>(argv)));
+}
+
+// The same test as CreatePipe but this time using strict interceptions.
+TEST(NamedPipePolicyTest, CreatePipeStrictInterceptions) {
+ TestRunner runner;
+ runner.GetPolicy()->SetStrictInterceptions();
+
+ // TODO(nsylvain): This policy is wrong because "*" is a valid char in a
+ // namedpipe name. Here we apply it like a wildcard. http://b/893603
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_NAMED_PIPES,
+ TargetPolicy::NAMEDPIPES_ALLOW_ANY,
+ L"\\\\.\\pipe\\test*"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\testbleh"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"NamedPipe_Create \\\\.\\pipe\\bleh"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/nt_internals.h b/security/sandbox/chromium/sandbox/win/src/nt_internals.h
new file mode 100644
index 0000000000..6041566436
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/nt_internals.h
@@ -0,0 +1,983 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file holds definitions related to the ntdll API.
+
+#ifndef SANDBOX_WIN_SRC_NT_INTERNALS_H__
+#define SANDBOX_WIN_SRC_NT_INTERNALS_H__
+
+#include <windows.h>
+
+#include <stddef.h>
+
+typedef LONG NTSTATUS;
+#define NT_SUCCESS(st) (st >= 0)
+#define NT_ERROR(st) ((((ULONG)(st)) >> 30) == 3)
+
+// clang-format off
+#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+#define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L)
+#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
+#define STATUS_NOT_IMPLEMENTED ((NTSTATUS)0xC0000002L)
+#define STATUS_INVALID_INFO_CLASS ((NTSTATUS)0xC0000003L)
+#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
+#ifndef STATUS_INVALID_PARAMETER
+// It is now defined in Windows 2008 SDK.
+#define STATUS_INVALID_PARAMETER ((NTSTATUS)0xC000000DL)
+#endif
+#define STATUS_CONFLICTING_ADDRESSES ((NTSTATUS)0xC0000018L)
+#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
+#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
+#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
+#define STATUS_OBJECT_NAME_COLLISION ((NTSTATUS)0xC0000035L)
+#define STATUS_PROCEDURE_NOT_FOUND ((NTSTATUS)0xC000007AL)
+#define STATUS_INVALID_IMAGE_FORMAT ((NTSTATUS)0xC000007BL)
+#define STATUS_NO_TOKEN ((NTSTATUS)0xC000007CL)
+#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
+#define STATUS_INVALID_IMAGE_HASH ((NTSTATUS)0xC0000428L)
+// clang-format on
+
+#define CURRENT_PROCESS ((HANDLE)-1)
+#define CURRENT_THREAD ((HANDLE)-2)
+#define NtCurrentProcess CURRENT_PROCESS
+
+typedef struct _UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+} UNICODE_STRING;
+typedef UNICODE_STRING* PUNICODE_STRING;
+typedef const UNICODE_STRING* PCUNICODE_STRING;
+
+typedef struct _STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PCHAR Buffer;
+} STRING;
+typedef STRING* PSTRING;
+
+typedef STRING ANSI_STRING;
+typedef PSTRING PANSI_STRING;
+typedef CONST PSTRING PCANSI_STRING;
+
+typedef STRING OEM_STRING;
+typedef PSTRING POEM_STRING;
+typedef CONST STRING* PCOEM_STRING;
+
+#define OBJ_CASE_INSENSITIVE 0x00000040L
+#define OBJ_OPENIF 0x00000080L
+
+typedef struct _OBJECT_ATTRIBUTES {
+ ULONG Length;
+ HANDLE RootDirectory;
+ PUNICODE_STRING ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+} OBJECT_ATTRIBUTES;
+typedef OBJECT_ATTRIBUTES* POBJECT_ATTRIBUTES;
+
+#define InitializeObjectAttributes(p, n, a, r, s) \
+ { \
+ (p)->Length = sizeof(OBJECT_ATTRIBUTES); \
+ (p)->RootDirectory = r; \
+ (p)->Attributes = a; \
+ (p)->ObjectName = n; \
+ (p)->SecurityDescriptor = s; \
+ (p)->SecurityQualityOfService = nullptr; \
+ }
+
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+// -----------------------------------------------------------------------
+// File IO
+
+// Create disposition values.
+
+#define FILE_SUPERSEDE 0x00000000
+#define FILE_OPEN 0x00000001
+#define FILE_CREATE 0x00000002
+#define FILE_OPEN_IF 0x00000003
+#define FILE_OVERWRITE 0x00000004
+#define FILE_OVERWRITE_IF 0x00000005
+#define FILE_MAXIMUM_DISPOSITION 0x00000005
+
+// Create/open option flags.
+
+#define FILE_DIRECTORY_FILE 0x00000001
+#define FILE_WRITE_THROUGH 0x00000002
+#define FILE_SEQUENTIAL_ONLY 0x00000004
+#define FILE_NO_INTERMEDIATE_BUFFERING 0x00000008
+
+#define FILE_SYNCHRONOUS_IO_ALERT 0x00000010
+#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
+#define FILE_NON_DIRECTORY_FILE 0x00000040
+#define FILE_CREATE_TREE_CONNECTION 0x00000080
+
+#define FILE_COMPLETE_IF_OPLOCKED 0x00000100
+#define FILE_NO_EA_KNOWLEDGE 0x00000200
+#define FILE_OPEN_REMOTE_INSTANCE 0x00000400
+#define FILE_RANDOM_ACCESS 0x00000800
+
+#define FILE_DELETE_ON_CLOSE 0x00001000
+#define FILE_OPEN_BY_FILE_ID 0x00002000
+#define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000
+#define FILE_NO_COMPRESSION 0x00008000
+
+#define FILE_RESERVE_OPFILTER 0x00100000
+#define FILE_OPEN_REPARSE_POINT 0x00200000
+#define FILE_OPEN_NO_RECALL 0x00400000
+#define FILE_OPEN_FOR_FREE_SPACE_QUERY 0x00800000
+
+// Create/open result values. These are the disposition values returned on the
+// io status information.
+#define FILE_SUPERSEDED 0x00000000
+#define FILE_OPENED 0x00000001
+#define FILE_CREATED 0x00000002
+#define FILE_OVERWRITTEN 0x00000003
+#define FILE_EXISTS 0x00000004
+#define FILE_DOES_NOT_EXIST 0x00000005
+
+typedef NTSTATUS(WINAPI* NtCreateFileFunction)(
+ OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN PLARGE_INTEGER AllocationSize OPTIONAL,
+ IN ULONG FileAttributes,
+ IN ULONG ShareAccess,
+ IN ULONG CreateDisposition,
+ IN ULONG CreateOptions,
+ IN PVOID EaBuffer OPTIONAL,
+ IN ULONG EaLength);
+
+typedef NTSTATUS(WINAPI* NtOpenFileFunction)(OUT PHANDLE FileHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG ShareAccess,
+ IN ULONG OpenOptions);
+
+typedef NTSTATUS(WINAPI* NtCloseFunction)(IN HANDLE Handle);
+
+typedef enum _FILE_INFORMATION_CLASS {
+ FileRenameInformation = 10
+} FILE_INFORMATION_CLASS,
+ *PFILE_INFORMATION_CLASS;
+
+typedef struct _FILE_RENAME_INFORMATION {
+ BOOLEAN ReplaceIfExists;
+ HANDLE RootDirectory;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtSetInformationFileFunction)(
+ IN HANDLE FileHandle,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ IN PVOID FileInformation,
+ IN ULONG Length,
+ IN FILE_INFORMATION_CLASS FileInformationClass);
+
+typedef struct FILE_BASIC_INFORMATION {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ ULONG FileAttributes;
+} FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQueryAttributesFileFunction)(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PFILE_BASIC_INFORMATION FileAttributes);
+
+typedef struct _FILE_NETWORK_OPEN_INFORMATION {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER EndOfFile;
+ ULONG FileAttributes;
+} FILE_NETWORK_OPEN_INFORMATION, *PFILE_NETWORK_OPEN_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQueryFullAttributesFileFunction)(
+ IN POBJECT_ATTRIBUTES ObjectAttributes,
+ OUT PFILE_NETWORK_OPEN_INFORMATION FileAttributes);
+
+// -----------------------------------------------------------------------
+// Sections
+
+typedef NTSTATUS(WINAPI* NtCreateSectionFunction)(
+ OUT PHANDLE SectionHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN PLARGE_INTEGER MaximumSize OPTIONAL,
+ IN ULONG SectionPageProtection,
+ IN ULONG AllocationAttributes,
+ IN HANDLE FileHandle OPTIONAL);
+
+typedef ULONG SECTION_INHERIT;
+#define ViewShare 1
+#define ViewUnmap 2
+
+typedef NTSTATUS(WINAPI* NtMapViewOfSectionFunction)(
+ IN HANDLE SectionHandle,
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN ULONG_PTR ZeroBits,
+ IN SIZE_T CommitSize,
+ IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
+ IN OUT PSIZE_T ViewSize,
+ IN SECTION_INHERIT InheritDisposition,
+ IN ULONG AllocationType,
+ IN ULONG Win32Protect);
+
+typedef NTSTATUS(WINAPI* NtUnmapViewOfSectionFunction)(IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress);
+
+typedef enum _SECTION_INFORMATION_CLASS {
+ SectionBasicInformation = 0,
+ SectionImageInformation
+} SECTION_INFORMATION_CLASS;
+
+typedef struct _SECTION_BASIC_INFORMATION {
+ PVOID BaseAddress;
+ ULONG Attributes;
+ LARGE_INTEGER Size;
+} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQuerySectionFunction)(
+ IN HANDLE SectionHandle,
+ IN SECTION_INFORMATION_CLASS SectionInformationClass,
+ OUT PVOID SectionInformation,
+ IN SIZE_T SectionInformationLength,
+ OUT PSIZE_T ReturnLength OPTIONAL);
+
+// -----------------------------------------------------------------------
+// Process and Thread
+
+typedef struct _CLIENT_ID {
+ PVOID UniqueProcess;
+ PVOID UniqueThread;
+} CLIENT_ID, *PCLIENT_ID;
+
+typedef NTSTATUS(WINAPI* NtOpenThreadFunction)(OUT PHANDLE ThreadHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN PCLIENT_ID ClientId);
+
+typedef NTSTATUS(WINAPI* NtOpenProcessFunction)(OUT PHANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN PCLIENT_ID ClientId);
+
+typedef enum _NT_THREAD_INFORMATION_CLASS {
+ ThreadBasicInformation,
+ ThreadTimes,
+ ThreadPriority,
+ ThreadBasePriority,
+ ThreadAffinityMask,
+ ThreadImpersonationToken,
+ ThreadDescriptorTableEntry,
+ ThreadEnableAlignmentFaultFixup,
+ ThreadEventPair,
+ ThreadQuerySetWin32StartAddress,
+ ThreadZeroTlsCell,
+ ThreadPerformanceCount,
+ ThreadAmILastThread,
+ ThreadIdealProcessor,
+ ThreadPriorityBoost,
+ ThreadSetTlsArrayAddress,
+ ThreadIsIoPending,
+ ThreadHideFromDebugger
+} NT_THREAD_INFORMATION_CLASS,
+ *PNT_THREAD_INFORMATION_CLASS;
+
+typedef NTSTATUS(WINAPI* NtSetInformationThreadFunction)(
+ IN HANDLE ThreadHandle,
+ IN NT_THREAD_INFORMATION_CLASS ThreadInformationClass,
+ IN PVOID ThreadInformation,
+ IN ULONG ThreadInformationLength);
+
+// Partial definition only:
+typedef enum _PROCESSINFOCLASS {
+ ProcessBasicInformation = 0,
+ ProcessExecuteFlags = 0x22
+} PROCESSINFOCLASS;
+
+// For the structure documentation, see
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813741(v=vs.85).aspx
+typedef struct _RTL_USER_PROCESS_PARAMETERS {
+ BYTE Reserved1[16];
+ PVOID Reserved2[10];
+ UNICODE_STRING ImagePathName;
+ UNICODE_STRING CommandLine;
+} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
+
+// Partial definition only, from
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx
+typedef struct _PEB {
+ BYTE InheritedAddressSpace;
+ BYTE ReadImageFileExecOptions;
+ BYTE BeingDebugged;
+ BYTE SpareBool;
+ PVOID Mutant;
+ PVOID ImageBaseAddress;
+ PVOID Ldr;
+ PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
+} PEB, *PPEB;
+
+typedef LONG KPRIORITY;
+
+typedef struct _PROCESS_BASIC_INFORMATION {
+ union {
+ NTSTATUS ExitStatus;
+ PVOID padding_for_x64_0;
+ };
+ PPEB PebBaseAddress;
+ KAFFINITY AffinityMask;
+ union {
+ KPRIORITY BasePriority;
+ PVOID padding_for_x64_1;
+ };
+ union {
+ DWORD UniqueProcessId;
+ PVOID padding_for_x64_2;
+ };
+ union {
+ DWORD InheritedFromUniqueProcessId;
+ PVOID padding_for_x64_3;
+ };
+} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQueryInformationProcessFunction)(
+ IN HANDLE ProcessHandle,
+ IN PROCESSINFOCLASS ProcessInformationClass,
+ OUT PVOID ProcessInformation,
+ IN ULONG ProcessInformationLength,
+ OUT PULONG ReturnLength OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtSetInformationProcessFunction)(
+ HANDLE ProcessHandle,
+ IN PROCESSINFOCLASS ProcessInformationClass,
+ IN PVOID ProcessInformation,
+ IN ULONG ProcessInformationLength);
+
+typedef NTSTATUS(WINAPI* NtOpenThreadTokenFunction)(IN HANDLE ThreadHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ IN BOOLEAN OpenAsSelf,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtOpenThreadTokenExFunction)(IN HANDLE ThreadHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ IN BOOLEAN OpenAsSelf,
+ IN ULONG HandleAttributes,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtOpenProcessTokenFunction)(IN HANDLE ProcessHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtOpenProcessTokenExFunction)(
+ IN HANDLE ProcessHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG HandleAttributes,
+ OUT PHANDLE TokenHandle);
+
+typedef NTSTATUS(WINAPI* NtQueryInformationTokenFunction)(
+ IN HANDLE TokenHandle,
+ IN TOKEN_INFORMATION_CLASS TokenInformationClass,
+ OUT PVOID TokenInformation,
+ IN ULONG TokenInformationLength,
+ OUT PULONG ReturnLength);
+
+typedef NTSTATUS(WINAPI* RtlCreateUserThreadFunction)(
+ IN HANDLE Process,
+ IN PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
+ IN BOOLEAN CreateSuspended,
+ IN ULONG ZeroBits,
+ IN SIZE_T MaximumStackSize,
+ IN SIZE_T CommittedStackSize,
+ IN LPTHREAD_START_ROUTINE StartAddress,
+ IN PVOID Parameter,
+ OUT PHANDLE Thread,
+ OUT PCLIENT_ID ClientId);
+
+typedef NTSTATUS(WINAPI* RtlConvertSidToUnicodeStringFunction)(
+ OUT PUNICODE_STRING UnicodeString,
+ IN PSID Sid,
+ IN BOOLEAN AllocateDestinationString);
+
+typedef VOID(WINAPI* RtlFreeUnicodeStringFunction)(
+ IN OUT PUNICODE_STRING UnicodeString);
+
+// -----------------------------------------------------------------------
+// Registry
+
+typedef enum _KEY_INFORMATION_CLASS {
+ KeyBasicInformation = 0,
+ KeyFullInformation = 2
+} KEY_INFORMATION_CLASS,
+ *PKEY_INFORMATION_CLASS;
+
+typedef struct _KEY_BASIC_INFORMATION {
+ LARGE_INTEGER LastWriteTime;
+ ULONG TitleIndex;
+ ULONG NameLength;
+ WCHAR Name[1];
+} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;
+
+typedef struct _KEY_FULL_INFORMATION {
+ LARGE_INTEGER LastWriteTime;
+ ULONG TitleIndex;
+ ULONG ClassOffset;
+ ULONG ClassLength;
+ ULONG SubKeys;
+ ULONG MaxNameLen;
+ ULONG MaxClassLen;
+ ULONG Values;
+ ULONG MaxValueNameLen;
+ ULONG MaxValueDataLen;
+ WCHAR Class[1];
+} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION;
+
+typedef enum _KEY_VALUE_INFORMATION_CLASS {
+ KeyValueFullInformation = 1
+} KEY_VALUE_INFORMATION_CLASS,
+ *PKEY_VALUE_INFORMATION_CLASS;
+
+typedef struct _KEY_VALUE_FULL_INFORMATION {
+ ULONG TitleIndex;
+ ULONG Type;
+ ULONG DataOffset;
+ ULONG DataLength;
+ ULONG NameLength;
+ WCHAR Name[1];
+} KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtCreateKeyFunction)(OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN ULONG TitleIndex,
+ IN PUNICODE_STRING Class OPTIONAL,
+ IN ULONG CreateOptions,
+ OUT PULONG Disposition OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtOpenKeyFunction)(OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes);
+
+typedef NTSTATUS(WINAPI* NtOpenKeyExFunction)(OUT PHANDLE KeyHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES
+ ObjectAttributes,
+ IN DWORD open_options);
+
+typedef NTSTATUS(WINAPI* NtDeleteKeyFunction)(IN HANDLE KeyHandle);
+
+typedef NTSTATUS(WINAPI* RtlFormatCurrentUserKeyPathFunction)(
+ OUT PUNICODE_STRING RegistryPath);
+
+typedef NTSTATUS(WINAPI* NtQueryKeyFunction)(IN HANDLE KeyHandle,
+ IN KEY_INFORMATION_CLASS
+ KeyInformationClass,
+ OUT PVOID KeyInformation,
+ IN ULONG Length,
+ OUT PULONG ResultLength);
+
+typedef NTSTATUS(WINAPI* NtEnumerateKeyFunction)(IN HANDLE KeyHandle,
+ IN ULONG Index,
+ IN KEY_INFORMATION_CLASS
+ KeyInformationClass,
+ OUT PVOID KeyInformation,
+ IN ULONG Length,
+ OUT PULONG ResultLength);
+
+typedef NTSTATUS(WINAPI* NtQueryValueKeyFunction)(IN HANDLE KeyHandle,
+ IN PUNICODE_STRING ValueName,
+ IN KEY_VALUE_INFORMATION_CLASS
+ KeyValueInformationClass,
+ OUT PVOID KeyValueInformation,
+ IN ULONG Length,
+ OUT PULONG ResultLength);
+
+typedef NTSTATUS(WINAPI* NtSetValueKeyFunction)(IN HANDLE KeyHandle,
+ IN PUNICODE_STRING ValueName,
+ IN ULONG TitleIndex OPTIONAL,
+ IN ULONG Type,
+ IN PVOID Data,
+ IN ULONG DataSize);
+
+// -----------------------------------------------------------------------
+// Memory
+
+// Don't really need this structure right now.
+typedef PVOID PRTL_HEAP_PARAMETERS;
+
+typedef PVOID(WINAPI* RtlCreateHeapFunction)(IN ULONG Flags,
+ IN PVOID HeapBase OPTIONAL,
+ IN SIZE_T ReserveSize OPTIONAL,
+ IN SIZE_T CommitSize OPTIONAL,
+ IN PVOID Lock OPTIONAL,
+ IN PRTL_HEAP_PARAMETERS Parameters
+ OPTIONAL);
+
+typedef PVOID(WINAPI* RtlDestroyHeapFunction)(IN PVOID HeapHandle);
+
+typedef PVOID(WINAPI* RtlAllocateHeapFunction)(IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN SIZE_T Size);
+
+typedef BOOLEAN(WINAPI* RtlFreeHeapFunction)(IN PVOID HeapHandle,
+ IN ULONG Flags,
+ IN PVOID HeapBase);
+
+typedef NTSTATUS(WINAPI* NtAllocateVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN ULONG_PTR ZeroBits,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG AllocationType,
+ IN ULONG Protect);
+
+typedef NTSTATUS(WINAPI* NtFreeVirtualMemoryFunction)(IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN OUT PSIZE_T RegionSize,
+ IN ULONG FreeType);
+
+typedef enum _MEMORY_INFORMATION_CLASS {
+ MemoryBasicInformation = 0,
+ MemoryWorkingSetList,
+ MemorySectionName,
+ MemoryBasicVlmInformation
+} MEMORY_INFORMATION_CLASS;
+
+typedef struct _MEMORY_SECTION_NAME { // Information Class 2
+ UNICODE_STRING SectionFileName;
+} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
+
+typedef NTSTATUS(WINAPI* NtQueryVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress,
+ IN MEMORY_INFORMATION_CLASS MemoryInformationClass,
+ OUT PVOID MemoryInformation,
+ IN SIZE_T MemoryInformationLength,
+ OUT PSIZE_T ReturnLength OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtProtectVirtualMemoryFunction)(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID* BaseAddress,
+ IN OUT PSIZE_T ProtectSize,
+ IN ULONG NewProtect,
+ OUT PULONG OldProtect);
+
+// -----------------------------------------------------------------------
+// Objects
+
+typedef enum _OBJECT_INFORMATION_CLASS {
+ ObjectBasicInformation,
+ ObjectNameInformation,
+ ObjectTypeInformation,
+ ObjectAllInformation,
+ ObjectDataInformation
+} OBJECT_INFORMATION_CLASS,
+ *POBJECT_INFORMATION_CLASS;
+
+typedef struct _OBJDIR_INFORMATION {
+ UNICODE_STRING ObjectName;
+ UNICODE_STRING ObjectTypeName;
+ BYTE Data[1];
+} OBJDIR_INFORMATION;
+
+typedef struct _PUBLIC_OBJECT_BASIC_INFORMATION {
+ ULONG Attributes;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+ ULONG Reserved[10]; // reserved for internal use
+} PUBLIC_OBJECT_BASIC_INFORMATION, *PPUBLIC_OBJECT_BASIC_INFORMATION;
+
+typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING TypeName;
+ ULONG Reserved[22]; // reserved for internal use
+} PUBLIC_OBJECT_TYPE_INFORMATION, *PPUBLIC_OBJECT_TYPE_INFORMATION;
+
+typedef enum _POOL_TYPE {
+ NonPagedPool,
+ PagedPool,
+ NonPagedPoolMustSucceed,
+ ReservedType,
+ NonPagedPoolCacheAligned,
+ PagedPoolCacheAligned,
+ NonPagedPoolCacheAlignedMustS
+} POOL_TYPE;
+
+typedef struct _OBJECT_BASIC_INFORMATION {
+ ULONG Attributes;
+ ACCESS_MASK GrantedAccess;
+ ULONG HandleCount;
+ ULONG PointerCount;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+ ULONG Reserved[3];
+ ULONG NameInformationLength;
+ ULONG TypeInformationLength;
+ ULONG SecurityDescriptorLength;
+ LARGE_INTEGER CreateTime;
+} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;
+
+typedef struct _OBJECT_TYPE_INFORMATION {
+ UNICODE_STRING Name;
+ ULONG TotalNumberOfObjects;
+ ULONG TotalNumberOfHandles;
+ ULONG TotalPagedPoolUsage;
+ ULONG TotalNonPagedPoolUsage;
+ ULONG TotalNamePoolUsage;
+ ULONG TotalHandleTableUsage;
+ ULONG HighWaterNumberOfObjects;
+ ULONG HighWaterNumberOfHandles;
+ ULONG HighWaterPagedPoolUsage;
+ ULONG HighWaterNonPagedPoolUsage;
+ ULONG HighWaterNamePoolUsage;
+ ULONG HighWaterHandleTableUsage;
+ ULONG InvalidAttributes;
+ GENERIC_MAPPING GenericMapping;
+ ULONG ValidAccess;
+ BOOLEAN SecurityRequired;
+ BOOLEAN MaintainHandleCount;
+ USHORT MaintainTypeList;
+ POOL_TYPE PoolType;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
+
+typedef enum _SYSTEM_INFORMATION_CLASS {
+ SystemHandleInformation = 16
+} SYSTEM_INFORMATION_CLASS;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION {
+ USHORT ProcessId;
+ USHORT CreatorBackTraceIndex;
+ UCHAR ObjectTypeNumber;
+ UCHAR Flags;
+ USHORT Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
+ ULONG NumberOfHandles;
+ SYSTEM_HANDLE_INFORMATION Information[1];
+} SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;
+
+typedef struct _OBJECT_NAME_INFORMATION {
+ UNICODE_STRING ObjectName;
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
+typedef NTSTATUS(WINAPI* NtQueryObjectFunction)(
+ IN HANDLE Handle,
+ IN OBJECT_INFORMATION_CLASS ObjectInformationClass,
+ OUT PVOID ObjectInformation OPTIONAL,
+ IN ULONG ObjectInformationLength,
+ OUT PULONG ReturnLength OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtDuplicateObjectFunction)(IN HANDLE SourceProcess,
+ IN HANDLE SourceHandle,
+ IN HANDLE TargetProcess,
+ OUT PHANDLE TargetHandle,
+ IN ACCESS_MASK
+ DesiredAccess,
+ IN ULONG Attributes,
+ IN ULONG Options);
+
+typedef NTSTATUS(WINAPI* NtSignalAndWaitForSingleObjectFunction)(
+ IN HANDLE HandleToSignal,
+ IN HANDLE HandleToWait,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER Timeout OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtWaitForSingleObjectFunction)(
+ IN HANDLE ObjectHandle,
+ IN BOOLEAN Alertable,
+ IN PLARGE_INTEGER TimeOut OPTIONAL);
+
+typedef NTSTATUS(WINAPI* NtQuerySystemInformation)(
+ IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ OUT PVOID SystemInformation,
+ IN ULONG SystemInformationLength,
+ OUT PULONG ReturnLength);
+
+typedef NTSTATUS(WINAPI* NtQueryObject)(IN HANDLE Handle,
+ IN OBJECT_INFORMATION_CLASS
+ ObjectInformationClass,
+ OUT PVOID ObjectInformation,
+ IN ULONG ObjectInformationLength,
+ OUT PULONG ReturnLength);
+
+// -----------------------------------------------------------------------
+// Strings
+
+typedef int(__cdecl* _strnicmpFunction)(IN const char* _Str1,
+ IN const char* _Str2,
+ IN size_t _MaxCount);
+
+typedef size_t(__cdecl* strlenFunction)(IN const char* _Str);
+
+typedef size_t(__cdecl* wcslenFunction)(IN const wchar_t* _Str);
+
+typedef void*(__cdecl* memcpyFunction)(IN void* dest,
+ IN const void* src,
+ IN size_t count);
+
+typedef NTSTATUS(WINAPI* RtlAnsiStringToUnicodeStringFunction)(
+ IN OUT PUNICODE_STRING DestinationString,
+ IN PANSI_STRING SourceString,
+ IN BOOLEAN AllocateDestinationString);
+
+typedef LONG(WINAPI* RtlCompareUnicodeStringFunction)(
+ IN PCUNICODE_STRING String1,
+ IN PCUNICODE_STRING String2,
+ IN BOOLEAN CaseInSensitive);
+
+typedef VOID(WINAPI* RtlInitUnicodeStringFunction)(IN OUT PUNICODE_STRING
+ DestinationString,
+ IN PCWSTR SourceString);
+
+typedef ULONG(WINAPI* RtlNtStatusToDosErrorFunction)(NTSTATUS status);
+
+typedef enum _EVENT_TYPE {
+ NotificationEvent,
+ SynchronizationEvent
+} EVENT_TYPE,
+ *PEVENT_TYPE;
+
+typedef NTSTATUS(WINAPI* NtCreateDirectoryObjectFunction)(
+ PHANDLE DirectoryHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS(WINAPI* NtOpenDirectoryObjectFunction)(
+ PHANDLE DirectoryHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef NTSTATUS(WINAPI* NtQuerySymbolicLinkObjectFunction)(
+ HANDLE LinkHandle,
+ PUNICODE_STRING LinkTarget,
+ PULONG ReturnedLength);
+
+typedef NTSTATUS(WINAPI* NtOpenSymbolicLinkObjectFunction)(
+ PHANDLE LinkHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+#define DIRECTORY_QUERY 0x0001
+#define DIRECTORY_TRAVERSE 0x0002
+#define DIRECTORY_CREATE_OBJECT 0x0004
+#define DIRECTORY_CREATE_SUBDIRECTORY 0x0008
+#define DIRECTORY_ALL_ACCESS 0x000F
+
+typedef NTSTATUS(WINAPI* NtCreateLowBoxToken)(
+ OUT PHANDLE token,
+ IN HANDLE original_handle,
+ IN ACCESS_MASK access,
+ IN POBJECT_ATTRIBUTES object_attribute,
+ IN PSID appcontainer_sid,
+ IN DWORD capabilityCount,
+ IN PSID_AND_ATTRIBUTES capabilities,
+ IN DWORD handle_count,
+ IN PHANDLE handles);
+
+typedef NTSTATUS(WINAPI* NtSetInformationProcess)(IN HANDLE process_handle,
+ IN ULONG info_class,
+ IN PVOID process_information,
+ IN ULONG information_length);
+
+struct PROCESS_ACCESS_TOKEN {
+ HANDLE token;
+ HANDLE thread;
+};
+
+const unsigned int NtProcessInformationAccessToken = 9;
+
+typedef NTSTATUS(WINAPI* RtlDeriveCapabilitySidsFromNameFunction)(
+ PCUNICODE_STRING SourceString,
+ PSID CapabilityGroupSid,
+ PSID CapabilitySid);
+
+// -----------------------------------------------------------------------
+// GDI OPM API and Supported Calls
+
+#define DXGKMDT_OPM_OMAC_SIZE 16
+#define DXGKMDT_OPM_128_BIT_RANDOM_NUMBER_SIZE 16
+#define DXGKMDT_OPM_ENCRYPTED_PARAMETERS_SIZE 256
+#define DXGKMDT_OPM_CONFIGURE_SETTING_DATA_SIZE 4056
+#define DXGKMDT_OPM_GET_INFORMATION_PARAMETERS_SIZE 4056
+#define DXGKMDT_OPM_REQUESTED_INFORMATION_SIZE 4076
+#define DXGKMDT_OPM_HDCP_KEY_SELECTION_VECTOR_SIZE 5
+#define DXGKMDT_OPM_PROTECTION_TYPE_SIZE 4
+
+enum DXGKMDT_CERTIFICATE_TYPE {
+ DXGKMDT_OPM_CERTIFICATE = 0,
+ DXGKMDT_COPP_CERTIFICATE = 1,
+ DXGKMDT_UAB_CERTIFICATE = 2,
+ DXGKMDT_FORCE_ULONG = 0xFFFFFFFF
+};
+
+enum DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS {
+ DXGKMDT_OPM_VOS_COPP_SEMANTICS = 0,
+ DXGKMDT_OPM_VOS_OPM_SEMANTICS = 1
+};
+
+enum DXGKMDT_DPCP_PROTECTION_LEVEL {
+ DXGKMDT_OPM_DPCP_OFF = 0,
+ DXGKMDT_OPM_DPCP_ON = 1,
+ DXGKMDT_OPM_DPCP_FORCE_ULONG = 0x7fffffff
+};
+
+enum DXGKMDT_OPM_HDCP_PROTECTION_LEVEL {
+ DXGKMDT_OPM_HDCP_OFF = 0,
+ DXGKMDT_OPM_HDCP_ON = 1,
+ DXGKMDT_OPM_HDCP_FORCE_ULONG = 0x7fffffff
+};
+
+enum DXGKMDT_OPM_HDCP_FLAG {
+ DXGKMDT_OPM_HDCP_FLAG_NONE = 0x00,
+ DXGKMDT_OPM_HDCP_FLAG_REPEATER = 0x01
+};
+
+enum DXGKMDT_OPM_PROTECTION_TYPE {
+ DXGKMDT_OPM_PROTECTION_TYPE_OTHER = 0x80000000,
+ DXGKMDT_OPM_PROTECTION_TYPE_NONE = 0x00000000,
+ DXGKMDT_OPM_PROTECTION_TYPE_COPP_COMPATIBLE_HDCP = 0x00000001,
+ DXGKMDT_OPM_PROTECTION_TYPE_ACP = 0x00000002,
+ DXGKMDT_OPM_PROTECTION_TYPE_CGMSA = 0x00000004,
+ DXGKMDT_OPM_PROTECTION_TYPE_HDCP = 0x00000008,
+ DXGKMDT_OPM_PROTECTION_TYPE_DPCP = 0x00000010,
+ DXGKMDT_OPM_PROTECTION_TYPE_MASK = 0x8000001F
+};
+
+typedef void* OPM_PROTECTED_OUTPUT_HANDLE;
+
+struct DXGKMDT_OPM_ENCRYPTED_PARAMETERS {
+ BYTE abEncryptedParameters[DXGKMDT_OPM_ENCRYPTED_PARAMETERS_SIZE];
+};
+
+struct DXGKMDT_OPM_OMAC {
+ BYTE abOMAC[DXGKMDT_OPM_OMAC_SIZE];
+};
+
+struct DXGKMDT_OPM_CONFIGURE_PARAMETERS {
+ DXGKMDT_OPM_OMAC omac;
+ GUID guidSetting;
+ ULONG ulSequenceNumber;
+ ULONG cbParametersSize;
+ BYTE abParameters[DXGKMDT_OPM_CONFIGURE_SETTING_DATA_SIZE];
+};
+
+struct DXGKMDT_OPM_RANDOM_NUMBER {
+ BYTE abRandomNumber[DXGKMDT_OPM_128_BIT_RANDOM_NUMBER_SIZE];
+};
+
+struct DXGKMDT_OPM_GET_INFO_PARAMETERS {
+ DXGKMDT_OPM_OMAC omac;
+ DXGKMDT_OPM_RANDOM_NUMBER rnRandomNumber;
+ GUID guidInformation;
+ ULONG ulSequenceNumber;
+ ULONG cbParametersSize;
+ BYTE abParameters[DXGKMDT_OPM_GET_INFORMATION_PARAMETERS_SIZE];
+};
+
+struct DXGKMDT_OPM_REQUESTED_INFORMATION {
+ DXGKMDT_OPM_OMAC omac;
+ ULONG cbRequestedInformationSize;
+ BYTE abRequestedInformation[DXGKMDT_OPM_REQUESTED_INFORMATION_SIZE];
+};
+
+struct DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS {
+ ULONG ulProtectionType;
+ ULONG ulProtectionLevel;
+ ULONG Reserved;
+ ULONG Reserved2;
+};
+
+struct DXGKMDT_OPM_STANDARD_INFORMATION {
+ DXGKMDT_OPM_RANDOM_NUMBER rnRandomNumber;
+ ULONG ulStatusFlags;
+ ULONG ulInformation;
+ ULONG ulReserved;
+ ULONG ulReserved2;
+};
+
+typedef NTSTATUS(WINAPI* GetSuggestedOPMProtectedOutputArraySizeFunction)(
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size);
+
+typedef NTSTATUS(WINAPI* CreateOPMProtectedOutputsFunction)(
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD output_array_size,
+ DWORD* num_in_output_array,
+ OPM_PROTECTED_OUTPUT_HANDLE* output_array);
+
+typedef NTSTATUS(WINAPI* GetCertificateFunction)(
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+typedef NTSTATUS(WINAPI* GetCertificateSizeFunction)(
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+typedef NTSTATUS(WINAPI* GetCertificateByHandleFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+typedef NTSTATUS(WINAPI* GetCertificateSizeByHandleFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+typedef NTSTATUS(WINAPI* DestroyOPMProtectedOutputFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output);
+
+typedef NTSTATUS(WINAPI* ConfigureOPMProtectedOutputFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters);
+
+typedef NTSTATUS(WINAPI* GetOPMInformationFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information);
+
+typedef NTSTATUS(WINAPI* GetOPMRandomNumberFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number);
+
+typedef NTSTATUS(WINAPI* SetOPMSigningKeyAndSequenceNumbersFunction)(
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters);
+
+#endif // SANDBOX_WIN_SRC_NT_INTERNALS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_broker.cc b/security/sandbox/chromium/sandbox/win/src/policy_broker.cc
new file mode 100644
index 0000000000..406057acbb
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_broker.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/policy_broker.h"
+
+#include <stddef.h>
+
+#include <map>
+
+#include "base/logging.h"
+#include "base/win/pe_image.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/target_process.h"
+
+// This code executes on the broker side, as a callback from the policy on the
+// target side (the child).
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+#define INIT_GLOBAL_NT(member) \
+ g_nt.member = reinterpret_cast<Nt##member##Function>( \
+ ntdll_image.GetProcAddress("Nt" #member)); \
+ if (!g_nt.member) \
+ return false
+
+#define INIT_GLOBAL_RTL(member) \
+ g_nt.member = \
+ reinterpret_cast<member##Function>(ntdll_image.GetProcAddress(#member)); \
+ if (!g_nt.member) \
+ return false
+
+bool InitGlobalNt() {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ base::win::PEImage ntdll_image(ntdll);
+
+ INIT_GLOBAL_NT(AllocateVirtualMemory);
+ INIT_GLOBAL_NT(Close);
+ INIT_GLOBAL_NT(DuplicateObject);
+ INIT_GLOBAL_NT(FreeVirtualMemory);
+ INIT_GLOBAL_NT(MapViewOfSection);
+ INIT_GLOBAL_NT(ProtectVirtualMemory);
+ INIT_GLOBAL_NT(QueryInformationProcess);
+ INIT_GLOBAL_NT(QueryObject);
+ INIT_GLOBAL_NT(QuerySection);
+ INIT_GLOBAL_NT(QueryVirtualMemory);
+ INIT_GLOBAL_NT(UnmapViewOfSection);
+ INIT_GLOBAL_NT(SignalAndWaitForSingleObject);
+ INIT_GLOBAL_NT(WaitForSingleObject);
+
+ INIT_GLOBAL_RTL(RtlAllocateHeap);
+ INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString);
+ INIT_GLOBAL_RTL(RtlCompareUnicodeString);
+ INIT_GLOBAL_RTL(RtlCreateHeap);
+ INIT_GLOBAL_RTL(RtlCreateUserThread);
+ INIT_GLOBAL_RTL(RtlDestroyHeap);
+ INIT_GLOBAL_RTL(RtlFreeHeap);
+ INIT_GLOBAL_RTL(_strnicmp);
+ INIT_GLOBAL_RTL(strlen);
+ INIT_GLOBAL_RTL(wcslen);
+ INIT_GLOBAL_RTL(memcpy);
+
+ return true;
+}
+
+bool SetupNtdllImports(TargetProcess* child) {
+ if (!InitGlobalNt()) {
+ return false;
+ }
+
+#ifndef NDEBUG
+ // Verify that the structure is fully initialized.
+ for (size_t i = 0; i < sizeof(g_nt) / sizeof(void*); i++)
+ DCHECK(reinterpret_cast<char**>(&g_nt)[i]);
+#endif
+ return (SBOX_ALL_OK == child->TransferVariable("g_nt", &g_nt, sizeof(g_nt)));
+}
+
+#undef INIT_GLOBAL_NT
+#undef INIT_GLOBAL_RTL
+
+bool SetupBasicInterceptions(InterceptionManager* manager,
+ bool is_csrss_connected) {
+ // Interceptions provided by process_thread_policy, without actual policy.
+ if (!INTERCEPT_NT(manager, NtOpenThread, OPEN_THREAD_ID, 20) ||
+ !INTERCEPT_NT(manager, NtOpenProcess, OPEN_PROCESS_ID, 20) ||
+ !INTERCEPT_NT(manager, NtOpenProcessToken, OPEN_PROCESS_TOKEN_ID, 16))
+ return false;
+
+ // Interceptions with neither policy nor IPC.
+ if (!INTERCEPT_NT(manager, NtSetInformationThread, SET_INFORMATION_THREAD_ID,
+ 20) ||
+ !INTERCEPT_NT(manager, NtOpenThreadToken, OPEN_THREAD_TOKEN_ID, 20))
+ return false;
+
+ // This one is also provided by process_thread_policy.
+ if (!INTERCEPT_NT(manager, NtOpenProcessTokenEx, OPEN_PROCESS_TOKEN_EX_ID,
+ 20))
+ return false;
+
+ if (!INTERCEPT_NT(manager, NtOpenThreadTokenEx, OPEN_THREAD_TOKEN_EX_ID, 24))
+ return false;
+
+ if (!is_csrss_connected) {
+ if (!INTERCEPT_EAT(manager, kKerneldllName, CreateThread, CREATE_THREAD_ID,
+ 28))
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_broker.h b/security/sandbox/chromium/sandbox/win/src/policy_broker.h
new file mode 100644
index 0000000000..95b123bd0d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_broker.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2010 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_POLICY_BROKER_H_
+#define SANDBOX_SRC_POLICY_BROKER_H_
+
+#include "sandbox/win/src/interception.h"
+
+namespace sandbox {
+
+class TargetProcess;
+
+// Initializes global imported symbols from ntdll.
+bool InitGlobalNt();
+
+// Sets up interceptions not controlled by explicit policies.
+bool SetupBasicInterceptions(InterceptionManager* manager,
+ bool is_csrss_connected);
+
+// Sets up imports from NTDLL for the given target process so the interceptions
+// can work.
+bool SetupNtdllImports(TargetProcess* child);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_BROKER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc
new file mode 100644
index 0000000000..02c636f4aa
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.cc
@@ -0,0 +1,450 @@
+// Copyright (c) 2006-2008 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/policy_engine_opcodes.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace {
+const unsigned short kMaxUniStrSize = 0xfffc / sizeof(wchar_t);
+
+bool InitStringUnicode(const wchar_t* source,
+ size_t length,
+ UNICODE_STRING* ustring) {
+ if (length > kMaxUniStrSize) {
+ return false;
+ }
+ ustring->Buffer = const_cast<wchar_t*>(source);
+ ustring->Length = static_cast<USHORT>(length) * sizeof(wchar_t);
+ ustring->MaximumLength = source ? ustring->Length + sizeof(wchar_t) : 0;
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Note: The opcodes are implemented as functions (as opposed to classes derived
+// from PolicyOpcode) because you should not add more member variables to the
+// PolicyOpcode class since it would cause object slicing on the target. So to
+// enforce that (instead of just trusting the developer) the opcodes became
+// just functions.
+//
+// In the code that follows I have keep the evaluation function and the factory
+// function together to stress the close relationship between both. For example,
+// only the factory method and the evaluation function know the stored argument
+// order and meaning.
+
+template <int>
+EvalResult OpcodeEval(PolicyOpcode* opcode,
+ const ParameterSet* pp,
+ MatchContext* match);
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAlwaysFalse:
+// Does not require input parameter.
+
+PolicyOpcode* OpcodeFactory::MakeOpAlwaysFalse(uint32_t options) {
+ return MakeBase(OP_ALWAYS_FALSE, options, -1);
+}
+
+template <>
+EvalResult OpcodeEval<OP_ALWAYS_FALSE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ return EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAlwaysTrue:
+// Does not require input parameter.
+
+PolicyOpcode* OpcodeFactory::MakeOpAlwaysTrue(uint32_t options) {
+ return MakeBase(OP_ALWAYS_TRUE, options, -1);
+}
+
+template <>
+EvalResult OpcodeEval<OP_ALWAYS_TRUE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ return EVAL_TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpAction:
+// Does not require input parameter.
+// Argument 0 contains the actual action to return.
+
+PolicyOpcode* OpcodeFactory::MakeOpAction(EvalResult action, uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_ACTION, options, -1);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, action);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_ACTION>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ int action = 0;
+ opcode->GetArgument(0, &action);
+ return static_cast<EvalResult>(action);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpNumberMatch:
+// Requires a uint32_t or void* in selected_param
+// Argument 0 is the stored number to match.
+// Argument 1 is the C++ type of the 0th argument.
+
+PolicyOpcode* OpcodeFactory::MakeOpNumberMatch(int16_t selected_param,
+ uint32_t match,
+ uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, match);
+ opcode->SetArgument(1, UINT32_TYPE);
+ return opcode;
+}
+
+PolicyOpcode* OpcodeFactory::MakeOpVoidPtrMatch(int16_t selected_param,
+ const void* match,
+ uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, match);
+ opcode->SetArgument(1, VOIDPTR_TYPE);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ uint32_t value_uint32 = 0;
+ if (param->Get(&value_uint32)) {
+ uint32_t match_uint32 = 0;
+ opcode->GetArgument(0, &match_uint32);
+ return (match_uint32 != value_uint32) ? EVAL_FALSE : EVAL_TRUE;
+ } else {
+ const void* value_ptr = nullptr;
+ if (param->Get(&value_ptr)) {
+ const void* match_ptr = nullptr;
+ opcode->GetArgument(0, &match_ptr);
+ return (match_ptr != value_ptr) ? EVAL_FALSE : EVAL_TRUE;
+ }
+ }
+ return EVAL_ERROR;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpNumberMatchRange
+// Requires a uint32_t in selected_param.
+// Argument 0 is the stored lower bound to match.
+// Argument 1 is the stored upper bound to match.
+
+PolicyOpcode* OpcodeFactory::MakeOpNumberMatchRange(int16_t selected_param,
+ uint32_t lower_bound,
+ uint32_t upper_bound,
+ uint32_t options) {
+ if (lower_bound > upper_bound) {
+ return nullptr;
+ }
+ PolicyOpcode* opcode =
+ MakeBase(OP_NUMBER_MATCH_RANGE, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, lower_bound);
+ opcode->SetArgument(1, upper_bound);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_MATCH_RANGE>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ uint32_t value = 0;
+ if (!param->Get(&value))
+ return EVAL_ERROR;
+
+ uint32_t lower_bound = 0;
+ uint32_t upper_bound = 0;
+ opcode->GetArgument(0, &lower_bound);
+ opcode->GetArgument(1, &upper_bound);
+ return ((lower_bound <= value) && (upper_bound >= value)) ? EVAL_TRUE
+ : EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpNumberAndMatch:
+// Requires a uint32_t in selected_param.
+// Argument 0 is the stored number to match.
+
+PolicyOpcode* OpcodeFactory::MakeOpNumberAndMatch(int16_t selected_param,
+ uint32_t match,
+ uint32_t options) {
+ PolicyOpcode* opcode = MakeBase(OP_NUMBER_AND_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ opcode->SetArgument(0, match);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_NUMBER_AND_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ uint32_t value = 0;
+ if (!param->Get(&value))
+ return EVAL_ERROR;
+
+ uint32_t number = 0;
+ opcode->GetArgument(0, &number);
+ return (number & value) ? EVAL_TRUE : EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode OpWStringMatch:
+// Requires a wchar_t* in selected_param.
+// Argument 0 is the byte displacement 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.
+
+PolicyOpcode* OpcodeFactory::MakeOpWStringMatch(int16_t selected_param,
+ const wchar_t* match_str,
+ int start_position,
+ StringMatchOptions match_opts,
+ uint32_t options) {
+ if (!match_str)
+ return nullptr;
+ if ('\0' == match_str[0])
+ return nullptr;
+
+ int length = lstrlenW(match_str);
+
+ PolicyOpcode* opcode = MakeBase(OP_WSTRING_MATCH, options, selected_param);
+ if (!opcode)
+ return nullptr;
+ ptrdiff_t delta_str = AllocRelative(opcode, match_str, wcslen(match_str) + 1);
+ if (0 == delta_str)
+ return nullptr;
+ opcode->SetArgument(0, delta_str);
+ opcode->SetArgument(1, length);
+ opcode->SetArgument(2, start_position);
+ opcode->SetArgument(3, match_opts);
+ return opcode;
+}
+
+template <>
+EvalResult OpcodeEval<OP_WSTRING_MATCH>(PolicyOpcode* opcode,
+ const ParameterSet* param,
+ MatchContext* context) {
+ if (!context) {
+ return EVAL_ERROR;
+ }
+ const wchar_t* source_str = nullptr;
+ if (!param->Get(&source_str))
+ return EVAL_ERROR;
+
+ int start_position = 0;
+ int match_len = 0;
+ unsigned int match_opts = 0;
+ opcode->GetArgument(1, &match_len);
+ opcode->GetArgument(2, &start_position);
+ opcode->GetArgument(3, &match_opts);
+
+ const wchar_t* match_str = opcode->GetRelativeString(0);
+ // Advance the source string to the last successfully evaluated position
+ // according to the match context.
+ source_str = &source_str[context->position];
+ int source_len = static_cast<int>(g_nt.wcslen(source_str));
+
+ if (0 == source_len) {
+ // If we reached the end of the source string there is nothing we can
+ // match against.
+ return EVAL_FALSE;
+ }
+ if (match_len > source_len) {
+ // There can't be a positive match when the target string is bigger than
+ // the source string
+ return EVAL_FALSE;
+ }
+
+ BOOLEAN case_sensitive = (match_opts & CASE_INSENSITIVE) ? TRUE : FALSE;
+
+ // We have three cases, depending on the value of start_pos:
+ // Case 1. We skip N characters and compare once.
+ // Case 2: We skip to the end and compare once.
+ // Case 3: We match the first substring (if we find any).
+ if (start_position >= 0) {
+ if (kSeekToEnd == start_position) {
+ start_position = source_len - match_len;
+ } 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;
+ }
+ }
+
+ // Advance start_pos characters. Warning! this does not consider
+ // utf16 encodings (surrogate pairs) or other Unicode 'features'.
+ source_str += start_position;
+
+ // Since we skipped, lets reevaluate just the lengths again.
+ if ((match_len + start_position) > source_len) {
+ return EVAL_FALSE;
+ }
+
+ UNICODE_STRING match_ustr;
+ UNICODE_STRING source_ustr;
+ if (!InitStringUnicode(match_str, match_len, &match_ustr) ||
+ !InitStringUnicode(source_str, match_len, &source_ustr))
+ return EVAL_ERROR;
+
+ if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr,
+ case_sensitive)) {
+ // Match! update the match context.
+ context->position += start_position + match_len;
+ return EVAL_TRUE;
+ } else {
+ return EVAL_FALSE;
+ }
+ } else if (start_position < 0) {
+ UNICODE_STRING match_ustr;
+ UNICODE_STRING source_ustr;
+ if (!InitStringUnicode(match_str, match_len, &match_ustr) ||
+ !InitStringUnicode(source_str, match_len, &source_ustr))
+ return EVAL_ERROR;
+
+ do {
+ if (0 == g_nt.RtlCompareUnicodeString(&match_ustr, &source_ustr,
+ case_sensitive)) {
+ // Match! update the match context.
+ context->position += (source_ustr.Buffer - source_str) + match_len;
+ return EVAL_TRUE;
+ }
+ ++source_ustr.Buffer;
+ --source_len;
+ } while (source_len >= match_len);
+ }
+ return EVAL_FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// OpcodeMaker (other member functions).
+
+PolicyOpcode* OpcodeFactory::MakeBase(OpcodeID opcode_id,
+ uint32_t options,
+ int16_t selected_param) {
+ if (memory_size() < sizeof(PolicyOpcode))
+ return nullptr;
+
+ // Create opcode using placement-new on the buffer memory.
+ PolicyOpcode* opcode = new (memory_top_) PolicyOpcode();
+
+ // Fill in the standard fields, that every opcode has.
+ memory_top_ += sizeof(PolicyOpcode);
+ opcode->opcode_id_ = opcode_id;
+ opcode->SetOptions(options);
+ opcode->parameter_ = selected_param;
+ return opcode;
+}
+
+ptrdiff_t OpcodeFactory::AllocRelative(void* start,
+ const wchar_t* str,
+ size_t length) {
+ size_t bytes = length * sizeof(wchar_t);
+ if (memory_size() < bytes)
+ return 0;
+ memory_bottom_ -= bytes;
+ if (reinterpret_cast<UINT_PTR>(memory_bottom_) & 1) {
+ // TODO(cpu) replace this for something better.
+ ::DebugBreak();
+ }
+ memcpy(memory_bottom_, str, bytes);
+ ptrdiff_t delta = memory_bottom_ - reinterpret_cast<char*>(start);
+ return delta;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Opcode evaluation dispatchers.
+
+// This function is the one and only entry for evaluating any opcode. It is
+// in charge of applying any relevant opcode options and calling EvaluateInner
+// were the actual dispatch-by-id is made. It would seem at first glance that
+// the dispatch should be done by virtual function (vtable) calls but you have
+// to remember that the opcodes are made in the broker process and copied as
+// raw memory to the target process.
+
+EvalResult PolicyOpcode::Evaluate(const ParameterSet* call_params,
+ size_t param_count,
+ MatchContext* match) {
+ if (!call_params)
+ return EVAL_ERROR;
+ const ParameterSet* selected_param = nullptr;
+ if (parameter_ >= 0) {
+ if (static_cast<size_t>(parameter_) >= param_count) {
+ return EVAL_ERROR;
+ }
+ selected_param = &call_params[parameter_];
+ }
+ EvalResult result = EvaluateHelper(selected_param, match);
+
+ // Apply the general options regardless of the particular type of opcode.
+ if (kPolNone == options_) {
+ return result;
+ }
+
+ if (options_ & kPolNegateEval) {
+ if (EVAL_TRUE == result) {
+ result = EVAL_FALSE;
+ } else if (EVAL_FALSE == result) {
+ result = EVAL_TRUE;
+ } else if (EVAL_ERROR != result) {
+ result = EVAL_ERROR;
+ }
+ }
+ if (match) {
+ if (options_ & kPolClearContext)
+ match->Clear();
+ if (options_ & kPolUseOREval)
+ match->options = kPolUseOREval;
+ }
+ return result;
+}
+
+#define OPCODE_EVAL(op, x, y, z) \
+ case op: \
+ return OpcodeEval<op>(x, y, z)
+
+EvalResult PolicyOpcode::EvaluateHelper(const ParameterSet* parameters,
+ MatchContext* match) {
+ switch (opcode_id_) {
+ OPCODE_EVAL(OP_ALWAYS_FALSE, this, parameters, match);
+ OPCODE_EVAL(OP_ALWAYS_TRUE, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_MATCH_RANGE, this, parameters, match);
+ OPCODE_EVAL(OP_NUMBER_AND_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_WSTRING_MATCH, this, parameters, match);
+ OPCODE_EVAL(OP_ACTION, this, parameters, match);
+ default:
+ return EVAL_ERROR;
+ }
+}
+
+#undef OPCODE_EVAL
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h
new file mode 100644
index 0000000000..8e8816df1c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_opcodes.h
@@ -0,0 +1,379 @@
+// Copyright (c) 2010 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_WIN_SRC_POLICY_ENGINE_OPCODES_H_
+#define SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "sandbox/win/src/policy_engine_params.h"
+
+// The low-level policy is implemented using the concept of policy 'opcodes'.
+// An opcode is a structure that contains enough information to perform one
+// comparison against one single input parameter. For example, an opcode can
+// encode just one of the following comparison:
+//
+// - Is input parameter 3 not equal to nullptr?
+// - Does input parameter 2 start with L"c:\\"?
+// - Is input parameter 5, bit 3 is equal 1?
+//
+// Each opcode is in fact equivalent to a function invocation where all
+// the parameters are known by the opcode except one. So say you have a
+// function of this form:
+// bool fn(a, b, c, d) with 4 arguments
+//
+// Then an opcode is:
+// op(fn, b, c, d)
+// Which stores the function to call and its 3 last arguments
+//
+// Then and opcode evaluation is:
+// op.eval(a) ------------------------> fn(a,b,c,d)
+// internally calls
+//
+// The idea is that complex policy rules can be split into streams of
+// opcodes which are evaluated in sequence. The evaluation is done in
+// groups of opcodes that have N comparison opcodes plus 1 action opcode:
+//
+// [comparison 1][comparison 2]...[comparison N][action][comparison 1]...
+// ----- evaluation order----------->
+//
+// Each opcode group encodes one high-level policy rule. The rule applies
+// only if all the conditions on the group evaluate to true. The action
+// opcode contains the policy outcome for that particular rule.
+//
+// Note that this header contains the main building blocks of low-level policy
+// but not the low level policy class.
+namespace sandbox {
+
+// These are the possible policy outcomes. Note that some of them might
+// not apply and can be removed. Also note that The following values only
+// specify what to do, not how to do it and it is acceptable given specific
+// cases to ignore the policy outcome.
+enum EvalResult {
+ // Comparison opcode values:
+ EVAL_TRUE, // Opcode condition evaluated true.
+ EVAL_FALSE, // Opcode condition evaluated false.
+ EVAL_ERROR, // Opcode condition generated an error while evaluating.
+ // Action opcode values:
+ ASK_BROKER, // The target must generate an IPC to the broker. On the broker
+ // side, this means grant access to the resource.
+ DENY_ACCESS, // No access granted to the resource.
+ GIVE_READONLY, // Give readonly access to the resource.
+ GIVE_ALLACCESS, // Give full access to the resource.
+ GIVE_CACHED, // IPC is not required. Target can return a cached handle.
+ GIVE_FIRST, // TODO(cpu)
+ SIGNAL_ALARM, // Unusual activity. Generate an alarm.
+ FAKE_SUCCESS, // Do not call original function. Just return 'success'.
+ FAKE_ACCESS_DENIED, // Do not call original function. Just return 'denied'
+ // and do not do IPC.
+ TERMINATE_PROCESS, // Destroy target process. Do IPC as well.
+};
+
+// The following are the implemented opcodes.
+enum OpcodeID {
+ OP_ALWAYS_FALSE, // Evaluates to false (EVAL_FALSE).
+ OP_ALWAYS_TRUE, // Evaluates to true (EVAL_TRUE).
+ OP_NUMBER_MATCH, // Match a 32-bit integer as n == a.
+ OP_NUMBER_MATCH_RANGE, // Match a 32-bit integer as a <= n <= b.
+ OP_NUMBER_AND_MATCH, // Match using bitwise AND; as in: n & a != 0.
+ OP_WSTRING_MATCH, // Match a string for equality.
+ OP_ACTION // Evaluates to an action opcode.
+};
+
+// Options that apply to every opcode. They are specified when creating
+// each opcode using OpcodeFactory::MakeOpXXXXX() family of functions
+// Do nothing special.
+const uint32_t kPolNone = 0;
+
+// Convert EVAL_TRUE into EVAL_FALSE and vice-versa. This allows to express
+// negated conditions such as if ( a && !b).
+const uint32_t kPolNegateEval = 1;
+
+// Zero the MatchContext context structure. This happens after the opcode
+// is evaluated.
+const uint32_t kPolClearContext = 2;
+
+// Use OR when evaluating this set of opcodes. The policy evaluator by default
+// uses AND when evaluating. Very helpful when
+// used with kPolNegateEval. For example if you have a condition best expressed
+// as if(! (a && b && c)), the use of this flags allows it to be expressed as
+// if ((!a) || (!b) || (!c)).
+const uint32_t kPolUseOREval = 4;
+
+// Keeps the evaluation state between opcode evaluations. This is used
+// for string matching where the next opcode needs to continue matching
+// from the last character position from the current opcode. The match
+// context is preserved across opcode evaluation unless an opcode specifies
+// as an option kPolClearContext.
+struct MatchContext {
+ size_t position;
+ uint32_t options;
+
+ MatchContext() { Clear(); }
+
+ void Clear() {
+ position = 0;
+ options = 0;
+ }
+};
+
+// Models a policy opcode; that is a condition evaluation were all the
+// arguments but one are stored in objects of this class. Use OpcodeFactory
+// to create objects of this type.
+// This class is just an implementation artifact and not exposed to the
+// API clients or visible in the intercepted service. Internally, an
+// opcode is just:
+// - An integer that identifies the actual opcode.
+// - An index to indicate which one is the input argument
+// - An array of arguments.
+// While an OO hierarchy of objects would have been a natural choice, the fact
+// that 1) this code can execute before the CRT is loaded, presents serious
+// problems in terms of guarantees about the actual state of the vtables and
+// 2) because the opcode objects are generated in the broker process, we need to
+// use plain objects. To preserve some minimal type safety templates are used
+// when possible.
+class PolicyOpcode {
+ friend class OpcodeFactory;
+
+ public:
+ // Evaluates the opcode. For a typical comparison opcode the return value
+ // is EVAL_TRUE or EVAL_FALSE. If there was an error in the evaluation the
+ // the return is EVAL_ERROR. If the opcode is an action opcode then the
+ // return can take other values such as ASK_BROKER.
+ // parameters: An array of all input parameters. This argument is normally
+ // created by the macros POLPARAMS_BEGIN() POLPARAMS_END.
+ // count: The number of parameters passed as first argument.
+ // match: The match context that is persisted across the opcode evaluation
+ // sequence.
+ EvalResult Evaluate(const ParameterSet* parameters,
+ size_t count,
+ MatchContext* match);
+
+ // Retrieves a stored argument by index. Valid index values are
+ // from 0 to < kArgumentCount.
+ template <typename T>
+ void GetArgument(size_t index, T* argument) const {
+ static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size");
+ *argument = *reinterpret_cast<const T*>(&arguments_[index].mem);
+ }
+
+ // Sets a stored argument by index. Valid index values are
+ // from 0 to < kArgumentCount.
+ template <typename T>
+ void SetArgument(size_t index, const T& argument) {
+ static_assert(sizeof(T) <= sizeof(arguments_[0]), "invalid size");
+ *reinterpret_cast<T*>(&arguments_[index].mem) = argument;
+ }
+
+ // Retrieves the actual address of a string argument. When using
+ // GetArgument() to retrieve an index that contains a string, the returned
+ // value is just an offset to the actual string.
+ // index: the stored string index. Valid values are from 0
+ // to < kArgumentCount.
+ const wchar_t* GetRelativeString(size_t index) const {
+ ptrdiff_t str_delta = 0;
+ GetArgument(index, &str_delta);
+ const char* delta = reinterpret_cast<const char*>(this) + str_delta;
+ return reinterpret_cast<const wchar_t*>(delta);
+ }
+
+ // Returns true if this opcode is an action opcode without actually
+ // evaluating it. Used to do a quick scan forward to the next opcode group.
+ bool IsAction() const { return (OP_ACTION == opcode_id_); }
+
+ // Returns the opcode type.
+ OpcodeID GetID() const { return opcode_id_; }
+
+ // Returns the stored options such as kPolNegateEval and others.
+ uint32_t GetOptions() const { return options_; }
+
+ // Sets the stored options such as kPolNegateEval.
+ void SetOptions(uint32_t options) { options_ = options; }
+
+ // Returns the parameter of the function the opcode concerns.
+ uint16_t GetParameter() const { return parameter_; }
+
+ private:
+ static const size_t kArgumentCount = 4; // The number of supported argument.
+
+ struct OpcodeArgument {
+ UINT_PTR mem;
+ };
+
+ // Better define placement new in the class instead of relying on the
+ // global definition which seems to be fubared.
+ void* operator new(size_t, void* location) { return location; }
+
+ // Helper function to evaluate the opcode. The parameters have the same
+ // meaning that in Evaluate().
+ EvalResult EvaluateHelper(const ParameterSet* parameters,
+ MatchContext* match);
+ OpcodeID opcode_id_;
+ int16_t parameter_;
+ uint32_t options_;
+ OpcodeArgument arguments_[PolicyOpcode::kArgumentCount];
+};
+
+enum StringMatchOptions {
+ CASE_SENSITIVE = 0, // Pay or Not attention to the case as defined by
+ CASE_INSENSITIVE = 1, // RtlCompareUnicodeString windows API.
+ EXACT_LENGTH = 2 // Don't do substring match. Do full string match.
+};
+
+// Opcodes that do string comparisons take a parameter that is the starting
+// position to perform the comparison so we can do substring matching. There
+// are two special values:
+//
+// Start from the current position and compare strings advancing forward until
+// a match is found if any. Similar to CRT strstr().
+const int kSeekForward = -1;
+// Perform a match with the end of the string. It only does a single comparison.
+const int kSeekToEnd = 0xfffff;
+
+// A PolicyBuffer is a variable size structure that contains all the opcodes
+// that are to be created or evaluated in sequence.
+struct PolicyBuffer {
+ size_t opcode_count;
+ PolicyOpcode opcodes[1];
+};
+
+// Helper class to create any opcode sequence. This class is normally invoked
+// only by the high level policy module or when you need to handcraft a special
+// policy.
+// The factory works by creating the opcodes using a chunk of memory given
+// in the constructor. The opcodes themselves are allocated from the beginning
+// (top) of the memory, while any string that an opcode needs is allocated from
+// the end (bottom) of the memory.
+//
+// In essence:
+//
+// low address ---> [opcode 1]
+// [opcode 2]
+// [opcode 3]
+// | | <--- memory_top_
+// | free |
+// | |
+// | | <--- memory_bottom_
+// [string 1]
+// high address --> [string 2]
+//
+// Note that this class does not keep track of the number of opcodes made and
+// it is designed to be a building block for low-level policy.
+//
+// Note that any of the MakeOpXXXXX member functions below can return nullptr on
+// failure. When that happens opcode sequence creation must be aborted.
+class OpcodeFactory {
+ public:
+ // memory: base pointer to a chunk of memory where the opcodes are created.
+ // memory_size: the size in bytes of the memory chunk.
+ OpcodeFactory(char* memory, size_t memory_size) : memory_top_(memory) {
+ memory_bottom_ = &memory_top_[memory_size];
+ }
+
+ // policy: contains the raw memory where the opcodes are created.
+ // memory_size: contains the actual size of the policy argument.
+ OpcodeFactory(PolicyBuffer* policy, size_t memory_size) {
+ memory_top_ = reinterpret_cast<char*>(&policy->opcodes[0]);
+ memory_bottom_ = &memory_top_[memory_size];
+ }
+
+ // Returns the available memory to make opcodes.
+ size_t memory_size() const {
+ DCHECK_GE(memory_bottom_, memory_top_);
+ return memory_bottom_ - memory_top_;
+ }
+
+ // Creates an OpAlwaysFalse opcode.
+ PolicyOpcode* MakeOpAlwaysFalse(uint32_t options);
+
+ // Creates an OpAlwaysFalse opcode.
+ PolicyOpcode* MakeOpAlwaysTrue(uint32_t options);
+
+ // Creates an OpAction opcode.
+ // action: The action to return when Evaluate() is called.
+ PolicyOpcode* MakeOpAction(EvalResult action, uint32_t options);
+
+ // Creates an OpNumberMatch opcode.
+ // selected_param: index of the input argument. It must be a uint32_t or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the number to compare against the selected_param.
+ PolicyOpcode* MakeOpNumberMatch(int16_t selected_param,
+ uint32_t match,
+ uint32_t options);
+
+ // Creates an OpNumberMatch opcode (void pointers are cast to numbers).
+ // selected_param: index of the input argument. It must be an void* or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the pointer numeric value to compare against selected_param.
+ PolicyOpcode* MakeOpVoidPtrMatch(int16_t selected_param,
+ const void* match,
+ uint32_t options);
+
+ // Creates an OpNumberMatchRange opcode using the memory passed in the ctor.
+ // selected_param: index of the input argument. It must be a uint32_t or the
+ // evaluation result will generate a EVAL_ERROR.
+ // lower_bound, upper_bound: the range to compare against selected_param.
+ PolicyOpcode* MakeOpNumberMatchRange(int16_t selected_param,
+ uint32_t lower_bound,
+ uint32_t upper_bound,
+ uint32_t options);
+
+ // Creates an OpWStringMatch opcode using the raw memory passed in the ctor.
+ // selected_param: index of the input argument. It must be a wide string
+ // pointer or the evaluation result will generate a EVAL_ERROR.
+ // match_str: string to compare against selected_param.
+ // start_position: when its value is from 0 to < 0x7fff it indicates an
+ // offset from the selected_param string where to perform the comparison. If
+ // the value is SeekForward then a substring search is performed. If the
+ // value is SeekToEnd the comparison is performed against the last part of
+ // the selected_param string.
+ // Note that the range in the position (0 to 0x7fff) is dictated by the
+ // current implementation.
+ // match_opts: Indicates additional matching flags. Currently CaseInsensitive
+ // is supported.
+ PolicyOpcode* MakeOpWStringMatch(int16_t selected_param,
+ const wchar_t* match_str,
+ int start_position,
+ StringMatchOptions match_opts,
+ uint32_t options);
+
+ // Creates an OpNumberAndMatch opcode using the raw memory passed in the ctor.
+ // selected_param: index of the input argument. It must be uint32_t or the
+ // evaluation result will generate a EVAL_ERROR.
+ // match: the value to bitwise AND against selected_param.
+ PolicyOpcode* MakeOpNumberAndMatch(int16_t selected_param,
+ uint32_t match,
+ uint32_t options);
+
+ private:
+ // Constructs the common part of every opcode. selected_param is the index
+ // of the input param to use when evaluating the opcode. Pass -1 in
+ // selected_param to indicate that no input parameter is required.
+ PolicyOpcode* MakeBase(OpcodeID opcode_id,
+ uint32_t options,
+ int16_t selected_param);
+
+ // 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 length);
+
+ // Points to the lowest currently available address of the memory
+ // used to make the opcodes. This pointer increments as opcodes are made.
+ char* memory_top_;
+
+ // Points to the highest currently available address of the memory
+ // used to make the opcodes. This pointer decrements as opcode strings are
+ // allocated.
+ char* memory_bottom_;
+
+ DISALLOW_COPY_AND_ASSIGN(OpcodeFactory);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_POLICY_ENGINE_OPCODES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h
new file mode 100644
index 0000000000..07fd7eac82
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_params.h
@@ -0,0 +1,190 @@
+// Copyright (c) 2006-2008 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_POLICY_ENGINE_PARAMS_H__
+#define SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__
+
+#include <stdint.h>
+
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+// This header defines the classes that allow the low level policy to select
+// the input parameters. In order to better make sense of this header is
+// recommended that you check policy_engine_opcodes.h first.
+
+namespace sandbox {
+
+// Models the set of interesting parameters of an intercepted system call
+// normally you don't create objects of this class directly, instead you
+// use the POLPARAMS_XXX macros.
+// For example, if an intercepted function has the following signature:
+//
+// NTSTATUS NtOpenFileFunction (PHANDLE FileHandle,
+// ACCESS_MASK DesiredAccess,
+// POBJECT_ATTRIBUTES ObjectAttributes,
+// PIO_STATUS_BLOCK IoStatusBlock,
+// ULONG ShareAccess,
+// ULONG OpenOptions);
+//
+// You could say that the following parameters are of interest to policy:
+//
+// POLPARAMS_BEGIN(open_params)
+// POLPARAM(DESIRED_ACCESS)
+// POLPARAM(OBJECT_NAME)
+// POLPARAM(SECURITY_DESCRIPTOR)
+// POLPARAM(IO_STATUS)
+// POLPARAM(OPEN_OPTIONS)
+// POLPARAMS_END;
+//
+// and the actual code will use this for defining the parameters:
+//
+// CountedParameterSet<open_params> p;
+// p[open_params::DESIRED_ACCESS] = ParamPickerMake(DesiredAccess);
+// p[open_params::OBJECT_NAME] =
+// ParamPickerMake(ObjectAttributes->ObjectName);
+// p[open_params::SECURITY_DESCRIPTOR] =
+// ParamPickerMake(ObjectAttributes->SecurityDescriptor);
+// p[open_params::IO_STATUS] = ParamPickerMake(IoStatusBlock);
+// p[open_params::OPEN_OPTIONS] = ParamPickerMake(OpenOptions);
+//
+// These will create an stack-allocated array of ParameterSet objects which
+// have each 1) the address of the parameter 2) a numeric id that encodes the
+// original C++ type. This allows the policy to treat any set of supported
+// argument types uniformily and with some type safety.
+//
+// TODO(cpu): support not fully implemented yet for unicode string and will
+// probably add other types as well.
+class ParameterSet {
+ public:
+ ParameterSet() : real_type_(INVALID_TYPE), address_(nullptr) {}
+
+ // Retrieve the stored parameter. If the type does not match ulong fail.
+ bool Get(uint32_t* destination) const {
+ if (real_type_ != UINT32_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<uint32_t>();
+ return true;
+ }
+
+ // Retrieve the stored parameter. If the type does not match void* fail.
+ bool Get(const void** destination) const {
+ if (real_type_ != VOIDPTR_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<void*>();
+ return true;
+ }
+
+ // Retrieve the stored parameter. If the type does not match wchar_t* fail.
+ bool Get(const wchar_t** destination) const {
+ if (real_type_ != WCHAR_TYPE) {
+ return false;
+ }
+ *destination = Void2TypePointerCopy<const wchar_t*>();
+ return true;
+ }
+
+ // False if the parameter is not properly initialized.
+ bool IsValid() const { return real_type_ != INVALID_TYPE; }
+
+ protected:
+ // The constructor can only be called by derived types, which should
+ // safely provide the real_type and the address of the argument.
+ ParameterSet(ArgType real_type, const void* address)
+ : real_type_(real_type), address_(address) {}
+
+ private:
+ // This template provides the same functionality as bits_cast but
+ // it works with pointer while the former works only with references.
+ template <typename T>
+ T Void2TypePointerCopy() const {
+ return *(reinterpret_cast<const T*>(address_));
+ }
+
+ ArgType real_type_;
+ const void* address_;
+};
+
+// To safely infer the type, we use a set of template specializations
+// in ParameterSetEx with a template function ParamPickerMake to do the
+// parameter type deduction.
+
+// Base template class. Not implemented so using unsupported types should
+// fail to compile.
+template <typename T>
+class ParameterSetEx : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address);
+};
+
+template <>
+class ParameterSetEx<void const*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(VOIDPTR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<void*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(VOIDPTR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<wchar_t*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(WCHAR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<wchar_t const*> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(WCHAR_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<uint32_t> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(UINT32_TYPE, address) {}
+};
+
+template <>
+class ParameterSetEx<UNICODE_STRING> : public ParameterSet {
+ public:
+ ParameterSetEx(const void* address) : ParameterSet(UNISTR_TYPE, address) {}
+};
+
+template <typename T>
+ParameterSet ParamPickerMake(T& parameter) {
+ return ParameterSetEx<T>(&parameter);
+}
+
+struct CountedParameterSetBase {
+ size_t count;
+ ParameterSet parameters[1];
+};
+
+// This template defines the actual list of policy parameters for a given
+// interception.
+// Warning: This template stores the address to the actual variables, in
+// other words, the values are not copied.
+template <typename T>
+struct CountedParameterSet {
+ CountedParameterSet() : count(T::PolParamLast) {}
+
+ ParameterSet& operator[](typename T::Args n) { return parameters[n]; }
+
+ CountedParameterSetBase* GetBase() {
+ return reinterpret_cast<CountedParameterSetBase*>(this);
+ }
+
+ size_t count;
+ ParameterSet parameters[T::PolParamLast];
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_ENGINE_PARAMS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc
new file mode 100644
index 0000000000..07969f31a8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2006-2008 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/policy_engine_processor.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace sandbox {
+
+void PolicyProcessor::SetInternalState(size_t index, EvalResult result) {
+ state_.current_index_ = index;
+ state_.current_result_ = result;
+}
+
+EvalResult PolicyProcessor::GetAction() const {
+ return state_.current_result_;
+}
+
+// Decides if an opcode can be skipped (not evaluated) or not. The function
+// takes as inputs the opcode and the current evaluation context and returns
+// true if the opcode should be skipped or not and also can set keep_skipping
+// to false to signal that the current instruction should be skipped but not
+// the next after the current one.
+bool SkipOpcode(const PolicyOpcode& opcode,
+ MatchContext* context,
+ bool* keep_skipping) {
+ if (opcode.IsAction()) {
+ uint32_t options = context->options;
+ context->Clear();
+ *keep_skipping = false;
+ return (kPolUseOREval != options);
+ }
+ *keep_skipping = true;
+ return true;
+}
+
+PolicyResult PolicyProcessor::Evaluate(uint32_t options,
+ ParameterSet* parameters,
+ size_t param_count) {
+ if (!policy_)
+ return NO_POLICY_MATCH;
+ if (0 == policy_->opcode_count)
+ return NO_POLICY_MATCH;
+ if (!(kShortEval & options))
+ return POLICY_ERROR;
+
+ MatchContext context;
+ bool evaluation = false;
+ bool skip_group = false;
+ SetInternalState(0, EVAL_FALSE);
+ size_t count = policy_->opcode_count;
+
+ // Loop over all the opcodes Evaluating in sequence. Since we only support
+ // short circuit evaluation, we stop as soon as we find an 'action' opcode
+ // and the current evaluation is true.
+ //
+ // Skipping opcodes can happen when we are in AND mode (!kPolUseOREval) and
+ // have got EVAL_FALSE or when we are in OR mode (kPolUseOREval) and got
+ // EVAL_TRUE. Skipping will stop at the next action opcode or at the opcode
+ // after the action depending on kPolUseOREval.
+
+ for (size_t ix = 0; ix != count; ++ix) {
+ PolicyOpcode& opcode = policy_->opcodes[ix];
+ // Skipping block.
+ if (skip_group) {
+ if (SkipOpcode(opcode, &context, &skip_group))
+ continue;
+ }
+ // Evaluation block.
+ EvalResult result = opcode.Evaluate(parameters, param_count, &context);
+ switch (result) {
+ case EVAL_FALSE:
+ evaluation = false;
+ if (kPolUseOREval != context.options)
+ skip_group = true;
+ break;
+ case EVAL_ERROR:
+ if (kStopOnErrors & options)
+ return POLICY_ERROR;
+ break;
+ case EVAL_TRUE:
+ evaluation = true;
+ if (kPolUseOREval == context.options)
+ skip_group = true;
+ break;
+ default:
+ // We have evaluated an action.
+ SetInternalState(ix, result);
+ return POLICY_MATCH;
+ }
+ }
+
+ if (evaluation) {
+ // Reaching the end of the policy with a positive evaluation is probably
+ // an error: we did not find a final action opcode?
+ return POLICY_ERROR;
+ }
+ return NO_POLICY_MATCH;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h
new file mode 100644
index 0000000000..b62973b14a
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_processor.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2010 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_POLICY_ENGINE_PROCESSOR_H__
+#define SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_engine_params.h"
+
+namespace sandbox {
+
+// This header contains the core policy evaluator. In its simplest form
+// it evaluates a stream of opcodes assuming that they are laid out in
+// memory as opcode groups.
+//
+// An opcode group has N comparison opcodes plus 1 action opcode. For
+// example here we have 3 opcode groups (A, B,C):
+//
+// [comparison 1] <-- group A start
+// [comparison 2]
+// [comparison 3]
+// [action A ]
+// [comparison 1] <-- group B start
+// [action B ]
+// [comparison 1] <-- group C start
+// [comparison 2]
+// [action C ]
+//
+// The opcode evaluator proceeds from the top, evaluating each opcode in
+// sequence. An opcode group is evaluated until the first comparison that
+// returns false. At that point the rest of the group is skipped and evaluation
+// resumes with the first comparison of the next group. When all the comparisons
+// in a group have evaluated to true and the action is reached. The group is
+// considered a matching group.
+//
+// In the 'ShortEval' mode evaluation stops when it reaches the end or the first
+// matching group. The action opcode from this group is the resulting policy
+// action.
+//
+// In the 'RankedEval' mode evaluation stops only when it reaches the end of the
+// the opcode stream. In the process all matching groups are saved and at the
+// end the 'best' group is selected (what makes the best is TBD) and the action
+// from this group is the resulting policy action.
+//
+// As explained above, the policy evaluation of a group is a logical AND of
+// the evaluation of each opcode. However an opcode can request kPolUseOREval
+// which makes the evaluation to use logical OR. Given that each opcode can
+// request its evaluation result to be negated with kPolNegateEval you can
+// achieve the negation of the total group evaluation. This means that if you
+// need to express:
+// if (!(c1 && c2 && c3))
+// You can do it by:
+// if ((!c1) || (!c2) || (!c3))
+//
+
+// Possible outcomes of policy evaluation.
+enum PolicyResult { NO_POLICY_MATCH, POLICY_MATCH, POLICY_ERROR };
+
+// Policy evaluation flags
+// TODO(cpu): implement the options kStopOnErrors & kRankedEval.
+//
+// Stop evaluating as soon as an error is encountered.
+const uint32_t kStopOnErrors = 1;
+// Ignore all non fatal opcode evaluation errors.
+const uint32_t kIgnoreErrors = 2;
+// Short-circuit evaluation: Only evaluate until opcode group that
+// evaluated to true has been found.
+const uint32_t kShortEval = 4;
+// Discussed briefly at the policy design meeting. It will evaluate
+// all rules and then return the 'best' rule that evaluated true.
+const uint32_t kRankedEval = 8;
+
+// This class evaluates a policy-opcode stream given the memory where the
+// opcodes are and an input 'parameter set'.
+//
+// This class is designed to be callable from interception points
+// as low as the NtXXXX service level (it is not currently safe, but
+// it is designed to be made safe).
+//
+// Its usage in an interception is:
+//
+// POLPARAMS_BEGIN(eval_params)
+// POLPARAM(param1)
+// POLPARAM(param2)
+// POLPARAM(param3)
+// POLPARAM(param4)
+// POLPARAM(param5)
+// POLPARAMS_END;
+//
+// PolicyProcessor pol_evaluator(policy_memory);
+// PolicyResult pr = pol_evaluator.Evaluate(ShortEval, eval_params,
+// _countof(eval_params));
+// if (NO_POLICY_MATCH == pr) {
+// EvalResult policy_action = pol_evaluator.GetAction();
+// // apply policy here...
+// }
+//
+// Where the POLPARAM() arguments are derived from the intercepted function
+// arguments, and represent all the 'interesting' policy inputs, and
+// policy_memory is a memory buffer containing the opcode stream that is the
+// relevant policy for this intercept.
+class PolicyProcessor {
+ public:
+ // policy_buffer contains opcodes made with OpcodeFactory. They are usually
+ // created in the broker process and evaluated in the target process.
+
+ // This constructor is just a variant of the previous constructor.
+ explicit PolicyProcessor(PolicyBuffer* policy) : policy_(policy) {
+ SetInternalState(0, EVAL_FALSE);
+ }
+
+ // Evaluates a policy-opcode stream. See the comments at the top of this
+ // class for more info. Returns POLICY_MATCH if a rule set was found that
+ // matches an active policy.
+ PolicyResult Evaluate(uint32_t options,
+ ParameterSet* parameters,
+ size_t parameter_count);
+
+ // If the result of Evaluate() was POLICY_MATCH, calling this function returns
+ // the recommended policy action.
+ EvalResult GetAction() const;
+
+ private:
+ struct {
+ size_t current_index_;
+ EvalResult current_result_;
+ } state_;
+
+ // Sets the currently matching action result.
+ void SetInternalState(size_t index, EvalResult result);
+
+ PolicyBuffer* policy_;
+ DISALLOW_COPY_AND_ASSIGN(PolicyProcessor);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_ENGINE_PROCESSOR_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc
new file mode 100644
index 0000000000..b8b2002108
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_engine_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2006-2008 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 <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = {
+#define POLPARAM(p) sandbox::ParamPickerMake(p),
+#define POLPARAMS_END }
+
+namespace sandbox {
+
+bool SetupNtdllImports();
+
+TEST(PolicyEngineTest, Rules1) {
+ SetupNtdllImports();
+
+ // Construct two policy rules that say:
+ //
+ // #1
+ // If the path is c:\\documents and settings\\* AND
+ // If the creation mode is 'open existing' AND
+ // If the security descriptor is null THEN
+ // Ask the broker.
+ //
+ // #2
+ // If the security descriptor is null AND
+ // If the path ends with *.txt AND
+ // If the creation mode is not 'create new' THEN
+ // return Access Denied.
+
+ enum FileCreateArgs {
+ FileNameArg,
+ CreationDispositionArg,
+ FlagsAndAttributesArg,
+ SecurityAttributes
+ };
+
+ const size_t policy_sz = 1024;
+ PolicyBuffer* policy = reinterpret_cast<PolicyBuffer*>(new char[policy_sz]);
+ OpcodeFactory opcode_maker(policy, policy_sz - 0x40);
+
+ // Add rule set #1
+ opcode_maker.MakeOpWStringMatch(FileNameArg, L"c:\\documents and settings\\",
+ 0, CASE_INSENSITIVE, kPolNone);
+ opcode_maker.MakeOpNumberMatch(CreationDispositionArg, OPEN_EXISTING,
+ kPolNone);
+ opcode_maker.MakeOpVoidPtrMatch(SecurityAttributes, nullptr, kPolNone);
+ opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+
+ // Add rule set #2
+ opcode_maker.MakeOpWStringMatch(FileNameArg, L".TXT", kSeekToEnd,
+ CASE_INSENSITIVE, kPolNone);
+ opcode_maker.MakeOpNumberMatch(CreationDispositionArg, CREATE_NEW,
+ kPolNegateEval);
+ opcode_maker.MakeOpAction(FAKE_ACCESS_DENIED, kPolNone);
+ policy->opcode_count = 7;
+
+ const wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
+ uint32_t creation_mode = OPEN_EXISTING;
+ uint32_t flags = FILE_ATTRIBUTE_NORMAL;
+ void* security_descriptor = nullptr;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename)
+ POLPARAM(creation_mode)
+ POLPARAM(flags)
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult pr;
+ PolicyProcessor pol_ev(policy);
+
+ // Test should match the first rule set.
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ // Test should still match the first rule set.
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ // Changing creation_mode such that evaluation should not match any rule.
+ creation_mode = CREATE_NEW;
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, pr);
+
+ // Changing creation_mode such that evaluation should match rule #2.
+ creation_mode = OPEN_ALWAYS;
+ pr = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, pr);
+ EXPECT_EQ(FAKE_ACCESS_DENIED, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc
new file mode 100644
index 0000000000..d987211c8b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level.cc
@@ -0,0 +1,355 @@
+// Copyright (c) 2006-2008 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/policy_low_level.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <string>
+
+namespace {
+
+// A single rule can use at most this amount of memory.
+const size_t kRuleBufferSize = 1024 * 4;
+
+// The possible states of the string matching opcode generator.
+enum {
+ PENDING_NONE,
+ PENDING_ASTERISK, // Have seen an '*' but have not generated an opcode.
+ PENDING_QMARK, // Have seen an '?' but have not generated an opcode.
+};
+
+// The category of the last character seen by the string matching opcode
+// generator.
+const uint32_t kLastCharIsNone = 0;
+const uint32_t kLastCharIsAlpha = 1;
+const uint32_t kLastCharIsWild = 2;
+const uint32_t kLastCharIsAsterisk = kLastCharIsWild + 4;
+const uint32_t kLastCharIsQuestionM = kLastCharIsWild + 8;
+
+} // namespace
+
+namespace sandbox {
+
+LowLevelPolicy::LowLevelPolicy(PolicyGlobal* policy_store)
+ : policy_store_(policy_store) {}
+
+// Adding a rule is nothing more than pushing it into an stl container. Done()
+// is called for the rule in case the code that made the rule in the first
+// place has not done it.
+bool LowLevelPolicy::AddRule(IpcTag service, PolicyRule* rule) {
+ if (!rule->Done()) {
+ return false;
+ }
+
+ PolicyRule* local_rule = new PolicyRule(*rule);
+ RuleNode node = {local_rule, service};
+ rules_.push_back(node);
+ return true;
+}
+
+LowLevelPolicy::~LowLevelPolicy() {
+ // Delete all the rules.
+ typedef std::list<RuleNode> RuleNodes;
+ for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
+ delete it->rule;
+ }
+}
+
+// Here is where the heavy byte shuffling is done. We take all the rules and
+// 'compile' them into a single memory region. Now, the rules are in random
+// order so the first step is to reorganize them into a stl map that is keyed
+// by the service id and as a value contains a list with all the rules that
+// belong to that service. Then we enter the big for-loop where we carve a
+// memory zone for the opcodes and the data and call RebindCopy on each rule
+// so they all end up nicely packed in the policy_store_.
+bool LowLevelPolicy::Done() {
+ typedef std::list<RuleNode> RuleNodes;
+ typedef std::list<const PolicyRule*> RuleList;
+ typedef std::map<IpcTag, RuleList> Mmap;
+ Mmap mmap;
+
+ for (RuleNodes::iterator it = rules_.begin(); it != rules_.end(); ++it) {
+ mmap[it->service].push_back(it->rule);
+ }
+
+ PolicyBuffer* current_buffer = &policy_store_->data[0];
+ char* buffer_end =
+ reinterpret_cast<char*>(current_buffer) + policy_store_->data_size;
+ size_t avail_size = policy_store_->data_size;
+
+ for (Mmap::iterator it = mmap.begin(); it != mmap.end(); ++it) {
+ IpcTag service = (*it).first;
+ if (static_cast<size_t>(service) >= kMaxServiceCount) {
+ return false;
+ }
+ policy_store_->entry[static_cast<size_t>(service)] = current_buffer;
+
+ RuleList::iterator rules_it = (*it).second.begin();
+ RuleList::iterator rules_it_end = (*it).second.end();
+
+ size_t svc_opcode_count = 0;
+
+ for (; rules_it != rules_it_end; ++rules_it) {
+ const PolicyRule* rule = (*rules_it);
+ size_t op_count = rule->GetOpcodeCount();
+
+ size_t opcodes_size = op_count * sizeof(PolicyOpcode);
+ if (avail_size < opcodes_size) {
+ return false;
+ }
+ size_t data_size = avail_size - opcodes_size;
+ PolicyOpcode* opcodes_start = &current_buffer->opcodes[svc_opcode_count];
+ if (!rule->RebindCopy(opcodes_start, opcodes_size, buffer_end,
+ &data_size)) {
+ return false;
+ }
+ size_t used = avail_size - data_size;
+ buffer_end -= used;
+ avail_size -= used;
+ svc_opcode_count += op_count;
+ }
+
+ current_buffer->opcode_count = svc_opcode_count;
+ size_t policy_buffers_occupied =
+ (svc_opcode_count * sizeof(PolicyOpcode)) / sizeof(current_buffer[0]);
+ current_buffer = &current_buffer[policy_buffers_occupied + 1];
+ }
+
+ return true;
+}
+
+PolicyRule::PolicyRule(EvalResult action) : action_(action), done_(false) {
+ char* memory = new char[sizeof(PolicyBuffer) + kRuleBufferSize];
+ buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
+ buffer_->opcode_count = 0;
+ opcode_factory_ =
+ new OpcodeFactory(buffer_, kRuleBufferSize + sizeof(PolicyOpcode));
+}
+
+PolicyRule::PolicyRule(const PolicyRule& other) {
+ if (this == &other)
+ return;
+ action_ = other.action_;
+ done_ = other.done_;
+ size_t buffer_size = sizeof(PolicyBuffer) + kRuleBufferSize;
+ char* memory = new char[buffer_size];
+ buffer_ = reinterpret_cast<PolicyBuffer*>(memory);
+ memcpy(buffer_, other.buffer_, buffer_size);
+
+ char* opcode_buffer = reinterpret_cast<char*>(&buffer_->opcodes[0]);
+ char* next_opcode = &opcode_buffer[GetOpcodeCount() * sizeof(PolicyOpcode)];
+ opcode_factory_ =
+ new OpcodeFactory(next_opcode, other.opcode_factory_->memory_size());
+}
+
+// This function get called from a simple state machine implemented in
+// AddStringMatch() which passes the current state (in state) and it passes
+// true in last_call if AddStringMatch() has finished processing the input
+// pattern string and this would be the last call to generate any pending
+// opcode. The skip_count is the currently accumulated number of '?' seen so
+// far and once the associated opcode is generated this function sets it back
+// to zero.
+bool PolicyRule::GenStringOpcode(RuleType rule_type,
+ StringMatchOptions match_opts,
+ uint16_t parameter,
+ int state,
+ bool last_call,
+ int* skip_count,
+ std::wstring* fragment) {
+ // The last opcode must:
+ // 1) Always clear the context.
+ // 2) Preserve the negation.
+ // 3) Remove the 'OR' mode flag.
+ uint32_t options = kPolNone;
+ if (last_call) {
+ if (IF_NOT == rule_type) {
+ options = kPolClearContext | kPolNegateEval;
+ } else {
+ options = kPolClearContext;
+ }
+ } else if (IF_NOT == rule_type) {
+ options = kPolUseOREval | kPolNegateEval;
+ }
+
+ PolicyOpcode* op = nullptr;
+
+ // The fragment string contains the accumulated characters to match with, it
+ // never contains wildcards (unless they have been escaped) and while there
+ // is no fragment there is no new string match opcode to generate.
+ if (fragment->empty()) {
+ // There is no new opcode to generate but in the last call we have to fix
+ // the previous opcode because it was really the last but we did not know
+ // it at that time.
+ if (last_call && (buffer_->opcode_count > 0)) {
+ op = &buffer_->opcodes[buffer_->opcode_count - 1];
+ op->SetOptions(options);
+ }
+ return true;
+ }
+
+ if (PENDING_ASTERISK == state) {
+ if (last_call) {
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
+ kSeekToEnd, match_opts, options);
+ } else {
+ op = opcode_factory_->MakeOpWStringMatch(
+ parameter, fragment->c_str(), kSeekForward, match_opts, options);
+ }
+
+ } else if (PENDING_QMARK == state) {
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(),
+ *skip_count, match_opts, options);
+ *skip_count = 0;
+ } else {
+ if (last_call) {
+ match_opts = static_cast<StringMatchOptions>(EXACT_LENGTH | match_opts);
+ }
+ op = opcode_factory_->MakeOpWStringMatch(parameter, fragment->c_str(), 0,
+ match_opts, options);
+ }
+ if (!op)
+ return false;
+ ++buffer_->opcode_count;
+ fragment->clear();
+ return true;
+}
+
+bool PolicyRule::AddStringMatch(RuleType rule_type,
+ int16_t parameter,
+ const wchar_t* string,
+ StringMatchOptions match_opts) {
+ if (done_) {
+ // Do not allow to add more rules after generating the action opcode.
+ return false;
+ }
+
+ const wchar_t* current_char = string;
+ uint32_t last_char = kLastCharIsNone;
+ int state = PENDING_NONE;
+ int skip_count = 0; // counts how many '?' we have seen in a row.
+ std::wstring fragment; // accumulates the non-wildcard part.
+
+ while (L'\0' != *current_char) {
+ switch (*current_char) {
+ case L'*':
+ if (kLastCharIsWild & last_char) {
+ // '**' and '&*' is an error.
+ return false;
+ }
+ if (!GenStringOpcode(rule_type, match_opts, parameter, state, false,
+ &skip_count, &fragment)) {
+ return false;
+ }
+ last_char = kLastCharIsAsterisk;
+ state = PENDING_ASTERISK;
+ break;
+ case L'?':
+ if (kLastCharIsAsterisk == last_char) {
+ // '*?' is an error.
+ return false;
+ }
+ if (!GenStringOpcode(rule_type, match_opts, parameter, state, false,
+ &skip_count, &fragment)) {
+ return false;
+ }
+ ++skip_count;
+ last_char = kLastCharIsQuestionM;
+ state = PENDING_QMARK;
+ break;
+ case L'/':
+ // Note: "/?" is an escaped '?'. Eat the slash and fall through.
+ if (L'?' == current_char[1]) {
+ ++current_char;
+ }
+ FALLTHROUGH;
+ default:
+ fragment += *current_char;
+ last_char = kLastCharIsAlpha;
+ }
+ ++current_char;
+ }
+
+ if (!GenStringOpcode(rule_type, match_opts, parameter, state, true,
+ &skip_count, &fragment)) {
+ return false;
+ }
+ return true;
+}
+
+bool PolicyRule::AddNumberMatch(RuleType rule_type,
+ int16_t parameter,
+ uint32_t number,
+ RuleOp comparison_op) {
+ if (done_) {
+ // Do not allow to add more rules after generating the action opcode.
+ return false;
+ }
+ uint32_t opts = (rule_type == IF_NOT) ? kPolNegateEval : kPolNone;
+
+ if (EQUAL == comparison_op) {
+ if (!opcode_factory_->MakeOpNumberMatch(parameter, number, opts))
+ return false;
+ } else if (AND == comparison_op) {
+ if (!opcode_factory_->MakeOpNumberAndMatch(parameter, number, opts))
+ return false;
+ }
+ ++buffer_->opcode_count;
+ return true;
+}
+
+bool PolicyRule::Done() {
+ if (done_) {
+ return true;
+ }
+ if (!opcode_factory_->MakeOpAction(action_, kPolNone))
+ return false;
+ ++buffer_->opcode_count;
+ done_ = true;
+ return true;
+}
+
+bool PolicyRule::RebindCopy(PolicyOpcode* opcode_start,
+ size_t opcode_size,
+ char* data_start,
+ size_t* data_size) const {
+ size_t count = buffer_->opcode_count;
+ for (size_t ix = 0; ix != count; ++ix) {
+ if (opcode_size < sizeof(PolicyOpcode)) {
+ return false;
+ }
+ PolicyOpcode& opcode = buffer_->opcodes[ix];
+ *opcode_start = opcode;
+ if (OP_WSTRING_MATCH == opcode.GetID()) {
+ // For this opcode argument 0 is a delta to the string and argument 1
+ // is the length (in chars) of the string.
+ const wchar_t* str = opcode.GetRelativeString(0);
+ size_t str_len;
+ opcode.GetArgument(1, &str_len);
+ str_len = str_len * sizeof(wchar_t);
+ if ((*data_size) < str_len) {
+ return false;
+ }
+ *data_size -= str_len;
+ data_start -= str_len;
+ memcpy(data_start, str, str_len);
+ // Recompute the string displacement
+ ptrdiff_t delta = data_start - reinterpret_cast<char*>(opcode_start);
+ opcode_start->SetArgument(0, delta);
+ }
+ ++opcode_start;
+ opcode_size -= sizeof(PolicyOpcode);
+ }
+
+ return true;
+}
+
+PolicyRule::~PolicyRule() {
+ delete[] reinterpret_cast<char*>(buffer_);
+ delete opcode_factory_;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level.h b/security/sandbox/chromium/sandbox/win/src/policy_low_level.h
new file mode 100644
index 0000000000..1586f96af9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level.h
@@ -0,0 +1,189 @@
+// Copyright (c) 2006-2008 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_POLICY_LOW_LEVEL_H__
+#define SANDBOX_SRC_POLICY_LOW_LEVEL_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <list>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_engine_params.h"
+
+// Low level policy classes.
+// Built on top of the PolicyOpcode and OpcodeFactory, the low level policy
+// provides a way to define rules on strings and numbers but it is unaware
+// of Windows specific details or how the Interceptions must be set up.
+// To use these classes you construct one or more rules and add them to the
+// LowLevelPolicy object like this:
+//
+// PolicyRule rule1(ASK_BROKER);
+// rule1.AddStringMatch(IF, 0, L"\\\\/?/?\\c:\\*Microsoft*\\*.exe", true);
+// rule1.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL);
+// rule1.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL);
+//
+// PolicyRule rule2(FAKE_SUCCESS);
+// rule2.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*", false));
+// rule2.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL));
+//
+// LowLevelPolicy policyGen(*policy_memory);
+// policyGen.AddRule(kNtCreateFileSvc, &rule1);
+// policyGen.AddRule(kNtCreateFileSvc, &rule2);
+// policyGen.Done();
+//
+// At this point (error checking omitted) the policy_memory can be copied
+// to the target process where it can be evaluated.
+
+namespace sandbox {
+
+// Defines the memory layout of the policy. This memory is filled by
+// LowLevelPolicy object.
+// For example:
+//
+// [Service 0] --points to---\
+// [Service 1] --------------|-----\
+// ...... | |
+// [Service N] | |
+// [data_size] | |
+// [Policy Buffer 0] <-------/ |
+// [opcodes of] |
+// ....... |
+// [Policy Buffer 1] <-------------/
+// [opcodes]
+// .......
+// .......
+// [Policy Buffer N]
+// [opcodes]
+// .......
+// <possibly unused space here>
+// .......
+// [opcode string ]
+// [opcode string ]
+// .......
+// [opcode string ]
+struct PolicyGlobal {
+ PolicyBuffer* entry[kMaxServiceCount];
+ size_t data_size;
+ PolicyBuffer data[1];
+};
+
+class PolicyRule;
+
+// Provides the means to collect rules into a policy store (memory)
+class LowLevelPolicy {
+ public:
+ // policy_store: must contain allocated memory and the internal
+ // size fields set to correct values.
+ explicit LowLevelPolicy(PolicyGlobal* policy_store);
+
+ // Destroys all the policy rules.
+ ~LowLevelPolicy();
+
+ // Adds a rule to be generated when Done() is called.
+ // service: The id of the service that this rule is associated with,
+ // for example the 'Open Thread' service or the "Create File" service.
+ // returns false on error.
+ bool AddRule(IpcTag service, PolicyRule* rule);
+
+ // Generates all the rules added with AddRule() into the memory area
+ // passed on the constructor. Returns false on error.
+ bool Done();
+
+ private:
+ struct RuleNode {
+ const PolicyRule* rule;
+ IpcTag service;
+ };
+ std::list<RuleNode> rules_;
+ PolicyGlobal* policy_store_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(LowLevelPolicy);
+};
+
+// There are 'if' rules and 'if not' comparisons
+enum RuleType {
+ IF = 0,
+ IF_NOT = 1,
+};
+
+// Possible comparisons for numbers
+enum RuleOp {
+ EQUAL,
+ AND,
+ RANGE // TODO(cpu): Implement this option.
+};
+
+// Provides the means to collect a set of comparisons into a single
+// rule and its associated action.
+class PolicyRule {
+ friend class LowLevelPolicy;
+
+ public:
+ explicit PolicyRule(EvalResult action);
+ PolicyRule(const PolicyRule& other);
+ ~PolicyRule();
+
+ // Adds a string comparison to the rule.
+ // rule_type: possible values are IF and IF_NOT.
+ // parameter: the expected index of the argument for this rule. For example
+ // in a 'create file' service the file name argument can be at index 0.
+ // string: is the desired matching pattern.
+ // match_opts: if the pattern matching is case sensitive or not.
+ bool AddStringMatch(RuleType rule_type,
+ int16_t parameter,
+ const wchar_t* string,
+ StringMatchOptions match_opts);
+
+ // Adds a number match comparison to the rule.
+ // rule_type: possible values are IF and IF_NOT.
+ // parameter: the expected index of the argument for this rule.
+ // number: the value to compare the input to.
+ // comparison_op: the comparison kind (equal, logical and, etc).
+ bool AddNumberMatch(RuleType rule_type,
+ int16_t parameter,
+ uint32_t number,
+ RuleOp comparison_op);
+
+ // Returns the number of opcodes generated so far.
+ size_t GetOpcodeCount() const { return buffer_->opcode_count; }
+
+ // Called when there is no more comparisons to add. Internally it generates
+ // the last opcode (the action opcode). Returns false if this operation fails.
+ bool Done();
+
+ private:
+ void operator=(const PolicyRule&);
+ // Called in a loop from AddStringMatch to generate the required string
+ // match opcodes. rule_type, match_opts and parameter are the same as
+ // in AddStringMatch.
+ bool GenStringOpcode(RuleType rule_type,
+ StringMatchOptions match_opts,
+ uint16_t parameter,
+ int state,
+ bool last_call,
+ int* skip_count,
+ std::wstring* fragment);
+
+ // Loop over all generated opcodes and copy them to increasing memory
+ // addresses from opcode_start and copy the extra data (strings usually) into
+ // decreasing addresses from data_start. Extra data is only present in the
+ // string evaluation opcodes.
+ bool RebindCopy(PolicyOpcode* opcode_start,
+ size_t opcode_size,
+ char* data_start,
+ size_t* data_size) const;
+ PolicyBuffer* buffer_;
+ OpcodeFactory* opcode_factory_;
+ EvalResult action_;
+ bool done_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_LOW_LEVEL_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc
new file mode 100644
index 0000000000..8484660096
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_low_level_unittest.cc
@@ -0,0 +1,684 @@
+// Copyright (c) 2006-2008 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/policy_low_level.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define POLPARAMS_BEGIN(x) sandbox::ParameterSet x[] = {
+#define POLPARAM(p) sandbox::ParamPickerMake(p),
+#define POLPARAMS_END }
+
+namespace sandbox {
+
+bool SetupNtdllImports();
+
+// Testing that we allow opcode generation on valid string patterns.
+TEST(PolicyEngineTest, StringPatternsOK) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\adobe\\ver??\\", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"*.tmp", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\*.doc", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddStringMatch(IF, 0, L"c:\\windows\\*", CASE_SENSITIVE));
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF, 0, L"d:\\adobe\\acrobat.exe", CASE_SENSITIVE));
+}
+
+// Testing that we signal invalid string patterns.
+TEST(PolicyEngineTest, StringPatternsBAD) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"one**two", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"**three", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"five?six*?seven", CASE_SENSITIVE));
+ EXPECT_FALSE(pr.AddStringMatch(IF, 0, L"eight?*nine", CASE_SENSITIVE));
+}
+
+// Helper function to allocate space (on the heap) for policy.
+PolicyGlobal* MakePolicyMemory() {
+ const size_t kTotalPolicySz = 4096 * 8;
+ char* mem = new char[kTotalPolicySz];
+ memset(mem, 0, kTotalPolicySz);
+ PolicyGlobal* policy = reinterpret_cast<PolicyGlobal*>(mem);
+ policy->data_size = kTotalPolicySz - sizeof(PolicyGlobal);
+ return policy;
+}
+
+// The simplest test using LowLevelPolicy it should test a single opcode which
+// does a exact string comparison.
+TEST(PolicyEngineTest, SimpleStrMatch) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF, 0, L"z:\\Directory\\domo.txt", CASE_INSENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::PING2;
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = L"Z:\\Directory\\domo.txt";
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"Z:\\Directory\\domo.txt.tmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatch) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::PING2;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatchWild1) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, SimpleIfNotStrMatchWild2) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*.txt", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\domo.bmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild1) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\Microsoft\\*", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ uint32_t access = 0;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(access) // Argument 1
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Microsoft\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\MicroNerd\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Micronesia\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, IfNotStrMatchTwoRulesWild2) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 1, 24, EQUAL));
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF_NOT, 0, L"c:\\GoogleV?\\*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 2, 66, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ const IpcTag kFakeService = IpcTag::NTCREATEFILE;
+ LowLevelPolicy policyGen(policy);
+
+ EXPECT_TRUE(policyGen.AddRule(kFakeService, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = nullptr;
+ uint32_t access = 0;
+ uint32_t sharing = 66;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(access) // Argument 1
+ POLPARAM(sharing) // Argument 2
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kFakeService)]);
+
+ filename = L"c:\\GoogleV2\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\GoogleV2\\domo.bmp";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\GoogleV23\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\GoogleV2\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Google\\domo.txt";
+ access = 24;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Micronesia\\domo.txt";
+ access = 42;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\GoogleV2\\domo.bmp";
+ access = 24;
+ sharing = 0;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+// Testing one single rule in one single service. The service is made to
+// resemble NtCreateFile.
+TEST(PolicyEngineTest, OneRuleTest) {
+ SetupNtdllImports();
+ PolicyRule pr(ASK_BROKER);
+ EXPECT_TRUE(
+ pr.AddStringMatch(IF, 0, L"c:\\*Microsoft*\\*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr.AddNumberMatch(IF_NOT, 1, CREATE_ALWAYS, EQUAL));
+ EXPECT_TRUE(pr.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+
+ const IpcTag kNtFakeCreateFile = IpcTag::NTCREATEFILE;
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* filename = L"c:\\Documents and Settings\\Microsoft\\BLAH.txt";
+ uint32_t creation_mode = OPEN_EXISTING;
+ uint32_t flags = FILE_ATTRIBUTE_NORMAL;
+ void* security_descriptor = nullptr;
+
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(creation_mode) // Argument 1
+ POLPARAM(flags) // Argument 2
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev(policy->entry[static_cast<size_t>(kNtFakeCreateFile)]);
+
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ creation_mode = CREATE_ALWAYS;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ creation_mode = OPEN_EXISTING;
+ filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Other\\Path\\Microsoft\\Another file.txt.tmp";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags = FILE_ATTRIBUTE_DEVICE;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Other\\Macrosoft\\Another file.txt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\Microsoft\\1.txt";
+ flags = FILE_ATTRIBUTE_NORMAL;
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev.GetAction());
+
+ filename = L"c:\\Microsoft\\1.ttt";
+ result = pol_ev.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+// Testing 3 rules in 3 services. Two of the services resemble File services.
+TEST(PolicyEngineTest, ThreeRulesTest) {
+ SetupNtdllImports();
+ PolicyRule pr_pipe(FAKE_SUCCESS);
+ EXPECT_TRUE(pr_pipe.AddStringMatch(IF, 0, L"\\\\/?/?\\Pipe\\Chrome.*",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 1, OPEN_EXISTING, EQUAL));
+ EXPECT_TRUE(pr_pipe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc1 = pr_pipe.GetOpcodeCount();
+ EXPECT_EQ(3u, opc1);
+
+ PolicyRule pr_dump(ASK_BROKER);
+ EXPECT_TRUE(pr_dump.AddStringMatch(IF, 0, L"\\\\/?/?\\*\\Crash Reports\\*",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 1, CREATE_ALWAYS, EQUAL));
+ EXPECT_TRUE(pr_dump.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc2 = pr_dump.GetOpcodeCount();
+ EXPECT_EQ(4u, opc2);
+
+ PolicyRule pr_winexe(SIGNAL_ALARM);
+ EXPECT_TRUE(pr_winexe.AddStringMatch(IF, 0, L"\\\\/?/?\\C:\\Windows\\*.exe",
+ CASE_INSENSITIVE));
+ EXPECT_TRUE(pr_winexe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc3 = pr_winexe.GetOpcodeCount();
+ EXPECT_EQ(3u, opc3);
+
+ PolicyRule pr_adobe(GIVE_CACHED);
+ EXPECT_TRUE(
+ pr_adobe.AddStringMatch(IF, 0, L"c:\\adobe\\ver?.?\\", CASE_SENSITIVE));
+ EXPECT_TRUE(pr_adobe.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_NORMAL, EQUAL));
+
+ size_t opc4 = pr_adobe.GetOpcodeCount();
+ EXPECT_EQ(4u, opc4);
+
+ PolicyRule pr_none(GIVE_FIRST);
+ EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_READONLY, AND));
+ EXPECT_TRUE(pr_none.AddNumberMatch(IF, 2, FILE_ATTRIBUTE_SYSTEM, AND));
+
+ size_t opc5 = pr_none.GetOpcodeCount();
+ EXPECT_EQ(2u, opc5);
+
+ PolicyGlobal* policy = MakePolicyMemory();
+
+ // These do not match the real tag values.
+ const IpcTag kNtFakeNone = static_cast<IpcTag>(4);
+ const IpcTag kNtFakeCreateFile = static_cast<IpcTag>(5);
+ const IpcTag kNtFakeOpenFile = static_cast<IpcTag>(6);
+
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_pipe));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_dump));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeCreateFile, &pr_winexe));
+
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_adobe));
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeOpenFile, &pr_pipe));
+
+ EXPECT_TRUE(policyGen.AddRule(kNtFakeNone, &pr_none));
+
+ EXPECT_TRUE(policyGen.Done());
+
+ // Inspect the policy structure manually.
+ EXPECT_FALSE(policy->entry[0]);
+ EXPECT_FALSE(policy->entry[1]);
+ EXPECT_FALSE(policy->entry[2]);
+ EXPECT_FALSE(policy->entry[3]);
+ EXPECT_TRUE(policy->entry[4]); // kNtFakeNone.
+ EXPECT_TRUE(policy->entry[5]); // kNtFakeCreateFile.
+ EXPECT_TRUE(policy->entry[6]); // kNtFakeOpenFile.
+ EXPECT_FALSE(policy->entry[7]);
+
+ // The total per service opcode counts now must take in account one
+ // extra opcode (action opcode) per rule.
+ ++opc1;
+ ++opc2;
+ ++opc3;
+ ++opc4;
+ ++opc5;
+
+ size_t tc1 = policy->entry[static_cast<size_t>(kNtFakeNone)]->opcode_count;
+ size_t tc2 =
+ policy->entry[static_cast<size_t>(kNtFakeCreateFile)]->opcode_count;
+ size_t tc3 =
+ policy->entry[static_cast<size_t>(kNtFakeOpenFile)]->opcode_count;
+
+ EXPECT_EQ(opc5, tc1);
+ EXPECT_EQ((opc1 + opc2 + opc3), tc2);
+ EXPECT_EQ((opc1 + opc4), tc3);
+
+ // Check the type of the first and last opcode of each service.
+
+ EXPECT_EQ(
+ OP_NUMBER_AND_MATCH,
+ policy->entry[static_cast<size_t>(kNtFakeNone)]->opcodes[0].GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[static_cast<size_t>(kNtFakeNone)]
+ ->opcodes[tc1 - 1]
+ .GetID());
+ EXPECT_EQ(OP_WSTRING_MATCH,
+ policy->entry[static_cast<size_t>(kNtFakeCreateFile)]
+ ->opcodes[0]
+ .GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[static_cast<size_t>(kNtFakeCreateFile)]
+ ->opcodes[tc2 - 1]
+ .GetID());
+ EXPECT_EQ(
+ OP_WSTRING_MATCH,
+ policy->entry[static_cast<size_t>(kNtFakeOpenFile)]->opcodes[0].GetID());
+ EXPECT_EQ(OP_ACTION, policy->entry[static_cast<size_t>(kNtFakeOpenFile)]
+ ->opcodes[tc3 - 1]
+ .GetID());
+
+ // Test the policy evaluation.
+
+ const wchar_t* filename = L"";
+ uint32_t creation_mode = OPEN_EXISTING;
+ uint32_t flags = FILE_ATTRIBUTE_NORMAL;
+ void* security_descriptor = nullptr;
+
+ POLPARAMS_BEGIN(params)
+ POLPARAM(filename) // Argument 0
+ POLPARAM(creation_mode) // Argument 1
+ POLPARAM(flags) // Argument 2
+ POLPARAM(security_descriptor)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor eval_CreateFile(
+ policy->entry[static_cast<size_t>(kNtFakeCreateFile)]);
+ PolicyProcessor eval_OpenFile(
+ policy->entry[static_cast<size_t>(kNtFakeOpenFile)]);
+ PolicyProcessor eval_None(policy->entry[static_cast<size_t>(kNtFakeNone)]);
+
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\c:\\Windows\\System32\\calc.exe";
+ flags = FILE_ATTRIBUTE_SYSTEM;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags += FILE_ATTRIBUTE_READONLY;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(GIVE_FIRST, eval_None.GetAction());
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ flags = FILE_ATTRIBUTE_NORMAL;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(SIGNAL_ALARM, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"c:\\adobe\\ver3.2\\temp";
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(GIVE_CACHED, eval_OpenFile.GetAction());
+
+ filename = L"c:\\adobe\\ver3.22\\temp";
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\c:\\some path\\other path\\crash reports\\some path";
+ creation_mode = CREATE_ALWAYS;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ filename = L"\\\\??\\Pipe\\Chrome.12345";
+ creation_mode = OPEN_EXISTING;
+ result = eval_CreateFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(FAKE_SUCCESS, eval_CreateFile.GetAction());
+ result = eval_None.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+ result = eval_OpenFile.Evaluate(kShortEval, params, _countof(params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(FAKE_SUCCESS, eval_OpenFile.GetAction());
+
+ delete[] reinterpret_cast<char*>(policy);
+}
+
+TEST(PolicyEngineTest, PolicyRuleCopyConstructorTwoStrings) {
+ SetupNtdllImports();
+ // Both pr_orig and pr_copy should allow hello.* but not *.txt files.
+ PolicyRule pr_orig(ASK_BROKER);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF, 0, L"hello.*", CASE_SENSITIVE));
+
+ PolicyRule pr_copy(pr_orig);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr_copy.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ LowLevelPolicy policyGen(policy);
+ EXPECT_TRUE(policyGen.AddRule(IpcTag::PING1, &pr_orig));
+ EXPECT_TRUE(policyGen.AddRule(IpcTag::PING2, &pr_copy));
+ EXPECT_TRUE(policyGen.Done());
+
+ const wchar_t* name = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(name)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev_orig(policy->entry[1]);
+ name = L"domo.txt";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_orig.GetAction());
+
+ PolicyProcessor pol_ev_copy(policy->entry[2]);
+ name = L"domo.txt";
+ result = pol_ev_copy.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_copy.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_copy.GetAction());
+}
+
+TEST(PolicyEngineTest, PolicyGenDoneCalledTwice) {
+ SetupNtdllImports();
+ // The specific rules here are not important.
+ PolicyRule pr_orig(ASK_BROKER);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF, 0, L"hello.*", CASE_SENSITIVE));
+
+ PolicyRule pr_copy(pr_orig);
+ EXPECT_TRUE(pr_orig.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+ EXPECT_TRUE(pr_copy.AddStringMatch(IF_NOT, 0, L"*.txt", CASE_SENSITIVE));
+
+ PolicyGlobal* policy = MakePolicyMemory();
+ LowLevelPolicy policyGen(policy);
+ const IpcTag tag1 = IpcTag::PING1;
+ const IpcTag tag2 = IpcTag::PING2;
+ EXPECT_TRUE(policyGen.AddRule(tag1, &pr_orig));
+ EXPECT_TRUE(policyGen.AddRule(tag2, &pr_copy));
+ EXPECT_TRUE(policyGen.Done());
+
+ // Obtain opcode counts.
+ size_t tc1 = policy->entry[static_cast<size_t>(IpcTag::PING1)]->opcode_count;
+ size_t tc2 = policy->entry[static_cast<size_t>(IpcTag::PING2)]->opcode_count;
+
+ // Call Done() again.
+ EXPECT_TRUE(policyGen.Done());
+
+ // Expect same opcode counts.
+ EXPECT_EQ(tc1,
+ policy->entry[static_cast<size_t>(IpcTag::PING1)]->opcode_count);
+ EXPECT_EQ(tc2,
+ policy->entry[static_cast<size_t>(IpcTag::PING2)]->opcode_count);
+
+ // Confirm the rules work as before.
+ const wchar_t* name = nullptr;
+ POLPARAMS_BEGIN(eval_params)
+ POLPARAM(name)
+ POLPARAMS_END;
+
+ PolicyResult result;
+ PolicyProcessor pol_ev_orig(policy->entry[1]);
+ name = L"domo.txt";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(NO_POLICY_MATCH, result);
+
+ name = L"hello.bmp";
+ result = pol_ev_orig.Evaluate(kShortEval, eval_params, _countof(eval_params));
+ EXPECT_EQ(POLICY_MATCH, result);
+ EXPECT_EQ(ASK_BROKER, pol_ev_orig.GetAction());
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc b/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc
new file mode 100644
index 0000000000..c83efcba18
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_opcodes_unittest.cc
@@ -0,0 +1,364 @@
+// Copyright (c) 2006-2008 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/policy_engine_opcodes.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#define INIT_GLOBAL_RTL(member) \
+ g_nt.member = \
+ reinterpret_cast<member##Function>(::GetProcAddress(ntdll, #member)); \
+ if (!g_nt.member) \
+ return false
+
+namespace sandbox {
+
+const size_t kOpcodeMemory = 1024;
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+bool SetupNtdllImports() {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+
+ INIT_GLOBAL_RTL(RtlAllocateHeap);
+ INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString);
+ INIT_GLOBAL_RTL(RtlCompareUnicodeString);
+ INIT_GLOBAL_RTL(RtlCreateHeap);
+ INIT_GLOBAL_RTL(RtlDestroyHeap);
+ INIT_GLOBAL_RTL(RtlFreeHeap);
+ INIT_GLOBAL_RTL(_strnicmp);
+ INIT_GLOBAL_RTL(strlen);
+ INIT_GLOBAL_RTL(wcslen);
+
+ return true;
+}
+
+TEST(PolicyEngineTest, ParameterSetTest) {
+ void* pv1 = reinterpret_cast<void*>(0x477EAA5);
+ const void* pv2 = reinterpret_cast<void*>(0x987654);
+ ParameterSet pset1 = ParamPickerMake(pv1);
+ ParameterSet pset2 = ParamPickerMake(pv2);
+
+ // Test that we can store and retrieve a void pointer:
+ const void* result1 = 0;
+ uint32_t result2 = 0;
+ EXPECT_TRUE(pset1.Get(&result1));
+ EXPECT_TRUE(pv1 == result1);
+ EXPECT_FALSE(pset1.Get(&result2));
+ EXPECT_TRUE(pset2.Get(&result1));
+ EXPECT_TRUE(pv2 == result1);
+ EXPECT_FALSE(pset2.Get(&result2));
+
+ // Test that we can store and retrieve a uint32_t:
+ uint32_t number = 12747;
+ ParameterSet pset3 = ParamPickerMake(number);
+ EXPECT_FALSE(pset3.Get(&result1));
+ EXPECT_TRUE(pset3.Get(&result2));
+ EXPECT_EQ(number, result2);
+
+ // Test that we can store and retrieve a string:
+ const wchar_t* txt = L"S231L";
+ ParameterSet pset4 = ParamPickerMake(txt);
+ const wchar_t* result3 = nullptr;
+ EXPECT_TRUE(pset4.Get(&result3));
+ EXPECT_EQ(0, wcscmp(txt, result3));
+}
+
+TEST(PolicyEngineTest, OpcodeConstraints) {
+ // Test that PolicyOpcode has no virtual functions
+ // because these objects are copied over to other processes
+ // so they cannot have vtables.
+ EXPECT_FALSE(__is_polymorphic(PolicyOpcode));
+ // Keep developers from adding smarts to the opcodes which should
+ // be pretty much a bag of bytes with a OO interface.
+ EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_copy(PolicyOpcode));
+}
+
+TEST(PolicyEngineTest, TrueFalseOpcodes) {
+ void* dummy = nullptr;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // This opcode always evaluates to true.
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_NE(nullptr, op1);
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, nullptr));
+ EXPECT_FALSE(op1->IsAction());
+
+ // This opcode always evaluates to false.
+ PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone);
+ ASSERT_NE(nullptr, op2);
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, nullptr));
+
+ // Nulls not allowed on the params.
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(nullptr, 0, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(nullptr, 1, nullptr));
+
+ // True and False opcodes do not 'require' a number of parameters
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, nullptr));
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, nullptr));
+
+ // Test Inverting the logic. Note that inversion is done outside
+ // any particular opcode evaluation so no need to repeat for all
+ // opcodes.
+ PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval);
+ ASSERT_NE(nullptr, op3);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, nullptr));
+ PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval);
+ ASSERT_NE(nullptr, op4);
+ EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, nullptr));
+
+ // Test that we clear the match context
+ PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext);
+ ASSERT_NE(nullptr, op5);
+ MatchContext context;
+ context.position = 1;
+ context.options = kPolUseOREval;
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context));
+ EXPECT_EQ(0u, context.position);
+ MatchContext context2;
+ EXPECT_EQ(context2.options, context.options);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase1) {
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ void* dummy = nullptr;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / sizeof(PolicyOpcode);
+
+ for (size_t ix = 0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_NE(nullptr, op);
+ EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, nullptr));
+ }
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_EQ(nullptr, op1);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase2) {
+ SetupNtdllImports();
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ // The difference with the previous test is that this opcodes allocate
+ // the string 'txt2' inside the same buffer.
+ const wchar_t* txt1 = L"1234";
+ const wchar_t txt2[] = L"123";
+
+ ParameterSet ppb1 = ParamPickerMake(txt1);
+ MatchContext mc1;
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2));
+
+ // Test that it does not overrun the buffer.
+ for (size_t ix = 0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(
+ 0, txt2, 0, CASE_SENSITIVE, kPolClearContext);
+ ASSERT_NE(nullptr, op);
+ EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1));
+ }
+
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 =
+ opcode_maker.MakeOpWStringMatch(0, txt2, 0, CASE_SENSITIVE, kPolNone);
+ ASSERT_EQ(nullptr, op1);
+}
+
+TEST(PolicyEngineTest, IntegerOpcodes) {
+ const wchar_t* txt = L"abcdef";
+ uint32_t num1 = 42;
+ uint32_t num2 = 113377;
+
+ ParameterSet pp_wrong1 = ParamPickerMake(txt);
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+ ParameterSet pp_num2 = ParamPickerMake(num2);
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // Test basic match for uint32s 42 == 42 and 42 != 113377.
+ PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, 42UL, kPolNone);
+ ASSERT_NE(nullptr, op_m42);
+ EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, nullptr));
+ EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, nullptr));
+
+ // Test basic match for void pointers.
+ const void* vp = nullptr;
+ ParameterSet pp_num3 = ParamPickerMake(vp);
+ PolicyOpcode* op_vp_null =
+ opcode_maker.MakeOpVoidPtrMatch(0, nullptr, kPolNone);
+ ASSERT_NE(nullptr, op_vp_null);
+ EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, nullptr));
+ EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, nullptr));
+
+ // Basic range test [41 43] (inclusive).
+ PolicyOpcode* op_range1 =
+ opcode_maker.MakeOpNumberMatchRange(0, 41, 43, kPolNone);
+ ASSERT_NE(nullptr, op_range1);
+ EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, nullptr));
+ EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, nullptr));
+ EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, nullptr));
+}
+
+TEST(PolicyEngineTest, LogicalOpcodes) {
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ uint32_t num1 = 0x10100702;
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+
+ PolicyOpcode* op_and1 =
+ opcode_maker.MakeOpNumberAndMatch(0, 0x00100000, kPolNone);
+ ASSERT_NE(nullptr, op_and1);
+ EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, nullptr));
+ PolicyOpcode* op_and2 =
+ opcode_maker.MakeOpNumberAndMatch(0, 0x00000001, kPolNone);
+ ASSERT_NE(nullptr, op_and2);
+ EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, nullptr));
+}
+
+TEST(PolicyEngineTest, WCharOpcodes1) {
+ SetupNtdllImports();
+
+ const wchar_t* txt1 = L"the quick fox jumps over the lazy dog";
+ const wchar_t txt2[] = L"the quick";
+ const wchar_t txt3[] = L" fox jumps";
+ const wchar_t txt4[] = L"the lazy dog";
+ const wchar_t txt5[] = L"jumps over";
+ const wchar_t txt6[] = L"g";
+
+ ParameterSet pp_tc1 = ParamPickerMake(txt1);
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ PolicyOpcode* op1 =
+ opcode_maker.MakeOpWStringMatch(0, txt2, 0, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op1);
+
+ // Simplest substring match from pos 0. It should be a successful match
+ // and the match context should be updated.
+ MatchContext mc1;
+ EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Matching again should fail and the context should be unmodified.
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Using the same match context we should continue where we left
+ // in the previous successful match,
+ PolicyOpcode* op3 =
+ opcode_maker.MakeOpWStringMatch(0, txt3, 0, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op3);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2);
+
+ // We now keep on matching but now we skip 6 characters which means
+ // we skip the string ' over '. And we zero the match context. This is
+ // the primitive that we use to build '??'.
+ PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(
+ 0, txt4, 6, CASE_SENSITIVE, kPolClearContext);
+ ASSERT_NE(nullptr, op4);
+ EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0u, mc1.position);
+
+ // Test that we can properly match the last part of the string
+ PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(
+ 0, txt4, kSeekToEnd, CASE_SENSITIVE, kPolClearContext);
+ ASSERT_NE(nullptr, op4b);
+ EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0u, mc1.position);
+
+ // Test matching 'jumps over' over the entire string. This is the
+ // primitive we build '*' from.
+ PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op5);
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(24u, mc1.position);
+
+ // Test that we don't match because it is not at the end of the string
+ PolicyOpcode* op5b = opcode_maker.MakeOpWStringMatch(
+ 0, txt5, kSeekToEnd, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op5b);
+ EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(24u, mc1.position);
+
+ // Test that we function if the string does not fit. In this case we
+ // try to match 'the lazy dog' against 'he lazy dog'.
+ PolicyOpcode* op6 =
+ opcode_maker.MakeOpWStringMatch(0, txt4, 2, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op6);
+ EXPECT_EQ(EVAL_FALSE, op6->Evaluate(&pp_tc1, 1, &mc1));
+
+ // Testing matching against 'g' which should be the last char.
+ MatchContext mc2;
+ PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op7);
+ EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2));
+ EXPECT_EQ(37u, mc2.position);
+
+ // Trying to match again should fail since we are in the last char.
+ // This also covers a couple of boundary conditions.
+ EXPECT_EQ(EVAL_FALSE, op7->Evaluate(&pp_tc1, 1, &mc2));
+ EXPECT_EQ(37u, mc2.position);
+}
+
+TEST(PolicyEngineTest, WCharOpcodes2) {
+ SetupNtdllImports();
+
+ const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt";
+ const wchar_t txt1[] = L"Settings\\microsoft";
+ ParameterSet pp_tc1 = ParamPickerMake(path1);
+
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+
+ // Testing case-insensitive does not buy us much since it this option
+ // is just passed to the Microsoft API that we use normally, but just for
+ // coverage, here it is:
+ PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(
+ 0, txt1, kSeekForward, CASE_SENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op1s);
+ PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(
+ 0, txt1, kSeekForward, CASE_INSENSITIVE, kPolNone);
+ ASSERT_NE(nullptr, op1i);
+ EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(35u, mc1.position);
+}
+
+TEST(PolicyEngineTest, ActionOpcodes) {
+ char memory[kOpcodeMemory];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+ void* dummy = nullptr;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+ ASSERT_NE(nullptr, op1);
+ EXPECT_TRUE(op1->IsAction());
+ EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_params.h b/security/sandbox/chromium/sandbox/win/src/policy_params.h
new file mode 100644
index 0000000000..aeec6d7edc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_params.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2006-2008 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_POLICY_PARAMS_H__
+#define SANDBOX_SRC_POLICY_PARAMS_H__
+
+#include "sandbox/win/src/policy_engine_params.h"
+
+namespace sandbox {
+
+class ParameterSet;
+
+// Warning: The following macros store the address to the actual variables, in
+// other words, the values are not copied.
+#define POLPARAMS_BEGIN(type) class type { public: enum Args {
+#define POLPARAM(arg) arg,
+#define POLPARAMS_END(type) PolParamLast }; }; \
+ typedef sandbox::ParameterSet type##Array [type::PolParamLast];
+
+// Policy parameters for file open / create.
+POLPARAMS_BEGIN(OpenFile)
+ POLPARAM(NAME)
+ POLPARAM(BROKER) // true if called from the broker.
+ POLPARAM(ACCESS)
+ POLPARAM(DISPOSITION)
+ POLPARAM(OPTIONS)
+POLPARAMS_END(OpenFile)
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(FileName)
+ POLPARAM(NAME)
+ POLPARAM(BROKER) // true if called from the broker.
+POLPARAMS_END(FileName)
+
+static_assert(OpenFile::NAME == static_cast<int>(FileName::NAME),
+ "to simplify fs policies");
+static_assert(OpenFile::BROKER == static_cast<int>(FileName::BROKER),
+ "to simplify fs policies");
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(NameBased)
+ POLPARAM(NAME)
+POLPARAMS_END(NameBased)
+
+// Policy parameters for open event.
+POLPARAMS_BEGIN(OpenEventParams)
+ POLPARAM(NAME)
+ POLPARAM(ACCESS)
+POLPARAMS_END(OpenEventParams)
+
+// Policy Parameters for reg open / create.
+POLPARAMS_BEGIN(OpenKey)
+ POLPARAM(NAME)
+ POLPARAM(ACCESS)
+POLPARAMS_END(OpenKey)
+
+// Policy parameter for name-based policies.
+POLPARAMS_BEGIN(HandleTarget)
+ POLPARAM(NAME)
+ POLPARAM(TARGET)
+POLPARAMS_END(HandleTarget)
+
+// Policy parameters where no parameter based checks are done.
+POLPARAMS_BEGIN(EmptyParams)
+POLPARAMS_END(EmptyParams)
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_POLICY_PARAMS_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target.cc b/security/sandbox/chromium/sandbox/win/src/policy_target.cc
new file mode 100644
index 0000000000..1727d622f8
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_target.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2006-2008 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/policy_target.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// Handle for our private heap.
+extern void* g_heap;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Policy data.
+extern void* volatile g_shared_policy_memory;
+SANDBOX_INTERCEPT size_t g_shared_policy_size;
+
+bool QueryBroker(IpcTag ipc_id, CountedParameterSetBase* params) {
+ DCHECK_NT(static_cast<size_t>(ipc_id) < kMaxServiceCount);
+ DCHECK_NT(g_shared_policy_memory);
+ DCHECK_NT(g_shared_policy_size > 0);
+
+ if (static_cast<size_t>(ipc_id) >= kMaxServiceCount)
+ return false;
+
+ PolicyGlobal* global_policy =
+ reinterpret_cast<PolicyGlobal*>(g_shared_policy_memory);
+
+ if (!global_policy->entry[static_cast<size_t>(ipc_id)])
+ return false;
+
+ PolicyBuffer* policy = reinterpret_cast<PolicyBuffer*>(
+ reinterpret_cast<char*>(g_shared_policy_memory) +
+ reinterpret_cast<size_t>(
+ global_policy->entry[static_cast<size_t>(ipc_id)]));
+
+ if ((reinterpret_cast<size_t>(
+ global_policy->entry[static_cast<size_t>(ipc_id)]) >
+ global_policy->data_size) ||
+ (g_shared_policy_size < global_policy->data_size)) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ for (size_t i = 0; i < params->count; i++) {
+ if (!params->parameters[i].IsValid()) {
+ NOTREACHED_NT();
+ return false;
+ }
+ }
+
+ PolicyProcessor processor(policy);
+ PolicyResult result =
+ processor.Evaluate(kShortEval, params->parameters, params->count);
+ DCHECK_NT(POLICY_ERROR != result);
+
+ return POLICY_MATCH == result && ASK_BROKER == processor.GetAction();
+}
+
+// -----------------------------------------------------------------------
+
+// Hooks NtSetInformationThread to block RevertToSelf from being
+// called before the actual call to LowerToken.
+NTSTATUS WINAPI TargetNtSetInformationThread(
+ NtSetInformationThreadFunction orig_SetInformationThread,
+ HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class,
+ PVOID thread_information,
+ ULONG thread_information_bytes) {
+ do {
+ if (SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ break;
+ if (ThreadImpersonationToken != thread_info_class)
+ break;
+ if (!thread_information)
+ break;
+ HANDLE token;
+ if (sizeof(token) > thread_information_bytes)
+ break;
+
+ NTSTATUS ret = CopyData(&token, thread_information, sizeof(token));
+ if (!NT_SUCCESS(ret) || NULL != token)
+ break;
+
+ // This is a revert to self.
+ return STATUS_SUCCESS;
+ } while (false);
+
+ return orig_SetInformationThread(
+ thread, thread_info_class, thread_information, thread_information_bytes);
+}
+
+// Hooks NtOpenThreadToken to force the open_as_self parameter to be set to
+// false if we are still running with the impersonation token. open_as_self set
+// to true means that the token will be open using the process token instead of
+// the impersonation token. This is bad because the process token does not have
+// access to open the thread token.
+NTSTATUS WINAPI
+TargetNtOpenThreadToken(NtOpenThreadTokenFunction orig_OpenThreadToken,
+ HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ PHANDLE token) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ open_as_self = false;
+
+ return orig_OpenThreadToken(thread, desired_access, open_as_self, token);
+}
+
+// See comment for TargetNtOpenThreadToken
+NTSTATUS WINAPI
+TargetNtOpenThreadTokenEx(NtOpenThreadTokenExFunction orig_OpenThreadTokenEx,
+ HANDLE thread,
+ ACCESS_MASK desired_access,
+ BOOLEAN open_as_self,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf())
+ open_as_self = false;
+
+ return orig_OpenThreadTokenEx(thread, desired_access, open_as_self,
+ handle_attributes, token);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target.h b/security/sandbox/chromium/sandbox/win/src/policy_target.h
new file mode 100644
index 0000000000..62686aaab2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_target.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2006-2008 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_WIN_SRC_POLICY_TARGET_H_
+#define SANDBOX_WIN_SRC_POLICY_TARGET_H_
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+struct CountedParameterSetBase;
+
+// Performs a policy lookup and returns true if the request should be passed to
+// the broker process.
+bool QueryBroker(IpcTag ipc_id, CountedParameterSetBase* params);
+
+extern "C" {
+
+// Interception of NtSetInformationThread on the child process.
+// It should never be called directly.
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtSetInformationThread(
+ NtSetInformationThreadFunction orig_SetInformationThread, HANDLE thread,
+ NT_THREAD_INFORMATION_CLASS thread_info_class, PVOID thread_information,
+ ULONG thread_information_bytes);
+
+// Interception of NtOpenThreadToken on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadToken(
+ NtOpenThreadTokenFunction orig_OpenThreadToken, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, PHANDLE token);
+
+// Interception of NtOpenThreadTokenEx on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenThreadTokenEx(
+ NtOpenThreadTokenExFunction orig_OpenThreadTokenEx, HANDLE thread,
+ ACCESS_MASK desired_access, BOOLEAN open_as_self, ULONG handle_attributes,
+ PHANDLE token);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_POLICY_TARGET_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc
new file mode 100644
index 0000000000..4ee03e19f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/policy_target_test.cc
@@ -0,0 +1,486 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/writable_shared_memory_region.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#include "base/win/win_util.h"
+#endif
+
+namespace sandbox {
+
+#define BINDNTDLL(name) \
+ name##Function name = reinterpret_cast<name##Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+// Reverts to self and verify that SetInformationToken was faked. Returns
+// SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t** argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+
+ ::RevertToSelf();
+ ::CloseHandle(thread_token);
+
+ int ret = SBOX_TEST_FAILED;
+ if (::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ false, &thread_token)) {
+ ret = SBOX_TEST_SUCCEEDED;
+ ::CloseHandle(thread_token);
+ }
+ return ret;
+}
+
+// Stores the high privilege token on a static variable, change impersonation
+// again to that one and verify that we are not interfering anymore with
+// RevertToSelf.
+SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t** argv) {
+ static HANDLE thread_token;
+ if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) {
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+ } else {
+ if (!::SetThreadToken(nullptr, thread_token))
+ return ::GetLastError();
+
+ // See if we fake the call again.
+ int ret = PolicyTargetTest_token(argc, argv);
+ ::CloseHandle(thread_token);
+ return ret;
+ }
+ return 0;
+}
+
+// Opens the thread token with and without impersonation.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t** argv) {
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ if (!::OpenThreadToken(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false,
+ &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ true, &thread_token))
+ return ::GetLastError();
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Opens the thread token with and without impersonation, using
+// NtOpenThreadTokenEX.
+SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t** argv) {
+ BINDNTDLL(NtOpenThreadTokenEx);
+ if (!NtOpenThreadTokenEx)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ HANDLE thread_token;
+ // Get the thread token, using impersonation.
+ NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE,
+ false, 0, &thread_token);
+ if (status == STATUS_NO_TOKEN)
+ return ERROR_NO_TOKEN;
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+
+ // Get the thread token, without impersonation.
+ status = NtOpenThreadTokenEx(GetCurrentThread(),
+ TOKEN_IMPERSONATE | TOKEN_DUPLICATE, true, 0,
+ &thread_token);
+ if (!NT_SUCCESS(status))
+ return SBOX_TEST_FAILED;
+
+ ::CloseHandle(thread_token);
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can open the current thread.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t** argv) {
+ DWORD thread_id = ::GetCurrentThreadId();
+ HANDLE thread = ::OpenThread(SYNCHRONIZE, false, thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// New thread entry point: do nothing.
+DWORD WINAPI PolicyTargetTest_thread_main(void* param) {
+ ::Sleep(INFINITE);
+ return 0;
+}
+
+// Tests that we can create a new thread, and open it.
+SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t** argv) {
+ // Use default values to create a new thread.
+ DWORD thread_id;
+ HANDLE thread = ::CreateThread(nullptr, 0, &PolicyTargetTest_thread_main, 0,
+ 0, &thread_id);
+ if (!thread)
+ return ::GetLastError();
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ thread = ::OpenThread(SYNCHRONIZE, false, thread_id);
+ if (!thread)
+ return ::GetLastError();
+
+ if (!::CloseHandle(thread))
+ return ::GetLastError();
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Tests that we can call CreateProcess.
+SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t** argv) {
+ // Use default values to create a new process.
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION temp_process_info = {};
+ // Note: CreateProcessW() can write to its lpCommandLine, don't pass a
+ // raw string literal.
+ std::wstring writable_cmdline_str(L"foo.exe");
+ if (!::CreateProcessW(L"foo.exe", &writable_cmdline_str[0], nullptr, nullptr,
+ false, 0, nullptr, nullptr, &startup_info,
+ &temp_process_info))
+ return SBOX_TEST_SUCCEEDED;
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+ return SBOX_TEST_FAILED;
+}
+
+TEST(PolicyTargetTest, SetInformationThread) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token"));
+
+ runner.SetTestState(EVERY_STATE);
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"PolicyTargetTest_steal"));
+}
+
+TEST(PolicyTargetTest, OpenThreadToken) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token2"));
+}
+
+TEST(PolicyTargetTest, OpenThreadTokenEx) {
+ TestRunner runner;
+
+ runner.SetTestState(BEFORE_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(ERROR_NO_TOKEN, runner.RunTest(L"PolicyTargetTest_token3"));
+}
+
+TEST(PolicyTargetTest, OpenThread) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread"))
+ << "Opens the current thread";
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread2"))
+ << "Creates a new thread and opens it";
+}
+
+TEST(PolicyTargetTest, OpenProcess) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process"))
+ << "Opens a process";
+}
+
+TEST(PolicyTargetTest, PolicyBaseNoJobLifetime) {
+ TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ runner.SetReleasePolicyInRun(true);
+ // TargetPolicy and its SharedMemIPCServer should continue to exist until
+ // the child process dies.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread"))
+ << "Opens the current thread";
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// desktop associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, DesktopPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ scoped_refptr<TargetPolicy> temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(false);
+ temp_policy = nullptr;
+
+ ASSERT_TRUE(broker);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ DWORD last_error = ERROR_SUCCESS;
+ base::win::ScopedProcessInformation target;
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(false);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ policy = nullptr;
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1u, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ EXPECT_TRUE(desk);
+ EXPECT_TRUE(::CloseDesktop(desk));
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy = nullptr;
+
+ // Make sure the desktop does not exist anymore.
+ desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ EXPECT_FALSE(desk);
+}
+
+// Launches the app in the sandbox and ask it to wait in an
+// infinite loop. Waits for 2 seconds and then check if the
+// winstation associated with the app thread is not the same as the
+// current desktop.
+TEST(PolicyTargetTest, WinstaPolicy) {
+ BrokerServices* broker = GetBroker();
+
+ // Precreate the desktop.
+ scoped_refptr<TargetPolicy> temp_policy = broker->CreatePolicy();
+ temp_policy->CreateAlternateDesktop(true);
+ temp_policy = nullptr;
+
+ ASSERT_TRUE(broker);
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 wait"; // Don't care about the "state" argument.
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->SetAlternateDesktop(true);
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ DWORD last_error = ERROR_SUCCESS;
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ std::wstring desktop_name = policy->GetAlternateDesktop();
+ policy = nullptr;
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1u, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_NE(::GetThreadDesktop(target.thread_id()),
+ ::GetThreadDesktop(::GetCurrentThreadId()));
+
+ ASSERT_FALSE(desktop_name.empty());
+
+ // Make sure there is a backslash, for the window station name.
+ EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos);
+
+ // Isolate the desktop name.
+ desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1);
+
+ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE);
+ // This should fail if the desktop is really on another window station.
+ EXPECT_FALSE(desk);
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+
+ // Close the desktop handle.
+ temp_policy = broker->CreatePolicy();
+ temp_policy->DestroyAlternateDesktop();
+ temp_policy = nullptr;
+}
+
+// Creates multiple policies, with alternate desktops on both local and
+// alternate winstations.
+TEST(PolicyTargetTest, BothLocalAndAlternateWinstationDesktop) {
+ BrokerServices* broker = GetBroker();
+
+ scoped_refptr<TargetPolicy> policy1 = broker->CreatePolicy();
+ scoped_refptr<TargetPolicy> policy2 = broker->CreatePolicy();
+ scoped_refptr<TargetPolicy> policy3 = broker->CreatePolicy();
+
+ ResultCode result;
+ result = policy1->SetAlternateDesktop(false);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ result = policy2->SetAlternateDesktop(true);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ result = policy3->SetAlternateDesktop(false);
+ EXPECT_EQ(SBOX_ALL_OK, result);
+
+ std::wstring policy1_desktop_name = policy1->GetAlternateDesktop();
+ std::wstring policy2_desktop_name = policy2->GetAlternateDesktop();
+
+ // Extract only the "desktop name" portion of
+ // "{winstation name}\\{desktop name}"
+ EXPECT_NE(policy1_desktop_name.substr(
+ policy1_desktop_name.find_first_of(L'\\') + 1),
+ policy2_desktop_name.substr(
+ policy2_desktop_name.find_first_of(L'\\') + 1));
+
+ policy1->DestroyAlternateDesktop();
+ policy2->DestroyAlternateDesktop();
+ policy3->DestroyAlternateDesktop();
+}
+
+// Launches the app in the sandbox and share a handle with it. The app should
+// be able to use the handle.
+TEST(PolicyTargetTest, ShareHandleTest) {
+ BrokerServices* broker = GetBroker();
+ ASSERT_TRUE(broker);
+
+ base::StringPiece contents = "Hello World";
+ base::WritableSharedMemoryRegion writable_region =
+ base::WritableSharedMemoryRegion::Create(contents.size());
+ ASSERT_TRUE(writable_region.IsValid());
+ base::WritableSharedMemoryMapping writable_mapping = writable_region.Map();
+ ASSERT_TRUE(writable_mapping.IsValid());
+ memcpy(writable_mapping.memory(), contents.data(), contents.size());
+
+ // Get the path to the sandboxed app.
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(nullptr, prog_name, MAX_PATH);
+
+ base::ReadOnlySharedMemoryRegion read_only_region =
+ base::WritableSharedMemoryRegion::ConvertToReadOnly(
+ std::move(writable_region));
+ ASSERT_TRUE(read_only_region.IsValid());
+
+ scoped_refptr<TargetPolicy> policy = broker->CreatePolicy();
+ policy->AddHandleToShare(read_only_region.GetPlatformHandle());
+
+ std::wstring arguments(L"\"");
+ arguments += prog_name;
+ arguments += L"\" -child 0 shared_memory_handle ";
+ arguments += base::AsWString(base::NumberToString16(
+ base::win::HandleToUint32(read_only_region.GetPlatformHandle())));
+
+ // Launch the app.
+ ResultCode result = SBOX_ALL_OK;
+ ResultCode warning_result = SBOX_ALL_OK;
+ base::win::ScopedProcessInformation target;
+
+ policy->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ PROCESS_INFORMATION temp_process_info = {};
+ DWORD last_error = ERROR_SUCCESS;
+ result =
+ broker->SpawnTarget(prog_name, arguments.c_str(), policy, &warning_result,
+ &last_error, &temp_process_info);
+ policy = nullptr;
+
+ EXPECT_EQ(SBOX_ALL_OK, result);
+ if (result == SBOX_ALL_OK)
+ target.Set(temp_process_info);
+
+ EXPECT_EQ(1u, ::ResumeThread(target.thread_handle()));
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::WaitForSingleObject(target.process_handle(), 2000));
+
+ EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0));
+
+ ::WaitForSingleObject(target.process_handle(), INFINITE);
+}
+
+// Dummy target that just reports that's it spawned correctly.
+SBOX_TESTS_COMMAND int PolicyTargetTest_SetEffectiveToken(int argc,
+ wchar_t** argv) {
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Test whether after using SetEffectiveToken spawning a target works as
+// expected.
+TEST(PolicyTargetTest, SetEffectiveToken) {
+ TestRunner runner;
+ HANDLE token;
+
+ // Get current process token.
+ EXPECT_TRUE(
+ ::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
+
+ // Setup token guard.
+ base::win::ScopedHandle token_guard(token);
+
+ // Set token and run target.
+ runner.GetPolicy()->SetEffectiveToken(token_guard.Get());
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"PolicyTargetTest_SetEffectiveToken"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
new file mode 100644
index 0000000000..557296c93f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.cc
@@ -0,0 +1,622 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations.h"
+
+#include <stddef.h>
+#include <windows.h>
+#include <wow64apiset.h>
+
+#include <algorithm>
+
+#include "base/win/windows_version.h"
+#include "build/build_config.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox_rand.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// API defined in libloaderapi.h >= Win8.
+using SetDefaultDllDirectoriesFunction = decltype(&SetDefaultDllDirectories);
+
+// APIs defined in processthreadsapi.h >= Win8.
+using SetProcessMitigationPolicyFunction =
+ decltype(&SetProcessMitigationPolicy);
+using GetProcessMitigationPolicyFunction =
+ decltype(&GetProcessMitigationPolicy);
+using SetThreadInformationFunction = decltype(&SetThreadInformation);
+
+// Returns a two-element array of mitigation flags supported on this machine.
+// - This function is only useful on >= base::win::Version::WIN8.
+const ULONG64* GetSupportedMitigations() {
+ static ULONG64 mitigations[2] = {};
+
+ // This static variable will only be initialized once.
+ if (!mitigations[0] && !mitigations[1]) {
+ GetProcessMitigationPolicyFunction get_process_mitigation_policy =
+ reinterpret_cast<GetProcessMitigationPolicyFunction>(::GetProcAddress(
+ ::GetModuleHandleA("kernel32.dll"), "GetProcessMitigationPolicy"));
+ if (get_process_mitigation_policy) {
+ // NOTE: the two-element-sized input array is only supported on >= Win10
+ // RS2.
+ // If an earlier version, the second element will be left 0.
+ size_t mits_size =
+ (base::win::GetVersion() >= base::win::Version::WIN10_RS2)
+ ? (sizeof(mitigations[0]) * 2)
+ : sizeof(mitigations[0]);
+ if (!get_process_mitigation_policy(::GetCurrentProcess(),
+ ProcessMitigationOptionsMask,
+ &mitigations, mits_size)) {
+ NOTREACHED();
+ }
+ }
+ }
+
+ return &mitigations[0];
+}
+
+// Returns true if this is 32-bit Chrome running on ARM64 with emulation.
+// Needed because ACG does not work with emulated code.
+// See
+// https://docs.microsoft.com/en-us/windows/uwp/porting/apps-on-arm-troubleshooting-x86.
+// See https://crbug.com/977723.
+// TODO(wfh): Move this code into base. See https://crbug.com/978257.
+bool IsRunning32bitEmulatedOnArm64() {
+#if defined(ARCH_CPU_X86)
+ using IsWow64Process2Function = decltype(&IsWow64Process2);
+
+ IsWow64Process2Function is_wow64_process2 =
+ reinterpret_cast<IsWow64Process2Function>(::GetProcAddress(
+ ::GetModuleHandleA("kernel32.dll"), "IsWow64Process2"));
+ if (!is_wow64_process2)
+ return false;
+ USHORT process_machine;
+ USHORT native_machine;
+ bool retval = is_wow64_process2(::GetCurrentProcess(), &process_machine,
+ &native_machine);
+ if (!retval)
+ return false;
+ if (native_machine == IMAGE_FILE_MACHINE_ARM64)
+ return true;
+#endif // defined(ARCH_CPU_X86)
+ return false;
+}
+
+// Returns true if user-mode Hardware-enforced Stack Protection is available for
+// the Win32 environment.
+bool IsUserCetWin32Available() {
+ static bool cetAvailable = []() -> bool {
+ using IsUserCetAvailableInEnvironmentFunction =
+ decltype(&IsUserCetAvailableInEnvironment);
+
+ IsUserCetAvailableInEnvironmentFunction is_user_cet_available =
+ reinterpret_cast<IsUserCetAvailableInEnvironmentFunction>(
+ ::GetProcAddress(::GetModuleHandleW(L"kernel32.dll"),
+ "IsUserCetAvailableInEnvironment"));
+ if (!is_user_cet_available) {
+ return false;
+ }
+
+ return is_user_cet_available(USER_CET_ENVIRONMENT_WIN32_PROCESS);
+ }();
+
+ return cetAvailable;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags) {
+ if (!CanSetProcessMitigationsPostStartup(flags))
+ return false;
+
+ base::win::Version version = base::win::GetVersion();
+ HMODULE module = ::GetModuleHandleA("kernel32.dll");
+
+ if (flags & MITIGATION_DLL_SEARCH_ORDER) {
+ SetDefaultDllDirectoriesFunction set_default_dll_directories =
+ reinterpret_cast<SetDefaultDllDirectoriesFunction>(
+ ::GetProcAddress(module, "SetDefaultDllDirectories"));
+
+ // Check for SetDefaultDllDirectories since it requires KB2533623.
+ if (set_default_dll_directories) {
+ if (!set_default_dll_directories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+ }
+
+ // Set the heap to terminate on corruption
+ if (flags & MITIGATION_HEAP_TERMINATE) {
+ if (!::HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption,
+ nullptr, 0) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (flags & MITIGATION_HARDEN_TOKEN_IL_POLICY) {
+ DWORD error = HardenProcessIntegrityLevelPolicy();
+ if ((error != ERROR_SUCCESS) && (error != ERROR_ACCESS_DENIED))
+ return false;
+ }
+
+#if !defined(_WIN64) // DEP is always enabled on 64-bit.
+ if (flags & MITIGATION_DEP) {
+ DWORD dep_flags = PROCESS_DEP_ENABLE;
+
+ if (flags & MITIGATION_DEP_NO_ATL_THUNK)
+ dep_flags |= PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION;
+
+ if (!::SetProcessDEPPolicy(dep_flags) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+#endif
+
+ // This is all we can do in Win7 and below.
+ if (version < base::win::Version::WIN8)
+ return true;
+
+ SetProcessMitigationPolicyFunction set_process_mitigation_policy =
+ reinterpret_cast<SetProcessMitigationPolicyFunction>(
+ ::GetProcAddress(module, "SetProcessMitigationPolicy"));
+ if (!set_process_mitigation_policy)
+ return false;
+
+ // Enable ASLR policies.
+ if (flags & MITIGATION_RELOCATE_IMAGE) {
+ PROCESS_MITIGATION_ASLR_POLICY policy = {};
+ policy.EnableForceRelocateImages = true;
+ policy.DisallowStrippedImages =
+ (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) ==
+ MITIGATION_RELOCATE_IMAGE_REQUIRED;
+
+ if (!set_process_mitigation_policy(ProcessASLRPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable strict handle policies.
+ if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
+ PROCESS_MITIGATION_STRICT_HANDLE_CHECK_POLICY policy = {};
+ policy.HandleExceptionsPermanentlyEnabled =
+ policy.RaiseExceptionOnInvalidHandleReference = true;
+
+ if (!set_process_mitigation_policy(ProcessStrictHandleCheckPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable system call policies.
+ if (flags & MITIGATION_WIN32K_DISABLE) {
+ PROCESS_MITIGATION_SYSTEM_CALL_DISABLE_POLICY policy = {};
+ policy.DisallowWin32kSystemCalls = true;
+
+ if (!set_process_mitigation_policy(ProcessSystemCallDisablePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable extension point policies.
+ if (flags & MITIGATION_EXTENSION_POINT_DISABLE) {
+ PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = {};
+ policy.DisableExtensionPoints = true;
+
+ if (!set_process_mitigation_policy(ProcessExtensionPointDisablePolicy,
+ &policy, sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN8_1)
+ return true;
+
+ // Enable dynamic code policies.
+ if (!IsRunning32bitEmulatedOnArm64() &&
+ (flags & MITIGATION_DYNAMIC_CODE_DISABLE)) {
+ // Verify caller is not accidentally setting both mutually exclusive
+ // policies.
+ DCHECK(!(flags & MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT));
+ PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy = {};
+ policy.ProhibitDynamicCode = true;
+
+ if (!set_process_mitigation_policy(ProcessDynamicCodePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN10)
+ return true;
+
+ // Enable font policies.
+ if (flags & MITIGATION_NONSYSTEM_FONT_DISABLE) {
+ PROCESS_MITIGATION_FONT_DISABLE_POLICY policy = {};
+ policy.DisableNonSystemFonts = true;
+
+ if (!set_process_mitigation_policy(ProcessFontDisablePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN10_TH2)
+ return true;
+
+ // Enable binary signing policies.
+ if (flags & MITIGATION_FORCE_MS_SIGNED_BINS) {
+ PROCESS_MITIGATION_BINARY_SIGNATURE_POLICY policy = {};
+ // Allow only MS signed binaries.
+ policy.MicrosoftSignedOnly = true;
+ // NOTE: there are two other flags available to allow
+ // 1) Only Windows Store signed.
+ // 2) MS-signed, Win Store signed, and WHQL signed binaries.
+ // Support not added at the moment.
+ if (!set_process_mitigation_policy(ProcessSignaturePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ // Enable image load policies.
+ if (flags & MITIGATION_IMAGE_LOAD_NO_REMOTE ||
+ flags & MITIGATION_IMAGE_LOAD_NO_LOW_LABEL ||
+ flags & MITIGATION_IMAGE_LOAD_PREFER_SYS32) {
+ PROCESS_MITIGATION_IMAGE_LOAD_POLICY policy = {};
+ if (flags & MITIGATION_IMAGE_LOAD_NO_REMOTE)
+ policy.NoRemoteImages = true;
+ if (flags & MITIGATION_IMAGE_LOAD_NO_LOW_LABEL)
+ policy.NoLowMandatoryLabelImages = true;
+ // PreferSystem32 is only supported on >= Anniversary.
+ if (version >= base::win::Version::WIN10_RS1 &&
+ flags & MITIGATION_IMAGE_LOAD_PREFER_SYS32) {
+ policy.PreferSystem32Images = true;
+ }
+
+ if (!set_process_mitigation_policy(ProcessImageLoadPolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ if (version < base::win::Version::WIN10_RS1)
+ return true;
+
+ // Enable dynamic code policies.
+ // Per-thread opt-out is only supported on >= Anniversary (RS1).
+ if (!IsRunning32bitEmulatedOnArm64() &&
+ (flags & MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT)) {
+ // Verify caller is not accidentally setting both mutually exclusive
+ // policies.
+ DCHECK(!(flags & MITIGATION_DYNAMIC_CODE_DISABLE));
+ PROCESS_MITIGATION_DYNAMIC_CODE_POLICY policy = {};
+ policy.ProhibitDynamicCode = true;
+ policy.AllowThreadOptOut = true;
+
+ if (!set_process_mitigation_policy(ProcessDynamicCodePolicy, &policy,
+ sizeof(policy)) &&
+ ERROR_ACCESS_DENIED != ::GetLastError()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ApplyMitigationsToCurrentThread(MitigationFlags flags) {
+ if (!CanSetMitigationsPerThread(flags))
+ return false;
+
+ base::win::Version version = base::win::GetVersion();
+
+ if (version < base::win::Version::WIN10_RS1)
+ return true;
+
+ // Enable dynamic code per-thread policies.
+ if (flags & MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD) {
+ DWORD thread_policy = THREAD_DYNAMIC_CODE_ALLOW;
+
+ // NOTE: SetThreadInformation API only exists on >= Win8. Dynamically
+ // get function handle.
+ SetThreadInformationFunction set_thread_info_function =
+ reinterpret_cast<SetThreadInformationFunction>(::GetProcAddress(
+ ::GetModuleHandleA("kernel32.dll"), "SetThreadInformation"));
+ if (!set_thread_info_function)
+ return false;
+
+ // NOTE: Must use the pseudo-handle here, a thread HANDLE won't work.
+ if (!set_thread_info_function(::GetCurrentThread(), ThreadDynamicCodePolicy,
+ &thread_policy, sizeof(thread_policy))) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
+ DWORD64* policy_flags,
+ size_t* size) {
+ base::win::Version version = base::win::GetVersion();
+
+ // |policy_flags| is a two-element array of DWORD64s. Ensure mitigation flags
+ // from PROCESS_CREATION_MITIGATION_POLICY2_* go into the second value. If
+ // any flags are set in value 2, update |size| to include both elements.
+ DWORD64* policy_value_1 = &policy_flags[0];
+ DWORD64* policy_value_2 = &policy_flags[1];
+ *policy_value_1 = 0;
+ *policy_value_2 = 0;
+
+#if defined(_WIN64)
+ *size = sizeof(*policy_flags);
+#elif defined(_M_IX86)
+ // A 64-bit flags attribute is illegal on 32-bit Win 7.
+ if (version < base::win::Version::WIN8)
+ *size = sizeof(DWORD);
+ else
+ *size = sizeof(*policy_flags);
+#else
+#error This platform is not supported.
+#endif
+
+// DEP and SEHOP are not valid for 64-bit Windows
+#if !defined(_WIN64)
+ if (flags & MITIGATION_DEP) {
+ *policy_value_1 |= PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE;
+ if (!(flags & MITIGATION_DEP_NO_ATL_THUNK))
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE;
+ }
+
+ if (flags & MITIGATION_SEHOP)
+ *policy_value_1 |= PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE;
+#endif
+
+ // Win 7
+ if (version < base::win::Version::WIN8)
+ return;
+
+ // Everything >= Win8, do not return before the end of the function where
+ // the final policy bitmap is sanity checked against what is supported on this
+ // machine. The API required to do so is only available since Win8.
+
+ // Mitigations >= Win8:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN8) {
+ if (flags & MITIGATION_RELOCATE_IMAGE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON;
+ if (flags & MITIGATION_RELOCATE_IMAGE_REQUIRED) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS;
+ }
+ }
+
+ if (flags & MITIGATION_HEAP_TERMINATE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_BOTTOM_UP_ASLR) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_HIGH_ENTROPY_ASLR) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_STRICT_HANDLE_CHECKS) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_WIN32K_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_EXTENSION_POINT_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win8.1:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN8_1) {
+ if (flags & MITIGATION_DYNAMIC_CODE_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_CONTROL_FLOW_GUARD_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_OFF;
+ }
+ }
+
+ // Mitigations >= Win10:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10) {
+ if (flags & MITIGATION_NONSYSTEM_FONT_DISABLE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 TH2:
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_TH2) {
+ if (flags & MITIGATION_FORCE_MS_SIGNED_BINS) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_IMAGE_LOAD_NO_REMOTE) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON;
+ }
+
+ if (flags & MITIGATION_IMAGE_LOAD_NO_LOW_LABEL) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 RS1 ("Anniversary"):
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_RS1) {
+ if (flags & MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON_ALLOW_OPT_OUT;
+ }
+
+ if (flags & MITIGATION_IMAGE_LOAD_PREFER_SYS32) {
+ *policy_value_1 |=
+ PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 RS3 ("Fall Creator's"):
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_RS3) {
+ // Note: This mitigation requires not only Win10 1709, but also the January
+ // 2018 security updates and any applicable firmware updates from the
+ // OEM device manufacturer.
+ // Note: Applying this mitigation attribute on creation will succeed, even
+ // if
+ // the underlying hardware does not support the implementation.
+ // Windows just does its best under the hood for the given hardware.
+ if (flags & MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION) {
+ *policy_value_2 |=
+ PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON;
+ }
+ }
+
+ // Mitigations >= Win10 20H1
+ //----------------------------------------------------------------------------
+ if (version >= base::win::Version::WIN10_20H1) {
+ if (flags & MITIGATION_CET_COMPAT_MODE && IsUserCetWin32Available()) {
+ *policy_value_2 |=
+ PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON;
+ }
+ }
+
+ // When done setting policy flags, sanity check supported policies on this
+ // machine, and then update |size|.
+
+ const ULONG64* supported = GetSupportedMitigations();
+
+ *policy_value_1 = *policy_value_1 & supported[0];
+ *policy_value_2 = *policy_value_2 & supported[1];
+
+ // Only include the second element in |size| if it is non-zero. Else,
+ // UpdateProcThreadAttribute() will return a failure when setting policies.
+ if (*policy_value_2 && version >= base::win::Version::WIN10_RS2) {
+ *size = sizeof(*policy_flags) * 2;
+ }
+
+ return;
+}
+
+MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags) {
+ base::win::Version version = base::win::GetVersion();
+
+ // Windows 7.
+ if (version < base::win::Version::WIN8) {
+ return flags & (MITIGATION_BOTTOM_UP_ASLR | MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HEAP_TERMINATE);
+ }
+
+ // Windows 8 and above.
+ return flags & (MITIGATION_BOTTOM_UP_ASLR | MITIGATION_DLL_SEARCH_ORDER);
+}
+
+bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process,
+ MitigationFlags flags) {
+// This is a hack to fake a weak bottom-up ASLR on 32-bit Windows.
+#if !defined(_WIN64)
+ if (flags & MITIGATION_BOTTOM_UP_ASLR) {
+ unsigned int limit;
+ GetRandom(&limit);
+ char* ptr = 0;
+ const size_t kMask64k = 0xFFFF;
+ // Random range (512k-16.5mb) in 64k steps.
+ const char* end = ptr + ((((limit % 16384) + 512) * 1024) & ~kMask64k);
+ while (ptr < end) {
+ MEMORY_BASIC_INFORMATION memory_info;
+ if (!::VirtualQueryEx(process, ptr, &memory_info, sizeof(memory_info)))
+ break;
+ size_t size = std::min((memory_info.RegionSize + kMask64k) & ~kMask64k,
+ static_cast<SIZE_T>(end - ptr));
+ if (ptr && memory_info.State == MEM_FREE)
+ ::VirtualAllocEx(process, ptr, size, MEM_RESERVE, PAGE_NOACCESS);
+ ptr += size;
+ }
+ }
+#endif
+
+ return true;
+}
+
+MitigationFlags GetAllowedPostStartupProcessMitigations() {
+ return MITIGATION_HEAP_TERMINATE |
+ MITIGATION_DEP |
+ MITIGATION_DEP_NO_ATL_THUNK |
+ MITIGATION_RELOCATE_IMAGE |
+ MITIGATION_RELOCATE_IMAGE_REQUIRED |
+ MITIGATION_BOTTOM_UP_ASLR |
+ MITIGATION_STRICT_HANDLE_CHECKS |
+ MITIGATION_EXTENSION_POINT_DISABLE |
+ MITIGATION_DLL_SEARCH_ORDER |
+ MITIGATION_HARDEN_TOKEN_IL_POLICY |
+ MITIGATION_WIN32K_DISABLE |
+ MITIGATION_DYNAMIC_CODE_DISABLE |
+ MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT |
+ MITIGATION_FORCE_MS_SIGNED_BINS |
+ MITIGATION_NONSYSTEM_FONT_DISABLE |
+ MITIGATION_IMAGE_LOAD_NO_REMOTE |
+ MITIGATION_IMAGE_LOAD_NO_LOW_LABEL |
+ MITIGATION_IMAGE_LOAD_PREFER_SYS32;
+}
+
+bool CanSetProcessMitigationsPostStartup(MitigationFlags flags) {
+ // All of these mitigations can be enabled after startup.
+ return !(flags & ~GetAllowedPostStartupProcessMitigations());
+}
+
+bool CanSetProcessMitigationsPreStartup(MitigationFlags flags) {
+ // These mitigations cannot be enabled prior to startup.
+ return !(flags &
+ (MITIGATION_STRICT_HANDLE_CHECKS | MITIGATION_DLL_SEARCH_ORDER));
+}
+
+bool CanSetMitigationsPerThread(MitigationFlags flags) {
+ // If any flags EXCEPT these are set, fail.
+ if (flags & ~(MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD))
+ return false;
+
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations.h
new file mode 100644
index 0000000000..26dc42e358
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
+#define SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
+
+#include <windows.h>
+
+#include <stddef.h>
+
+#include "sandbox/win/src/security_level.h"
+
+namespace sandbox {
+
+// Sets the mitigation policy for the current process, ignoring any settings
+// that are invalid for the current version of Windows.
+bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags flags);
+
+// Sets the mitigation policy for the current thread, ignoring any settings
+// that are invalid for the current version of Windows.
+bool ApplyMitigationsToCurrentThread(MitigationFlags flags);
+
+// Returns the flags that must be enforced after startup for the current OS
+// version.
+MitigationFlags FilterPostStartupProcessMitigations(MitigationFlags flags);
+
+// Converts sandbox flags to the PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES
+// policy flags used by UpdateProcThreadAttribute().
+// - |policy_flags| must be a two-element DWORD64 array.
+// - |size| is a size_t so that it can be passed directly into
+// UpdateProcThreadAttribute().
+void ConvertProcessMitigationsToPolicy(MitigationFlags flags,
+ DWORD64* policy_flags,
+ size_t* size);
+
+// Adds mitigations that need to be performed on the suspended target process
+// before execution begins.
+bool ApplyProcessMitigationsToSuspendedProcess(HANDLE process,
+ MitigationFlags flags);
+
+// Returns the list of process mitigations which can be enabled post startup.
+MitigationFlags GetAllowedPostStartupProcessMitigations();
+
+// Returns true if all the supplied flags can be set after a process starts.
+bool CanSetProcessMitigationsPostStartup(MitigationFlags flags);
+
+// Returns true if all the supplied flags can be set before a process starts.
+bool CanSetProcessMitigationsPreStartup(MitigationFlags flags);
+
+// Returns true if all the supplied flags can be set on the current thread.
+bool CanSetMitigationsPerThread(MitigationFlags flags);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WIN_PROCESS_MITIGATIONS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
new file mode 100644
index 0000000000..4d2d94b865
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.cc
@@ -0,0 +1,592 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_dispatcher.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/memory/platform_shared_memory_region.h"
+#include "base/memory/unsafe_shared_memory_region.h"
+#include "base/unguessable_token.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+
+namespace sandbox {
+
+namespace {
+
+base::UnsafeSharedMemoryRegion GetSharedMemoryRegion(
+ const ClientInfo& client_info,
+ HANDLE handle,
+ size_t size) {
+ HANDLE dup_handle = nullptr;
+ intptr_t handle_int = reinterpret_cast<intptr_t>(handle);
+ if (handle_int <= 0 ||
+ !::DuplicateHandle(client_info.process, handle, ::GetCurrentProcess(),
+ &dup_handle, 0, false, DUPLICATE_SAME_ACCESS)) {
+ return {};
+ }
+ // The raw handle returned from ::DuplicateHandle() must be wrapped in a
+ // base::PlatformSharedMemoryRegion.
+ base::subtle::PlatformSharedMemoryRegion platform_region =
+ base::subtle::PlatformSharedMemoryRegion::Take(
+ base::win::ScopedHandle(dup_handle),
+ base::subtle::PlatformSharedMemoryRegion::Mode::kUnsafe, size,
+ base::UnguessableToken::Create());
+ // |platform_region| can now be wrapped in a base::UnsafeSharedMemoryRegion.
+ return base::UnsafeSharedMemoryRegion::Deserialize(
+ std::move(platform_region));
+}
+
+} // namespace
+
+ProtectedVideoOutput::~ProtectedVideoOutput() {
+ ProcessMitigationsWin32KLockdownPolicy::DestroyOPMProtectedOutputAction(
+ handle_);
+}
+
+scoped_refptr<ProtectedVideoOutput>
+ProcessMitigationsWin32KDispatcher::GetProtectedVideoOutput(
+ HANDLE handle,
+ bool destroy_output) {
+ base::AutoLock lock(protected_outputs_lock_);
+ scoped_refptr<ProtectedVideoOutput> result;
+ auto it = protected_outputs_.find(handle);
+ if (it != protected_outputs_.end()) {
+ result = it->second;
+ if (destroy_output)
+ protected_outputs_.erase(it);
+ }
+ return result;
+}
+
+ProcessMitigationsWin32KDispatcher::ProcessMitigationsWin32KDispatcher(
+ PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall enum_display_monitors_params = {
+ {IpcTag::USER_ENUMDISPLAYMONITORS, {INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::EnumDisplayMonitors)};
+ static const IPCCall get_monitor_info_params = {
+ {IpcTag::USER_GETMONITORINFO, {VOIDPTR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetMonitorInfo)};
+ static const IPCCall get_suggested_output_size_params = {
+ {IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE, {WCHAR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::
+ GetSuggestedOPMProtectedOutputArraySize)};
+ static const IPCCall create_protected_outputs_params = {
+ {IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS, {WCHAR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::CreateOPMProtectedOutputs)};
+ static const IPCCall get_cert_size_params = {
+ {IpcTag::GDI_GETCERTIFICATESIZE, {WCHAR_TYPE, VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetCertificateSize)};
+ static const IPCCall get_cert_params = {
+ {IpcTag::GDI_GETCERTIFICATE,
+ {WCHAR_TYPE, VOIDPTR_TYPE, VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetCertificate)};
+ static const IPCCall destroy_protected_output_params = {
+ {IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT, {VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::DestroyOPMProtectedOutput)};
+ static const IPCCall get_random_number_params = {
+ {IpcTag::GDI_GETOPMRANDOMNUMBER, {VOIDPTR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetOPMRandomNumber)};
+ static const IPCCall set_signing_key_params = {
+ {IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS,
+ {VOIDPTR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::
+ SetOPMSigningKeyAndSequenceNumbers)};
+ static const IPCCall configure_protected_output_params = {
+ {IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT, {VOIDPTR_TYPE, VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::ConfigureOPMProtectedOutput)};
+ static const IPCCall get_information_params = {
+ {IpcTag::GDI_GETOPMINFORMATION, {VOIDPTR_TYPE, VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ProcessMitigationsWin32KDispatcher::GetOPMInformation)};
+
+ ipc_calls_.push_back(enum_display_monitors_params);
+ ipc_calls_.push_back(get_monitor_info_params);
+ ipc_calls_.push_back(get_suggested_output_size_params);
+ ipc_calls_.push_back(create_protected_outputs_params);
+ ipc_calls_.push_back(get_cert_size_params);
+ ipc_calls_.push_back(get_cert_params);
+ ipc_calls_.push_back(destroy_protected_output_params);
+ ipc_calls_.push_back(get_random_number_params);
+ ipc_calls_.push_back(set_signing_key_params);
+ ipc_calls_.push_back(configure_protected_output_params);
+ ipc_calls_.push_back(get_information_params);
+}
+
+ProcessMitigationsWin32KDispatcher::~ProcessMitigationsWin32KDispatcher() {}
+
+bool ProcessMitigationsWin32KDispatcher::SetupService(
+ InterceptionManager* manager,
+ IpcTag service) {
+ if (!(policy_base_->GetProcessMitigations() &
+ sandbox::MITIGATION_WIN32K_DISABLE)) {
+ return false;
+ }
+
+ switch (service) {
+ case IpcTag::GDI_GDIDLLINITIALIZE: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GdiDllInitialize,
+ GDIINITIALIZE_ID, 12)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::GDI_GETSTOCKOBJECT: {
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetStockObject,
+ GETSTOCKOBJECT_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_REGISTERCLASSW: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", RegisterClassW,
+ REGISTERCLASSW_ID, 8)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_ENUMDISPLAYMONITORS: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", EnumDisplayMonitors,
+ ENUMDISPLAYMONITORS_ID, 20)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_ENUMDISPLAYDEVICES: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", EnumDisplayDevicesA,
+ ENUMDISPLAYDEVICESA_ID, 20)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::USER_GETMONITORINFO: {
+ if (!INTERCEPT_EAT(manager, L"user32.dll", GetMonitorInfoA,
+ GETMONITORINFOA_ID, 12)) {
+ return false;
+ }
+
+ if (!INTERCEPT_EAT(manager, L"user32.dll", GetMonitorInfoW,
+ GETMONITORINFOW_ID, 12)) {
+ return false;
+ }
+ return true;
+ }
+
+ case IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", CreateOPMProtectedOutputs,
+ CREATEOPMPROTECTEDOUTPUTS_ID, 24)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETCERTIFICATE:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificate,
+ GETCERTIFICATE_ID, 20)) {
+ return false;
+ }
+ if (base::win::GetVersion() < base::win::Version::WIN10_TH2)
+ return true;
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificateByHandle,
+ GETCERTIFICATEBYHANDLE_ID, 20)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETCERTIFICATESIZE:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificateSize,
+ GETCERTIFICATESIZE_ID, 16)) {
+ return false;
+ }
+ if (base::win::GetVersion() < base::win::Version::WIN10_TH2)
+ return true;
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetCertificateSizeByHandle,
+ GETCERTIFICATESIZEBYHANDLE_ID, 16)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", DestroyOPMProtectedOutput,
+ DESTROYOPMPROTECTEDOUTPUT_ID, 8)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", ConfigureOPMProtectedOutput,
+ CONFIGUREOPMPROTECTEDOUTPUT_ID, 20)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETOPMINFORMATION:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetOPMInformation,
+ GETOPMINFORMATION_ID, 16)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETOPMRANDOMNUMBER:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll", GetOPMRandomNumber,
+ GETOPMRANDOMNUMBER_ID, 12)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll",
+ GetSuggestedOPMProtectedOutputArraySize,
+ GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE_ID, 12)) {
+ return false;
+ }
+ return true;
+
+ case IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS:
+ if (!INTERCEPT_EAT(manager, L"gdi32.dll",
+ SetOPMSigningKeyAndSequenceNumbers,
+ SETOPMSIGNINGKEYANDSEQUENCENUMBERS_ID, 12)) {
+ return false;
+ }
+ return true;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+bool ProcessMitigationsWin32KDispatcher::EnumDisplayMonitors(
+ IPCInfo* ipc,
+ CountedBuffer* buffer) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.win32_result = ERROR_ACCESS_DENIED;
+ return true;
+ }
+
+ if (buffer->Size() != sizeof(EnumMonitorsResult)) {
+ ipc->return_info.win32_result = ERROR_INVALID_PARAMETER;
+ return true;
+ }
+ HMONITOR monitor_list[kMaxEnumMonitors] = {};
+
+ uint32_t monitor_list_count =
+ ProcessMitigationsWin32KLockdownPolicy::EnumDisplayMonitorsAction(
+ *ipc->client_info, monitor_list, kMaxEnumMonitors);
+ DCHECK(monitor_list_count <= kMaxEnumMonitors);
+
+ EnumMonitorsResult* result =
+ static_cast<EnumMonitorsResult*>(buffer->Buffer());
+ for (uint32_t monitor_pos = 0; monitor_pos < monitor_list_count;
+ ++monitor_pos) {
+ result->monitors[monitor_pos] = monitor_list[monitor_pos];
+ }
+ result->monitor_count = monitor_list_count;
+ ipc->return_info.win32_result = 0;
+
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetMonitorInfo(IPCInfo* ipc,
+ void* monitor,
+ CountedBuffer* buffer) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.win32_result = ERROR_ACCESS_DENIED;
+ return true;
+ }
+ if (buffer->Size() != sizeof(MONITORINFOEXW)) {
+ ipc->return_info.win32_result = ERROR_INVALID_PARAMETER;
+ return true;
+ }
+ MONITORINFO* monitor_info = static_cast<MONITORINFO*>(buffer->Buffer());
+ // Ensure size is valid and represents what we've been passed.
+ monitor_info->cbSize = buffer->Size();
+ HMONITOR monitor_handle = static_cast<HMONITOR>(monitor);
+ bool success = ProcessMitigationsWin32KLockdownPolicy::GetMonitorInfoAction(
+ *ipc->client_info, monitor_handle, monitor_info);
+ ipc->return_info.win32_result =
+ success ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::
+ GetSuggestedOPMProtectedOutputArraySize(IPCInfo* ipc,
+ std::wstring* device_name) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status = ProcessMitigationsWin32KLockdownPolicy::
+ GetSuggestedOPMProtectedOutputArraySizeAction(
+ *ipc->client_info, *device_name,
+ &ipc->return_info.extended[0].unsigned_int);
+ if (!status) {
+ ipc->return_info.extended_count = 1;
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::CreateOPMProtectedOutputs(
+ IPCInfo* ipc,
+ std::wstring* device_name,
+ CountedBuffer* protected_outputs) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ uint32_t output_array_size = 0;
+ uint32_t input_array_size = protected_outputs->Size() / sizeof(HANDLE);
+ HANDLE* handles = static_cast<HANDLE*>(protected_outputs->Buffer());
+ NTSTATUS status =
+ ProcessMitigationsWin32KLockdownPolicy::CreateOPMProtectedOutputsAction(
+ *ipc->client_info, *device_name, handles, input_array_size,
+ &output_array_size);
+ if (!status && (output_array_size <= input_array_size)) {
+ base::AutoLock lock(protected_outputs_lock_);
+ ipc->return_info.extended_count = 1;
+ ipc->return_info.extended[0].unsigned_int = output_array_size;
+ for (uint32_t handle_pos = 0; handle_pos < output_array_size;
+ handle_pos++) {
+ HANDLE handle = handles[handle_pos];
+ protected_outputs_[handle] = new ProtectedVideoOutput(handle);
+ }
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetCertificateSize(
+ IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (device_name->size() > 0) {
+ status = ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeAction(
+ *ipc->client_info, *device_name,
+ &ipc->return_info.extended[0].unsigned_int);
+ } else {
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (output) {
+ status = ProcessMitigationsWin32KLockdownPolicy::
+ GetCertificateSizeByHandleAction(
+ *ipc->client_info, output.get()->handle(),
+ &ipc->return_info.extended[0].unsigned_int);
+ }
+ }
+ if (!status) {
+ ipc->return_info.extended_count = 1;
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetCertificate(
+ IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output,
+ void* shared_buffer_handle,
+ uint32_t shared_buffer_size) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ // Don't let caller map an arbitrarily large buffer into memory.
+ if (shared_buffer_size > kProtectedVideoOutputSectionSize) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ base::UnsafeSharedMemoryRegion region = GetSharedMemoryRegion(
+ *ipc->client_info, shared_buffer_handle, shared_buffer_size);
+ if (!region.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ base::WritableSharedMemoryMapping cert_data = region.Map();
+ if (!cert_data.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (device_name->size() > 0) {
+ status = ProcessMitigationsWin32KLockdownPolicy::GetCertificateAction(
+ *ipc->client_info, *device_name,
+ cert_data.GetMemoryAsSpan<BYTE>().data(), shared_buffer_size);
+ } else {
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (output) {
+ status =
+ ProcessMitigationsWin32KLockdownPolicy::GetCertificateByHandleAction(
+ *ipc->client_info, output.get()->handle(),
+ cert_data.GetMemoryAsSpan<BYTE>().data(), shared_buffer_size);
+ }
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::DestroyOPMProtectedOutput(
+ IPCInfo* ipc,
+ void* protected_output) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, true);
+ NTSTATUS status = STATUS_INVALID_HANDLE;
+ if (output)
+ status = STATUS_SUCCESS;
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetOPMRandomNumber(
+ IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* random_number) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (!output || random_number->Size() != sizeof(DXGKMDT_OPM_RANDOM_NUMBER)) {
+ status = STATUS_INVALID_PARAMETER;
+ } else {
+ status = ProcessMitigationsWin32KLockdownPolicy::GetOPMRandomNumberAction(
+ *ipc->client_info, output.get()->handle(), random_number->Buffer());
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::SetOPMSigningKeyAndSequenceNumbers(
+ IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* parameters) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ NTSTATUS status = STATUS_INVALID_PARAMETER;
+ if (!output ||
+ parameters->Size() != sizeof(DXGKMDT_OPM_ENCRYPTED_PARAMETERS)) {
+ status = STATUS_INVALID_PARAMETER;
+ } else {
+ status = ProcessMitigationsWin32KLockdownPolicy::
+ SetOPMSigningKeyAndSequenceNumbersAction(
+ *ipc->client_info, output.get()->handle(), parameters->Buffer());
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::ConfigureOPMProtectedOutput(
+ IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (!output) {
+ ipc->return_info.nt_status = STATUS_INVALID_HANDLE;
+ return true;
+ };
+ base::UnsafeSharedMemoryRegion region =
+ GetSharedMemoryRegion(*ipc->client_info, shared_buffer_handle,
+ sizeof(DXGKMDT_OPM_CONFIGURE_PARAMETERS));
+ if (!region.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ base::WritableSharedMemoryMapping buffer = region.Map();
+ if (!buffer.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ NTSTATUS status =
+ ProcessMitigationsWin32KLockdownPolicy::ConfigureOPMProtectedOutputAction(
+ *ipc->client_info, output.get()->handle(), buffer.memory());
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+bool ProcessMitigationsWin32KDispatcher::GetOPMInformation(
+ IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle) {
+ if (!policy_base_->GetEnableOPMRedirection()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ scoped_refptr<ProtectedVideoOutput> output =
+ GetProtectedVideoOutput(protected_output, false);
+ if (!output) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ size_t shared_buffer_size =
+ std::max(sizeof(DXGKMDT_OPM_GET_INFO_PARAMETERS),
+ sizeof(DXGKMDT_OPM_REQUESTED_INFORMATION));
+
+ base::UnsafeSharedMemoryRegion region = GetSharedMemoryRegion(
+ *ipc->client_info, shared_buffer_handle, shared_buffer_size);
+ if (!region.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ base::WritableSharedMemoryMapping buffer = region.Map();
+ if (!buffer.IsValid()) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+ DXGKMDT_OPM_REQUESTED_INFORMATION requested_info = {};
+ NTSTATUS status =
+ ProcessMitigationsWin32KLockdownPolicy::GetOPMInformationAction(
+ *ipc->client_info, output.get()->handle(), buffer.memory(),
+ &requested_info);
+ if (!status) {
+ memcpy(buffer.memory(), &requested_info,
+ sizeof(DXGKMDT_OPM_REQUESTED_INFORMATION));
+ }
+ ipc->return_info.nt_status = status;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h
new file mode 100644
index 0000000000..0714191fcf
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_dispatcher.h
@@ -0,0 +1,89 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
+
+#include <map>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// Class to maintain a reference to a OPM protected output handle.
+class ProtectedVideoOutput
+ : public base::RefCountedThreadSafe<ProtectedVideoOutput> {
+ public:
+ ProtectedVideoOutput(HANDLE handle) : handle_(handle) {}
+ HANDLE handle() { return handle_; }
+
+ private:
+ friend class base::RefCountedThreadSafe<ProtectedVideoOutput>;
+ ~ProtectedVideoOutput();
+
+ HANDLE handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProtectedVideoOutput);
+};
+
+// This class sets up intercepts for the Win32K lockdown policy which is set
+// on Windows 8 and beyond.
+class ProcessMitigationsWin32KDispatcher : public Dispatcher {
+ public:
+ explicit ProcessMitigationsWin32KDispatcher(PolicyBase* policy_base);
+ ~ProcessMitigationsWin32KDispatcher() override;
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ bool EnumDisplayMonitors(IPCInfo* ipc, CountedBuffer* buffer);
+ bool GetMonitorInfo(IPCInfo* ipc, void* monitor, CountedBuffer* buffer);
+ bool GetSuggestedOPMProtectedOutputArraySize(IPCInfo* ipc,
+ std::wstring* device_name);
+ bool CreateOPMProtectedOutputs(IPCInfo* ipc,
+ std::wstring* device_name,
+ CountedBuffer* protected_outputs);
+ bool GetCertificateSize(IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output);
+ bool GetCertificate(IPCInfo* ipc,
+ std::wstring* device_name,
+ void* protected_output,
+ void* shared_buffer_handle,
+ uint32_t shared_buffer_size);
+ bool DestroyOPMProtectedOutput(IPCInfo* ipc, void* protected_output);
+ bool GetOPMRandomNumber(IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* random_number);
+ bool SetOPMSigningKeyAndSequenceNumbers(IPCInfo* ipc,
+ void* protected_output,
+ CountedBuffer* parameters);
+ bool ConfigureOPMProtectedOutput(IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle);
+ bool GetOPMInformation(IPCInfo* ipc,
+ void* protected_output,
+ void* shared_buffer_handle);
+
+ private:
+ scoped_refptr<ProtectedVideoOutput> GetProtectedVideoOutput(
+ HANDLE handle,
+ bool destroy_output);
+
+ PolicyBase* policy_base_;
+ std::map<HANDLE, scoped_refptr<ProtectedVideoOutput>> protected_outputs_;
+ base::Lock protected_outputs_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMitigationsWin32KDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc
new file mode 100644
index 0000000000..1a16d85adf
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.cc
@@ -0,0 +1,523 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+
+#include <algorithm>
+
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+namespace {
+
+// Implement a simple shared memory class as we can't use the base one.
+class ScopedSharedMemory {
+ public:
+ ScopedSharedMemory(uint32_t size) : memory_(nullptr) {
+ handle_.Set(::CreateFileMapping(INVALID_HANDLE_VALUE, nullptr,
+ PAGE_READWRITE | SEC_COMMIT, 0, size,
+ nullptr));
+ if (handle_.IsValid()) {
+ memory_ = ::MapViewOfFile(handle_.Get(), FILE_MAP_READ | FILE_MAP_WRITE,
+ 0, 0, size);
+ }
+ }
+ ~ScopedSharedMemory() {
+ if (memory_)
+ ::UnmapViewOfFile(memory_);
+ }
+
+ void* handle() { return handle_.Get(); }
+ void* memory() { return memory_; }
+ bool IsValid() { return handle_.IsValid() && memory_; }
+
+ private:
+ base::win::ScopedHandle handle_;
+ void* memory_;
+};
+
+void UnicodeStringToString(PUNICODE_STRING unicode_string,
+ std::wstring* result) {
+ *result = std::wstring(
+ unicode_string->Buffer,
+ unicode_string->Buffer +
+ (unicode_string->Length / sizeof(unicode_string->Buffer[0])));
+}
+
+bool CallMonitorInfo(HMONITOR monitor, MONITORINFOEXW* monitor_info_ptr) {
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return false;
+
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return false;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ InOutCountedBuffer buffer(monitor_info_ptr, sizeof(*monitor_info_ptr));
+ ResultCode code = CrossCall(ipc, IpcTag::USER_GETMONITORINFO,
+ static_cast<void*>(monitor), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return false;
+
+ if (answer.win32_result != ERROR_SUCCESS)
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+BOOL WINAPI
+TargetGdiDllInitialize(GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason) {
+ return true;
+}
+
+HGDIOBJ WINAPI
+TargetGetStockObject(GetStockObjectFunction orig_get_stock_object, int object) {
+ return nullptr;
+}
+
+ATOM WINAPI
+TargetRegisterClassW(RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class) {
+ return true;
+}
+
+BOOL WINAPI TargetEnumDisplayMonitors(EnumDisplayMonitorsFunction,
+ HDC hdc,
+ LPCRECT lprcClip,
+ MONITORENUMPROC lpfnEnum,
+ LPARAM dwData) {
+ if (!lpfnEnum || hdc || lprcClip) {
+ return false;
+ }
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return false;
+
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return false;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = 0;
+ EnumMonitorsResult result = {};
+ InOutCountedBuffer result_buffer(&result, sizeof(result));
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::USER_ENUMDISPLAYMONITORS, result_buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return false;
+
+ if (answer.win32_result)
+ return false;
+
+ if (result.monitor_count > kMaxEnumMonitors)
+ return false;
+
+ for (uint32_t monitor_pos = 0; monitor_pos < result.monitor_count;
+ ++monitor_pos) {
+ bool continue_enum =
+ lpfnEnum(result.monitors[monitor_pos], nullptr, nullptr, dwData);
+ if (!continue_enum)
+ return false;
+ }
+
+ return true;
+}
+
+BOOL WINAPI TargetEnumDisplayDevicesA(EnumDisplayDevicesAFunction,
+ LPCSTR lpDevice,
+ DWORD iDevNum,
+ PDISPLAY_DEVICEA lpDisplayDevice,
+ DWORD dwFlags) {
+ return false;
+}
+
+BOOL WINAPI TargetGetMonitorInfoA(GetMonitorInfoAFunction,
+ HMONITOR monitor,
+ MONITORINFO* monitor_info_ptr) {
+ if (!monitor_info_ptr)
+ return false;
+ DWORD size = monitor_info_ptr->cbSize;
+ if (size != sizeof(MONITORINFO) && size != sizeof(MONITORINFOEXA))
+ return false;
+ MONITORINFOEXW monitor_info_tmp = {};
+ monitor_info_tmp.cbSize = sizeof(monitor_info_tmp);
+ bool success = CallMonitorInfo(monitor, &monitor_info_tmp);
+ if (!success)
+ return false;
+ memcpy(monitor_info_ptr, &monitor_info_tmp, sizeof(*monitor_info_ptr));
+ if (size == sizeof(MONITORINFOEXA)) {
+ MONITORINFOEXA* monitor_info_exa =
+ reinterpret_cast<MONITORINFOEXA*>(monitor_info_ptr);
+ if (!::WideCharToMultiByte(CP_ACP, 0, monitor_info_tmp.szDevice, -1,
+ monitor_info_exa->szDevice,
+ sizeof(monitor_info_exa->szDevice), nullptr,
+ nullptr)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+BOOL WINAPI TargetGetMonitorInfoW(GetMonitorInfoWFunction,
+ HMONITOR monitor,
+ LPMONITORINFO monitor_info_ptr) {
+ if (!monitor_info_ptr)
+ return false;
+ DWORD size = monitor_info_ptr->cbSize;
+ if (size != sizeof(MONITORINFO) && size != sizeof(MONITORINFOEXW))
+ return false;
+ MONITORINFOEXW monitor_info_tmp = {};
+ monitor_info_tmp.cbSize = sizeof(monitor_info_tmp);
+ if (!CallMonitorInfo(monitor, &monitor_info_tmp))
+ return false;
+ memcpy(monitor_info_ptr, &monitor_info_tmp, size);
+ return true;
+}
+
+static NTSTATUS GetCertificateCommon(
+ PUNICODE_STRING device_name,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_size) {
+ // Don't support arbitrarily large certificate buffers.
+ if (certificate_size > kProtectedVideoOutputSectionSize)
+ return STATUS_INVALID_PARAMETER;
+ if (certificate_type != DXGKMDT_OPM_CERTIFICATE)
+ return STATUS_INVALID_PARAMETER;
+ if (device_name && device_name->Length == 0)
+ return STATUS_INVALID_PARAMETER;
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ ScopedSharedMemory buffer(certificate_size);
+ if (!buffer.IsValid())
+ return STATUS_INVALID_PARAMETER;
+ std::wstring device_name_str;
+ void* protected_output_handle = nullptr;
+ if (device_name) {
+ if (device_name->Length == 0)
+ return STATUS_INVALID_PARAMETER;
+ UnicodeStringToString(device_name, &device_name_str);
+ } else {
+ protected_output_handle = protected_output;
+ }
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETCERTIFICATE, device_name_str.c_str(),
+ protected_output_handle, buffer.handle(),
+ static_cast<uint32_t>(certificate_size), &answer);
+
+ if (code != SBOX_ALL_OK) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (!answer.nt_status)
+ memcpy(certificate, buffer.memory(), certificate_size);
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetGetCertificate(GetCertificateFunction,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_size) {
+ return GetCertificateCommon(device_name, nullptr, certificate_type,
+ certificate, certificate_size);
+}
+
+static NTSTATUS GetCertificateSizeCommon(
+ PUNICODE_STRING device_name,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ if (certificate_type != DXGKMDT_OPM_CERTIFICATE)
+ return STATUS_INVALID_PARAMETER;
+ if (device_name && device_name->Length == 0)
+ return STATUS_INVALID_PARAMETER;
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ std::wstring device_name_str;
+ void* protected_output_handle = nullptr;
+ if (device_name) {
+ UnicodeStringToString(device_name, &device_name_str);
+ } else {
+ protected_output_handle = protected_output;
+ }
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETCERTIFICATESIZE, device_name_str.c_str(),
+ protected_output_handle, &answer);
+
+ if (code != SBOX_ALL_OK) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ if (!answer.nt_status)
+ *certificate_length = answer.extended[0].unsigned_int;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI
+TargetGetCertificateSize(GetCertificateSizeFunction,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ return GetCertificateSizeCommon(device_name, nullptr, certificate_type,
+ certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateByHandle(
+ GetCertificateByHandleFunction orig_get_certificate_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length) {
+ return GetCertificateCommon(nullptr, protected_output, certificate_type,
+ certificate, certificate_length);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateSizeByHandle(
+ GetCertificateSizeByHandleFunction orig_get_certificate_size_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length) {
+ return GetCertificateSizeCommon(nullptr, protected_output, certificate_type,
+ certificate_length);
+}
+
+NTSTATUS WINAPI
+TargetDestroyOPMProtectedOutput(DestroyOPMProtectedOutputFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code = CrossCall(ipc, IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT,
+ static_cast<void*>(protected_output), &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetConfigureOPMProtectedOutput(
+ ConfigureOPMProtectedOutputFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters) {
+ // Don't support additional parameters.
+ if (additional_parameters_size > 0)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ ScopedSharedMemory buffer(sizeof(*parameters));
+ if (!buffer.IsValid())
+ return STATUS_INVALID_PARAMETER;
+ memcpy(buffer.memory(), parameters, sizeof(*parameters));
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT,
+ static_cast<void*>(protected_output), buffer.handle(), &answer);
+
+ if (code != SBOX_ALL_OK) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetGetOPMInformation(
+ GetOPMInformationFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information) {
+ size_t max_size =
+ std::max(sizeof(*parameters), sizeof(*requested_information));
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ ScopedSharedMemory buffer(base::checked_cast<uint32_t>(max_size));
+ if (!buffer.IsValid())
+ return STATUS_INVALID_PARAMETER;
+ memcpy(buffer.memory(), parameters, sizeof(*parameters));
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETOPMINFORMATION,
+ static_cast<void*>(protected_output), buffer.handle(), &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ if (!answer.nt_status) {
+ memcpy(requested_information, buffer.memory(),
+ sizeof(*requested_information));
+ }
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI
+TargetGetOPMRandomNumber(GetOPMRandomNumberFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ InOutCountedBuffer buffer(random_number, sizeof(*random_number));
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETOPMRANDOMNUMBER,
+ static_cast<void*>(protected_output), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetGetSuggestedOPMProtectedOutputArraySize(
+ GetSuggestedOPMProtectedOutputArraySizeFunction,
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_size) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ std::wstring device_name_str;
+ UnicodeStringToString(device_name, &device_name_str);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
+ device_name_str.c_str(), &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ if (!answer.nt_status)
+ *suggested_output_size = answer.extended[0].unsigned_int;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers(
+ SetOPMSigningKeyAndSequenceNumbersFunction,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters) {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ DXGKMDT_OPM_ENCRYPTED_PARAMETERS temp_parameters = *parameters;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ InOutCountedBuffer buffer(&temp_parameters, sizeof(temp_parameters));
+ ResultCode code =
+ CrossCall(ipc, IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS,
+ static_cast<void*>(protected_output), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ return answer.nt_status;
+}
+
+NTSTATUS WINAPI
+TargetCreateOPMProtectedOutputs(CreateOPMProtectedOutputsFunction,
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD outputs_array_size,
+ DWORD* output_size,
+ OPM_PROTECTED_OUTPUT_HANDLE* outputs_array) {
+ if (vos != DXGKMDT_OPM_VOS_OPM_SEMANTICS)
+ return STATUS_INVALID_PARAMETER;
+
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return STATUS_ACCESS_DENIED;
+ void* ipc_memory = GetGlobalIPCMemory();
+ if (!ipc_memory)
+ return STATUS_ACCESS_DENIED;
+
+ CrossCallReturn answer = {};
+ SharedMemIPCClient ipc(ipc_memory);
+ base::CheckedNumeric<uint32_t> array_size = outputs_array_size;
+ array_size *= sizeof(HANDLE);
+ if (!array_size.IsValid())
+ return STATUS_INVALID_PARAMETER;
+
+ InOutCountedBuffer buffer(outputs_array, array_size.ValueOrDie());
+ std::wstring device_name_str;
+ UnicodeStringToString(device_name, &device_name_str);
+ ResultCode code = CrossCall(ipc, IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS,
+ device_name_str.c_str(), buffer, &answer);
+
+ if (code != SBOX_ALL_OK)
+ return STATUS_ACCESS_DENIED;
+
+ if (!answer.nt_status)
+ *output_size = answer.extended[0].unsigned_int;
+
+ return answer.nt_status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h
new file mode 100644
index 0000000000..befcda2767
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_interception.h
@@ -0,0 +1,151 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
+
+#include <windows.h>
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+const DWORD kProtectedVideoOutputSectionSize = 16 * 1024;
+const DWORD kMaxEnumMonitors = 32;
+
+struct EnumMonitorsResult {
+ DWORD monitor_count;
+ HMONITOR monitors[kMaxEnumMonitors];
+};
+
+typedef BOOL(WINAPI* GdiDllInitializeFunction)(HANDLE dll,
+ DWORD reason,
+ LPVOID reserved);
+
+using GetStockObjectFunction = decltype(&::GetStockObject);
+
+using RegisterClassWFunction = decltype(&::RegisterClassW);
+
+using EnumDisplayMonitorsFunction = decltype(&::EnumDisplayMonitors);
+
+using EnumDisplayDevicesAFunction = decltype(&::EnumDisplayDevicesA);
+
+using GetMonitorInfoWFunction = decltype(&::GetMonitorInfoW);
+using GetMonitorInfoAFunction = decltype(&::GetMonitorInfoA);
+
+extern "C" {
+
+// Interceptor for the GdiDllInitialize function.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGdiDllInitialize(GdiDllInitializeFunction orig_gdi_dll_initialize,
+ HANDLE dll,
+ DWORD reason);
+
+// Interceptor for the GetStockObject function.
+SANDBOX_INTERCEPT HGDIOBJ WINAPI
+TargetGetStockObject(GetStockObjectFunction orig_get_stock_object, int object);
+
+// Interceptor for the RegisterClassW function.
+SANDBOX_INTERCEPT ATOM WINAPI
+TargetRegisterClassW(RegisterClassWFunction orig_register_class_function,
+ const WNDCLASS* wnd_class);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetEnumDisplayMonitors(
+ EnumDisplayMonitorsFunction orig_enum_display_monitors_function,
+ HDC hdc,
+ LPCRECT clip_rect,
+ MONITORENUMPROC enum_function,
+ LPARAM data);
+
+SANDBOX_INTERCEPT BOOL WINAPI TargetEnumDisplayDevicesA(
+ EnumDisplayDevicesAFunction orig_enum_display_devices_function,
+ LPCSTR device,
+ DWORD device_number,
+ PDISPLAY_DEVICEA display_device,
+ DWORD flags);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoA(GetMonitorInfoAFunction orig_get_monitor_info_function,
+ HMONITOR monitor,
+ LPMONITORINFO monitor_info);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetGetMonitorInfoW(GetMonitorInfoWFunction orig_get_monitor_info_function,
+ HMONITOR monitor,
+ LPMONITORINFO monitor_info);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetCreateOPMProtectedOutputs(
+ CreateOPMProtectedOutputsFunction orig_create_proceted_outputs_function,
+ PUNICODE_STRING device_name,
+ DXGKMDT_OPM_VIDEO_OUTPUT_SEMANTICS vos,
+ DWORD protected_output_array_size,
+ DWORD* output_array_size,
+ OPM_PROTECTED_OUTPUT_HANDLE* protected_outputs);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetGetCertificate(GetCertificateFunction orig_get_certificate_function,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateSize(
+ GetCertificateSizeFunction orig_get_certificate_size_function,
+ PUNICODE_STRING device_name,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateByHandle(
+ GetCertificateByHandleFunction orig_get_certificate_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ BYTE* certificate,
+ ULONG certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetCertificateSizeByHandle(
+ GetCertificateSizeByHandleFunction orig_get_certificate_size_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_CERTIFICATE_TYPE certificate_type,
+ ULONG* certificate_length);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetDestroyOPMProtectedOutput(
+ DestroyOPMProtectedOutputFunction orig_destroy_protected_output_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetConfigureOPMProtectedOutput(
+ ConfigureOPMProtectedOutputFunction
+ origin_configure_protected_output_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_CONFIGURE_PARAMETERS* parameters,
+ ULONG additional_parameters_size,
+ const BYTE* additional_parameters);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetOPMInformation(
+ GetOPMInformationFunction origin_get_information_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_GET_INFO_PARAMETERS* parameters,
+ DXGKMDT_OPM_REQUESTED_INFORMATION* requested_information);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetOPMRandomNumber(
+ GetOPMRandomNumberFunction orig_get_random_number_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ DXGKMDT_OPM_RANDOM_NUMBER* random_number);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetGetSuggestedOPMProtectedOutputArraySize(
+ GetSuggestedOPMProtectedOutputArraySizeFunction
+ orig_get_suggested_size_function,
+ PUNICODE_STRING device_name,
+ DWORD* suggested_output_array_size);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetSetOPMSigningKeyAndSequenceNumbers(
+ SetOPMSigningKeyAndSequenceNumbersFunction orig_set_signing_keys_function,
+ OPM_PROTECTED_OUTPUT_HANDLE protected_output,
+ const DXGKMDT_OPM_ENCRYPTED_PARAMETERS* parameters);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc
new file mode 100644
index 0000000000..8ab915ec33
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.cc
@@ -0,0 +1,410 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/process_mitigations_win32k_interception.h"
+
+namespace sandbox {
+
+namespace {
+
+// Define GUIDs for OPM APIs
+const GUID DXGKMDT_OPM_GET_CONNECTOR_TYPE = {
+ 0x81d0bfd5,
+ 0x6afe,
+ 0x48c2,
+ {0x99, 0xc0, 0x95, 0xa0, 0x8f, 0x97, 0xc5, 0xda}};
+const GUID DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES = {
+ 0x38f2a801,
+ 0x9a6c,
+ 0x48bb,
+ {0x91, 0x07, 0xb6, 0x69, 0x6e, 0x6f, 0x17, 0x97}};
+const GUID DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL = {
+ 0xb2075857,
+ 0x3eda,
+ 0x4d5d,
+ {0x88, 0xdb, 0x74, 0x8f, 0x8c, 0x1a, 0x05, 0x49}};
+const GUID DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL = {
+ 0x1957210a,
+ 0x7766,
+ 0x452a,
+ {0xb9, 0x9a, 0xd2, 0x7a, 0xed, 0x54, 0xf0, 0x3a}};
+const GUID DXGKMDT_OPM_SET_PROTECTION_LEVEL = {
+ 0x9bb9327c,
+ 0x4eb5,
+ 0x4727,
+ {0x9f, 0x00, 0xb4, 0x2b, 0x09, 0x19, 0xc0, 0xda}};
+
+void StringToUnicodeString(PUNICODE_STRING unicode_string,
+ const std::wstring& device_name) {
+ static RtlInitUnicodeStringFunction RtlInitUnicodeString;
+ if (!RtlInitUnicodeString) {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ RtlInitUnicodeString = reinterpret_cast<RtlInitUnicodeStringFunction>(
+ GetProcAddress(ntdll, "RtlInitUnicodeString"));
+ }
+ RtlInitUnicodeString(unicode_string, device_name.c_str());
+}
+
+struct MonitorListState {
+ HMONITOR* monitor_list;
+ uint32_t monitor_list_size;
+ uint32_t monitor_list_pos;
+};
+
+BOOL CALLBACK DisplayMonitorEnumProc(HMONITOR monitor,
+ HDC hdc_monitor,
+ LPRECT rect_monitor,
+ LPARAM data) {
+ MonitorListState* state = reinterpret_cast<MonitorListState*>(data);
+ if (state->monitor_list_pos >= state->monitor_list_size)
+ return false;
+ state->monitor_list[state->monitor_list_pos++] = monitor;
+ return true;
+}
+
+template <typename T>
+T GetExportedFunc(const wchar_t* libname, const char* name) {
+ OverrideForTestFunction test_override =
+ ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback();
+ if (test_override)
+ return reinterpret_cast<T>(test_override(name));
+
+ static T func = nullptr;
+ if (!func) {
+ func =
+ reinterpret_cast<T>(::GetProcAddress(::GetModuleHandle(libname), name));
+ DCHECK(!!func);
+ }
+ return func;
+}
+
+#define GDIFUNC(name) GetExportedFunc<name##Function>(L"gdi32.dll", #name)
+#define USERFUNC(name) GetExportedFunc<name##Function>(L"user32.dll", #name)
+
+struct ValidateMonitorParams {
+ HMONITOR monitor;
+ std::wstring device_name;
+ bool result;
+};
+
+bool GetMonitorDeviceName(HMONITOR monitor, std::wstring* device_name) {
+ MONITORINFOEXW monitor_info = {};
+ monitor_info.cbSize = sizeof(monitor_info);
+ if (!USERFUNC(GetMonitorInfoW)(monitor, &monitor_info))
+ return false;
+ if (monitor_info.szDevice[CCHDEVICENAME - 1] != 0)
+ return false;
+ *device_name = monitor_info.szDevice;
+ return true;
+}
+
+BOOL CALLBACK ValidateMonitorEnumProc(HMONITOR monitor,
+ HDC,
+ LPRECT,
+ LPARAM data) {
+ ValidateMonitorParams* valid_params =
+ reinterpret_cast<ValidateMonitorParams*>(data);
+ std::wstring device_name;
+ bool result = false;
+ if (valid_params->device_name.empty()) {
+ result = monitor == valid_params->monitor;
+ } else if (GetMonitorDeviceName(monitor, &device_name)) {
+ result = device_name == valid_params->device_name;
+ }
+ valid_params->result = result;
+ if (!result)
+ return true;
+ return false;
+}
+
+bool IsValidMonitorOrDeviceName(HMONITOR monitor, const wchar_t* device_name) {
+ ValidateMonitorParams params = {};
+ params.monitor = monitor;
+ if (device_name)
+ params.device_name = device_name;
+ USERFUNC(EnumDisplayMonitors)
+ (nullptr, nullptr, ValidateMonitorEnumProc,
+ reinterpret_cast<LPARAM>(&params));
+ return params.result;
+}
+
+} // namespace
+
+OverrideForTestFunction
+ ProcessMitigationsWin32KLockdownPolicy::override_callback_;
+
+bool ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ PolicyRule rule(FAKE_SUCCESS);
+ if (!policy->AddRule(IpcTag::GDI_GDIDLLINITIALIZE, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETSTOCKOBJECT, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::USER_REGISTERCLASSW, &rule))
+ return false;
+ if (semantics != TargetPolicy::IMPLEMENT_OPM_APIS)
+ return true;
+ if (!policy->AddRule(IpcTag::USER_ENUMDISPLAYMONITORS, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::USER_ENUMDISPLAYDEVICES, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::USER_GETMONITORINFO, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETCERTIFICATE, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETCERTIFICATESIZE, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETOPMINFORMATION, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETOPMRANDOMNUMBER, &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE,
+ &rule))
+ return false;
+ if (!policy->AddRule(IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS, &rule))
+ return false;
+ return true;
+}
+
+uint32_t ProcessMitigationsWin32KLockdownPolicy::EnumDisplayMonitorsAction(
+ const ClientInfo& client_info,
+ HMONITOR* monitor_list,
+ uint32_t monitor_list_size) {
+ MonitorListState state = {monitor_list, monitor_list_size, 0};
+ USERFUNC(EnumDisplayMonitors)
+ (nullptr, nullptr, DisplayMonitorEnumProc, reinterpret_cast<LPARAM>(&state));
+ return state.monitor_list_pos;
+}
+
+bool ProcessMitigationsWin32KLockdownPolicy::GetMonitorInfoAction(
+ const ClientInfo& client_info,
+ HMONITOR monitor,
+ MONITORINFO* monitor_info_ptr) {
+ if (!IsValidMonitorOrDeviceName(monitor, nullptr))
+ return false;
+ MONITORINFOEXW monitor_info = {};
+ monitor_info.cbSize = sizeof(MONITORINFOEXW);
+
+ bool success = USERFUNC(GetMonitorInfoW)(
+ monitor, reinterpret_cast<MONITORINFO*>(&monitor_info));
+ if (success)
+ memcpy(monitor_info_ptr, &monitor_info, sizeof(monitor_info));
+ return success;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
+ GetSuggestedOPMProtectedOutputArraySizeAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* suggested_array_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+ DWORD suggested_array_size_dword = 0;
+ NTSTATUS status = GDIFUNC(GetSuggestedOPMProtectedOutputArraySize)(
+ &unicode_device_name, &suggested_array_size_dword);
+ if (!status)
+ *suggested_array_size = suggested_array_size_dword;
+ return status;
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::CreateOPMProtectedOutputsAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ HANDLE* protected_outputs,
+ uint32_t array_input_size,
+ uint32_t* array_output_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+ DWORD output_size = 0;
+
+ NTSTATUS status = GDIFUNC(CreateOPMProtectedOutputs)(
+ &unicode_device_name, DXGKMDT_OPM_VOS_OPM_SEMANTICS, array_input_size,
+ &output_size,
+ reinterpret_cast<OPM_PROTECTED_OUTPUT_HANDLE*>(protected_outputs));
+ if (!status)
+ *array_output_size = output_size;
+ return status;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* cert_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+
+ return GDIFUNC(GetCertificateSize)(&unicode_device_name,
+ DXGKMDT_OPM_CERTIFICATE,
+ reinterpret_cast<DWORD*>(cert_size));
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ BYTE* cert_data,
+ uint32_t cert_size) {
+ if (!IsValidMonitorOrDeviceName(nullptr, device_name.c_str())) {
+ return STATUS_ACCESS_DENIED;
+ }
+ UNICODE_STRING unicode_device_name;
+ StringToUnicodeString(&unicode_device_name, device_name);
+
+ return GDIFUNC(GetCertificate)(&unicode_device_name, DXGKMDT_OPM_CERTIFICATE,
+ cert_data, cert_size);
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::GetCertificateSizeByHandleAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ uint32_t* cert_size) {
+ auto get_certificate_size_func = GDIFUNC(GetCertificateSizeByHandle);
+ if (get_certificate_size_func) {
+ return get_certificate_size_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
+ reinterpret_cast<DWORD*>(cert_size));
+ }
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetCertificateByHandleAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ BYTE* cert_data,
+ uint32_t cert_size) {
+ auto get_certificate_func = GDIFUNC(GetCertificateByHandle);
+ if (get_certificate_func) {
+ return get_certificate_func(protected_output, DXGKMDT_OPM_CERTIFICATE,
+ cert_data, cert_size);
+ }
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMRandomNumberAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* random_number) {
+ return GDIFUNC(GetOPMRandomNumber)(
+ protected_output, static_cast<DXGKMDT_OPM_RANDOM_NUMBER*>(random_number));
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::
+ SetOPMSigningKeyAndSequenceNumbersAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters) {
+ return GDIFUNC(SetOPMSigningKeyAndSequenceNumbers)(
+ protected_output,
+ static_cast<DXGKMDT_OPM_ENCRYPTED_PARAMETERS*>(parameters));
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::ConfigureOPMProtectedOutputAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr) {
+ DXGKMDT_OPM_CONFIGURE_PARAMETERS parameters;
+ memcpy(&parameters, parameters_ptr, sizeof(parameters));
+ if (parameters.guidSetting != DXGKMDT_OPM_SET_PROTECTION_LEVEL ||
+ parameters.cbParametersSize !=
+ sizeof(DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ DXGKMDT_OPM_SET_PROTECTION_LEVEL_PARAMETERS prot_level;
+ memcpy(&prot_level, parameters.abParameters, sizeof(prot_level));
+ if (prot_level.Reserved || prot_level.Reserved2)
+ return STATUS_INVALID_PARAMETER;
+
+ if (prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_HDCP &&
+ prot_level.ulProtectionType != DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ // Protection levels are same for HDCP and DPCP.
+ if (prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_OFF &&
+ prot_level.ulProtectionLevel != DXGKMDT_OPM_HDCP_ON) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ return GDIFUNC(ConfigureOPMProtectedOutput)(protected_output, &parameters, 0,
+ nullptr);
+}
+
+NTSTATUS ProcessMitigationsWin32KLockdownPolicy::GetOPMInformationAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr,
+ void* requested_info_ptr) {
+ DXGKMDT_OPM_GET_INFO_PARAMETERS parameters;
+ memcpy(&parameters, parameters_ptr, sizeof(parameters));
+
+ bool valid_parameters = false;
+ // Validate sizes based on the type being requested.
+ if ((parameters.guidInformation == DXGKMDT_OPM_GET_CONNECTOR_TYPE ||
+ parameters.guidInformation ==
+ DXGKMDT_OPM_GET_SUPPORTED_PROTECTION_TYPES) &&
+ parameters.cbParametersSize == 0) {
+ valid_parameters = true;
+ } else if ((parameters.guidInformation ==
+ DXGKMDT_OPM_GET_VIRTUAL_PROTECTION_LEVEL ||
+ parameters.guidInformation ==
+ DXGKMDT_OPM_GET_ACTUAL_PROTECTION_LEVEL) &&
+ parameters.cbParametersSize == sizeof(uint32_t)) {
+ uint32_t param_value;
+ memcpy(&param_value, parameters.abParameters, sizeof(param_value));
+ if (param_value == DXGKMDT_OPM_PROTECTION_TYPE_HDCP ||
+ param_value == DXGKMDT_OPM_PROTECTION_TYPE_DPCP) {
+ valid_parameters = true;
+ }
+ }
+ if (!valid_parameters)
+ return STATUS_INVALID_PARAMETER;
+ DXGKMDT_OPM_REQUESTED_INFORMATION requested_info = {};
+ NTSTATUS status = GDIFUNC(GetOPMInformation)(protected_output, &parameters,
+ &requested_info);
+ if (!status)
+ memcpy(requested_info_ptr, &requested_info, sizeof(requested_info));
+
+ return status;
+}
+
+NTSTATUS
+ProcessMitigationsWin32KLockdownPolicy::DestroyOPMProtectedOutputAction(
+ HANDLE protected_output) {
+ return GDIFUNC(DestroyOPMProtectedOutput)(protected_output);
+}
+
+void ProcessMitigationsWin32KLockdownPolicy::SetOverrideForTestCallback(
+ OverrideForTestFunction callback) {
+ override_callback_ = callback;
+}
+
+OverrideForTestFunction
+ProcessMitigationsWin32KLockdownPolicy::GetOverrideForTestCallback() {
+ return override_callback_;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h
new file mode 100644
index 0000000000..0c2b2dff95
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_mitigations_win32k_policy.h
@@ -0,0 +1,91 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+#define SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// A callback function type to get a function for testing.
+typedef void* (*OverrideForTestFunction)(const char* name);
+
+// This class centralizes most of the knowledge related to the process
+// mitigations Win32K lockdown policy.
+class ProcessMitigationsWin32KLockdownPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for the Win32K process mitigation policy.
+ // name is the object name, semantics is the desired semantics for the
+ // open or create and policy is the policy generator to which the rules are
+ // going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ static uint32_t EnumDisplayMonitorsAction(const ClientInfo& client_info,
+ HMONITOR* monitor_list,
+ uint32_t monitor_list_size);
+ static bool GetMonitorInfoAction(const ClientInfo& client_info,
+ HMONITOR monitor,
+ MONITORINFO* monitor_info);
+
+ static NTSTATUS GetSuggestedOPMProtectedOutputArraySizeAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* suggested_array_size);
+
+ static NTSTATUS CreateOPMProtectedOutputsAction(
+ const ClientInfo& client_info,
+ const std::wstring& device_name,
+ HANDLE* protected_outputs,
+ uint32_t array_input_size,
+ uint32_t* array_output_size);
+
+ static NTSTATUS GetCertificateSizeAction(const ClientInfo& client_info,
+ const std::wstring& device_name,
+ uint32_t* cert_size);
+ static NTSTATUS GetCertificateAction(const ClientInfo& client_info,
+ const std::wstring& device_name,
+ BYTE* cert_data,
+ uint32_t cert_size);
+ static NTSTATUS GetCertificateSizeByHandleAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ uint32_t* cert_size);
+ static NTSTATUS GetCertificateByHandleAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ BYTE* cert_data,
+ uint32_t cert_size);
+ static NTSTATUS GetOPMRandomNumberAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* random_number);
+ static NTSTATUS SetOPMSigningKeyAndSequenceNumbersAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters);
+ static NTSTATUS ConfigureOPMProtectedOutputAction(
+ const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr);
+ static NTSTATUS GetOPMInformationAction(const ClientInfo& client_info,
+ HANDLE protected_output,
+ void* parameters_ptr,
+ void* requested_information_ptr);
+ static NTSTATUS DestroyOPMProtectedOutputAction(HANDLE protected_output);
+ static void SetOverrideForTestCallback(OverrideForTestFunction callback);
+ static OverrideForTestFunction GetOverrideForTestCallback();
+
+ private:
+ static OverrideForTestFunction override_callback_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_MITIGATIONS_WIN32K_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/process_policy_test.cc
new file mode 100644
index 0000000000..c321c3a088
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_policy_test.cc
@@ -0,0 +1,548 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <memory>
+#include <string>
+
+#include "base/memory/free_deleter.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "base/win/windows_version.h"
+#include "build/build_config.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Creates a process with the |exe| and |command| parameter using the
+// unicode and ascii version of the api.
+sandbox::SboxTestResult CreateProcessHelper(const std::wstring& exe,
+ const std::wstring& command) {
+ base::win::ScopedProcessInformation pi;
+ STARTUPINFOW si = {sizeof(si)};
+ const wchar_t* exe_name = nullptr;
+ if (!exe.empty())
+ exe_name = exe.c_str();
+
+ std::unique_ptr<wchar_t, base::FreeDeleter> writable_command(
+ _wcsdup(command.c_str()));
+
+ // Create the process with the unicode version of the API.
+ sandbox::SboxTestResult ret1 = sandbox::SBOX_TEST_FAILED;
+ PROCESS_INFORMATION temp_process_info = {};
+ if (::CreateProcessW(
+ exe_name, command.empty() ? nullptr : writable_command.get(), nullptr,
+ nullptr, false, 0, nullptr, nullptr, &si, &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret1 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret1 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret1 = sandbox::SBOX_TEST_FAILED;
+ }
+ }
+
+ pi.Close();
+
+ // Do the same with the ansi version of the api
+ STARTUPINFOA sia = {sizeof(sia)};
+ sandbox::SboxTestResult ret2 = sandbox::SBOX_TEST_FAILED;
+
+ std::string narrow_cmd_line =
+ base::SysWideToMultiByte(command.c_str(), CP_UTF8);
+ if (::CreateProcessA(
+ exe_name ? base::SysWideToMultiByte(exe_name, CP_UTF8).c_str()
+ : nullptr,
+ command.empty() ? nullptr : &narrow_cmd_line[0], nullptr, nullptr,
+ false, 0, nullptr, nullptr, &sia, &temp_process_info)) {
+ pi.Set(temp_process_info);
+ ret2 = sandbox::SBOX_TEST_SUCCEEDED;
+ } else {
+ DWORD last_error = GetLastError();
+ if ((ERROR_NOT_ENOUGH_QUOTA == last_error) ||
+ (ERROR_ACCESS_DENIED == last_error) ||
+ (ERROR_FILE_NOT_FOUND == last_error)) {
+ ret2 = sandbox::SBOX_TEST_DENIED;
+ } else {
+ ret2 = sandbox::SBOX_TEST_FAILED;
+ }
+ }
+
+ if (ret1 == ret2)
+ return ret1;
+
+ return sandbox::SBOX_TEST_FAILED;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Process_RunApp1(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ // TEST 1: Try with the path in the app_name.
+ return CreateProcessHelper(path, std::wstring());
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp2(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ // TEST 2: Try with the path in the cmd_line.
+ std::wstring cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\"";
+ return CreateProcessHelper(std::wstring(), cmd_line);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp3(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ // TEST 3: Try file name in the cmd_line.
+ return CreateProcessHelper(std::wstring(), argv[0]);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp4(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // TEST 4: Try file name in the app_name and current directory sets correctly.
+ std::wstring system32 = MakePathToSys(L"", false);
+ wchar_t current_directory[MAX_PATH + 1];
+ DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ if (!ret)
+ return SBOX_TEST_FIRST_ERROR;
+ if (ret >= MAX_PATH)
+ return SBOX_TEST_FAILED;
+
+ current_directory[ret] = L'\\';
+ current_directory[ret + 1] = L'\0';
+ if (!::SetCurrentDirectory(system32.c_str()))
+ return SBOX_TEST_SECOND_ERROR;
+
+ const int result4 = CreateProcessHelper(argv[0], std::wstring());
+ return ::SetCurrentDirectory(current_directory) ? result4 : SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp5(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ // TEST 5: Try with the path in the cmd_line and arguments.
+ std::wstring cmd_line = L"\"";
+ cmd_line += path;
+ cmd_line += L"\" /I";
+ return CreateProcessHelper(std::wstring(), cmd_line);
+}
+
+SBOX_TESTS_COMMAND int Process_RunApp6(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ // TEST 6: Try with the file_name in the cmd_line and arguments.
+ std::wstring cmd_line = argv[0];
+ cmd_line += L" /I";
+ return CreateProcessHelper(std::wstring(), cmd_line);
+}
+
+// Creates a process and checks if it's possible to get a handle to it's token.
+SBOX_TESTS_COMMAND int Process_GetChildProcessToken(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ STARTUPINFOW si = {sizeof(si)};
+
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessW(path.c_str(), nullptr, nullptr, nullptr, false,
+ CREATE_SUSPENDED, nullptr, nullptr, &si,
+ &temp_process_info)) {
+ return SBOX_TEST_FAILED;
+ }
+ base::win::ScopedProcessInformation pi(temp_process_info);
+
+ HANDLE token = nullptr;
+ bool result =
+ ::OpenProcessToken(pi.process_handle(), TOKEN_IMPERSONATE, &token);
+ DWORD error = ::GetLastError();
+
+ base::win::ScopedHandle token_handle(token);
+
+ if (!::TerminateProcess(pi.process_handle(), 0))
+ return SBOX_TEST_FAILED;
+
+ if (result && token)
+ return SBOX_TEST_SUCCEEDED;
+
+ if (ERROR_ACCESS_DENIED == error)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+// Creates a suspended process using CreateProcessA then kill it.
+SBOX_TESTS_COMMAND int Process_CreateProcessA(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (!argv || !argv[0])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ STARTUPINFOA si = {sizeof(si)};
+
+ std::wstring path = MakePathToSys(argv[0], false);
+
+ PROCESS_INFORMATION temp_process_info = {};
+ // Create suspended to avoid popping calc.
+ if (!::CreateProcessA(base::SysWideToMultiByte(path, CP_UTF8).c_str(),
+ nullptr, nullptr, nullptr, false, CREATE_SUSPENDED,
+ nullptr, nullptr, &si, &temp_process_info)) {
+ return SBOX_TEST_FAILED;
+ }
+ base::win::ScopedProcessInformation pi(temp_process_info);
+
+ if (!::TerminateProcess(pi.process_handle(), 0))
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+SBOX_TESTS_COMMAND int Process_OpenToken(int argc, wchar_t** argv) {
+ HANDLE token;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
+ if (ERROR_ACCESS_DENIED == ::GetLastError()) {
+ return SBOX_TEST_DENIED;
+ }
+ } else {
+ ::CloseHandle(token);
+ return SBOX_TEST_SUCCEEDED;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int Process_Crash(int argc, wchar_t** argv) {
+ __debugbreak();
+ return SBOX_TEST_FAILED;
+}
+// Generate a event name, used to test thread creation.
+std::wstring GenerateEventName(DWORD pid) {
+ wchar_t buff[30] = {0};
+ int res = swprintf_s(buff, sizeof(buff) / sizeof(buff[0]),
+ L"ProcessPolicyTest_%08x", pid);
+ if (-1 != res) {
+ return std::wstring(buff);
+ }
+ return std::wstring();
+}
+
+// This is the function that is called when testing thread creation.
+// It is expected to set an event that the caller is waiting on.
+DWORD WINAPI TestThreadFunc(LPVOID lpdwThreadParam) {
+ std::wstring event_name = GenerateEventName(
+ static_cast<DWORD>(reinterpret_cast<uintptr_t>(lpdwThreadParam)));
+ if (!event_name.length())
+ return 1;
+ HANDLE event = ::OpenEvent(EVENT_ALL_ACCESS | EVENT_MODIFY_STATE, false,
+ event_name.c_str());
+ if (!event)
+ return 1;
+ if (!SetEvent(event))
+ return 1;
+ return 0;
+}
+
+SBOX_TESTS_COMMAND int Process_CreateThread(int argc, wchar_t** argv) {
+ DWORD pid = ::GetCurrentProcessId();
+ std::wstring event_name = GenerateEventName(pid);
+ if (!event_name.length())
+ return SBOX_TEST_FIRST_ERROR;
+ HANDLE event = ::CreateEvent(nullptr, true, false, event_name.c_str());
+ if (!event)
+ return SBOX_TEST_SECOND_ERROR;
+
+ DWORD thread_id = 0;
+ HANDLE thread = nullptr;
+ thread = ::CreateThread(nullptr, 0, &TestThreadFunc,
+ reinterpret_cast<LPVOID>(static_cast<uintptr_t>(pid)),
+ 0, &thread_id);
+
+ if (!thread)
+ return SBOX_TEST_THIRD_ERROR;
+ if (!thread_id)
+ return SBOX_TEST_FOURTH_ERROR;
+ if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
+ return SBOX_TEST_FIFTH_ERROR;
+ DWORD exit_code = 0;
+ if (!GetExitCodeThread(thread, &exit_code))
+ return SBOX_TEST_SIXTH_ERROR;
+ if (exit_code)
+ return SBOX_TEST_SEVENTH_ERROR;
+ if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0)
+ return SBOX_TEST_FAILED;
+ return SBOX_TEST_SUCCEEDED;
+}
+
+// Creates a process and checks its exit code. Succeeds on exit code 0.
+SBOX_TESTS_COMMAND int Process_CheckExitCode(int argc, wchar_t** argv) {
+ if (argc != 3)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ if (!argv || !argv[0] || !argv[1] || !argv[2])
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ std::wstring path = MakePathToSys(argv[0], false);
+ std::wstring cmdline = argv[1];
+ std::wstring cwd = argv[2];
+
+ STARTUPINFOW si = {sizeof(si)};
+
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessW(path.c_str(), &cmdline[0], nullptr, nullptr, false, 0,
+ nullptr, cwd.c_str(), &si, &temp_process_info)) {
+ return SBOX_TEST_FAILED;
+ }
+ base::win::ScopedProcessInformation pi(temp_process_info);
+ DWORD ret = WaitForSingleObject(pi.process_handle(), 1000);
+ if (ret != WAIT_OBJECT_0)
+ return SBOX_TEST_FAILED;
+
+ DWORD exit_code;
+ if (!GetExitCodeProcess(pi.process_handle(), &exit_code))
+ return SBOX_TEST_FAILED;
+
+ if (exit_code != 0)
+ return SBOX_TEST_FAILED;
+
+ return SBOX_TEST_SUCCEEDED;
+}
+
+TEST(ProcessPolicyTest, TestAllAccess) {
+ // Check if the "all access" rule fails to be added when the token is too
+ // powerful.
+ TestRunner runner;
+
+ // Check the failing case.
+ runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_LOCKDOWN);
+ EXPECT_EQ(SBOX_ERROR_UNSUPPORTED,
+ runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ L"this is not important"));
+
+ // Check the working case.
+ runner.GetPolicy()->SetTokenLevel(USER_INTERACTIVE, USER_INTERACTIVE);
+
+ EXPECT_EQ(SBOX_ALL_OK,
+ runner.GetPolicy()->AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC,
+ L"this is not important"));
+}
+
+TEST(ProcessPolicyTest, CreateProcessAW) {
+ TestRunner runner;
+ std::wstring maybe_virtual_exe_path = MakePathToSys(L"findstr.exe", false);
+ std::wstring non_virtual_exe_path = MakePathToSys32(L"findstr.exe", false);
+ ASSERT_TRUE(!maybe_virtual_exe_path.empty());
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ maybe_virtual_exe_path.c_str()));
+
+ if (non_virtual_exe_path != maybe_virtual_exe_path) {
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC,
+ non_virtual_exe_path.c_str()));
+ }
+
+ // Need to add directory rules for the directories that we use in
+ // SetCurrentDirectory.
+ EXPECT_TRUE(runner.AddRuleSys32(TargetPolicy::FILES_ALLOW_DIR_ANY, L""));
+
+ wchar_t current_directory[MAX_PATH];
+ DWORD ret = ::GetCurrentDirectory(MAX_PATH, current_directory);
+ ASSERT_TRUE(0 != ret && ret < MAX_PATH);
+
+ wcscat_s(current_directory, MAX_PATH, L"\\");
+ EXPECT_TRUE(
+ runner.AddFsRule(TargetPolicy::FILES_ALLOW_DIR_ANY, current_directory));
+
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp1 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp2 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp3 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp4 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp5 calc.exe"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Process_RunApp6 calc.exe"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp1 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp2 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp3 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp4 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp5 findstr.exe"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_RunApp6 findstr.exe"));
+}
+
+// Tests that the broker correctly handles a process crashing within the job.
+// Fails on Windows ARM64: https://crbug.com/905526
+#if defined(ARCH_CPU_ARM64)
+#define MAYBE_CreateProcessCrashy DISABLED_CreateProcessCrashy
+#else
+#define MAYBE_CreateProcessCrashy CreateProcessCrashy
+#endif
+TEST(ProcessPolicyTest, MAYBE_CreateProcessCrashy) {
+ TestRunner runner;
+ EXPECT_EQ(static_cast<int>(STATUS_BREAKPOINT),
+ runner.RunTest(L"Process_Crash"));
+}
+
+TEST(ProcessPolicyTest, CreateProcessWithCWD) {
+ TestRunner runner;
+ std::wstring sys_path = MakePathToSys(L"", false);
+ while (!sys_path.empty() && sys_path.back() == L'\\')
+ sys_path.erase(sys_path.length() - 1);
+
+ std::wstring exe_path = MakePathToSys(L"cmd.exe", false);
+ std::wstring cmd_line =
+ L"\"/c if \\\"%CD%\\\" NEQ \\\"" + sys_path + L"\\\" exit 1\"";
+
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC, exe_path.c_str()));
+
+ std::wstring command =
+ L"Process_CheckExitCode cmd.exe " + cmd_line + L" " + sys_path;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(command.c_str()));
+}
+
+TEST(ProcessPolicyTest, OpenToken) {
+ TestRunner runner;
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_OpenToken"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMinAccess) {
+ TestRunner runner;
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccess) {
+ TestRunner runner(JOB_UNPROTECTED, USER_INTERACTIVE, USER_INTERACTIVE);
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMinAccessNoJob) {
+ TestRunner runner(JOB_NONE, USER_RESTRICTED_SAME_ACCESS, USER_LOCKDOWN);
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_MIN_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestGetProcessTokenMaxAccessNoJob) {
+ TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
+ std::wstring exe_path = MakePathToSys(L"findstr.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC, exe_path.c_str()));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_GetChildProcessToken findstr.exe"));
+}
+
+TEST(ProcessPolicyTest, TestCreateProcessA) {
+ TestRunner runner;
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->SetJobLevel(JOB_NONE, 0);
+ policy->SetTokenLevel(USER_UNPROTECTED, USER_UNPROTECTED);
+ std::wstring exe_path = MakePathToSys(L"calc.exe", false);
+ ASSERT_TRUE(!exe_path.empty());
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_PROCESS,
+ TargetPolicy::PROCESS_ALL_EXEC, exe_path.c_str()));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Process_CreateProcessA calc.exe"));
+}
+
+// This tests that the CreateThread works with CSRSS not locked down.
+// In other words, that the interception passes through OK.
+TEST(ProcessPolicyTest, TestCreateThreadWithCsrss) {
+ TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
+ runner.SetDisableCsrss(false);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_CreateThread"));
+}
+
+// This tests that the CreateThread works with CSRSS locked down.
+// In other words, that the interception correctly works.
+TEST(ProcessPolicyTest, TestCreateThreadWithoutCsrss) {
+ TestRunner runner(JOB_NONE, USER_INTERACTIVE, USER_INTERACTIVE);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Process_CreateThread"));
+}
+
+// This tests that our CreateThread interceptors works when called directly.
+TEST(ProcessPolicyTest, TestCreateThreadOutsideSandbox) {
+ DWORD pid = ::GetCurrentProcessId();
+ std::wstring event_name = GenerateEventName(pid);
+ ASSERT_STRNE(nullptr, event_name.c_str());
+ HANDLE event = ::CreateEvent(nullptr, true, false, event_name.c_str());
+ EXPECT_NE(static_cast<HANDLE>(nullptr), event);
+
+ DWORD thread_id = 0;
+ HANDLE thread = nullptr;
+ thread = TargetCreateThread(
+ ::CreateThread, nullptr, 0, &TestThreadFunc,
+ reinterpret_cast<LPVOID>(static_cast<uintptr_t>(pid)), 0, &thread_id);
+ EXPECT_NE(static_cast<HANDLE>(nullptr), thread);
+ EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(thread, INFINITE));
+ EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(event, INFINITE));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc
new file mode 100644
index 0000000000..7f0b0a6542
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.cc
@@ -0,0 +1,275 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_thread_dispatcher.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/logging.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/process_thread_interception.h"
+#include "sandbox/win/src/process_thread_policy.h"
+#include "sandbox/win/src/sandbox.h"
+
+namespace {
+
+// Extracts the application name from a command line.
+//
+// The application name is the first element of the command line. If
+// there is no quotes, the first element is delimited by the first space.
+// If there are quotes, the first element is delimited by the quotes.
+//
+// The create process call is smarter than us. It tries really hard to launch
+// the process even if the command line is wrong. For example:
+// "c:\program files\test param" will first try to launch c:\program.exe then
+// c:\program files\test.exe. We don't do that, we stop after at the first
+// space when there is no quotes.
+std::wstring GetPathFromCmdLine(const std::wstring& cmd_line) {
+ std::wstring exe_name;
+ // Check if it starts with '"'.
+ if (cmd_line[0] == L'\"') {
+ // Find the position of the second '"', this terminates the path.
+ std::wstring::size_type pos = cmd_line.find(L'\"', 1);
+ if (std::wstring::npos == pos)
+ return cmd_line;
+ exe_name = cmd_line.substr(1, pos - 1);
+ } else {
+ // There is no '"', that means that the appname is terminated at the
+ // first space.
+ std::wstring::size_type pos = cmd_line.find(L' ');
+ if (std::wstring::npos == pos) {
+ // There is no space, the cmd_line contains only the app_name
+ exe_name = cmd_line;
+ } else {
+ exe_name = cmd_line.substr(0, pos);
+ }
+ }
+
+ return exe_name;
+}
+
+// Returns true is the path in parameter is relative. False if it's
+// absolute.
+bool IsPathRelative(const std::wstring& path) {
+ // A path is Relative if it's not a UNC path beginnning with \\ or a
+ // path beginning with a drive. (i.e. X:\)
+ if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1)
+ return false;
+ return true;
+}
+
+// Converts a relative path to an absolute path.
+bool ConvertToAbsolutePath(const std::wstring& child_current_directory,
+ bool use_env_path,
+ std::wstring* path) {
+ wchar_t file_buffer[MAX_PATH];
+ wchar_t* file_part = nullptr;
+
+ // Here we should start by looking at the path where the child application was
+ // started. We don't have this information yet.
+ DWORD result = 0;
+ if (use_env_path) {
+ // Try with the complete path
+ result = ::SearchPath(nullptr, path->c_str(), nullptr, MAX_PATH,
+ file_buffer, &file_part);
+ }
+
+ if (0 == result) {
+ // Try with the current directory of the child
+ result = ::SearchPath(child_current_directory.c_str(), path->c_str(),
+ nullptr, MAX_PATH, file_buffer, &file_part);
+ }
+
+ if (0 == result || result >= MAX_PATH)
+ return false;
+
+ *path = file_buffer;
+ return true;
+}
+
+} // namespace
+namespace sandbox {
+
+ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall open_thread = {
+ {IpcTag::NTOPENTHREAD, {UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenThread)};
+
+ static const IPCCall open_process = {
+ {IpcTag::NTOPENPROCESS, {UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcess)};
+
+ static const IPCCall process_token = {
+ {IpcTag::NTOPENPROCESSTOKEN, {VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcessToken)};
+
+ static const IPCCall process_tokenex = {
+ {IpcTag::NTOPENPROCESSTOKENEX, {VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::NtOpenProcessTokenEx)};
+
+ static const IPCCall create_params = {
+ {IpcTag::CREATEPROCESSW,
+ {WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::CreateProcessW)};
+
+ // NOTE(liamjm): 2nd param is size_t: Using VOIDPTR_TYPE as they are
+ // the same size on windows.
+ static_assert(sizeof(size_t) == sizeof(void*),
+ "VOIDPTR_TYPE not same size as size_t");
+ static const IPCCall create_thread_params = {
+ {IpcTag::CREATETHREAD,
+ {VOIDPTR_TYPE, VOIDPTR_TYPE, VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(
+ &ThreadProcessDispatcher::CreateThread)};
+
+ ipc_calls_.push_back(open_thread);
+ ipc_calls_.push_back(open_process);
+ ipc_calls_.push_back(process_token);
+ ipc_calls_.push_back(process_tokenex);
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(create_thread_params);
+}
+
+bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ switch (service) {
+ case IpcTag::NTOPENTHREAD:
+ case IpcTag::NTOPENPROCESS:
+ case IpcTag::NTOPENPROCESSTOKEN:
+ case IpcTag::NTOPENPROCESSTOKENEX:
+ case IpcTag::CREATETHREAD:
+ // There is no explicit policy for these services.
+ NOTREACHED();
+ return false;
+
+ case IpcTag::CREATEPROCESSW:
+ return INTERCEPT_EAT(manager, kKerneldllName, CreateProcessW,
+ CREATE_PROCESSW_ID, 44) &&
+ INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
+ CREATE_PROCESSA_ID, 44);
+
+ default:
+ return false;
+ }
+}
+
+bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc,
+ uint32_t desired_access,
+ uint32_t thread_id) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenThreadAction(
+ *ipc->client_info, desired_access, thread_id, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc,
+ uint32_t desired_access,
+ uint32_t process_id) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessAction(
+ *ipc->client_info, desired_access, process_id, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc,
+ HANDLE process,
+ uint32_t desired_access) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(
+ *ipc->client_info, process, desired_access, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc,
+ HANDLE process,
+ uint32_t desired_access,
+ uint32_t attributes) {
+ HANDLE handle;
+ NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(
+ *ipc->client_info, process, desired_access, attributes, &handle);
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc,
+ std::wstring* name,
+ std::wstring* cmd_line,
+ std::wstring* cur_dir,
+ std::wstring* target_cur_dir,
+ CountedBuffer* info) {
+ if (sizeof(PROCESS_INFORMATION) != info->Size())
+ return false;
+
+ // Check if there is an application name.
+ std::wstring exe_name;
+ if (!name->empty())
+ exe_name = *name;
+ else
+ exe_name = GetPathFromCmdLine(*cmd_line);
+
+ if (IsPathRelative(exe_name)) {
+ if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) {
+ // Cannot find the path. Maybe the file does not exist.
+ ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND;
+ return true;
+ }
+ }
+
+ const wchar_t* const_exe_name = exe_name.c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(const_exe_name);
+
+ EvalResult eval =
+ policy_base_->EvalPolicy(IpcTag::CREATEPROCESSW, params.GetBase());
+
+ PROCESS_INFORMATION* proc_info =
+ reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer());
+ // Here we force the app_name to be the one we used for the policy lookup.
+ // If our logic was wrong, at least we wont allow create a random process.
+ DWORD ret = ProcessPolicy::CreateProcessWAction(
+ eval, *ipc->client_info, exe_name, *cmd_line, *target_cur_dir, proc_info);
+
+ ipc->return_info.win32_result = ret;
+ return true;
+}
+
+bool ThreadProcessDispatcher::CreateThread(IPCInfo* ipc,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags) {
+ if (!start_address) {
+ return false;
+ }
+
+ HANDLE handle;
+ DWORD ret = ProcessPolicy::CreateThreadAction(
+ *ipc->client_info, stack_size, start_address, parameter, creation_flags,
+ nullptr, &handle);
+
+ ipc->return_info.nt_status = ret;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h
new file mode 100644
index 0000000000..07466c46e2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_dispatcher.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2010 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_PROCESS_THREAD_DISPATCHER_H_
+#define SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles process and thread-related IPC calls.
+class ThreadProcessDispatcher : public Dispatcher {
+ public:
+ explicit ThreadProcessDispatcher(PolicyBase* policy_base);
+ ~ThreadProcessDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtOpenThread() in the target.
+ bool NtOpenThread(IPCInfo* ipc, uint32_t desired_access, uint32_t thread_id);
+
+ // Processes IPC requests coming from calls to NtOpenProcess() in the target.
+ bool NtOpenProcess(IPCInfo* ipc,
+ uint32_t desired_access,
+ uint32_t process_id);
+
+ // Processes IPC requests from calls to NtOpenProcessToken() in the target.
+ bool NtOpenProcessToken(IPCInfo* ipc,
+ HANDLE process,
+ uint32_t desired_access);
+
+ // Processes IPC requests from calls to NtOpenProcessTokenEx() in the target.
+ bool NtOpenProcessTokenEx(IPCInfo* ipc,
+ HANDLE process,
+ uint32_t desired_access,
+ uint32_t attributes);
+
+ // Processes IPC requests coming from calls to CreateProcessW() in the target.
+ bool CreateProcessW(IPCInfo* ipc,
+ std::wstring* name,
+ std::wstring* cmd_line,
+ std::wstring* cur_dir,
+ std::wstring* target_cur_dir,
+ CountedBuffer* info);
+
+ // Processes IPC requests coming from calls to CreateThread() in the target.
+ bool CreateThread(IPCInfo* ipc,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(ThreadProcessDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc
new file mode 100644
index 0000000000..0bd3d5a8d2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.cc
@@ -0,0 +1,520 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/process_thread_interception.h"
+
+#include <stdint.h>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+// Hooks NtOpenThread and proxy the call to the broker if it's trying to
+// open a thread in the same process.
+NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
+ PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NTSTATUS status =
+ orig_OpenThread(thread, desired_access, object_attributes, client_id);
+ if (NT_SUCCESS(status))
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenThread");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+ if (!client_id)
+ break;
+
+ uint32_t thread_id = 0;
+ bool should_break = false;
+ __try {
+ // We support only the calls for the current process
+ if (client_id->UniqueProcess)
+ should_break = true;
+
+ // Object attributes should be nullptr or empty.
+ if (!should_break && object_attributes) {
+ if (object_attributes->Attributes || object_attributes->ObjectName ||
+ object_attributes->RootDirectory ||
+ object_attributes->SecurityDescriptor ||
+ object_attributes->SecurityQualityOfService) {
+ should_break = true;
+ }
+ }
+
+ thread_id = static_cast<uint32_t>(
+ reinterpret_cast<ULONG_PTR>(client_id->UniqueThread));
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ if (should_break)
+ break;
+
+ if (!ValidParameter(thread, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENTHREAD, desired_access,
+ thread_id, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // The nt_status here is most likely STATUS_INVALID_CID because
+ // in the broker we set the process id in the CID (client ID) param
+ // to be the current process. If you try to open a thread from another
+ // process you will get this INVALID_CID error. On the other hand, if you
+ // try to open a thread in your own process, it should return success.
+ // We don't want to return STATUS_INVALID_CID here, so we return the
+ // return of the original open thread status, which is most likely
+ // STATUS_ACCESS_DENIED.
+ break;
+
+ __try {
+ // Write the output parameters.
+ *thread = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ mozilla::sandboxing::LogAllowed("NtOpenThread");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+// Hooks NtOpenProcess and proxy the call to the broker if it's trying to
+// open the current process.
+NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,
+ PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id) {
+ NTSTATUS status =
+ orig_OpenProcess(process, desired_access, object_attributes, client_id);
+ if (NT_SUCCESS(status))
+ return status;
+
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+ if (!client_id)
+ break;
+
+ uint32_t process_id = 0;
+ bool should_break = false;
+ __try {
+ // Object attributes should be nullptr or empty.
+ if (!should_break && object_attributes) {
+ if (object_attributes->Attributes || object_attributes->ObjectName ||
+ object_attributes->RootDirectory ||
+ object_attributes->SecurityDescriptor ||
+ object_attributes->SecurityQualityOfService) {
+ should_break = true;
+ }
+ }
+
+ process_id = static_cast<uint32_t>(
+ reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess));
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ if (should_break)
+ break;
+
+ if (!ValidParameter(process, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESS, desired_access,
+ process_id, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *process = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessToken(process, desired_access, token);
+ if (NT_SUCCESS(status))
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenProcessToken");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESSTOKEN, process,
+ desired_access, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ mozilla::sandboxing::LogAllowed("NtOpenProcessToken");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token) {
+ NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access,
+ handle_attributes, token);
+ if (NT_SUCCESS(status))
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenProcessTokenEx");
+ do {
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ break;
+
+ if (CURRENT_PROCESS != process)
+ break;
+
+ if (!ValidParameter(token, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENPROCESSTOKENEX, process,
+ desired_access, handle_attributes, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ return answer.nt_status;
+
+ __try {
+ // Write the output parameters.
+ *token = answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ mozilla::sandboxing::LogAllowed("NtOpenProcessTokenEx");
+ return answer.nt_status;
+ } while (false);
+
+ return status;
+}
+
+BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,
+ LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ if (SandboxFactory::GetTargetServices()->GetState()->IsCsrssConnected() &&
+ orig_CreateProcessW(application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags,
+ environment, current_directory, startup_info,
+ process_information)) {
+ return true;
+ }
+
+ mozilla::sandboxing::LogBlocked("CreateProcessW", application_name);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return false;
+
+ // Don't call GetLastError before InitCalled() succeeds because kernel32 may
+ // not be mapped yet.
+ DWORD original_error = ::GetLastError();
+
+ do {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ const wchar_t* cur_dir = nullptr;
+
+ wchar_t this_current_directory[MAX_PATH];
+ DWORD result = ::GetCurrentDirectory(MAX_PATH, this_current_directory);
+ if (0 != result && result < MAX_PATH)
+ cur_dir = this_current_directory;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ InOutCountedBuffer proc_info(process_information,
+ sizeof(PROCESS_INFORMATION));
+
+ ResultCode code =
+ CrossCall(ipc, IpcTag::CREATEPROCESSW, application_name, command_line,
+ cur_dir, current_directory, proc_info, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return false;
+
+ mozilla::sandboxing::LogAllowed("CreateProcessW", application_name);
+ return true;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return false;
+}
+
+BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
+ LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information) {
+ if (SandboxFactory::GetTargetServices()->GetState()->IsCsrssConnected() &&
+ orig_CreateProcessA(application_name, command_line, process_attributes,
+ thread_attributes, inherit_handles, flags,
+ environment, current_directory, startup_info,
+ process_information)) {
+ return true;
+ }
+
+ mozilla::sandboxing::LogBlocked("CreateProcessA", application_name);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return false;
+
+ // Don't call GetLastError before InitCalled() succeeds because kernel32 may
+ // not be mapped yet.
+ DWORD original_error = ::GetLastError();
+
+ do {
+ if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
+ WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ // Convert the input params to unicode.
+ UNICODE_STRING* cmd_unicode = nullptr;
+ UNICODE_STRING* app_unicode = nullptr;
+ UNICODE_STRING* cwd_unicode = nullptr;
+ if (command_line) {
+ cmd_unicode = AnsiToUnicode(command_line);
+ if (!cmd_unicode)
+ break;
+ }
+
+ if (application_name) {
+ app_unicode = AnsiToUnicode(application_name);
+ if (!app_unicode) {
+ operator delete(cmd_unicode, NT_ALLOC);
+ break;
+ }
+ }
+
+ if (current_directory) {
+ cwd_unicode = AnsiToUnicode(current_directory);
+ if (!cwd_unicode) {
+ operator delete(cmd_unicode, NT_ALLOC);
+ operator delete(app_unicode, NT_ALLOC);
+ break;
+ }
+ }
+
+ const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : nullptr;
+ const wchar_t* app_name = app_unicode ? app_unicode->Buffer : nullptr;
+ const wchar_t* cwd = cwd_unicode ? cwd_unicode->Buffer : nullptr;
+ const wchar_t* cur_dir = nullptr;
+
+ wchar_t target_current_directory[MAX_PATH];
+ DWORD result = ::GetCurrentDirectory(MAX_PATH, target_current_directory);
+ if (0 != result && result < MAX_PATH)
+ cur_dir = target_current_directory;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ InOutCountedBuffer proc_info(process_information,
+ sizeof(PROCESS_INFORMATION));
+
+ ResultCode code = CrossCall(ipc, IpcTag::CREATEPROCESSW, app_name, cmd_line,
+ cur_dir, cwd, proc_info, &answer);
+
+ operator delete(cmd_unicode, NT_ALLOC);
+ operator delete(app_unicode, NT_ALLOC);
+ operator delete(cwd_unicode, NT_ALLOC);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return false;
+
+ mozilla::sandboxing::LogAllowed("CreateProcessA", application_name);
+ return true;
+ } while (false);
+
+ ::SetLastError(original_error);
+ return false;
+}
+
+HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id) {
+ HANDLE hThread = nullptr;
+
+ TargetServices* target_services = SandboxFactory::GetTargetServices();
+ if (!target_services || target_services->GetState()->IsCsrssConnected()) {
+ hThread = orig_CreateThread(thread_attributes, stack_size, start_address,
+ parameter, creation_flags, thread_id);
+ if (hThread)
+ return hThread;
+ }
+
+ DWORD original_error = ::GetLastError();
+ do {
+ if (!target_services)
+ break;
+
+ // We don't trust that the IPC can work this early.
+ if (!target_services->GetState()->InitCalled())
+ break;
+
+ __try {
+ if (thread_id && !ValidParameter(thread_id, sizeof(*thread_id), WRITE))
+ break;
+
+ if (!start_address)
+ break;
+ // We don't support thread_attributes not being null.
+ if (thread_attributes)
+ break;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ // NOTE: we don't pass the thread_attributes through. This matches the
+ // approach in CreateProcess and in CreateThreadInternal().
+ ResultCode code = CrossCall(ipc, IpcTag::CREATETHREAD,
+ reinterpret_cast<LPVOID>(stack_size),
+ reinterpret_cast<LPVOID>(start_address),
+ parameter, creation_flags, &answer);
+ if (SBOX_ALL_OK != code)
+ break;
+
+ ::SetLastError(answer.win32_result);
+ if (ERROR_SUCCESS != answer.win32_result)
+ return nullptr;
+
+ __try {
+ if (thread_id)
+ *thread_id = ::GetThreadId(answer.handle);
+ return answer.handle;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ ::SetLastError(original_error);
+ return nullptr;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_interception.h b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.h
new file mode 100644
index 0000000000..2608d7d31e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_interception.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_PROCESS_THREAD_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_PROCESS_THREAD_INTERCEPTION_H_
+
+#include <windows.h>
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+namespace {
+
+using CreateProcessWFunction = decltype(&::CreateProcessW);
+
+using CreateProcessAFunction = decltype(&::CreateProcessA);
+
+using CreateThreadFunction = decltype(&::CreateThread);
+
+using GetUserDefaultLCIDFunction = decltype(&::GetUserDefaultLCID);
+
+} // namespace
+
+extern "C" {
+
+// Interception of NtOpenThread on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
+ PHANDLE thread,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcess on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,
+ PHANDLE process,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PCLIENT_ID client_id);
+
+// Interception of NtOpenProcessToken on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessToken(NtOpenProcessTokenFunction orig_OpenProcessToken,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ PHANDLE token);
+
+// Interception of NtOpenProcessTokenEx on the child process.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenProcessTokenEx(NtOpenProcessTokenExFunction orig_OpenProcessTokenEx,
+ HANDLE process,
+ ACCESS_MASK desired_access,
+ ULONG handle_attributes,
+ PHANDLE token);
+
+// Interception of CreateProcessW and A in kernel32.dll.
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,
+ LPCWSTR application_name,
+ LPWSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCWSTR current_directory,
+ LPSTARTUPINFOW startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+SANDBOX_INTERCEPT BOOL WINAPI
+TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
+ LPCSTR application_name,
+ LPSTR command_line,
+ LPSECURITY_ATTRIBUTES process_attributes,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ BOOL inherit_handles,
+ DWORD flags,
+ LPVOID environment,
+ LPCSTR current_directory,
+ LPSTARTUPINFOA startup_info,
+ LPPROCESS_INFORMATION process_information);
+
+// Interception of CreateThread in kernel32.dll.
+SANDBOX_INTERCEPT HANDLE WINAPI
+TargetCreateThread(CreateThreadFunction orig_CreateThread,
+ LPSECURITY_ATTRIBUTES thread_attributes,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ LPVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_PROCESS_THREAD_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc
new file mode 100644
index 0000000000..20146a83df
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.cc
@@ -0,0 +1,269 @@
+// Copyright (c) 2011 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/process_thread_policy.h"
+
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/memory/free_deleter.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// These are the only safe rights that can be given to a sandboxed
+// process for the process created by the broker. All others are potential
+// vectors of privilege elevation.
+const DWORD kProcessRights = SYNCHRONIZE | PROCESS_QUERY_INFORMATION |
+ PROCESS_QUERY_LIMITED_INFORMATION |
+ PROCESS_TERMINATE | PROCESS_SUSPEND_RESUME;
+
+const DWORD kThreadRights = SYNCHRONIZE | THREAD_TERMINATE |
+ THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION |
+ THREAD_QUERY_LIMITED_INFORMATION |
+ THREAD_SET_LIMITED_INFORMATION;
+
+// Creates a child process and duplicates the handles to 'target_process'. The
+// remaining parameters are the same as CreateProcess().
+bool CreateProcessExWHelper(HANDLE target_process,
+ bool give_full_access,
+ LPCWSTR lpApplicationName,
+ LPWSTR lpCommandLine,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ bool bInheritHandles,
+ DWORD dwCreationFlags,
+ LPVOID lpEnvironment,
+ LPCWSTR lpCurrentDirectory,
+ LPSTARTUPINFOW lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation) {
+ if (!::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
+ lpThreadAttributes, bInheritHandles, dwCreationFlags,
+ lpEnvironment, lpCurrentDirectory, lpStartupInfo,
+ lpProcessInformation)) {
+ return false;
+ }
+
+ DWORD process_access = kProcessRights;
+ DWORD thread_access = kThreadRights;
+ if (give_full_access) {
+ process_access = PROCESS_ALL_ACCESS;
+ thread_access = THREAD_ALL_ACCESS;
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hProcess,
+ target_process, &lpProcessInformation->hProcess,
+ process_access, false, DUPLICATE_CLOSE_SOURCE)) {
+ ::CloseHandle(lpProcessInformation->hThread);
+ return false;
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hThread,
+ target_process, &lpProcessInformation->hThread,
+ thread_access, false, DUPLICATE_CLOSE_SOURCE)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool ProcessPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::unique_ptr<PolicyRule> process;
+ switch (semantics) {
+ case TargetPolicy::PROCESS_MIN_EXEC: {
+ process.reset(new PolicyRule(GIVE_READONLY));
+ break;
+ };
+ case TargetPolicy::PROCESS_ALL_EXEC: {
+ process.reset(new PolicyRule(GIVE_ALLACCESS));
+ break;
+ };
+ default: { return false; };
+ }
+
+ if (!process->AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::CREATEPROCESSW, process.get())) {
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info,
+ uint32_t desired_access,
+ uint32_t thread_id,
+ HANDLE* handle) {
+ *handle = nullptr;
+
+ NtOpenThreadFunction NtOpenThread = nullptr;
+ ResolveNTFunctionPtr("NtOpenThread", &NtOpenThread);
+
+ OBJECT_ATTRIBUTES attributes = {0};
+ attributes.Length = sizeof(attributes);
+ CLIENT_ID client_id = {0};
+ client_id.UniqueProcess =
+ reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(client_info.process_id));
+ client_id.UniqueThread =
+ reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(thread_id));
+
+ HANDLE local_handle = nullptr;
+ NTSTATUS status =
+ NtOpenThread(&local_handle, desired_access, &attributes, &client_id);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info,
+ uint32_t desired_access,
+ uint32_t process_id,
+ HANDLE* handle) {
+ *handle = nullptr;
+
+ NtOpenProcessFunction NtOpenProcess = nullptr;
+ ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess);
+
+ if (client_info.process_id != process_id)
+ return STATUS_ACCESS_DENIED;
+
+ OBJECT_ATTRIBUTES attributes = {0};
+ attributes.Length = sizeof(attributes);
+ CLIENT_ID client_id = {0};
+ client_id.UniqueProcess =
+ reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(client_info.process_id));
+ HANDLE local_handle = nullptr;
+ NTSTATUS status =
+ NtOpenProcess(&local_handle, desired_access, &attributes, &client_id);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ HANDLE* handle) {
+ *handle = nullptr;
+ NtOpenProcessTokenFunction NtOpenProcessToken = nullptr;
+ ResolveNTFunctionPtr("NtOpenProcessToken", &NtOpenProcessToken);
+
+ if (CURRENT_PROCESS != process)
+ return STATUS_ACCESS_DENIED;
+
+ HANDLE local_handle = nullptr;
+ NTSTATUS status =
+ NtOpenProcessToken(client_info.process, desired_access, &local_handle);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ return status;
+}
+
+NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ uint32_t attributes,
+ HANDLE* handle) {
+ *handle = nullptr;
+ NtOpenProcessTokenExFunction NtOpenProcessTokenEx = nullptr;
+ ResolveNTFunctionPtr("NtOpenProcessTokenEx", &NtOpenProcessTokenEx);
+
+ if (CURRENT_PROCESS != process)
+ return STATUS_ACCESS_DENIED;
+
+ HANDLE local_handle = nullptr;
+ NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access,
+ attributes, &local_handle);
+ if (NT_SUCCESS(status)) {
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ return status;
+}
+
+DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& app_name,
+ const std::wstring& command_line,
+ const std::wstring& current_dir,
+ PROCESS_INFORMATION* process_info) {
+ // The only action supported is ASK_BROKER which means create the process.
+ if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) {
+ return ERROR_ACCESS_DENIED;
+ }
+
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(
+ _wcsdup(command_line.c_str()));
+
+ bool should_give_full_access = (GIVE_ALLACCESS == eval_result);
+
+ const wchar_t* cwd = current_dir.c_str();
+ if (current_dir.empty())
+ cwd = nullptr;
+
+ if (!CreateProcessExWHelper(client_info.process, should_give_full_access,
+ app_name.c_str(), cmd_line.get(), nullptr,
+ nullptr, false, 0, nullptr, cwd, &startup_info,
+ process_info)) {
+ return ERROR_ACCESS_DENIED;
+ }
+ return ERROR_SUCCESS;
+}
+
+DWORD ProcessPolicy::CreateThreadAction(
+ const ClientInfo& client_info,
+ const SIZE_T stack_size,
+ const LPTHREAD_START_ROUTINE start_address,
+ const LPVOID parameter,
+ const DWORD creation_flags,
+ LPDWORD thread_id,
+ HANDLE* handle) {
+ *handle = nullptr;
+ HANDLE local_handle =
+ ::CreateRemoteThread(client_info.process, nullptr, stack_size,
+ start_address, parameter, creation_flags, thread_id);
+ if (!local_handle) {
+ return ::GetLastError();
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return ERROR_ACCESS_DENIED;
+ }
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h
new file mode 100644
index 0000000000..f6f96dd0ff
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/process_thread_policy.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2006-2010 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_PROCESS_THREAD_POLICY_H_
+#define SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to process execution.
+class ProcessPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level.
+ // policy rule for process creation
+ // 'name' is the executable to be spawn.
+ // 'semantics' is the desired semantics.
+ // 'policy' is the policy generator to which the rules are going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Opens a thread from the child process and returns the handle.
+ // client_info contains the information about the child process,
+ // desired_access is the access requested by the child and thread_id
+ // is the thread_id to be opened.
+ // The function returns the return value of NtOpenThread.
+ static NTSTATUS OpenThreadAction(const ClientInfo& client_info,
+ uint32_t desired_access,
+ uint32_t thread_id,
+ HANDLE* handle);
+
+ // Opens the process id passed in and returns the duplicated handle to
+ // the child. We only allow the child processes to open themselves. Any other
+ // pid open is denied.
+ static NTSTATUS OpenProcessAction(const ClientInfo& client_info,
+ uint32_t desired_access,
+ uint32_t process_id,
+ HANDLE* handle);
+
+ // Opens the token associated with the process and returns the duplicated
+ // handle to the child. We only allow the child processes to open its own
+ // token (using ::GetCurrentProcess()).
+ static NTSTATUS OpenProcessTokenAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ HANDLE* handle);
+
+ // Opens the token associated with the process and returns the duplicated
+ // handle to the child. We only allow the child processes to open its own
+ // token (using ::GetCurrentProcess()).
+ static NTSTATUS OpenProcessTokenExAction(const ClientInfo& client_info,
+ HANDLE process,
+ uint32_t desired_access,
+ uint32_t attributes,
+ HANDLE* handle);
+
+ // Processes a 'CreateProcessW()' request from the target.
+ // 'client_info' : the target process that is making the request.
+ // 'eval_result' : The desired policy action to accomplish.
+ // 'app_name' : The full path of the process to be created.
+ // 'command_line' : The command line passed to the created process.
+ // 'current_dir' : The CWD with which to spawn the child process.
+ static DWORD CreateProcessWAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& app_name,
+ const std::wstring& command_line,
+ const std::wstring& current_dir,
+ PROCESS_INFORMATION* process_info);
+
+ // Processes a 'CreateThread()' request from the target.
+ // 'client_info' : the target process that is making the request.
+ static DWORD CreateThreadAction(const ClientInfo& client_info,
+ SIZE_T stack_size,
+ LPTHREAD_START_ROUTINE start_address,
+ PVOID parameter,
+ DWORD creation_flags,
+ LPDWORD thread_id,
+ HANDLE* handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_PROCESS_THREAD_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc
new file mode 100644
index 0000000000..26d2bb3ffc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2011 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/registry_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/registry_interception.h"
+#include "sandbox/win/src/registry_policy.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+// Builds a path using the root directory and the name.
+bool GetCompletePath(HANDLE root,
+ const std::wstring& name,
+ std::wstring* complete_name) {
+ if (root) {
+ if (!sandbox::GetPathFromHandle(root, complete_name))
+ return false;
+
+ *complete_name += L"\\";
+ *complete_name += name;
+ } else {
+ *complete_name = name;
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+RegistryDispatcher::RegistryDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::NTCREATEKEY,
+ {WCHAR_TYPE, UINT32_TYPE, VOIDPTR_TYPE, UINT32_TYPE, UINT32_TYPE,
+ UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtCreateKey)};
+
+ static const IPCCall open_params = {
+ {IpcTag::NTOPENKEY, {WCHAR_TYPE, UINT32_TYPE, VOIDPTR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&RegistryDispatcher::NtOpenKey)};
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_params);
+}
+
+bool RegistryDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (IpcTag::NTCREATEKEY == service)
+ return INTERCEPT_NT(manager, NtCreateKey, CREATE_KEY_ID, 32);
+
+ if (IpcTag::NTOPENKEY == service) {
+ bool result = INTERCEPT_NT(manager, NtOpenKey, OPEN_KEY_ID, 16);
+ result &= INTERCEPT_NT(manager, NtOpenKeyEx, OPEN_KEY_EX_ID, 20);
+ return result;
+ }
+
+ return false;
+}
+
+bool RegistryDispatcher::NtCreateKey(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ uint32_t desired_access,
+ uint32_t title_index,
+ uint32_t create_options) {
+ base::win::ScopedHandle root_handle;
+ std::wstring real_path = *name;
+
+ // If there is a root directory, we need to duplicate the handle to make
+ // it valid in this process.
+ if (root) {
+ if (!::DuplicateHandle(ipc->client_info->process, root,
+ ::GetCurrentProcess(), &root, 0, false,
+ DUPLICATE_SAME_ACCESS))
+ return false;
+
+ root_handle.Set(root);
+ }
+
+ if (!GetCompletePath(root, *name, &real_path))
+ return false;
+
+ const wchar_t* regname = real_path.c_str();
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::NAME] = ParamPickerMake(regname);
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTCREATEKEY, params.GetBase());
+
+ HANDLE handle;
+ NTSTATUS nt_status;
+ ULONG disposition = 0;
+ if (!RegistryPolicy::CreateKeyAction(
+ result, *ipc->client_info, *name, attributes, root, desired_access,
+ title_index, create_options, &handle, &nt_status, &disposition)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.extended[0].unsigned_int = disposition;
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool RegistryDispatcher::NtOpenKey(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ uint32_t desired_access) {
+ base::win::ScopedHandle root_handle;
+ std::wstring real_path = *name;
+
+ // If there is a root directory, we need to duplicate the handle to make
+ // it valid in this process.
+ if (root) {
+ if (!::DuplicateHandle(ipc->client_info->process, root,
+ ::GetCurrentProcess(), &root, 0, false,
+ DUPLICATE_SAME_ACCESS))
+ return false;
+ root_handle.Set(root);
+ }
+
+ if (!GetCompletePath(root, *name, &real_path))
+ return false;
+
+ const wchar_t* regname = real_path.c_str();
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::NAME] = ParamPickerMake(regname);
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTOPENKEY, params.GetBase());
+ HANDLE handle;
+ NTSTATUS nt_status;
+ if (!RegistryPolicy::OpenKeyAction(result, *ipc->client_info, *name,
+ attributes, root, desired_access, &handle,
+ &nt_status)) {
+ ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
+ return true;
+ }
+
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = nt_status;
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h
new file mode 100644
index 0000000000..c23b95098b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_dispatcher.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 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_REGISTRY_DISPATCHER_H_
+#define SANDBOX_SRC_REGISTRY_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles registry-related IPC calls.
+class RegistryDispatcher : public Dispatcher {
+ public:
+ explicit RegistryDispatcher(PolicyBase* policy_base);
+ ~RegistryDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to NtCreateKey in the target.
+ bool NtCreateKey(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ uint32_t desired_access,
+ uint32_t title_index,
+ uint32_t create_options);
+
+ // Processes IPC requests coming from calls to NtOpenKey in the target.
+ bool NtOpenKey(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t attributes,
+ HANDLE root,
+ uint32_t desired_access);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(RegistryDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_interception.cc b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc
new file mode 100644
index 0000000000..d8bb82949b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_interception.cc
@@ -0,0 +1,261 @@
+// Copyright (c) 2006-2008 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/registry_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+NTSTATUS WINAPI TargetNtCreateKey(NtCreateKeyFunction orig_CreateKey,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG title_index,
+ PUNICODE_STRING class_name,
+ ULONG create_options,
+ PULONG disposition) {
+ // Check if the process can create it first.
+ NTSTATUS status =
+ orig_CreateKey(key, desired_access, object_attributes, title_index,
+ class_name, create_options, disposition);
+ if (NT_SUCCESS(status))
+ return status;
+
+ if (STATUS_OBJECT_NAME_NOT_FOUND != status) {
+ mozilla::sandboxing::LogBlocked("NtCreateKey",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ }
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(key, sizeof(HANDLE), WRITE))
+ break;
+
+ if (disposition && !ValidParameter(disposition, sizeof(ULONG), WRITE))
+ break;
+
+ // At this point we don't support class_name.
+ if (class_name && class_name->Buffer && class_name->Length)
+ break;
+
+ // We don't support creating link keys, volatile keys and backup/restore.
+ if (create_options)
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ HANDLE root_directory = 0;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ &root_directory);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
+
+ bool query_broker = false;
+ {
+ std::unique_ptr<wchar_t, NtAllocDeleter> full_name;
+ const wchar_t* name_ptr = name.get();
+ const wchar_t* full_name_ptr = nullptr;
+
+ if (root_directory) {
+ ret = sandbox::AllocAndGetFullPath(root_directory, name.get(),
+ &full_name);
+ if (!NT_SUCCESS(ret) || !full_name)
+ break;
+ full_name_ptr = full_name.get();
+ params[OpenKey::NAME] = ParamPickerMake(full_name_ptr);
+ } else {
+ params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+ }
+
+ query_broker = QueryBroker(IpcTag::NTCREATEKEY, params.GetBase());
+ }
+
+ if (!query_broker)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ ResultCode code = CrossCall(ipc, IpcTag::NTCREATEKEY, name.get(),
+ attributes, root_directory, desired_access,
+ title_index, create_options, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // TODO(nsylvain): We should return answer.nt_status here instead
+ // of status. We can do this only after we checked the policy.
+ // otherwise we will returns ACCESS_DENIED for all paths
+ // that are not specified by a policy, even though your token allows
+ // access to that path, and the original call had a more meaningful
+ // error. Bug 4369
+ break;
+
+ __try {
+ *key = answer.handle;
+
+ if (disposition)
+ *disposition = answer.extended[0].unsigned_int;
+
+ status = answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtCreateKey",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI CommonNtOpenKey(NTSTATUS status,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(key, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes;
+ HANDLE root_directory;
+ NTSTATUS ret = AllocAndCopyName(object_attributes, &name, &attributes,
+ &root_directory);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ uint32_t desired_access_uint32 = desired_access;
+ CountedParameterSet<OpenKey> params;
+ params[OpenKey::ACCESS] = ParamPickerMake(desired_access_uint32);
+
+ bool query_broker = false;
+ {
+ std::unique_ptr<wchar_t, NtAllocDeleter> full_name;
+ const wchar_t* name_ptr = name.get();
+ const wchar_t* full_name_ptr = nullptr;
+
+ if (root_directory) {
+ ret = sandbox::AllocAndGetFullPath(root_directory, name.get(),
+ &full_name);
+ if (!NT_SUCCESS(ret) || !full_name)
+ break;
+ full_name_ptr = full_name.get();
+ params[OpenKey::NAME] = ParamPickerMake(full_name_ptr);
+ } else {
+ params[OpenKey::NAME] = ParamPickerMake(name_ptr);
+ }
+
+ query_broker = QueryBroker(IpcTag::NTOPENKEY, params.GetBase());
+ }
+
+ if (!query_broker)
+ break;
+
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+ ResultCode code = CrossCall(ipc, IpcTag::NTOPENKEY, name.get(), attributes,
+ root_directory, desired_access, &answer);
+
+ if (SBOX_ALL_OK != code)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ // TODO(nsylvain): We should return answer.nt_status here instead
+ // of status. We can do this only after we checked the policy.
+ // otherwise we will returns ACCESS_DENIED for all paths
+ // that are not specified by a policy, even though your token allows
+ // access to that path, and the original call had a more meaningful
+ // error. Bug 4369
+ break;
+
+ __try {
+ *key = answer.handle;
+ status = answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtOpenKey[Ex]",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenKey(NtOpenKeyFunction orig_OpenKey,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ // Check if the process can open it first.
+ NTSTATUS status = orig_OpenKey(key, desired_access, object_attributes);
+ if (NT_SUCCESS(status))
+ return status;
+
+ if (STATUS_OBJECT_NAME_NOT_FOUND != status) {
+ mozilla::sandboxing::LogBlocked("NtOpenKey",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ }
+
+ return CommonNtOpenKey(status, key, desired_access, object_attributes);
+}
+
+NTSTATUS WINAPI TargetNtOpenKeyEx(NtOpenKeyExFunction orig_OpenKeyEx,
+ PHANDLE key,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ ULONG open_options) {
+ // Check if the process can open it first.
+ NTSTATUS status =
+ orig_OpenKeyEx(key, desired_access, object_attributes, open_options);
+
+ // We do not support open_options at this time. The 2 current known values
+ // are REG_OPTION_CREATE_LINK, to open a symbolic link, and
+ // REG_OPTION_BACKUP_RESTORE to open the key with special privileges.
+ if (NT_SUCCESS(status) || open_options != 0)
+ return status;
+
+ if (STATUS_OBJECT_NAME_NOT_FOUND != status) {
+ mozilla::sandboxing::LogBlocked("NtOpenKeyEx",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ }
+
+ return CommonNtOpenKey(status, key, desired_access, object_attributes);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_interception.h b/security/sandbox/chromium/sandbox/win/src/registry_interception.h
new file mode 100644
index 0000000000..f47e56ce39
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_interception.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2006-2008 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_WIN_SRC_REGISTRY_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_REGISTRY_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtCreateKey on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtCreateKey(
+ NtCreateKeyFunction orig_CreateKey, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG title_index,
+ PUNICODE_STRING class_name, ULONG create_options, PULONG disposition);
+
+// Interception of NtOpenKey on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKey(
+ NtOpenKeyFunction orig_OpenKey, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+// Interception of NtOpenKeyEx on the child process.
+// It should never be called directly
+SANDBOX_INTERCEPT NTSTATUS WINAPI TargetNtOpenKeyEx(
+ NtOpenKeyExFunction orig_OpenKeyEx, PHANDLE key, ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes, ULONG open_options);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_REGISTRY_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy.cc b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc
new file mode 100644
index 0000000000..212ff713c4
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_policy.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2006-2008 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/registry_policy.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+static const uint32_t kAllowedRegFlags =
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | KEY_NOTIFY | KEY_READ |
+ GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL | KEY_WOW64_64KEY |
+ KEY_WOW64_32KEY;
+
+// Opens the key referenced by |obj_attributes| with |access| and
+// checks what permission was given. Remove the WRITE flags and update
+// |access| with the new value.
+NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes,
+ DWORD* access) {
+ NtOpenKeyFunction NtOpenKey = nullptr;
+ ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey);
+
+ NtCloseFunction NtClose = nullptr;
+ ResolveNTFunctionPtr("NtClose", &NtClose);
+
+ NtQueryObjectFunction NtQueryObject = nullptr;
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ // Open the key.
+ HANDLE handle;
+ NTSTATUS status = NtOpenKey(&handle, *access, obj_attributes);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ OBJECT_BASIC_INFORMATION info = {0};
+ status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info),
+ nullptr);
+ CHECK(NT_SUCCESS(NtClose(handle)));
+ if (!NT_SUCCESS(status))
+ return status;
+
+ *access = info.GrantedAccess & kAllowedRegFlags;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS NtCreateKeyInTarget(HANDLE* target_key_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ ULONG title_index,
+ UNICODE_STRING* class_name,
+ ULONG create_options,
+ ULONG* disposition,
+ HANDLE target_process) {
+ *target_key_handle = nullptr;
+ NtCreateKeyFunction NtCreateKey = nullptr;
+ ResolveNTFunctionPtr("NtCreateKey", &NtCreateKey);
+
+ if (MAXIMUM_ALLOWED & desired_access) {
+ NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access);
+ if (!NT_SUCCESS(status))
+ return STATUS_ACCESS_DENIED;
+ }
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status =
+ NtCreateKey(&local_handle, desired_access, obj_attributes, title_index,
+ class_name, create_options, disposition);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process,
+ target_key_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS NtOpenKeyInTarget(HANDLE* target_key_handle,
+ ACCESS_MASK desired_access,
+ OBJECT_ATTRIBUTES* obj_attributes,
+ HANDLE target_process) {
+ *target_key_handle = nullptr;
+ NtOpenKeyFunction NtOpenKey = nullptr;
+ ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey);
+
+ if (MAXIMUM_ALLOWED & desired_access) {
+ NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access);
+ if (!NT_SUCCESS(status))
+ return STATUS_ACCESS_DENIED;
+ }
+
+ HANDLE local_handle = INVALID_HANDLE_VALUE;
+ NTSTATUS status = NtOpenKey(&local_handle, desired_access, obj_attributes);
+
+ if (!NT_SUCCESS(status))
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, target_process,
+ target_key_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return STATUS_SUCCESS;
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool RegistryPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring resolved_name(name);
+ if (resolved_name.empty()) {
+ return false;
+ }
+
+ if (!ResolveRegistryName(resolved_name, &resolved_name))
+ return false;
+
+ name = resolved_name.c_str();
+
+ EvalResult result = ASK_BROKER;
+
+ PolicyRule open(result);
+ PolicyRule create(result);
+
+ switch (semantics) {
+ case TargetPolicy::REG_ALLOW_READONLY: {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write. Here we also support MAXIMUM_ALLOWED, but we are going
+ // to expand it to read-only before the call.
+ uint32_t restricted_flags = ~(kAllowedRegFlags | MAXIMUM_ALLOWED);
+ open.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND);
+ create.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND);
+ break;
+ }
+ case TargetPolicy::REG_ALLOW_ANY: {
+ break;
+ }
+ default: {
+ NOTREACHED();
+ return false;
+ }
+ }
+
+ if (!create.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTCREATEKEY, &create)) {
+ return false;
+ }
+
+ if (!open.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) ||
+ !policy->AddRule(IpcTag::NTOPENKEY, &open)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool RegistryPolicy::CreateKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ uint32_t title_index,
+ uint32_t create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG* disposition) {
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ // We don't support creating link keys, volatile keys or backup/restore.
+ if (create_options) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(key, attributes, root_directory, &obj_attributes, &uni_name,
+ nullptr);
+ *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes,
+ title_index, nullptr, create_options,
+ disposition, client_info.process);
+ return true;
+}
+
+bool RegistryPolicy::OpenKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ HANDLE* handle,
+ NTSTATUS* nt_status) {
+ // The only action supported is ASK_BROKER which means open the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result) {
+ *nt_status = STATUS_ACCESS_DENIED;
+ return false;
+ }
+
+ UNICODE_STRING uni_name = {0};
+ OBJECT_ATTRIBUTES obj_attributes = {0};
+ InitObjectAttribs(key, attributes, root_directory, &obj_attributes, &uni_name,
+ nullptr);
+ *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes,
+ client_info.process);
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy.h b/security/sandbox/chromium/sandbox/win/src/registry_policy.h
new file mode 100644
index 0000000000..9a36932869
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_policy.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2006-2008 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_REGISTRY_POLICY_H__
+#define SANDBOX_SRC_REGISTRY_POLICY_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to registry policy
+class RegistryPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for registry IO, in particular open or create actions.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a create request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool CreateKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ uint32_t title_index,
+ uint32_t create_options,
+ HANDLE* handle,
+ NTSTATUS* nt_status,
+ ULONG* disposition);
+
+ // Performs the desired policy action on an open request with an
+ // API that is compatible with the IPC-received parameters.
+ static bool OpenKeyAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& key,
+ uint32_t attributes,
+ HANDLE root_directory,
+ uint32_t desired_access,
+ HANDLE* handle,
+ NTSTATUS* nt_status);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_REGISTRY_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc
new file mode 100644
index 0000000000..83ce586877
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/registry_policy_test.cc
@@ -0,0 +1,322 @@
+// Copyright (c) 2006-2010 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 <shlobj.h>
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/registry_policy.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS |
+ KEY_NOTIFY | KEY_READ | GENERIC_READ |
+ GENERIC_EXECUTE | READ_CONTROL;
+
+#define BINDNTDLL(name) \
+ name##Function name = reinterpret_cast<name##Function>( \
+ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name))
+
+bool IsKeyOpenForRead(HKEY handle) {
+ BINDNTDLL(NtQueryObject);
+
+ OBJECT_BASIC_INFORMATION info = {0};
+ NTSTATUS status = NtQueryObject(handle, ObjectBasicInformation, &info,
+ sizeof(info), nullptr);
+
+ if (!NT_SUCCESS(status))
+ return false;
+
+ if ((info.GrantedAccess & (~kAllowedRegFlags)) != 0)
+ return false;
+ return true;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Reg_OpenKey(int argc, wchar_t** argv) {
+ if (argc != 4)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ REGSAM desired_access = 0;
+ ULONG options = 0;
+ if (wcscmp(argv[1], L"read") == 0) {
+ desired_access = KEY_READ;
+ } else if (wcscmp(argv[1], L"write") == 0) {
+ desired_access = KEY_ALL_ACCESS;
+ } else if (wcscmp(argv[1], L"link") == 0) {
+ options = REG_OPTION_CREATE_LINK;
+ desired_access = KEY_ALL_ACCESS;
+ } else {
+ desired_access = MAXIMUM_ALLOWED;
+ }
+
+ HKEY root = GetReservedKeyFromName(argv[2]);
+ HKEY key;
+ LRESULT result = 0;
+
+ if (wcscmp(argv[0], L"create") == 0)
+ result = ::RegCreateKeyEx(root, argv[3], 0, nullptr, options,
+ desired_access, nullptr, &key, nullptr);
+ else
+ result = ::RegOpenKeyEx(root, argv[3], 0, desired_access, &key);
+
+ if (ERROR_SUCCESS == result) {
+ if (MAXIMUM_ALLOWED == desired_access) {
+ if (!IsKeyOpenForRead(key)) {
+ ::RegCloseKey(key);
+ return SBOX_TEST_FAILED;
+ }
+ }
+ ::RegCloseKey(key);
+ return SBOX_TEST_SUCCEEDED;
+ } else if (ERROR_ACCESS_DENIED == result) {
+ return SBOX_TEST_DENIED;
+ }
+
+ return SBOX_TEST_FAILED;
+}
+
+TEST(RegistryPolicyTest, TestKeyAnyAccess) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Microsoft"));
+
+ // Tests read access on key allowed for read-write.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft"));
+
+ if (::IsUserAnAdmin()) {
+ // Tests write access on key allowed for read-write.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey create write HKEY_LOCAL_MACHINE "
+ L"software\\microsoft"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey open write HKEY_LOCAL_MACHINE software\\microsoft"));
+ }
+
+ // Tests subdirectory access on keys where we don't have subdirectory acess.
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey create read "
+ L"HKEY_LOCAL_MACHINE software\\microsoft\\Windows"));
+
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey open read "
+ L"HKEY_LOCAL_MACHINE software\\microsoft\\windows"));
+
+ // Tests to see if we can create keys where we dont have subdirectory access.
+ // This is denied.
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Microsoft\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Microsoft\\google_unit_tests");
+
+ // Tests if we need to handle differently the "\\" at the end.
+ // This is denied. We need to add both rules.
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software\\microsoft\\"));
+
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software\\microsoft\\"));
+}
+
+TEST(RegistryPolicyTest, TestKeyNoAccess) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ // Tests read access where we don't have access at all.
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey create read HKEY_LOCAL_MACHINE software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey open read HKEY_LOCAL_MACHINE software"));
+}
+
+TEST(RegistryPolicyTest, TestKeyReadOnlyAccess) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ // Tests subdirectory acess on keys where we have subdirectory acess.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey create read "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey open read "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\microsoft"));
+
+ // Tests to see if we can create keys where we have subdirectory access.
+ EXPECT_EQ(SBOX_TEST_DENIED,
+ runner.RunTest(
+ L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
+}
+
+TEST(RegistryPolicyTest, TestKeyAllAccessSubDir) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ if (::IsUserAnAdmin()) {
+ // Tests to see if we can create keys where we have subdirectory access.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create write "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ RegDeleteKey(HKEY_LOCAL_MACHINE, L"software\\Policies\\google_unit_tests");
+ }
+}
+
+TEST(RegistryPolicyTest, TestKeyCreateLink) {
+ TestRunner runner;
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_LOCAL_MACHINE"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Policies\\*"));
+
+ // Tests to see if we can create a registry link key.
+ // NOTE: In theory here we should make sure to check for SBOX_TEST_DENIED
+ // instead of !SBOX_TEST_SUCCEEDED, but unfortunately the result is not
+ // access denied. Internally RegCreateKeyEx (At least on Vista 64) tries to
+ // create the link, and we return successfully access denied, then, it
+ // decides to try to break the path in multiple chunks, and create the links
+ // one by one. In this scenario, it tries to create "HKLM\Software" as a
+ // link key, which obviously fail with STATUS_OBJECT_NAME_COLLISION, and
+ // this is what is returned to the user.
+ EXPECT_NE(SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create link "
+ L"HKEY_LOCAL_MACHINE software\\Policies\\google_unit_tests"));
+
+ // In case our code fails, and the call works, we need to delete the new
+ // link. There is no api for this, so we need to use the NT call.
+ HKEY key = nullptr;
+ LRESULT result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ L"software\\Policies\\google_unit_tests",
+ REG_OPTION_OPEN_LINK, MAXIMUM_ALLOWED, &key);
+
+ if (!result) {
+ HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
+ NtDeleteKeyFunction NtDeleteKey = reinterpret_cast<NtDeleteKeyFunction>(
+ GetProcAddress(ntdll, "NtDeleteKey"));
+ NtDeleteKey(key);
+ }
+}
+
+TEST(RegistryPolicyTest, TestKeyReadOnlyHKCU) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_CURRENT_USER\\Software"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_USERS\\.default"));
+
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_READONLY,
+ L"HKEY_USERS\\.default\\software"));
+
+ // Tests read access where we only have read-only access.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey create read HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(L"Reg_OpenKey open read HKEY_CURRENT_USER software"));
+
+ // Tests write access where we only have read-only acess.
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey create write HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_DENIED,
+ runner.RunTest(L"Reg_OpenKey open write HKEY_CURRENT_USER software"));
+
+ // Tests maximum allowed access where we only have read-only access.
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey create maximum_allowed HKEY_CURRENT_USER software"));
+
+ EXPECT_EQ(
+ SBOX_TEST_SUCCEEDED,
+ runner.RunTest(
+ L"Reg_OpenKey open maximum_allowed HKEY_CURRENT_USER software"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/resolver.cc b/security/sandbox/chromium/sandbox/win/src/resolver.cc
new file mode 100644
index 0000000000..6ed20e9b51
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2006-2010 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/resolver.h"
+
+#include <stddef.h>
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS ResolverThunk::Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes) {
+ if (!thunk_storage || 0 == storage_bytes || !target_module || !target_name)
+ return STATUS_INVALID_PARAMETER;
+
+ if (storage_bytes < GetThunkSize())
+ return STATUS_BUFFER_TOO_SMALL;
+
+ NTSTATUS ret = STATUS_SUCCESS;
+ if (!interceptor_entry_point) {
+ ret = ResolveInterceptor(interceptor_module, interceptor_name,
+ &interceptor_entry_point);
+ if (!NT_SUCCESS(ret))
+ return ret;
+ }
+
+ ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ interceptor_ = interceptor_entry_point;
+
+ return ret;
+}
+
+NTSTATUS ResolverThunk::ResolveInterceptor(const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ DCHECK_NT(address);
+ if (!interceptor_module)
+ return STATUS_INVALID_PARAMETER;
+
+ base::win::PEImage pe(interceptor_module);
+ if (!pe.VerifyMagic())
+ return STATUS_INVALID_IMAGE_FORMAT;
+
+ *address = reinterpret_cast<void*>(pe.GetProcAddress(interceptor_name));
+
+ if (!(*address))
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ return STATUS_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/resolver.h b/security/sandbox/chromium/sandbox/win/src/resolver.h
new file mode 100644
index 0000000000..3ce427b74b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver.h
@@ -0,0 +1,107 @@
+// Copyright (c) 2010 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_WIN_SRC_RESOLVER_H_
+#define SANDBOX_WIN_SRC_RESOLVER_H_
+
+// Defines ResolverThunk, the interface for classes that perform interceptions.
+// For more details see
+// http://dev.chromium.org/developers/design-documents/sandbox .
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+// A resolver is the object in charge of performing the actual interception of
+// a function. There should be a concrete implementation of a resolver roughly
+// per type of interception.
+class ResolverThunk {
+ public:
+ ResolverThunk() {}
+ virtual ~ResolverThunk() {}
+
+ // Performs the actual interception of a function.
+ // target_name is an exported function from the module loaded at
+ // target_module, and must be replaced by interceptor_name, exported from
+ // interceptor_module. interceptor_entry_point can be provided instead of
+ // interceptor_name / interceptor_module.
+ // thunk_storage must point to a buffer on the child's address space, to hold
+ // the patch thunk, and related data. If provided, storage_used will receive
+ // the number of bytes used from thunk_storage.
+ //
+ // Example: (without error checking)
+ //
+ // size_t size = resolver.GetThunkSize();
+ // char* buffer = ::VirtualAllocEx(child_process, nullptr, size,
+ // MEM_COMMIT, PAGE_READWRITE);
+ // resolver.Setup(ntdll_module, nullptr, L"NtCreateFile", nullptr,
+ // &MyReplacementFunction, buffer, size, nullptr);
+ //
+ // In general, the idea is to allocate a single big buffer for all
+ // interceptions on the same dll, and call Setup n times.
+ // WARNING: This means that any data member that is specific to a single
+ // interception must be reset within this method.
+ virtual NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) = 0;
+
+ // Gets the address of function_name inside module (main exe).
+ virtual NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address);
+
+ // Gets the address of an exported function_name inside module.
+ virtual NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address);
+
+ // Gets the required buffer size for this type of thunk.
+ virtual size_t GetThunkSize() const = 0;
+
+ protected:
+ // Performs basic initialization on behalf of a concrete instance of a
+ // resolver. That is, parameter validation and resolution of the target
+ // and the interceptor into the member variables.
+ //
+ // target_name is an exported function from the module loaded at
+ // target_module, and must be replaced by interceptor_name, exported from
+ // interceptor_module. interceptor_entry_point can be provided instead of
+ // interceptor_name / interceptor_module.
+ // thunk_storage must point to a buffer on the child's address space, to hold
+ // the patch thunk, and related data.
+ virtual NTSTATUS Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes);
+
+ // Gets the required buffer size for the internal part of the thunk.
+ size_t GetInternalThunkSize() const;
+
+ // Initializes the internal part of the thunk.
+ // interceptor is the function to be called instead of original_function.
+ bool SetInternalThunk(void* storage, size_t storage_bytes,
+ const void* original_function, const void* interceptor);
+
+ // Holds the resolved interception target.
+ void* target_;
+ // Holds the resolved interception interceptor.
+ const void* interceptor_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunk);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_RESOLVER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/resolver_32.cc b/security/sandbox/chromium/sandbox/win/src/resolver_32.cc
new file mode 100644
index 0000000000..ff78f53fb1
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver_32.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2006-2010 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/resolver.h"
+
+#include <stddef.h>
+
+// For placement new. This file must not depend on the CRT at runtime, but
+// placement operator new is inline.
+#include <new>
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+#pragma pack(push, 1)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // sub esp, 8 // Create working space
+ // push edx // Save register
+ // mov edx, [esp + 0xc] // Get return adddress
+ // mov [esp + 8], edx // Store return address
+ // mov dword ptr [esp + 0xc], 0x7c401200 // Store extra argument
+ // mov dword ptr [esp + 4], 0x40010203 // Store address to jump to
+ // pop edx // Restore register
+ // ret // Jump to interceptor
+ //
+ // This code only modifies esp and eip so it must work with to normal calling
+ // convention. It is assembled as:
+ //
+ // 00 83ec08 sub esp,8
+ // 03 52 push edx
+ // 04 8b54240c mov edx,dword ptr [esp + 0Ch]
+ // 08 89542408 mov dword ptr [esp + 8], edx
+ // 0c c744240c0012407c mov dword ptr [esp + 0Ch], 7C401200h
+ // 14 c744240403020140 mov dword ptr [esp + 4], 40010203h
+ // 1c 5a pop edx
+ // 1d c3 ret
+ InternalThunk() {
+ opcodes_1 = 0x5208ec83;
+ opcodes_2 = 0x0c24548b;
+ opcodes_3 = 0x08245489;
+ opcodes_4 = 0x0c2444c7;
+ opcodes_5 = 0x042444c7;
+ opcodes_6 = 0xc35a;
+ extra_argument = 0;
+ interceptor_function = 0;
+ }
+ ULONG opcodes_1; // = 0x5208ec83
+ ULONG opcodes_2; // = 0x0c24548b
+ ULONG opcodes_3; // = 0x08245489
+ ULONG opcodes_4; // = 0x0c2444c7
+ ULONG extra_argument;
+ ULONG opcodes_5; // = 0x042444c7
+ ULONG interceptor_function;
+ USHORT opcodes_6; // = 0xc35a
+};
+#pragma pack(pop)
+
+} // namespace
+
+namespace sandbox {
+
+bool ResolverThunk::SetInternalThunk(void* storage,
+ size_t storage_bytes,
+ const void* original_function,
+ const void* interceptor) {
+ if (storage_bytes < sizeof(InternalThunk))
+ return false;
+
+ InternalThunk* thunk = new (storage) InternalThunk;
+
+#pragma warning(push)
+#pragma warning(disable : 4311)
+ // These casts generate warnings because they are 32 bit specific.
+ thunk->interceptor_function = reinterpret_cast<ULONG>(interceptor);
+ thunk->extra_argument = reinterpret_cast<ULONG>(original_function);
+#pragma warning(pop)
+
+ return true;
+}
+
+size_t ResolverThunk::GetInternalThunkSize() const {
+ return sizeof(InternalThunk);
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ const void** casted = const_cast<const void**>(address);
+ return ResolverThunk::ResolveInterceptor(module, function_name, casted);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/resolver_64.cc b/security/sandbox/chromium/sandbox/win/src/resolver_64.cc
new file mode 100644
index 0000000000..573b2bc3a9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/resolver_64.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2006-2010 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/resolver.h"
+
+#include <stddef.h>
+
+// For placement new. This file must not depend on the CRT at runtime, but
+// placement operator new is inline.
+#include <new>
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+#if defined(_M_X64)
+
+const USHORT kMovRax = 0xB848;
+const USHORT kJmpRax = 0xe0ff;
+
+#pragma pack(push, 1)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // 01 48b8f0debc9a78563412 mov rax,123456789ABCDEF0h
+ // ff e0 jmp rax
+ //
+ // The code modifies rax, but that's fine for x64 ABI.
+
+ InternalThunk() {
+ mov_rax = kMovRax;
+ jmp_rax = kJmpRax;
+ interceptor_function = 0;
+ }
+ USHORT mov_rax; // = 48 B8
+ ULONG_PTR interceptor_function;
+ USHORT jmp_rax; // = ff e0
+};
+#pragma pack(pop)
+
+#elif defined(_M_ARM64)
+
+const ULONG kLdrX16Pc4 = 0x58000050;
+const ULONG kBrX16 = 0xD61F0200;
+
+#pragma pack(push, 4)
+struct InternalThunk {
+ // This struct contains roughly the following code:
+ // 00 58000050 ldr x16, pc+4
+ // 04 D61F0200 br x16
+ // 08 123456789ABCDEF0H
+
+ InternalThunk() {
+ ldr_x16_pc4 = kLdrX16Pc4;
+ br_x16 = kBrX16;
+ interceptor_function = 0;
+ }
+ ULONG ldr_x16_pc4;
+ ULONG br_x16;
+ ULONG_PTR interceptor_function;
+};
+#pragma pack(pop)
+#else
+#error "Unsupported Windows 64-bit Arch"
+#endif
+
+} // namespace.
+
+namespace sandbox {
+
+size_t ResolverThunk::GetInternalThunkSize() const {
+ return sizeof(InternalThunk);
+}
+
+bool ResolverThunk::SetInternalThunk(void* storage,
+ size_t storage_bytes,
+ const void* original_function,
+ const void* interceptor) {
+ if (storage_bytes < sizeof(InternalThunk))
+ return false;
+
+ InternalThunk* thunk = new (storage) InternalThunk;
+ thunk->interceptor_function = reinterpret_cast<ULONG_PTR>(interceptor);
+
+ return true;
+}
+
+NTSTATUS ResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ // We don't support sidestep & co.
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc
new file mode 100644
index 0000000000..5b20e88264
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.cc
@@ -0,0 +1,432 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/restricted_token.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/logging.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+// Wrapper for utility version to unwrap ScopedHandle.
+std::unique_ptr<BYTE[]> GetTokenInfo(const base::win::ScopedHandle& token,
+ TOKEN_INFORMATION_CLASS info_class,
+ DWORD* error) {
+ std::unique_ptr<BYTE[]> buffer;
+ *error = sandbox::GetTokenInformation(token.Get(), info_class, &buffer);
+ if (*error != ERROR_SUCCESS)
+ return nullptr;
+ return buffer;
+}
+
+} // namespace
+
+namespace sandbox {
+
+RestrictedToken::RestrictedToken()
+ : integrity_level_(INTEGRITY_LEVEL_LAST),
+ init_(false),
+ lockdown_default_dacl_(false) {}
+
+RestrictedToken::~RestrictedToken() {}
+
+DWORD RestrictedToken::Init(const HANDLE effective_token) {
+ if (init_)
+ return ERROR_ALREADY_INITIALIZED;
+
+ HANDLE temp_token;
+ if (effective_token) {
+ // We duplicate the handle to be able to use it even if the original handle
+ // is closed.
+ if (!::DuplicateHandle(::GetCurrentProcess(), effective_token,
+ ::GetCurrentProcess(), &temp_token, 0, false,
+ DUPLICATE_SAME_ACCESS)) {
+ return ::GetLastError();
+ }
+ } else {
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &temp_token)) {
+ return ::GetLastError();
+ }
+ }
+ effective_token_.Set(temp_token);
+
+ init_ = true;
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::GetRestrictedToken(
+ base::win::ScopedHandle* token) const {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ size_t deny_size = sids_for_deny_only_.size();
+ size_t restrict_size = sids_to_restrict_.size();
+ size_t privileges_size = privileges_to_disable_.size();
+
+ SID_AND_ATTRIBUTES* deny_only_array = nullptr;
+ if (deny_size) {
+ deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
+
+ for (unsigned int i = 0; i < sids_for_deny_only_.size(); ++i) {
+ deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
+ deny_only_array[i].Sid = sids_for_deny_only_[i].GetPSID();
+ }
+ }
+
+ SID_AND_ATTRIBUTES* sids_to_restrict_array = nullptr;
+ if (restrict_size) {
+ sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size];
+
+ for (unsigned int i = 0; i < restrict_size; ++i) {
+ sids_to_restrict_array[i].Attributes = 0;
+ sids_to_restrict_array[i].Sid = sids_to_restrict_[i].GetPSID();
+ }
+ }
+
+ LUID_AND_ATTRIBUTES* privileges_to_disable_array = nullptr;
+ if (privileges_size) {
+ privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size];
+
+ for (unsigned int i = 0; i < privileges_size; ++i) {
+ privileges_to_disable_array[i].Attributes = 0;
+ privileges_to_disable_array[i].Luid = privileges_to_disable_[i];
+ }
+ }
+
+ bool result = true;
+ HANDLE new_token_handle = nullptr;
+ if (deny_size || restrict_size || privileges_size) {
+ result = ::CreateRestrictedToken(
+ effective_token_.Get(), 0, static_cast<DWORD>(deny_size),
+ deny_only_array, static_cast<DWORD>(privileges_size),
+ privileges_to_disable_array, static_cast<DWORD>(restrict_size),
+ sids_to_restrict_array, &new_token_handle);
+ } else {
+ // Duplicate the token even if it's not modified at this point
+ // because any subsequent changes to this token would also affect the
+ // current process.
+ result = ::DuplicateTokenEx(effective_token_.Get(), TOKEN_ALL_ACCESS,
+ nullptr, SecurityIdentification, TokenPrimary,
+ &new_token_handle);
+ }
+ auto last_error = ::GetLastError();
+
+ if (deny_only_array)
+ delete[] deny_only_array;
+
+ if (sids_to_restrict_array)
+ delete[] sids_to_restrict_array;
+
+ if (privileges_to_disable_array)
+ delete[] privileges_to_disable_array;
+
+ if (!result)
+ return last_error;
+
+ base::win::ScopedHandle new_token(new_token_handle);
+
+ if (lockdown_default_dacl_) {
+ // Don't add Restricted sid and also remove logon sid access.
+ if (!RevokeLogonSidFromDefaultDacl(new_token.Get()))
+ return ::GetLastError();
+ } else {
+ // Modify the default dacl on the token to contain Restricted.
+ if (!AddSidToDefaultDacl(new_token.Get(), WinRestrictedCodeSid,
+ GRANT_ACCESS, GENERIC_ALL)) {
+ return ::GetLastError();
+ }
+ }
+
+ for (const auto& default_dacl_sid : sids_for_default_dacl_) {
+ if (!AddSidToDefaultDacl(new_token.Get(), std::get<0>(default_dacl_sid),
+ std::get<1>(default_dacl_sid),
+ std::get<2>(default_dacl_sid))) {
+ return ::GetLastError();
+ }
+ }
+
+ // Add user to default dacl.
+ if (!AddUserSidToDefaultDacl(new_token.Get(), GENERIC_ALL))
+ return ::GetLastError();
+
+ DWORD error = SetTokenIntegrityLevel(new_token.Get(), integrity_level_);
+ if (ERROR_SUCCESS != error)
+ return error;
+
+ HANDLE token_handle;
+ if (!::DuplicateHandle(::GetCurrentProcess(), new_token.Get(),
+ ::GetCurrentProcess(), &token_handle, TOKEN_ALL_ACCESS,
+ false, // Don't inherit.
+ 0)) {
+ return ::GetLastError();
+ }
+
+ token->Set(token_handle);
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::GetRestrictedTokenForImpersonation(
+ base::win::ScopedHandle* token) const {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ base::win::ScopedHandle restricted_token;
+ DWORD err_code = GetRestrictedToken(&restricted_token);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+
+ HANDLE impersonation_token_handle;
+ if (!::DuplicateToken(restricted_token.Get(), SecurityImpersonation,
+ &impersonation_token_handle)) {
+ return ::GetLastError();
+ }
+ base::win::ScopedHandle impersonation_token(impersonation_token_handle);
+
+ HANDLE token_handle;
+ if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token.Get(),
+ ::GetCurrentProcess(), &token_handle, TOKEN_ALL_ACCESS,
+ false, // Don't inherit.
+ 0)) {
+ return ::GetLastError();
+ }
+
+ token->Set(token_handle);
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid>* exceptions) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD error;
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenGroups, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get());
+
+ // Build the list of the deny only group SIDs
+ for (unsigned int i = 0; i < token_groups->GroupCount; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
+ (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) {
+ bool should_ignore = false;
+ if (exceptions) {
+ for (unsigned int j = 0; j < exceptions->size(); ++j) {
+ if (::EqualSid((*exceptions)[j].GetPSID(),
+ token_groups->Groups[i].Sid)) {
+ should_ignore = true;
+ break;
+ }
+ }
+ }
+ if (!should_ignore) {
+ sids_for_deny_only_.push_back(
+ reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
+ }
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddSidForDenyOnly(const Sid& sid) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_for_deny_only_.push_back(sid);
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddUserSidForDenyOnly() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ std::unique_ptr<BYTE[]> buffer(new BYTE[size]);
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer.get());
+
+ bool result = ::GetTokenInformation(effective_token_.Get(), TokenUser,
+ token_user, size, &size);
+
+ if (!result)
+ return ::GetLastError();
+
+ Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
+ sids_for_deny_only_.push_back(user);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::DeleteAllPrivileges(
+ const std::vector<std::wstring>* exceptions) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD error;
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenPrivileges, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_PRIVILEGES* token_privileges =
+ reinterpret_cast<TOKEN_PRIVILEGES*>(buffer.get());
+
+ // Build the list of privileges to disable
+ for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) {
+ bool should_ignore = false;
+ if (exceptions) {
+ for (unsigned int j = 0; j < exceptions->size(); ++j) {
+ LUID luid = {0};
+ ::LookupPrivilegeValue(nullptr, (*exceptions)[j].c_str(), &luid);
+ if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart &&
+ token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) {
+ should_ignore = true;
+ break;
+ }
+ }
+ }
+ if (!should_ignore) {
+ privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid);
+ }
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::DeletePrivilege(const wchar_t* privilege) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ LUID luid = {0};
+ if (LookupPrivilegeValue(nullptr, privilege, &luid))
+ privileges_to_disable_.push_back(luid);
+ else
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSid(const Sid& sid) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_to_restrict_.push_back(sid); // No attributes
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSidLogonSession() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD error;
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenGroups, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get());
+
+ SID* logon_sid = nullptr;
+ for (unsigned int i = 0; i < token_groups->GroupCount; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
+ logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
+ break;
+ }
+ }
+
+ if (logon_sid)
+ sids_to_restrict_.push_back(logon_sid);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSidCurrentUser() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ std::unique_ptr<BYTE[]> buffer(new BYTE[size]);
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(buffer.get());
+
+ bool result = ::GetTokenInformation(effective_token_.Get(), TokenUser,
+ token_user, size, &size);
+
+ if (!result)
+ return ::GetLastError();
+
+ Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
+ sids_to_restrict_.push_back(user);
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::AddRestrictingSidAllSids() {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ // Add the current user to the list.
+ DWORD error = AddRestrictingSidCurrentUser();
+ if (ERROR_SUCCESS != error)
+ return error;
+
+ std::unique_ptr<BYTE[]> buffer =
+ GetTokenInfo(effective_token_, TokenGroups, &error);
+
+ if (!buffer)
+ return error;
+
+ TOKEN_GROUPS* token_groups = reinterpret_cast<TOKEN_GROUPS*>(buffer.get());
+
+ // Build the list of restricting sids from all groups.
+ for (unsigned int i = 0; i < token_groups->GroupCount; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0)
+ AddRestrictingSid(reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
+ }
+
+ return ERROR_SUCCESS;
+}
+
+DWORD RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ integrity_level_ = integrity_level;
+ return ERROR_SUCCESS;
+}
+
+void RestrictedToken::SetLockdownDefaultDacl() {
+ lockdown_default_dacl_ = true;
+}
+
+DWORD RestrictedToken::AddDefaultDaclSid(const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access) {
+ DCHECK(init_);
+ if (!init_)
+ return ERROR_NO_TOKEN;
+
+ sids_for_default_dacl_.push_back(std::make_tuple(sid, access_mode, access));
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token.h b/security/sandbox/chromium/sandbox/win/src/restricted_token.h
new file mode 100644
index 0000000000..c89427af14
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2010 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_RESTRICTED_TOKEN_H_
+#define SANDBOX_SRC_RESTRICTED_TOKEN_H_
+
+#include <windows.h>
+
+#include <tuple>
+#include <vector>
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/security_level.h"
+#include "sandbox/win/src/sid.h"
+
+// Flags present in the Group SID list. These 2 flags are new in Windows Vista
+#ifndef SE_GROUP_INTEGRITY
+#define SE_GROUP_INTEGRITY (0x00000020L)
+#endif
+#ifndef SE_GROUP_INTEGRITY_ENABLED
+#define SE_GROUP_INTEGRITY_ENABLED (0x00000040L)
+#endif
+
+namespace sandbox {
+
+// Handles the creation of a restricted token using the effective token or
+// any token handle.
+// Sample usage:
+// RestrictedToken restricted_token;
+// DWORD err_code = restricted_token.Init(nullptr); // Use the current
+// // effective token
+// if (ERROR_SUCCESS != err_code) {
+// // handle error.
+// }
+//
+// restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID());
+// base::win::ScopedHandle token_handle;
+// err_code = restricted_token.GetRestrictedToken(&token_handle);
+// if (ERROR_SUCCESS != err_code) {
+// // handle error.
+// }
+// [...]
+class RestrictedToken {
+ public:
+ // Init() has to be called before calling any other method in the class.
+ RestrictedToken();
+ ~RestrictedToken();
+
+ // Initializes the RestrictedToken object with effective_token.
+ // If effective_token is nullptr, it initializes the RestrictedToken object
+ // with the effective token of the current process.
+ DWORD Init(HANDLE effective_token);
+
+ // Creates a restricted token.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD GetRestrictedToken(base::win::ScopedHandle* token) const;
+
+ // Creates a restricted token and uses this new token to create a new token
+ // for impersonation. Returns this impersonation token.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ //
+ // The sample usage is the same as the GetRestrictedToken function.
+ DWORD GetRestrictedTokenForImpersonation(
+ base::win::ScopedHandle* token) const;
+
+ // Lists all sids in the token and mark them as Deny Only except for those
+ // present in the exceptions parameter. If there is no exception needed,
+ // the caller can pass an empty list or nullptr for the exceptions
+ // parameter.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ //
+ // Sample usage:
+ // std::vector<Sid> sid_exceptions;
+ // sid_exceptions.push_back(ATL::Sids::Users().GetPSID());
+ // sid_exceptions.push_back(ATL::Sids::World().GetPSID());
+ // restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
+ // Note: A Sid marked for Deny Only in a token cannot be used to grant
+ // access to any resource. It can only be used to deny access.
+ DWORD AddAllSidsForDenyOnly(std::vector<Sid>* exceptions);
+
+ // Adds a user or group SID for Deny Only in the restricted token.
+ // Parameter: sid is the SID to add in the Deny Only list.
+ // The return value is always ERROR_SUCCESS.
+ //
+ // Sample Usage:
+ // restricted_token.AddSidForDenyOnly(ATL::Sids::Admins().GetPSID());
+ DWORD AddSidForDenyOnly(const Sid& sid);
+
+ // Adds the user sid of the token for Deny Only in the restricted token.
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD AddUserSidForDenyOnly();
+
+ // Lists all privileges in the token and add them to the list of privileges
+ // to remove except for those present in the exceptions parameter. If
+ // there is no exception needed, the caller can pass an empty list or nullptr
+ // for the exceptions parameter.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ //
+ // Sample usage:
+ // std::vector<std::wstring> privilege_exceptions;
+ // privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ // restricted_token.DeleteAllPrivileges(&privilege_exceptions);
+ DWORD DeleteAllPrivileges(const std::vector<std::wstring>* exceptions);
+
+ // Adds a privilege to the list of privileges to remove in the restricted
+ // token.
+ // Parameter: privilege is the privilege name to remove. This is the string
+ // representing the privilege. (e.g. "SeChangeNotifyPrivilege").
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ //
+ // Sample usage:
+ // restricted_token.DeletePrivilege(SE_LOAD_DRIVER_NAME);
+ DWORD DeletePrivilege(const wchar_t* privilege);
+
+ // Adds a SID to the list of restricting sids in the restricted token.
+ // Parameter: sid is the sid to add to the list restricting sids.
+ // The return value is always ERROR_SUCCESS.
+ //
+ // Sample usage:
+ // restricted_token.AddRestrictingSid(ATL::Sids::Users().GetPSID());
+ // Note: The list of restricting is used to force Windows to perform all
+ // access checks twice. The first time using your user SID and your groups,
+ // and the second time using your list of restricting sids. The access has
+ // to be granted in both places to get access to the resource requested.
+ DWORD AddRestrictingSid(const Sid& sid);
+
+ // Adds the logon sid of the token in the list of restricting sids for the
+ // restricted token.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD AddRestrictingSidLogonSession();
+
+ // Adds the owner sid of the token in the list of restricting sids for the
+ // restricted token.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD AddRestrictingSidCurrentUser();
+
+ // Adds all group sids and the user sid to the restricting sids list.
+ //
+ // If the function succeeds, the return value is ERROR_SUCCESS. If the
+ // function fails, the return value is the win32 error code corresponding to
+ // the error.
+ DWORD AddRestrictingSidAllSids();
+
+ // Sets the token integrity level. This is only valid on Vista. The integrity
+ // level cannot be higher than your current integrity level.
+ DWORD SetIntegrityLevel(IntegrityLevel integrity_level);
+
+ // Set a flag which indicates the created token should have a locked down
+ // default DACL when created.
+ void SetLockdownDefaultDacl();
+
+ // Add a SID to the default DACL. These SIDs are added regardless of the
+ // SetLockdownDefaultDacl state.
+ DWORD AddDefaultDaclSid(const Sid& sid,
+ ACCESS_MODE access_mode,
+ ACCESS_MASK access);
+
+ private:
+ // The list of restricting sids in the restricted token.
+ std::vector<Sid> sids_to_restrict_;
+ // The list of privileges to remove in the restricted token.
+ std::vector<LUID> privileges_to_disable_;
+ // The list of sids to mark as Deny Only in the restricted token.
+ std::vector<Sid> sids_for_deny_only_;
+ // The list of sids to add to the default DACL of the restricted token.
+ std::vector<std::tuple<Sid, ACCESS_MODE, ACCESS_MASK>> sids_for_default_dacl_;
+ // The token to restrict. Can only be set in a constructor.
+ base::win::ScopedHandle effective_token_;
+ // The token integrity level. Only valid on Vista.
+ IntegrityLevel integrity_level_;
+ // Tells if the object is initialized or not (if Init() has been called)
+ bool init_;
+ // Lockdown the default DACL when creating new tokens.
+ bool lockdown_default_dacl_;
+
+ DISALLOW_COPY_AND_ASSIGN(RestrictedToken);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_RESTRICTED_TOKEN_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc
new file mode 100644
index 0000000000..1855054a7c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc
@@ -0,0 +1,829 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for the RestrictedToken.
+
+#include "sandbox/win/src/restricted_token.h"
+
+#include <vector>
+
+#include "base/win/atl.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sid.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void TestDefaultDalc(bool restricted_required) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ if (!restricted_required)
+ token.SetLockdownDefaultDacl();
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ base::win::ScopedHandle handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(handle.Take());
+
+ ATL::CDacl dacl;
+ ASSERT_TRUE(restricted_token.GetDefaultDacl(&dacl));
+
+ ATL::CSid logon_sid;
+ ASSERT_TRUE(restricted_token.GetLogonSid(&logon_sid));
+
+ bool restricted_found = false;
+ bool logon_sid_found = false;
+
+ unsigned int ace_count = dacl.GetAceCount();
+ for (unsigned int i = 0; i < ace_count; ++i) {
+ ATL::CSid sid;
+ ACCESS_MASK mask = 0;
+ dacl.GetAclEntry(i, &sid, &mask);
+ if (sid == ATL::Sids::RestrictedCode() && mask == GENERIC_ALL) {
+ restricted_found = true;
+ } else if (sid == logon_sid) {
+ logon_sid_found = true;
+ }
+ }
+
+ ASSERT_EQ(restricted_required, restricted_found);
+ if (!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 CheckDaclForPackageSid(const base::win::ScopedHandle& token,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool package_sid_required) {
+ DWORD length_needed = 0;
+ ::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, nullptr, 0,
+ &length_needed);
+ ASSERT_EQ(::GetLastError(), DWORD{ERROR_INSUFFICIENT_BUFFER});
+
+ std::vector<char> security_desc_buffer(length_needed);
+ SECURITY_DESCRIPTOR* security_desc =
+ reinterpret_cast<SECURITY_DESCRIPTOR*>(security_desc_buffer.data());
+
+ ASSERT_TRUE(::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION,
+ security_desc, length_needed,
+ &length_needed));
+
+ ATL::CSecurityDesc token_sd(*security_desc);
+ ATL::CDacl dacl;
+ ASSERT_TRUE(token_sd.GetDacl(&dacl));
+
+ ATL::CSid package_sid(
+ static_cast<SID*>(security_capabilities->AppContainerSid));
+ ATL::CSid all_package_sid(
+ static_cast<SID*>(sandbox::Sid(::WinBuiltinAnyPackageSid).GetPSID()));
+
+ unsigned int ace_count = dacl.GetAceCount();
+ for (unsigned int i = 0; i < ace_count; ++i) {
+ ATL::CSid sid;
+ ACCESS_MASK mask = 0;
+ BYTE type = 0;
+ dacl.GetAclEntry(i, &sid, &mask, &type);
+ if (mask != TOKEN_ALL_ACCESS || type != ACCESS_ALLOWED_ACE_TYPE)
+ continue;
+ if (sid == package_sid)
+ EXPECT_TRUE(package_sid_required);
+ else if (sid == all_package_sid)
+ EXPECT_FALSE(package_sid_required);
+ }
+}
+
+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));
+ }
+
+ CheckDaclForPackageSid(token, security_capabilities, true);
+}
+
+// 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.
+TEST(RestrictedTokenTest, InvalidHandle) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_INVALID_HANDLE),
+ token.Init(reinterpret_cast<HANDLE>(0x5555)));
+}
+
+// Tests the initialization with nullptr as parameter.
+TEST(RestrictedTokenTest, DefaultInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Create the token using the current token.
+ RestrictedToken token_default;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token_default.Init(nullptr));
+
+ // Get the handle to the restricted token.
+
+ base::win::ScopedHandle restricted_token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token_default.GetRestrictedToken(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle.Take());
+
+ ATL::CSid sid_user_restricted;
+ ATL::CSid sid_user_default;
+ ATL::CSid sid_owner_restricted;
+ ATL::CSid sid_owner_default;
+ ASSERT_TRUE(restricted_token.GetUser(&sid_user_restricted));
+ ASSERT_TRUE(access_token.GetUser(&sid_user_default));
+ ASSERT_TRUE(restricted_token.GetOwner(&sid_owner_restricted));
+ ASSERT_TRUE(access_token.GetOwner(&sid_owner_default));
+
+ // Check if both token have the same owner and user.
+ ASSERT_EQ(sid_user_restricted, sid_user_default);
+ ASSERT_EQ(sid_owner_restricted, sid_owner_default);
+}
+
+// Tests the initialization with a custom token as parameter.
+TEST(RestrictedTokenTest, CustomInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Change the primary group.
+ access_token.SetPrimaryGroup(ATL::Sids::World());
+
+ // Create the token using the current token.
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+
+ // Get the handle to the restricted token.
+
+ base::win::ScopedHandle restricted_token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle.Take());
+
+ ATL::CSid sid_restricted;
+ ATL::CSid sid_default;
+ ASSERT_TRUE(restricted_token.GetPrimaryGroup(&sid_restricted));
+ ASSERT_TRUE(access_token.GetPrimaryGroup(&sid_default));
+
+ // Check if both token have the same owner.
+ ASSERT_EQ(sid_restricted, sid_default);
+}
+
+// Verifies that the token created by the object are valid.
+TEST(RestrictedTokenTest, ResultToken) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ base::win::ScopedHandle restricted_token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&restricted_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(restricted_token.Get()));
+
+ DWORD length = 0;
+ TOKEN_TYPE type;
+ ASSERT_TRUE(::GetTokenInformation(restricted_token.Get(), ::TokenType, &type,
+ sizeof(type), &length));
+
+ ASSERT_EQ(type, TokenPrimary);
+
+ base::win::ScopedHandle impersonation_token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedTokenForImpersonation(&impersonation_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(impersonation_token.Get()));
+
+ ASSERT_TRUE(::GetTokenInformation(impersonation_token.Get(), ::TokenType,
+ &type, sizeof(type), &length));
+
+ ASSERT_EQ(type, TokenImpersonation);
+}
+
+// Verifies that the token created has "Restricted" in its default dacl.
+TEST(RestrictedTokenTest, DefaultDacl) {
+ TestDefaultDalc(true);
+}
+
+// Verifies that the token created does not have "Restricted" in its default
+// dacl.
+TEST(RestrictedTokenTest, DefaultDaclLockdown) {
+ TestDefaultDalc(false);
+}
+
+// Tests the method "AddSidForDenyOnly".
+TEST(RestrictedTokenTest, DenySid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddSidForDenyOnly(Sid(WinWorldSid)));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly".
+TEST(RestrictedTokenTest, DenySids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddAllSidsForDenyOnly(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly" using an exception list.
+TEST(RestrictedTokenTest, DenySidsException) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ std::vector<Sid> sids_exception;
+ sids_exception.push_back(Sid(WinWorldSid));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddAllSidsForDenyOnly(&sids_exception));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(0u, attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ } else {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly.
+TEST(RestrictedTokenTest, DenyOwnerSid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.AddUserSidForDenyOnly());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly with a custom effective token.
+TEST(RestrictedTokenTest, DenyOwnerSidCustom) {
+ // Get the current process token.
+ HANDLE access_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &access_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, access_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(access_handle);
+
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.AddUserSidForDenyOnly());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method DeleteAllPrivileges.
+TEST(RestrictedTokenTest, DeleteAllPrivileges) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeleteAllPrivileges(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ASSERT_EQ(0u, privileges.GetCount());
+}
+
+// Tests the method DeleteAllPrivileges with an exception list.
+TEST(RestrictedTokenTest, DeleteAllPrivilegesException) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ std::vector<std::wstring> exceptions;
+ exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeleteAllPrivileges(&exceptions));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ ASSERT_EQ(1u, privileges.GetCount());
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_EQ(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Tests the method DeletePrivilege.
+TEST(RestrictedTokenTest, DeletePrivilege) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeletePrivilege(SE_CHANGE_NOTIFY_NAME));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_NE(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Tests the method AddRestrictingSid.
+TEST(RestrictedTokenTest, AddRestrictingSid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ CheckRestrictingSid(restricted_token, ATL::Sids::World(), 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser with a custom effective token.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUserCustom) {
+ // Get the current process token.
+ HANDLE access_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &access_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, access_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(access_handle);
+
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidLogonSession.
+TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ CheckRestrictingSid(restricted_token, session, 1);
+}
+
+// Tests adding a lot of restricting sids.
+TEST(RestrictedTokenTest, AddMultipleRestrictingSids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ 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);
+ ASSERT_EQ(3u, atl_groups.GetCount());
+}
+
+// Tests the method "AddRestrictingSidAllSids".
+TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidAllSids());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all group sids are in the restricting sid list.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ CheckRestrictingSid(restricted_token, sids[i], -1);
+ }
+ }
+
+ // Verify that the user is in the restricting sid list.
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+ CheckRestrictingSid(restricted_token, user, -1);
+}
+
+// Checks the error code when the object is initialized twice.
+TEST(RestrictedTokenTest, DoubleInit) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_ALREADY_INITIALIZED), token.Init(nullptr));
+}
+
+TEST(RestrictedTokenTest, LockdownDefaultDaclNoLogonSid) {
+ ATL::CAccessToken anonymous_token;
+ ASSERT_TRUE(::ImpersonateAnonymousToken(::GetCurrentThread()));
+ ASSERT_TRUE(anonymous_token.GetThreadToken(TOKEN_ALL_ACCESS));
+ ::RevertToSelf();
+ ATL::CSid logon_sid;
+ // Verify that the anonymous token doesn't have the logon sid.
+ ASSERT_FALSE(anonymous_token.GetLogonSid(&logon_sid));
+
+ RestrictedToken token;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, token.Init(anonymous_token.GetHandle()));
+ token.SetLockdownDefaultDacl();
+
+ base::win::ScopedHandle handle;
+ 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_TRUE(ReplacePackageSidInDacl(token.Get(), SE_KERNEL_OBJECT,
+ Sid(caps_no_capabilities.AppContainerSid),
+ TOKEN_ALL_ACCESS));
+ CheckDaclForPackageSid(token, &caps_no_capabilities, false);
+
+ 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/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
new file mode 100644
index 0000000000..863dee8d69
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.cc
@@ -0,0 +1,480 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/restricted_token_utils.h"
+
+#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 {
+
+namespace {
+
+DWORD GetObjectSecurityDescriptor(HANDLE handle,
+ SECURITY_INFORMATION security_info,
+ std::vector<char>* security_desc_buffer,
+ PSECURITY_DESCRIPTOR* security_desc) {
+ DWORD last_error = 0;
+ DWORD length_needed = 0;
+
+ ::GetKernelObjectSecurity(handle, security_info, nullptr, 0, &length_needed);
+ last_error = ::GetLastError();
+ if (last_error != ERROR_INSUFFICIENT_BUFFER)
+ return last_error;
+
+ security_desc_buffer->resize(length_needed);
+ *security_desc =
+ reinterpret_cast<PSECURITY_DESCRIPTOR>(security_desc_buffer->data());
+
+ if (!::GetKernelObjectSecurity(handle, security_info, *security_desc,
+ length_needed, &length_needed)) {
+ return ::GetLastError();
+ }
+
+ return ERROR_SUCCESS;
+}
+
+} // namespace
+
+DWORD CreateRestrictedToken(HANDLE effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
+ PSID unique_restricted_sid,
+ bool use_restricting_sids,
+ base::win::ScopedHandle* token) {
+ RestrictedToken restricted_token;
+ restricted_token.Init(effective_token);
+ if (lockdown_default_dacl)
+ restricted_token.SetLockdownDefaultDacl();
+ if (unique_restricted_sid) {
+ restricted_token.AddDefaultDaclSid(Sid(unique_restricted_sid), GRANT_ACCESS,
+ GENERIC_ALL);
+ restricted_token.AddDefaultDaclSid(Sid(WinCreatorOwnerRightsSid),
+ GRANT_ACCESS, READ_CONTROL);
+ }
+
+ std::vector<std::wstring> privilege_exceptions;
+ std::vector<Sid> sid_exceptions;
+
+ bool deny_sids = true;
+ bool remove_privileges = true;
+
+ switch (security_level) {
+ case USER_UNPROTECTED: {
+ deny_sids = false;
+ remove_privileges = false;
+ break;
+ }
+ case USER_RESTRICTED_SAME_ACCESS: {
+ deny_sids = false;
+ remove_privileges = false;
+
+ if (use_restricting_sids) {
+ unsigned err_code = restricted_token.AddRestrictingSidAllSids();
+ if (ERROR_SUCCESS != err_code) {
+ return err_code;
+ }
+ }
+
+ break;
+ }
+ case USER_NON_ADMIN: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ break;
+ }
+ case USER_RESTRICTED_NON_ADMIN: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinInteractiveSid);
+ restricted_token.AddRestrictingSid(WinAuthenticatedUserSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
+ break;
+ }
+ case USER_INTERACTIVE: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ sid_exceptions.push_back(WinAuthenticatedUserSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ restricted_token.AddRestrictingSidCurrentUser();
+ restricted_token.AddRestrictingSidLogonSession();
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ case USER_LIMITED: {
+ sid_exceptions.push_back(WinBuiltinUsersSid);
+ sid_exceptions.push_back(WinWorldSid);
+ sid_exceptions.push_back(WinInteractiveSid);
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
+ restricted_token.AddRestrictingSid(WinWorldSid);
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+
+ // This token has to be able to create objects in BNO.
+ // Unfortunately, on Vista+, it needs the current logon sid
+ // in the token to achieve this. You should also set the process to be
+ // low integrity level so it can't access object created by other
+ // processes.
+ restricted_token.AddRestrictingSidLogonSession();
+ } else {
+ restricted_token.AddUserSidForDenyOnly();
+ }
+ break;
+ }
+ case USER_RESTRICTED: {
+ privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ case USER_LOCKDOWN: {
+ restricted_token.AddUserSidForDenyOnly();
+ if (use_restricting_sids) {
+ restricted_token.AddRestrictingSid(WinNullSid);
+ if (unique_restricted_sid)
+ restricted_token.AddRestrictingSid(Sid(unique_restricted_sid));
+ }
+ break;
+ }
+ default: { return ERROR_BAD_ARGUMENTS; }
+ }
+
+ DWORD err_code = ERROR_SUCCESS;
+ if (deny_sids) {
+ err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+ }
+
+ if (remove_privileges) {
+ err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions);
+ if (ERROR_SUCCESS != err_code)
+ return err_code;
+ }
+
+ restricted_token.SetIntegrityLevel(integrity_level);
+
+ switch (token_type) {
+ case PRIMARY: {
+ err_code = restricted_token.GetRestrictedToken(token);
+ break;
+ }
+ case IMPERSONATION: {
+ err_code = restricted_token.GetRestrictedTokenForImpersonation(token);
+ break;
+ }
+ default: {
+ err_code = ERROR_BAD_ARGUMENTS;
+ break;
+ }
+ }
+
+ return err_code;
+}
+
+DWORD SetObjectIntegrityLabel(HANDLE handle,
+ SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid) {
+ // Build the SDDL string for the label.
+ std::wstring sddl = L"S:("; // SDDL for a SACL.
+ sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label".
+ sddl += L";;"; // No Ace Flags.
+ sddl += ace_access; // Add the ACE access.
+ sddl += L";;;"; // No ObjectType and Inherited Object Type.
+ sddl += integrity_level_sid; // Trustee Sid.
+ sddl += L")";
+
+ DWORD error = ERROR_SUCCESS;
+ PSECURITY_DESCRIPTOR sec_desc = nullptr;
+
+ PACL sacl = nullptr;
+ BOOL sacl_present = false;
+ BOOL sacl_defaulted = false;
+
+ if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(
+ sddl.c_str(), SDDL_REVISION, &sec_desc, nullptr)) {
+ if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ error = ::SetSecurityInfo(handle, type, LABEL_SECURITY_INFORMATION,
+ nullptr, nullptr, nullptr, sacl);
+ } else {
+ error = ::GetLastError();
+ }
+
+ ::LocalFree(sec_desc);
+ } else {
+ return ::GetLastError();
+ }
+
+ return error;
+}
+
+const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) {
+ switch (integrity_level) {
+ case INTEGRITY_LEVEL_SYSTEM:
+ return L"S-1-16-16384";
+ case INTEGRITY_LEVEL_HIGH:
+ return L"S-1-16-12288";
+ case INTEGRITY_LEVEL_MEDIUM:
+ return L"S-1-16-8192";
+ case INTEGRITY_LEVEL_MEDIUM_LOW:
+ return L"S-1-16-6144";
+ case INTEGRITY_LEVEL_LOW:
+ return L"S-1-16-4096";
+ case INTEGRITY_LEVEL_BELOW_LOW:
+ return L"S-1-16-2048";
+ case INTEGRITY_LEVEL_UNTRUSTED:
+ return L"S-1-16-0";
+ case INTEGRITY_LEVEL_LAST:
+ return nullptr;
+ }
+
+ NOTREACHED();
+ return nullptr;
+}
+DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
+ const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level);
+ if (!integrity_level_str) {
+ // No mandatory level specified, we don't change it.
+ return ERROR_SUCCESS;
+ }
+
+ PSID integrity_sid = nullptr;
+ if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid))
+ return ::GetLastError();
+
+ TOKEN_MANDATORY_LABEL label = {};
+ label.Label.Attributes = SE_GROUP_INTEGRITY;
+ label.Label.Sid = integrity_sid;
+
+ DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid);
+ bool result = ::SetTokenInformation(token, TokenIntegrityLevel, &label, size);
+ auto last_error = ::GetLastError();
+ ::LocalFree(integrity_sid);
+
+ return result ? ERROR_SUCCESS : last_error;
+}
+
+DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
+ // We don't check for an invalid level here because we'll just let it
+ // fail on the SetTokenIntegrityLevel call later on.
+ if (integrity_level == INTEGRITY_LEVEL_LAST) {
+ // No mandatory level specified, we don't change it.
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ return SetTokenIntegrityLevel(token.Get(), integrity_level);
+}
+
+DWORD HardenTokenIntegrityLevelPolicy(HANDLE token) {
+ std::vector<char> security_desc_buffer;
+ PSECURITY_DESCRIPTOR security_desc = nullptr;
+ DWORD last_error = GetObjectSecurityDescriptor(
+ token, LABEL_SECURITY_INFORMATION, &security_desc_buffer, &security_desc);
+ if (last_error != ERROR_SUCCESS)
+ return last_error;
+
+ PACL sacl = nullptr;
+ BOOL sacl_present = false;
+ BOOL sacl_defaulted = false;
+
+ if (!::GetSecurityDescriptorSacl(security_desc, &sacl_present, &sacl,
+ &sacl_defaulted)) {
+ return ::GetLastError();
+ }
+
+ for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) {
+ PSYSTEM_MANDATORY_LABEL_ACE ace;
+
+ if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace)) &&
+ ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) {
+ ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP |
+ SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP;
+ break;
+ }
+ }
+
+ if (!::SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
+ security_desc))
+ return ::GetLastError();
+
+ return ERROR_SUCCESS;
+}
+
+DWORD HardenProcessIntegrityLevelPolicy() {
+ HANDLE token_handle;
+ if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL | WRITE_OWNER,
+ &token_handle))
+ return ::GetLastError();
+
+ base::win::ScopedHandle token(token_handle);
+
+ 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_count > 0 ? saved_handles : nullptr);
+ 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 = std::move(token_lowbox_handle);
+ return ERROR_SUCCESS;
+ }
+
+ HANDLE dup_handle = nullptr;
+ if (!::DuplicateTokenEx(token_lowbox_handle.Get(), TOKEN_ALL_ACCESS, nullptr,
+ ::SecurityImpersonation, ::TokenImpersonation,
+ &dup_handle)) {
+ return ::GetLastError();
+ }
+
+ // Copy security descriptor from primary token as the new object will have
+ // the DACL from the current token's default DACL.
+ base::win::ScopedHandle token_for_sd(dup_handle);
+ std::vector<char> security_desc_buffer;
+ PSECURITY_DESCRIPTOR security_desc = nullptr;
+ DWORD last_error = GetObjectSecurityDescriptor(
+ token_lowbox_handle.Get(), DACL_SECURITY_INFORMATION,
+ &security_desc_buffer, &security_desc);
+ if (last_error != ERROR_SUCCESS)
+ return last_error;
+
+ if (!::SetKernelObjectSecurity(token_for_sd.Get(), DACL_SECURITY_INFORMATION,
+ security_desc)) {
+ return ::GetLastError();
+ }
+
+ *token = std::move(token_for_sd);
+
+ 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);
+ std::wstring 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/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
new file mode 100644
index 0000000000..e1f75a89c9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_utils.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2006-2008 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_RESTRICTED_TOKEN_UTILS_H__
+#define SANDBOX_SRC_RESTRICTED_TOKEN_UTILS_H__
+
+#include <accctrl.h>
+#include <windows.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/restricted_token.h"
+#include "sandbox/win/src/security_level.h"
+
+// Contains the utility functions to be able to create restricted tokens based
+// on a security profiles.
+
+namespace sandbox {
+
+// The type of the token returned by the CreateNakedToken.
+enum TokenType { IMPERSONATION = 0, PRIMARY };
+
+// Creates a restricted token from effective token. If it's nullptr then
+// effective token of process is used instead. The parameter security_level
+// determines how much the token isrestricted. The token_type determines if
+// the token will be used as a primarytoken or impersonation token. The
+// integrity level of the token is set to |integrity level| on Vista only.
+// |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.
+// If the function succeeds, the return value is ERROR_SUCCESS. If the
+// function fails, the return value is the win32 error code corresponding to
+// the error.
+DWORD CreateRestrictedToken(HANDLE effective_token,
+ TokenLevel security_level,
+ IntegrityLevel integrity_level,
+ TokenType token_type,
+ bool lockdown_default_dacl,
+ PSID unique_restricted_sid,
+ bool use_restricting_sids,
+ base::win::ScopedHandle* token);
+
+// Sets the integrity label on a object handle.
+DWORD SetObjectIntegrityLabel(HANDLE handle,
+ SE_OBJECT_TYPE type,
+ const wchar_t* ace_access,
+ const wchar_t* integrity_level_sid);
+
+// Sets the integrity level on a token. This is only valid on Vista. It returns
+// without failing on XP. If the integrity level that you specify is greater
+// than the current integrity level, the function will fail.
+DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level);
+
+// Returns the integrity level SDDL string associated with a given
+// IntegrityLevel value.
+const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level);
+
+// Sets the integrity level on the current process on Vista. It returns without
+// failing on XP. If the integrity level that you specify is greater than the
+// current integrity level, the function will fail.
+DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level);
+
+// Hardens the integrity level policy on a token. This is only valid on Win 7
+// and above. Specifically it sets the policy to block read and execute so
+// that a lower privileged process cannot open the token for impersonate or
+// duplicate permissions. This should limit potential security holes.
+DWORD HardenTokenIntegrityLevelPolicy(HANDLE token);
+
+// Hardens the integrity level policy on the current process. This is only
+// valid on Win 7 and above. Specifically it sets the policy to block read
+// and execute so that a lower privileged process cannot open the token for
+// impersonate or duplicate permissions. This should limit potential security
+// 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/security/sandbox/chromium/sandbox/win/src/sandbox.cc b/security/sandbox/chromium/sandbox/win/src/sandbox.cc
new file mode 100644
index 0000000000..f65e379683
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 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/sandbox.h"
+
+#include <windows.h>
+
+#include "sandbox/win/src/broker_services.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+// The section for IPC and policy.
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+static bool s_is_broker = false;
+
+// GetBrokerServices: the current implementation relies on a shared section
+// that is created by the broker and opened by the target.
+BrokerServices* SandboxFactory::GetBrokerServices() {
+ // Can't be the broker if the shared section is open.
+ if (g_shared_section)
+ return nullptr;
+ // If the shared section does not exist we are the broker, then create
+ // the broker object.
+ s_is_broker = true;
+ return BrokerServicesBase::GetInstance();
+}
+
+// GetTargetServices implementation must follow the same technique as the
+// GetBrokerServices, but in this case the logic is the opposite.
+TargetServices* SandboxFactory::GetTargetServices() {
+ // Can't be the target if the section handle is not valid.
+ if (!g_shared_section)
+ return nullptr;
+ // We are the target
+ s_is_broker = false;
+ // Creates and returns the target services implementation.
+ return TargetServicesBase::GetInstance();
+}
+
+} // namespace sandbox
+
+// Allows querying for whether the current process has been sandboxed.
+extern "C" bool __declspec(dllexport) IsSandboxedProcess() {
+ return !!sandbox::g_shared_section;
+}
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.h b/security/sandbox/chromium/sandbox/win/src/sandbox.h
new file mode 100644
index 0000000000..858c350558
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox.h
@@ -0,0 +1,228 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Sandbox is a sandbox library for windows processes. Use when you want a
+// 'privileged' process and a 'locked down process' to interact with.
+// The privileged process is called the broker and it is started by external
+// means (such as the user starting it). The 'sandboxed' process is called the
+// target and it is started by the broker. There can be many target processes
+// started by a single broker process. This library provides facilities
+// for both the broker and the target.
+//
+// The design rationale and relevant documents can be found at http://go/sbox.
+//
+// Note: this header does not include the SandboxFactory definitions because
+// there are cases where the Sandbox library is linked against the main .exe
+// while its API needs to be used in a DLL.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_H_
+#define SANDBOX_WIN_SRC_SANDBOX_H_
+
+#if !defined(SANDBOX_FUZZ_TARGET)
+#include <windows.h>
+#else
+#include "sandbox/win/fuzzer/fuzzer_types.h"
+#endif
+
+#include <stddef.h>
+#include <memory>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+// sandbox: Google User-Land Application Sandbox
+namespace sandbox {
+
+class BrokerServices;
+class PolicyDiagnosticsReceiver;
+class ProcessState;
+class TargetPolicy;
+class TargetServices;
+
+// BrokerServices exposes all the broker API.
+// The basic use is to start the target(s) and wait for them to end.
+//
+// This API is intended to be called in the following order
+// (error checking omitted):
+// BrokerServices* broker = SandboxFactory::GetBrokerServices();
+// broker->Init();
+// PROCESS_INFORMATION target;
+// broker->SpawnTarget(target_exe_path, target_args, &target);
+// ::ResumeThread(target->hThread);
+// // -- later you can call:
+// broker->WaitForAllTargets(option);
+//
+class BrokerServices {
+ public:
+ // Initializes the broker. Must be called before any other on this class.
+ // returns ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode Init() = 0;
+
+ // Returns the interface pointer to a new, empty policy object. Use this
+ // interface to specify the sandbox policy for new processes created by
+ // SpawnTarget()
+ virtual scoped_refptr<TargetPolicy> CreatePolicy() = 0;
+
+ // Creates a new target (child process) in a suspended state.
+ // Parameters:
+ // exe_path: This is the full path to the target binary. This parameter
+ // can be null and in this case the exe path must be the first argument
+ // of the command_line.
+ // command_line: The arguments to be passed as command line to the new
+ // process. This can be null if the exe_path parameter is not null.
+ // policy: This is the pointer to the policy object for the sandbox to
+ // be created.
+ // last_warning: The argument will contain an indication on whether
+ // the process security was initialized completely, Only set if the
+ // process can be used without a serious compromise in security.
+ // last_error: If an error or warning is returned from this method this
+ // parameter will hold the last Win32 error value.
+ // target: returns the resulting target process information such as process
+ // handle and PID just as if CreateProcess() had been called. The caller is
+ // responsible for closing the handles returned in this structure.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ virtual ResultCode SpawnTarget(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ base::EnvironmentMap& env_map,
+ scoped_refptr<TargetPolicy> policy,
+ ResultCode* last_warning,
+ DWORD* last_error,
+ PROCESS_INFORMATION* target) = 0;
+
+ // This call blocks (waits) for all the targets to terminate.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode WaitForAllTargets() = 0;
+
+ // Adds an unsandboxed process as a peer for policy decisions (e.g.
+ // HANDLES_DUP_ANY policy).
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode AddTargetPeer(HANDLE peer_process) = 0;
+
+ // This call creates a snapshot of policies managed by the sandbox and
+ // returns them via a helper class.
+ // Parameters:
+ // receiver: The |PolicyDiagnosticsReceiver| implementation will be
+ // called to accept the results of the call.
+ // Returns:
+ // ALL_OK if the request was dispatched. All other return values
+ // imply failure, and the responder will not receive its completion
+ // callback.
+ virtual ResultCode GetPolicyDiagnostics(
+ std::unique_ptr<PolicyDiagnosticsReceiver> receiver) = 0;
+
+ // Derive a capability PSID from the given string.
+ virtual bool DeriveCapabilitySidFromName(const wchar_t* name,
+ PSID derived_sid,
+ DWORD sid_buffer_length) = 0;
+
+ protected:
+ ~BrokerServices() {}
+};
+
+// TargetServices models the current process from the perspective
+// of a target process. To obtain a pointer to it use
+// Sandbox::GetTargetServices(). Note that this call returns a non-null
+// pointer only if this process is in fact a target. A process is a target
+// only if the process was spawned by a call to BrokerServices::SpawnTarget().
+//
+// This API allows the target to gain access to resources with a high
+// privilege token and then when it is ready to perform dangerous activities
+// (such as download content from the web) it can lower its token and
+// enter into locked-down (sandbox) mode.
+// The typical usage is as follows:
+//
+// TargetServices* target_services = Sandbox::GetTargetServices();
+// if (target_services) {
+// // We are the target.
+// target_services->Init();
+// // Do work that requires high privileges here.
+// // ....
+// // When ready to enter lock-down mode call LowerToken:
+// target_services->LowerToken();
+// }
+//
+// For more information see the BrokerServices API documentation.
+class TargetServices {
+ public:
+ // Initializes the target. Must call this function before any other.
+ // returns ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode Init() = 0;
+
+ // Discards the impersonation token and uses the lower token, call before
+ // processing any untrusted data or running third-party code. If this call
+ // fails the current process could be terminated immediately.
+ virtual void LowerToken() = 0;
+
+ // Returns the ProcessState object. Through that object it's possible to have
+ // information about the current state of the process, such as whether
+ // LowerToken has been called or not.
+ virtual ProcessState* GetState() = 0;
+
+ // Requests the broker to duplicate the supplied handle into the target
+ // process. The target process must be an active sandbox child process
+ // and the source process must have a corresponding policy allowing
+ // handle duplication for this object type.
+ // Returns:
+ // ALL_OK if successful. All other return values imply failure.
+ // If the return is ERROR_GENERIC, you can call ::GetLastError() to get
+ // more information.
+ virtual ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) = 0;
+
+ virtual ResultCode GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before) = 0;
+
+ protected:
+ ~TargetServices() {}
+};
+
+class PolicyInfo {
+ public:
+ // Returns a JSON representation of the policy snapshot.
+ // This pointer has the same lifetime as this PolicyInfo object.
+ virtual const char* JsonString() = 0;
+ virtual ~PolicyInfo() {}
+};
+
+// This is returned by BrokerServices::GetPolicyDiagnostics().
+// PolicyInfo entries need not be ordered.
+class PolicyList {
+ public:
+ virtual std::vector<std::unique_ptr<PolicyInfo>>::iterator begin() = 0;
+ virtual std::vector<std::unique_ptr<PolicyInfo>>::iterator end() = 0;
+ virtual size_t size() const = 0;
+ virtual ~PolicyList() {}
+};
+
+// This class mediates calls to BrokerServices::GetPolicyDiagnostics().
+class PolicyDiagnosticsReceiver {
+ public:
+ // ReceiveDiagnostics() should return quickly and should not block the
+ // thread on which it is called.
+ virtual void ReceiveDiagnostics(std::unique_ptr<PolicyList> policies) = 0;
+ // OnError() is passed any errors encountered and |ReceiveDiagnostics|
+ // will not be called.
+ virtual void OnError(ResultCode code) = 0;
+ virtual ~PolicyDiagnosticsReceiver() {}
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox.vcproj b/security/sandbox/chromium/sandbox/win/src/sandbox.vcproj
new file mode 100644
index 0000000000..229441cbd5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox.vcproj
@@ -0,0 +1,648 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="sandbox"
+ ProjectGUID="{881F6A97-D539-4C48-B401-DF04385B2343}"
+ RootNamespace="sandbox"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="2"
+ ForcedIncludeFiles="stdafx.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="security"
+ >
+ <File
+ RelativePath=".\acl.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\acl.h"
+ >
+ </File>
+ <File
+ RelativePath=".\dep.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\dep.h"
+ >
+ </File>
+ <File
+ RelativePath=".\job.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\job.h"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token.h"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\restricted_token_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\security_level.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sid.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sid.h"
+ >
+ </File>
+ <File
+ RelativePath=".\window.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\window.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Interception"
+ >
+ <File
+ RelativePath=".\eat_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\eat_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_agent.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_agent.h"
+ >
+ </File>
+ <File
+ RelativePath=".\interception_internal.h"
+ >
+ </File>
+ <File
+ RelativePath=".\pe_image.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\pe_image.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\service_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\service_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep_resolver.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep_resolver.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_interceptions.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_interceptions.h"
+ >
+ </File>
+ <File
+ RelativePath=".\Wow64.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\Wow64.h"
+ >
+ </File>
+ <Filter
+ Name="sidestep"
+ >
+ <File
+ RelativePath=".\sidestep\ia32_modrm_map.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\ia32_opcode_map.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\mini_disassembler_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\preamble_patcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sidestep\preamble_patcher_with_stub.cpp"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="nt_level"
+ >
+ <File
+ RelativePath=".\nt_internals.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_target.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_target.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_util.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_nt_util.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Policy_handlers"
+ >
+ <File
+ RelativePath=".\filesystem_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\filesystem_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\named_pipe_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\process_thread_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\registry_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_dispatcher.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_dispatcher.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_interception.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_interception.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_policy.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sync_policy.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="IPC"
+ >
+ <File
+ RelativePath=".\crosscall_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_server.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\crosscall_server.h"
+ >
+ </File>
+ <File
+ RelativePath=".\ipc_tags.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_client.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_client.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_server.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sharedmem_ipc_server.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Policy_base"
+ >
+ <File
+ RelativePath=".\policy_engine_opcodes.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_opcodes.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_params.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_processor.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_engine_processor.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_low_level.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_low_level.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy_base.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy_base.h"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath=".\broker_services.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\broker_services.h"
+ >
+ </File>
+ <File
+ RelativePath=".\internal_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_broker.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\policy_broker.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_factory.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_policy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_types.h"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\sandbox_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\shared_handles.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\shared_handles.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cc"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_process.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_process.h"
+ >
+ </File>
+ <File
+ RelativePath=".\target_services.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\target_services.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win2k_threadpool.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\win2k_threadpool.h"
+ >
+ </File>
+ <File
+ RelativePath=".\win_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\win_utils.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h b/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h
new file mode 100644
index 0000000000..07402521dc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_factory.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2006-2008 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_SANDBOX_FACTORY_H__
+#define SANDBOX_SRC_SANDBOX_FACTORY_H__
+
+#include "base/macros.h"
+#include "sandbox/win/src/sandbox.h"
+
+// SandboxFactory is a set of static methods to get access to the broker
+// or target services object. Only one of the two methods (GetBrokerServices,
+// GetTargetServices) will return a non-null pointer and that should be used
+// as the indication that the process is the broker or the target:
+//
+// BrokerServices* broker_services = SandboxFactory::GetBrokerServices();
+// if (broker_services) {
+// //we are the broker, call broker api here
+// broker_services->Init();
+// } else {
+// TargetServices* target_services = SandboxFactory::GetTargetServices();
+// if (target_services) {
+// //we are the target, call target api here
+// target_services->Init();
+// }
+//
+// The methods in this class are expected to be called from a single thread
+//
+// The Sandbox library needs to be linked against the main executable, but
+// sometimes the API calls are issued from a DLL that loads into the exe
+// process. These factory methods then need to be called from the main
+// exe and the interface pointers then can be safely passed to the DLL where
+// the Sandbox API calls are made.
+namespace sandbox {
+
+class SandboxFactory {
+ public:
+ // Returns the Broker API interface, returns nullptr if this process is the
+ // target.
+ static BrokerServices* GetBrokerServices();
+
+ // Returns the Target API interface, returns nullptr if this process is the
+ // broker.
+ static TargetServices* GetTargetServices();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(SandboxFactory);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_FACTORY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc
new file mode 100644
index 0000000000..3387e3f604
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_globals.cc
@@ -0,0 +1,18 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// The section for IPC and policy.
+SANDBOX_INTERCEPT HANDLE g_shared_section = nullptr;
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt = {};
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h
new file mode 100644
index 0000000000..eff0f019d3
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_types.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2008 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_SANDBOX_NT_TYPES_H__
+#define SANDBOX_SRC_SANDBOX_NT_TYPES_H__
+
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+struct NtExports {
+ NtAllocateVirtualMemoryFunction AllocateVirtualMemory;
+ NtCloseFunction Close;
+ NtDuplicateObjectFunction DuplicateObject;
+ NtFreeVirtualMemoryFunction FreeVirtualMemory;
+ NtMapViewOfSectionFunction MapViewOfSection;
+ NtProtectVirtualMemoryFunction ProtectVirtualMemory;
+ NtQueryInformationProcessFunction QueryInformationProcess;
+ NtQueryObjectFunction QueryObject;
+ NtQuerySectionFunction QuerySection;
+ NtQueryVirtualMemoryFunction QueryVirtualMemory;
+ NtUnmapViewOfSectionFunction UnmapViewOfSection;
+ NtSignalAndWaitForSingleObjectFunction SignalAndWaitForSingleObject;
+ NtWaitForSingleObjectFunction WaitForSingleObject;
+ RtlAllocateHeapFunction RtlAllocateHeap;
+ RtlAnsiStringToUnicodeStringFunction RtlAnsiStringToUnicodeString;
+ RtlCompareUnicodeStringFunction RtlCompareUnicodeString;
+ RtlCreateHeapFunction RtlCreateHeap;
+ RtlCreateUserThreadFunction RtlCreateUserThread;
+ RtlDestroyHeapFunction RtlDestroyHeap;
+ RtlFreeHeapFunction RtlFreeHeap;
+ _strnicmpFunction _strnicmp;
+ strlenFunction strlen;
+ wcslenFunction wcslen;
+ memcpyFunction memcpy;
+};
+
+// This is the value used for the ntdll level allocator.
+enum AllocationType {
+ NT_ALLOC,
+ NT_PAGE
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_NT_TYPES_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
new file mode 100644
index 0000000000..2641a13d04
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.cc
@@ -0,0 +1,755 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+
+namespace sandbox {
+
+// This is the list of all imported symbols from ntdll.dll.
+SANDBOX_INTERCEPT NtExports g_nt;
+
+} // namespace sandbox
+
+namespace {
+
+#if defined(_WIN64)
+// Align a pointer to the next allocation granularity boundary.
+inline char* AlignToBoundary(void* ptr, size_t increment) {
+ const size_t kAllocationGranularity = (64 * 1024) - 1;
+ uintptr_t ptr_int = reinterpret_cast<uintptr_t>(ptr);
+ uintptr_t ret_ptr =
+ (ptr_int + increment + kAllocationGranularity) & ~kAllocationGranularity;
+ // Check for overflow.
+ if (ret_ptr < ptr_int)
+ return nullptr;
+ return reinterpret_cast<char*>(ret_ptr);
+}
+
+// Allocate a memory block somewhere within 2GiB of a specified base address.
+// This is used for the DLL hooking code to get a valid trampoline location
+// which must be within +/- 2GiB of the base. We only consider +2GiB for now.
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+ // 2GiB, maximum upper bound the allocation address must be within.
+ const size_t kMaxSize = 0x80000000ULL;
+ // We don't support null as a base as this would just pick an arbitrary
+ // address when passed to NtAllocateVirtualMemory.
+ if (!source)
+ return nullptr;
+ // Ignore an allocation which is larger than the maximum.
+ if (size > kMaxSize)
+ return nullptr;
+
+ // Ensure base address is aligned to the allocation granularity boundary.
+ char* base = AlignToBoundary(source, 0);
+ if (!base)
+ return nullptr;
+ // Set top address to be base + 2GiB.
+ const char* top_address = base + kMaxSize;
+
+ while (base < top_address) {
+ // Avoid memset inserted by -ftrivial-auto-var-init=pattern.
+ STACK_UNINITIALIZED MEMORY_BASIC_INFORMATION mem_info;
+ NTSTATUS status =
+ g_nt.QueryVirtualMemory(NtCurrentProcess, base, MemoryBasicInformation,
+ &mem_info, sizeof(mem_info), nullptr);
+ if (!NT_SUCCESS(status))
+ break;
+
+ if ((mem_info.State == MEM_FREE) && (mem_info.RegionSize >= size)) {
+ // We've found a valid free block, try and allocate it for use.
+ // Note that we need to both commit and reserve the block for the
+ // allocation to succeed as per Windows virtual memory requirements.
+ void* ret_base = mem_info.BaseAddress;
+ status =
+ g_nt.AllocateVirtualMemory(NtCurrentProcess, &ret_base, 0, &size,
+ MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ // Shouldn't fail, but if it does we'll just continue and try next block.
+ if (NT_SUCCESS(status))
+ return ret_base;
+ }
+
+ // Update base past current allocation region.
+ base = AlignToBoundary(mem_info.BaseAddress, mem_info.RegionSize);
+ if (!base)
+ break;
+ }
+ return nullptr;
+}
+#else // defined(_WIN64).
+void* AllocateNearTo(void* source, size_t size) {
+ using sandbox::g_nt;
+
+ // In 32-bit processes allocations below 512k are predictable, so mark
+ // anything in that range as reserved and retry until we get a good address.
+ const void* const kMinAddress = reinterpret_cast<void*>(512 * 1024);
+ NTSTATUS ret;
+ SIZE_T actual_size;
+ void* base;
+ do {
+ base = nullptr;
+ actual_size = 64 * 1024;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_RESERVE, PAGE_NOACCESS);
+ if (!NT_SUCCESS(ret))
+ return nullptr;
+ } while (base < kMinAddress);
+
+ actual_size = size;
+ ret = g_nt.AllocateVirtualMemory(NtCurrentProcess, &base, 0, &actual_size,
+ MEM_COMMIT, PAGE_READWRITE);
+ if (!NT_SUCCESS(ret))
+ return nullptr;
+ return base;
+}
+#endif // defined(_WIN64).
+
+} // namespace.
+
+namespace sandbox {
+
+// Handle for our private heap.
+void* g_heap = nullptr;
+
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+SANDBOX_INTERCEPT size_t g_shared_IPC_size = 0;
+SANDBOX_INTERCEPT size_t g_shared_policy_size = 0;
+
+void* volatile g_shared_policy_memory = nullptr;
+void* volatile g_shared_IPC_memory = nullptr;
+
+// Both the IPC and the policy share a single region of memory in which the IPC
+// memory is first and the policy memory is last.
+bool MapGlobalMemory() {
+ if (!g_shared_IPC_memory) {
+ void* memory = nullptr;
+ SIZE_T size = 0;
+ // Map the entire shared section from the start.
+ NTSTATUS ret =
+ g_nt.MapViewOfSection(g_shared_section, NtCurrentProcess, &memory, 0, 0,
+ nullptr, &size, ViewUnmap, 0, PAGE_READWRITE);
+
+ if (!NT_SUCCESS(ret) || !memory) {
+ NOTREACHED_NT();
+ return false;
+ }
+
+ if (_InterlockedCompareExchangePointer(&g_shared_IPC_memory, memory,
+ nullptr)) {
+ // Somebody beat us to the memory setup.
+ VERIFY_SUCCESS(g_nt.UnmapViewOfSection(NtCurrentProcess, memory));
+ }
+ DCHECK_NT(g_shared_IPC_size > 0);
+ g_shared_policy_memory =
+ reinterpret_cast<char*>(g_shared_IPC_memory) + g_shared_IPC_size;
+ }
+ DCHECK_NT(g_shared_policy_memory);
+ DCHECK_NT(g_shared_policy_size > 0);
+ return true;
+}
+
+void* GetGlobalIPCMemory() {
+ if (!MapGlobalMemory())
+ return nullptr;
+ return g_shared_IPC_memory;
+}
+
+void* GetGlobalPolicyMemory() {
+ if (!MapGlobalMemory())
+ return nullptr;
+ return g_shared_policy_memory;
+}
+
+bool InitHeap() {
+ if (!g_heap) {
+ // Create a new heap using default values for everything.
+ void* heap =
+ g_nt.RtlCreateHeap(HEAP_GROWABLE, nullptr, 0, 0, nullptr, nullptr);
+ if (!heap)
+ return false;
+
+ if (_InterlockedCompareExchangePointer(&g_heap, heap, nullptr)) {
+ // Somebody beat us to the memory setup.
+ g_nt.RtlDestroyHeap(heap);
+ }
+ }
+ return !!g_heap;
+}
+
+// Physically reads or writes from memory to verify that (at this time), it is
+// valid. Returns a dummy value.
+int TouchMemory(void* buffer, size_t size_bytes, RequiredAccess intent) {
+ const int kPageSize = 4096;
+ int dummy = 0;
+ volatile char* start = reinterpret_cast<char*>(buffer);
+ volatile char* end = start + size_bytes - 1;
+
+ if (WRITE == intent) {
+ for (; start < end; start += kPageSize) {
+ *start = *start;
+ }
+ *end = *end;
+ } else {
+ for (; start < end; start += kPageSize) {
+ dummy += *start;
+ }
+ dummy += *end;
+ }
+
+ return dummy;
+}
+
+bool ValidParameter(void* buffer, size_t size, RequiredAccess intent) {
+ DCHECK_NT(size);
+ __try {
+ TouchMemory(buffer, size, intent);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ return false;
+ }
+ return true;
+}
+
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ __try {
+ g_nt.memcpy(destination, source, bytes);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+ return ret;
+}
+
+NTSTATUS AllocAndGetFullPath(
+ HANDLE root,
+ const wchar_t* path,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* full_path) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(full_path);
+ DCHECK_NT(path);
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ static NtQueryObjectFunction NtQueryObject = nullptr;
+ if (!NtQueryObject)
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ ULONG size = 0;
+ // Query the name information a first time to get the size of the name.
+ ret = NtQueryObject(root, ObjectNameInformation, nullptr, 0, &size);
+
+ std::unique_ptr<OBJECT_NAME_INFORMATION, NtAllocDeleter> handle_name;
+ if (size) {
+ handle_name.reset(reinterpret_cast<OBJECT_NAME_INFORMATION*>(
+ new (NT_ALLOC) BYTE[size]));
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ ret = NtQueryObject(root, ObjectNameInformation, handle_name.get(),
+ size, &size);
+ }
+
+ if (STATUS_SUCCESS != ret)
+ break;
+
+ // Space for path + '\' + name + '\0'.
+ size_t name_length =
+ handle_name->ObjectName.Length + (wcslen(path) + 2) * sizeof(wchar_t);
+ full_path->reset(new (NT_ALLOC) wchar_t[name_length / sizeof(wchar_t)]);
+ if (!*full_path)
+ break;
+ wchar_t* off = full_path->get();
+ ret = CopyData(off, handle_name->ObjectName.Buffer,
+ handle_name->ObjectName.Length);
+ if (!NT_SUCCESS(ret))
+ break;
+ off += handle_name->ObjectName.Length / sizeof(wchar_t);
+ *off = L'\\';
+ off += 1;
+ ret = CopyData(off, path, wcslen(path) * sizeof(wchar_t));
+ if (!NT_SUCCESS(ret))
+ break;
+ off += wcslen(path);
+ *off = L'\0';
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(ret) && *full_path)
+ full_path->reset(nullptr);
+
+ return ret;
+}
+
+// Hacky code... replace with AllocAndCopyObjectAttributes.
+NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* out_name,
+ uint32_t* attributes,
+ HANDLE* root) {
+ if (!InitHeap())
+ return STATUS_NO_MEMORY;
+
+ DCHECK_NT(out_name);
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+ __try {
+ do {
+ if (in_object->RootDirectory != static_cast<HANDLE>(0) && !root)
+ break;
+ if (!in_object->ObjectName)
+ break;
+ if (!in_object->ObjectName->Buffer)
+ break;
+
+ size_t size = in_object->ObjectName->Length + sizeof(wchar_t);
+ out_name->reset(new (NT_ALLOC) wchar_t[size / sizeof(wchar_t)]);
+ if (!*out_name)
+ break;
+
+ ret = CopyData(out_name->get(), in_object->ObjectName->Buffer,
+ size - sizeof(wchar_t));
+ if (!NT_SUCCESS(ret))
+ break;
+
+ out_name->get()[size / sizeof(wchar_t) - 1] = L'\0';
+
+ if (attributes)
+ *attributes = in_object->Attributes;
+
+ if (root)
+ *root = in_object->RootDirectory;
+ ret = STATUS_SUCCESS;
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ ret = GetExceptionCode();
+ }
+
+ if (!NT_SUCCESS(ret) && *out_name)
+ out_name->reset(nullptr);
+
+ return ret;
+}
+
+NTSTATUS GetProcessId(HANDLE process, DWORD* process_id) {
+ PROCESS_BASIC_INFORMATION proc_info;
+ ULONG bytes_returned;
+
+ NTSTATUS ret =
+ g_nt.QueryInformationProcess(process, ProcessBasicInformation, &proc_info,
+ sizeof(proc_info), &bytes_returned);
+ if (!NT_SUCCESS(ret) || sizeof(proc_info) != bytes_returned)
+ return ret;
+
+ *process_id = proc_info.UniqueProcessId;
+ return STATUS_SUCCESS;
+}
+
+bool IsSameProcess(HANDLE process) {
+ if (NtCurrentProcess == process)
+ return true;
+
+ static DWORD s_process_id = 0;
+
+ if (!s_process_id) {
+ NTSTATUS ret = GetProcessId(NtCurrentProcess, &s_process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+ }
+
+ DWORD process_id;
+ NTSTATUS ret = GetProcessId(process, &process_id);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ return (process_id == s_process_id);
+}
+
+bool IsValidImageSection(HANDLE section,
+ PVOID* base,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size) {
+ if (!section || !base || !view_size || offset)
+ return false;
+
+ HANDLE query_section;
+
+ NTSTATUS ret =
+ g_nt.DuplicateObject(NtCurrentProcess, section, NtCurrentProcess,
+ &query_section, SECTION_QUERY, 0, 0);
+ if (!NT_SUCCESS(ret))
+ return false;
+
+ SECTION_BASIC_INFORMATION basic_info;
+ SIZE_T bytes_returned;
+ ret = g_nt.QuerySection(query_section, SectionBasicInformation, &basic_info,
+ sizeof(basic_info), &bytes_returned);
+
+ VERIFY_SUCCESS(g_nt.Close(query_section));
+
+ if (!NT_SUCCESS(ret) || sizeof(basic_info) != bytes_returned)
+ return false;
+
+ if (!(basic_info.Attributes & SEC_IMAGE))
+ return false;
+
+ // Windows 10 2009+ may open PEs as SEC_IMAGE_NO_EXECUTE in non-dll-loading
+ // paths which looks identical to dll-loading unless we check if the section
+ // handle has execute rights.
+ // Avoid memset inserted by -ftrivial-auto-var-init=pattern.
+ STACK_UNINITIALIZED OBJECT_BASIC_INFORMATION obj_info;
+ ULONG obj_size_returned;
+ ret = g_nt.QueryObject(section, ObjectBasicInformation, &obj_info,
+ sizeof(obj_info), &obj_size_returned);
+
+ if (!NT_SUCCESS(ret) || sizeof(obj_info) != obj_size_returned)
+ return false;
+
+ if (!(obj_info.GrantedAccess & SECTION_MAP_EXECUTE))
+ return false;
+
+ return true;
+}
+
+UNICODE_STRING* AnsiToUnicode(const char* string) {
+ ANSI_STRING ansi_string;
+ ansi_string.Length = static_cast<USHORT>(g_nt.strlen(string));
+ ansi_string.MaximumLength = ansi_string.Length + 1;
+ ansi_string.Buffer = const_cast<char*>(string);
+
+ if (ansi_string.Length > ansi_string.MaximumLength)
+ return nullptr;
+
+ size_t name_bytes =
+ ansi_string.MaximumLength * sizeof(wchar_t) + sizeof(UNICODE_STRING);
+
+ UNICODE_STRING* out_string =
+ reinterpret_cast<UNICODE_STRING*>(new (NT_ALLOC) char[name_bytes]);
+ if (!out_string)
+ return nullptr;
+
+ out_string->MaximumLength = ansi_string.MaximumLength * sizeof(wchar_t);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+
+ BOOLEAN alloc_destination = false;
+ NTSTATUS ret = g_nt.RtlAnsiStringToUnicodeString(out_string, &ansi_string,
+ alloc_destination);
+ DCHECK_NT(STATUS_BUFFER_OVERFLOW != ret);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return nullptr;
+ }
+
+ return out_string;
+}
+
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags) {
+// PEImage's dtor won't be run during SEH unwinding, but that's OK.
+#pragma warning(push)
+#pragma warning(disable : 4509)
+ UNICODE_STRING* out_name = nullptr;
+ __try {
+ do {
+ *flags = 0;
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+ *flags |= MODULE_IS_PE_IMAGE;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports) {
+ char* name = reinterpret_cast<char*>(pe.RVAToAddr(exports->Name));
+ out_name = AnsiToUnicode(name);
+ }
+
+ PIMAGE_NT_HEADERS headers = pe.GetNTHeaders();
+ if (headers) {
+ if (headers->OptionalHeader.AddressOfEntryPoint)
+ *flags |= MODULE_HAS_ENTRY_POINT;
+ if (headers->OptionalHeader.SizeOfCode)
+ *flags |= MODULE_HAS_CODE;
+ }
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+#pragma warning(pop)
+}
+
+const char* GetAnsiImageInfoFromModule(HMODULE module) {
+// PEImage's dtor won't be run during SEH unwinding, but that's OK.
+#pragma warning(push)
+#pragma warning(disable : 4509)
+ const char* out_name = nullptr;
+ __try {
+ do {
+ base::win::PEImage pe(module);
+
+ if (!pe.VerifyMagic())
+ break;
+
+ PIMAGE_EXPORT_DIRECTORY exports = pe.GetExportDirectory();
+ if (exports)
+ out_name = static_cast<const char*>(pe.RVAToAddr(exports->Name));
+ } while (false);
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+
+ return out_name;
+#pragma warning(pop)
+}
+
+UNICODE_STRING* GetBackingFilePath(PVOID address) {
+ // We'll start with something close to max_path charactes for the name.
+ SIZE_T buffer_bytes = MAX_PATH * 2;
+
+ for (;;) {
+ MEMORY_SECTION_NAME* section_name = reinterpret_cast<MEMORY_SECTION_NAME*>(
+ new (NT_ALLOC) char[buffer_bytes]);
+
+ if (!section_name)
+ return nullptr;
+
+ SIZE_T returned_bytes;
+ NTSTATUS ret =
+ g_nt.QueryVirtualMemory(NtCurrentProcess, address, MemorySectionName,
+ section_name, buffer_bytes, &returned_bytes);
+
+ if (STATUS_BUFFER_OVERFLOW == ret) {
+ // Retry the call with the given buffer size.
+ operator delete(section_name, NT_ALLOC);
+ section_name = nullptr;
+ buffer_bytes = returned_bytes;
+ continue;
+ }
+ if (!NT_SUCCESS(ret)) {
+ operator delete(section_name, NT_ALLOC);
+ return nullptr;
+ }
+
+ return reinterpret_cast<UNICODE_STRING*>(section_name);
+ }
+}
+
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path) {
+ if ((!module_path) || (!module_path->Buffer))
+ return nullptr;
+
+ wchar_t* sep = nullptr;
+ int start_pos = module_path->Length / sizeof(wchar_t) - 1;
+ int ix = start_pos;
+
+ for (; ix >= 0; --ix) {
+ if (module_path->Buffer[ix] == L'\\') {
+ sep = &module_path->Buffer[ix];
+ break;
+ }
+ }
+
+ // Ends with path separator. Not a valid module name.
+ if ((ix == start_pos) && sep)
+ return nullptr;
+
+ // No path separator found. Use the entire name.
+ if (!sep) {
+ sep = &module_path->Buffer[-1];
+ }
+
+ // Add one to the size so we can null terminate the string.
+ size_t size_bytes = (start_pos - ix + 1) * sizeof(wchar_t);
+
+ // Based on the code above, size_bytes should always be small enough
+ // to make the static_cast below safe.
+ DCHECK_NT(UINT16_MAX > size_bytes);
+ char* str_buffer = new (NT_ALLOC) char[size_bytes + sizeof(UNICODE_STRING)];
+ if (!str_buffer)
+ return nullptr;
+
+ UNICODE_STRING* out_string = reinterpret_cast<UNICODE_STRING*>(str_buffer);
+ out_string->Buffer = reinterpret_cast<wchar_t*>(&out_string[1]);
+ out_string->Length = static_cast<USHORT>(size_bytes - sizeof(wchar_t));
+ out_string->MaximumLength = static_cast<USHORT>(size_bytes);
+
+ NTSTATUS ret = CopyData(out_string->Buffer, &sep[1], out_string->Length);
+ if (!NT_SUCCESS(ret)) {
+ operator delete(out_string, NT_ALLOC);
+ return nullptr;
+ }
+
+ out_string->Buffer[out_string->Length / sizeof(wchar_t)] = L'\0';
+ return out_string;
+}
+
+NTSTATUS AutoProtectMemory::ChangeProtection(void* address,
+ size_t bytes,
+ ULONG protect) {
+ DCHECK_NT(!changed_);
+ SIZE_T new_bytes = bytes;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(NtCurrentProcess, &address,
+ &new_bytes, protect, &old_protect_);
+ if (NT_SUCCESS(ret)) {
+ changed_ = true;
+ address_ = address;
+ bytes_ = new_bytes;
+ }
+
+ return ret;
+}
+
+NTSTATUS AutoProtectMemory::RevertProtection() {
+ if (!changed_)
+ return STATUS_SUCCESS;
+
+ DCHECK_NT(address_);
+ DCHECK_NT(bytes_);
+
+ SIZE_T new_bytes = bytes_;
+ NTSTATUS ret = g_nt.ProtectVirtualMemory(
+ NtCurrentProcess, &address_, &new_bytes, old_protect_, &old_protect_);
+ DCHECK_NT(NT_SUCCESS(ret));
+
+ changed_ = false;
+ address_ = nullptr;
+ bytes_ = 0;
+ old_protect_ = 0;
+
+ return ret;
+}
+
+bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info,
+ DWORD length,
+ uint32_t file_info_class) {
+ if (FileRenameInformation != file_info_class)
+ return false;
+
+ if (length < sizeof(FILE_RENAME_INFORMATION))
+ return false;
+
+ // Make sure file name length doesn't exceed the message length
+ if (length - offsetof(FILE_RENAME_INFORMATION, FileName) <
+ file_info->FileNameLength)
+ return false;
+
+ // We don't support a root directory.
+ if (file_info->RootDirectory)
+ return false;
+
+ static const wchar_t kPathPrefix[] = {L'\\', L'?', L'?', L'\\'};
+
+ // Check if it starts with \\??\\. We don't support relative paths.
+ if (file_info->FileNameLength < sizeof(kPathPrefix) ||
+ file_info->FileNameLength > UINT16_MAX)
+ return false;
+
+ if (file_info->FileName[0] != kPathPrefix[0] ||
+ file_info->FileName[1] != kPathPrefix[1] ||
+ file_info->FileName[2] != kPathPrefix[2] ||
+ file_info->FileName[3] != kPathPrefix[3])
+ return false;
+
+ return true;
+}
+
+bool NtGetPathFromHandle(HANDLE handle,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* path) {
+ OBJECT_NAME_INFORMATION initial_buffer;
+ OBJECT_NAME_INFORMATION* name;
+ ULONG size = 0;
+ // Query the name information a first time to get the size of the name.
+ NTSTATUS status = g_nt.QueryObject(handle, ObjectNameInformation,
+ &initial_buffer, size, &size);
+
+ if (!NT_SUCCESS(status) && status != STATUS_INFO_LENGTH_MISMATCH)
+ return false;
+
+ std::unique_ptr<BYTE[], NtAllocDeleter> name_ptr;
+ if (!size)
+ return false;
+ name_ptr.reset(new (NT_ALLOC) BYTE[size]);
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ status = g_nt.QueryObject(handle, ObjectNameInformation, name, size, &size);
+
+ if (STATUS_SUCCESS != status)
+ return false;
+ size_t num_path_wchars = (name->ObjectName.Length / sizeof(wchar_t)) + 1;
+ path->reset(new (NT_ALLOC) wchar_t[num_path_wchars]);
+ status =
+ CopyData(path->get(), name->ObjectName.Buffer, name->ObjectName.Length);
+ path->get()[num_path_wchars - 1] = L'\0';
+ if (STATUS_SUCCESS != status)
+ return false;
+ return true;
+}
+
+} // namespace sandbox
+
+void* operator new(size_t size, sandbox::AllocationType type, void* near_to) {
+ void* result = nullptr;
+ if (type == sandbox::NT_ALLOC) {
+ if (sandbox::InitHeap()) {
+ // Use default flags for the allocation.
+ result = sandbox::g_nt.RtlAllocateHeap(sandbox::g_heap, 0, size);
+ }
+ } else if (type == sandbox::NT_PAGE) {
+ result = AllocateNearTo(near_to, size);
+ } else {
+ NOTREACHED_NT();
+ }
+
+ // TODO: Returning nullptr from operator new has undefined behavior, but
+ // the Allocate() functions called above can return nullptr. Consider checking
+ // for nullptr here and crashing or throwing.
+
+ return result;
+}
+
+void* operator new [](size_t size, sandbox::AllocationType type,
+ void* near_to) {
+ return operator new(size, type, near_to);
+}
+
+void operator delete(void* memory, sandbox::AllocationType type) {
+ if (type == sandbox::NT_ALLOC) {
+ // Use default flags.
+ VERIFY(sandbox::g_nt.RtlFreeHeap(sandbox::g_heap, 0, memory));
+ } else if (type == sandbox::NT_PAGE) {
+ void* base = memory;
+ SIZE_T size = 0;
+ VERIFY_SUCCESS(sandbox::g_nt.FreeVirtualMemory(NtCurrentProcess, &base,
+ &size, MEM_RELEASE));
+ } else {
+ NOTREACHED_NT();
+ }
+}
+
+void operator delete(void* memory,
+ sandbox::AllocationType type,
+ void* near_to) {
+ operator delete(memory, type);
+}
+
+void* __cdecl operator new(size_t size,
+ void* buffer,
+ sandbox::AllocationType type) {
+ return buffer;
+}
+
+void __cdecl operator delete(void* memory,
+ void* buffer,
+ sandbox::AllocationType type) {}
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h
new file mode 100644
index 0000000000..70d011dfb4
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_nt_util.h
@@ -0,0 +1,220 @@
+// Copyright (c) 2010 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_SANDBOX_NT_UTIL_H_
+#define SANDBOX_SRC_SANDBOX_NT_UTIL_H_
+
+#include <intrin.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+
+// Placement new and delete to be used from ntdll interception code.
+void* __cdecl operator new(size_t size,
+ sandbox::AllocationType type,
+ void* near_to = nullptr);
+void* __cdecl operator new[](size_t size,
+ sandbox::AllocationType type,
+ void* near_to = nullptr);
+void __cdecl operator delete(void* memory, sandbox::AllocationType type);
+// Add operator delete that matches the placement form of the operator new
+// above. This is required by compiler to generate code to call operator delete
+// in case the object's constructor throws an exception.
+// See http://msdn.microsoft.com/en-us/library/cxdxz3x6.aspx
+void __cdecl operator delete(void* memory,
+ sandbox::AllocationType type,
+ void* near_to);
+
+// Regular placement new and delete
+void* __cdecl operator new(size_t size,
+ void* buffer,
+ sandbox::AllocationType type);
+void __cdecl operator delete(void* memory,
+ void* buffer,
+ sandbox::AllocationType type);
+
+// DCHECK_NT is defined to be pretty much an assert at this time because we
+// don't have logging from the ntdll layer on the child.
+//
+// VERIFY_NT and VERIFY_SUCCESS are the standard asserts on debug, but
+// execute the actual argument on release builds. VERIFY_NT expects an action
+// returning a bool, while VERIFY_SUCCESS expects an action returning
+// NTSTATUS.
+#ifndef NDEBUG
+#define DCHECK_NT(condition) \
+ { (condition) ? (void)0 : __debugbreak(); }
+#define VERIFY(action) DCHECK_NT(action)
+#define VERIFY_SUCCESS(action) DCHECK_NT(NT_SUCCESS(action))
+#else
+#define DCHECK_NT(condition)
+#define VERIFY(action) (action)
+#define VERIFY_SUCCESS(action) (action)
+#endif
+
+#define CHECK_NT(condition) \
+ { (condition) ? (void)0 : __debugbreak(); }
+
+#define NOTREACHED_NT() DCHECK_NT(false)
+
+namespace sandbox {
+
+#if defined(_M_X64) || defined(_M_ARM64)
+#pragma intrinsic(_InterlockedCompareExchange)
+#pragma intrinsic(_InterlockedCompareExchangePointer)
+
+#elif defined(_M_IX86)
+extern "C" long _InterlockedCompareExchange(long volatile* destination,
+ long exchange,
+ long comperand);
+
+#pragma intrinsic(_InterlockedCompareExchange)
+
+// We want to make sure that we use an intrinsic version of the function, not
+// the one provided by kernel32.
+__forceinline void* _InterlockedCompareExchangePointer(
+ void* volatile* destination,
+ void* exchange,
+ void* comperand) {
+ long ret = _InterlockedCompareExchange(
+ reinterpret_cast<long volatile*>(destination),
+ static_cast<long>(reinterpret_cast<size_t>(exchange)),
+ static_cast<long>(reinterpret_cast<size_t>(comperand)));
+
+ return reinterpret_cast<void*>(static_cast<size_t>(ret));
+}
+
+#else
+#error Architecture not supported.
+
+#endif
+
+struct NtAllocDeleter {
+ inline void operator()(void* ptr) const {
+ operator delete(ptr, AllocationType::NT_ALLOC);
+ }
+};
+
+// Returns a pointer to the IPC shared memory.
+void* GetGlobalIPCMemory();
+
+// Returns a pointer to the Policy shared memory.
+void* GetGlobalPolicyMemory();
+
+enum RequiredAccess { READ, WRITE };
+
+// Performs basic user mode buffer validation. In any case, buffers access must
+// be protected by SEH. intent specifies if the buffer should be tested for read
+// or write.
+bool ValidParameter(void* buffer, size_t size, RequiredAccess intent);
+
+// Copies data from a user buffer to our buffer. Returns the operation status.
+NTSTATUS CopyData(void* destination, const void* source, size_t bytes);
+
+// Copies the name from an object attributes.
+NTSTATUS AllocAndCopyName(const OBJECT_ATTRIBUTES* in_object,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* out_name,
+ uint32_t* attributes,
+ HANDLE* root);
+
+// Determine full path name from object root and path.
+NTSTATUS AllocAndGetFullPath(
+ HANDLE root,
+ const wchar_t* path,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* full_path);
+
+// Initializes our ntdll level heap
+bool InitHeap();
+
+// Returns true if the provided handle refers to the current process.
+bool IsSameProcess(HANDLE process);
+
+enum MappedModuleFlags {
+ MODULE_IS_PE_IMAGE = 1, // Module is an executable.
+ MODULE_HAS_ENTRY_POINT = 2, // Execution entry point found.
+ MODULE_HAS_CODE = 4 // Non zero size of executable sections.
+};
+
+// Returns the name and characteristics for a given PE module. The return
+// value is the name as defined by the export table and the flags is any
+// combination of the MappedModuleFlags enumeration.
+//
+// The returned buffer must be freed with a placement delete from the ntdll
+// level allocator:
+//
+// UNICODE_STRING* name = GetPEImageInfoFromModule(HMODULE module, &flags);
+// if (!name) {
+// // probably not a valid dll
+// return;
+// }
+// InsertYourLogicHere(name);
+// operator delete(name, NT_ALLOC);
+UNICODE_STRING* GetImageInfoFromModule(HMODULE module, uint32_t* flags);
+
+// Returns the name and characteristics for a given PE module. The return
+// value is the name as defined by the export table.
+//
+// The returned buffer is within the PE module and must not be freed.
+const char* GetAnsiImageInfoFromModule(HMODULE module);
+
+// Returns the full path and filename for a given dll.
+// May return nullptr if the provided address is not backed by a named section,
+// or if the current OS version doesn't support the call. The returned buffer
+// must be freed with a placement delete (see GetImageNameFromModule example).
+UNICODE_STRING* GetBackingFilePath(PVOID address);
+
+// Returns the last component of a path that contains the module name.
+// It will return nullptr if the path ends with the path separator. The returned
+// buffer must be freed with a placement delete (see GetImageNameFromModule
+// example).
+UNICODE_STRING* ExtractModuleName(const UNICODE_STRING* module_path);
+
+// Returns true if the parameters correspond to a dll mapped as code.
+bool IsValidImageSection(HANDLE section,
+ PVOID* base,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size);
+
+// Converts an ansi string to an UNICODE_STRING.
+UNICODE_STRING* AnsiToUnicode(const char* string);
+
+// Resolves a handle to an nt path. Returns true if the handle can be resolved.
+bool NtGetPathFromHandle(HANDLE handle,
+ std::unique_ptr<wchar_t, NtAllocDeleter>* path);
+
+// Provides a simple way to temporarily change the protection of a memory page.
+class AutoProtectMemory {
+ public:
+ AutoProtectMemory()
+ : changed_(false), address_(nullptr), bytes_(0), old_protect_(0) {}
+
+ ~AutoProtectMemory() { RevertProtection(); }
+
+ // Sets the desired protection of a given memory range.
+ NTSTATUS ChangeProtection(void* address, size_t bytes, ULONG protect);
+
+ // Restores the original page protection.
+ NTSTATUS RevertProtection();
+
+ private:
+ bool changed_;
+ void* address_;
+ size_t bytes_;
+ ULONG old_protect_;
+
+ DISALLOW_COPY_AND_ASSIGN(AutoProtectMemory);
+};
+
+// Returns true if the file_rename_information structure is supported by our
+// rename handler.
+bool IsSupportedRenameCall(FILE_RENAME_INFORMATION* file_info,
+ DWORD length,
+ uint32_t file_info_class);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_NT_UTIL_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
new file mode 100644
index 0000000000..75514ef595
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy.h
@@ -0,0 +1,296 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
+#define SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/memory/scoped_refptr.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/security_level.h"
+
+namespace sandbox {
+
+class AppContainerProfile;
+
+class TargetPolicy {
+ public:
+ // Windows subsystems that can have specific rules.
+ // Note: The process subsystem(SUBSYS_PROCESS) does not evaluate the request
+ // exactly like the CreateProcess API does. See the comment at the top of
+ // process_thread_dispatcher.cc for more details.
+ enum SubSystem {
+ SUBSYS_FILES, // Creation and opening of files and pipes.
+ SUBSYS_NAMED_PIPES, // Creation of named pipes.
+ SUBSYS_PROCESS, // Creation of child processes.
+ SUBSYS_REGISTRY, // Creation and opening of registry keys.
+ SUBSYS_SYNC, // Creation of named sync objects.
+ SUBSYS_HANDLES, // Duplication of handles to other processes.
+ SUBSYS_WIN32K_LOCKDOWN, // Win32K Lockdown related policy.
+ SUBSYS_SIGNED_BINARY, // Signed binary policy.
+ SUBSYS_LINE_BREAK // Complex line break policy.
+ };
+
+ // Allowable semantics when a rule is matched.
+ enum Semantics {
+ FILES_ALLOW_ANY, // Allows open or create for any kind of access that
+ // the file system supports.
+ FILES_ALLOW_READONLY, // Allows open or create with read access only.
+ FILES_ALLOW_QUERY, // Allows access to query the attributes of a file.
+ FILES_ALLOW_DIR_ANY, // Allows open or create with directory semantics
+ // only.
+ HANDLES_DUP_ANY, // Allows duplicating handles opened with any
+ // access permissions.
+ HANDLES_DUP_BROKER, // Allows duplicating handles to the broker process.
+ NAMEDPIPES_ALLOW_ANY, // Allows creation of a named pipe.
+ PROCESS_MIN_EXEC, // Allows to create a process with minimal rights
+ // over the resulting process and thread handles.
+ // No other parameters besides the command line are
+ // passed to the child process.
+ PROCESS_ALL_EXEC, // Allows the creation of a process and return full
+ // access on the returned handles.
+ // This flag can be used only when the main token of
+ // the sandboxed application is at least INTERACTIVE.
+ EVENTS_ALLOW_ANY, // Allows the creation of an event with full access.
+ EVENTS_ALLOW_READONLY, // Allows opening an even with synchronize access.
+ REG_ALLOW_READONLY, // Allows readonly access to a registry key.
+ REG_ALLOW_ANY, // Allows read and write access to a registry key.
+ FAKE_USER_GDI_INIT, // Fakes user32 and gdi32 initialization. This can
+ // be used to allow the DLLs to load and initialize
+ // even if the process cannot access that subsystem.
+ IMPLEMENT_OPM_APIS, // Implements FAKE_USER_GDI_INIT and also exposes
+ // IPC calls to handle Output Protection Manager
+ // APIs.
+ SIGNED_ALLOW_LOAD, // Allows loading the module when CIG is enabled.
+ LINE_BREAK_ALLOW // Allow complex line break brokering.
+ };
+
+ // Increments the reference count of this object. The reference count must
+ // be incremented if this interface is given to another component.
+ virtual void AddRef() = 0;
+
+ // 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.
+ virtual void Release() = 0;
+
+ // Sets the security level for the target process' two tokens.
+ // This setting is permanent and cannot be changed once the target process is
+ // spawned.
+ // initial: the security level for the initial token. This is the token that
+ // is used by the process from the creation of the process until the moment
+ // the process calls TargetServices::LowerToken() or the process calls
+ // win32's RevertToSelf(). Once this happens the initial token is no longer
+ // available and the lockdown token is in effect. Using an initial token is
+ // not compatible with AppContainer, see SetAppContainer.
+ // lockdown: the security level for the token that comes into force after the
+ // process calls TargetServices::LowerToken() or the process calls
+ // RevertToSelf(). See the explanation of each level in the TokenLevel
+ // definition.
+ // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise.
+ // Returns false if the lockdown value is more permissive than the initial
+ // value.
+ //
+ // Important: most of the sandbox-provided security relies on this single
+ // setting. The caller should strive to set the lockdown level as restricted
+ // as possible.
+ virtual ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) = 0;
+
+ // Returns the initial token level.
+ virtual TokenLevel GetInitialTokenLevel() const = 0;
+
+ // Returns the lockdown token level.
+ virtual TokenLevel GetLockdownTokenLevel() const = 0;
+
+ // Sets that we should not use restricting SIDs in the access tokens. We need
+ // to do this in some circumstances even though it weakens the sandbox.
+ // The default is to use them.
+ virtual void SetDoNotUseRestrictingSIDs() = 0;
+
+ // Sets the security level of the Job Object to which the target process will
+ // belong. This setting is permanent and cannot be changed once the target
+ // process is spawned. The job controls the global security settings which
+ // can not be specified in the token security profile.
+ // job_level: the security level for the job. See the explanation of each
+ // level in the JobLevel definition.
+ // ui_exceptions: specify what specific rights that are disabled in the
+ // chosen job_level that need to be granted. Use this parameter to avoid
+ // selecting the next permissive job level unless you need all the rights
+ // that are granted in such level.
+ // The exceptions can be specified as a combination of the following
+ // constants:
+ // JOB_OBJECT_UILIMIT_HANDLES : grant access to all user-mode handles. These
+ // include windows, icons, menus and various GDI objects. In addition the
+ // target process can set hooks, and broadcast messages to other processes
+ // that belong to the same desktop.
+ // JOB_OBJECT_UILIMIT_READCLIPBOARD : grant read-only access to the clipboard.
+ // JOB_OBJECT_UILIMIT_WRITECLIPBOARD : grant write access to the clipboard.
+ // JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS : allow changes to the system-wide
+ // parameters as defined by the Win32 call SystemParametersInfo().
+ // JOB_OBJECT_UILIMIT_DISPLAYSETTINGS : allow programmatic changes to the
+ // display settings.
+ // JOB_OBJECT_UILIMIT_GLOBALATOMS : allow access to the global atoms table.
+ // JOB_OBJECT_UILIMIT_DESKTOP : allow the creation of new desktops.
+ // JOB_OBJECT_UILIMIT_EXITWINDOWS : allow the call to ExitWindows().
+ //
+ // Return value: SBOX_ALL_OK if the setting succeeds and false otherwise.
+ //
+ // Note: JOB_OBJECT_XXXX constants are defined in winnt.h and documented at
+ // length in:
+ // http://msdn2.microsoft.com/en-us/library/ms684152.aspx
+ //
+ // Note: the recommended level is JOB_RESTRICTED or JOB_LOCKDOWN.
+ virtual ResultCode SetJobLevel(JobLevel job_level,
+ uint32_t ui_exceptions) = 0;
+
+ // Returns the job level.
+ virtual JobLevel GetJobLevel() const = 0;
+
+ // Sets a hard limit on the size of the commit set for the sandboxed process.
+ // If the limit is reached, the process will be terminated with
+ // SBOX_FATAL_MEMORY_EXCEEDED (7012).
+ virtual ResultCode SetJobMemoryLimit(size_t memory_limit) = 0;
+
+ // Specifies the desktop on which the application is going to run. If the
+ // desktop does not exist, it will be created. If alternate_winstation is
+ // set to true, the desktop will be created on an alternate window station.
+ virtual ResultCode SetAlternateDesktop(bool alternate_winstation) = 0;
+
+ // Returns the name of the alternate desktop used. If an alternate window
+ // station is specified, the name is prepended by the window station name,
+ // followed by a backslash.
+ virtual std::wstring GetAlternateDesktop() const = 0;
+
+ // Precreates the desktop and window station, if any.
+ virtual ResultCode CreateAlternateDesktop(bool alternate_winstation) = 0;
+
+ // Destroys the desktop and windows station.
+ virtual void DestroyAlternateDesktop() = 0;
+
+ // Sets the integrity level of the process in the sandbox. Both the initial
+ // token and the main token will be affected by this. If the integrity level
+ // is set to a level higher than the current level, the sandbox will fail
+ // to start.
+ virtual ResultCode SetIntegrityLevel(IntegrityLevel level) = 0;
+
+ // Returns the initial integrity level used.
+ virtual IntegrityLevel GetIntegrityLevel() const = 0;
+
+ // Sets the integrity level of the process in the sandbox. The integrity level
+ // will not take effect before you call LowerToken. User Interface Privilege
+ // Isolation is not affected by this setting and will remain off for the
+ // process in the sandbox. If the integrity level is set to a level higher
+ // than the current level, the sandbox will fail to start.
+ virtual ResultCode SetDelayedIntegrityLevel(IntegrityLevel level) = 0;
+
+ // Sets the LowBox token for sandboxed process. This is mutually exclusive
+ // with SetAppContainer method.
+ virtual ResultCode SetLowBox(const wchar_t* sid) = 0;
+
+ // Sets the mitigations enabled when the process is created. Most of these
+ // are implemented as attributes passed via STARTUPINFOEX. So they take
+ // effect before any thread in the target executes. The declaration of
+ // MitigationFlags is followed by a detailed description of each flag.
+ virtual ResultCode SetProcessMitigations(MitigationFlags flags) = 0;
+
+ // Returns the currently set mitigation flags.
+ virtual MitigationFlags GetProcessMitigations() = 0;
+
+ // Sets process mitigation flags that don't take effect before the call to
+ // LowerToken().
+ virtual ResultCode SetDelayedProcessMitigations(MitigationFlags flags) = 0;
+
+ // Returns the currently set delayed mitigation flags.
+ virtual MitigationFlags GetDelayedProcessMitigations() const = 0;
+
+ // Disconnect the target from CSRSS when TargetServices::LowerToken() is
+ // called inside the target.
+ virtual ResultCode SetDisconnectCsrss() = 0;
+
+ // Sets the interceptions to operate in strict mode. By default, interceptions
+ // are performed in "relaxed" mode, where if something inside NTDLL.DLL is
+ // already patched we attempt to intercept it anyway. Setting interceptions
+ // to strict mode means that when we detect that the function is patched we'll
+ // refuse to perform the interception.
+ virtual void SetStrictInterceptions() = 0;
+
+ // Set the handles the target process should inherit for stdout and
+ // stderr. The handles the caller passes must remain valid for the
+ // lifetime of the policy object. This only has an effect on
+ // Windows Vista and later versions. These methods accept pipe and
+ // file handles, but not console handles.
+ virtual ResultCode SetStdoutHandle(HANDLE handle) = 0;
+ virtual ResultCode SetStderrHandle(HANDLE handle) = 0;
+
+ // Adds a policy rule effective for processes spawned using this policy.
+ // subsystem: One of the above enumerated windows subsystems.
+ // semantics: One of the above enumerated FileSemantics.
+ // pattern: A specific full path or a full path with wildcard patterns.
+ // The valid wildcards are:
+ // '*' : Matches zero or more character. Only one in series allowed.
+ // '?' : Matches a single character. One or more in series are allowed.
+ // Examples:
+ // "c:\\documents and settings\\vince\\*.dmp"
+ // "c:\\documents and settings\\*\\crashdumps\\*.dmp"
+ // "c:\\temp\\app_log_?????_chrome.txt"
+ virtual ResultCode AddRule(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) = 0;
+
+ // Adds a dll that will be unloaded in the target process before it gets
+ // a chance to initialize itself. Typically, dlls that cause the target
+ // to crash go here.
+ virtual ResultCode AddDllToUnload(const wchar_t* dll_name) = 0;
+
+ // Adds a handle that will be closed in the target process after lockdown.
+ // A nullptr value for handle_name indicates all handles of the specified
+ // type. An empty string for handle_name indicates the handle is unnamed.
+ virtual ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) = 0;
+
+ // Adds a handle that will be shared with the target process. Does not take
+ // ownership of the handle.
+ virtual void AddHandleToShare(HANDLE handle) = 0;
+
+ // Locks down the default DACL of the created lockdown and initial tokens
+ // to restrict what other processes are allowed to access a process' kernel
+ // resources.
+ virtual void SetLockdownDefaultDacl() = 0;
+
+ // Adds a restricting random SID to the restricted SIDs list as well as
+ // the default DACL.
+ virtual void AddRestrictingRandomSid() = 0;
+
+ // Enable OPM API redirection when in Win32k lockdown.
+ virtual void SetEnableOPMRedirection() = 0;
+ // Enable OPM API emulation when in Win32k lockdown.
+ virtual bool GetEnableOPMRedirection() = 0;
+
+ // Configure policy to use an AppContainer profile. |package_name| is the
+ // name of the profile to use. Specifying True for |create_profile| ensures
+ // the profile exists, if set to False process creation will fail if the
+ // profile has not already been created.
+ virtual ResultCode AddAppContainerProfile(const wchar_t* package_name,
+ bool create_profile) = 0;
+
+ // Get the configured AppContainerProfile.
+ virtual scoped_refptr<AppContainerProfile> GetAppContainerProfile() = 0;
+
+ // Set effective token that will be used for creating the initial and
+ // lockdown tokens. The token the caller passes must remain valid for the
+ // lifetime of the policy object.
+ virtual void SetEffectiveToken(HANDLE token) = 0;
+
+ protected:
+ ~TargetPolicy() {}
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
new file mode 100644
index 0000000000..f228dbbc31
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.cc
@@ -0,0 +1,832 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+#include <sddl.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/win/win_util.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/filesystem_policy.h"
+#include "sandbox/win/src/handle_policy.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/job.h"
+#include "sandbox/win/src/line_break_policy.h"
+#include "sandbox/win/src/named_pipe_policy.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_engine_processor.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/process_mitigations_win32k_policy.h"
+#include "sandbox/win/src/process_thread_policy.h"
+#include "sandbox/win/src/registry_policy.h"
+#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/signed_policy.h"
+#include "sandbox/win/src/sync_policy.h"
+#include "sandbox/win/src/target_process.h"
+#include "sandbox/win/src/top_level_dispatcher.h"
+#include "sandbox/win/src/window.h"
+
+namespace {
+
+// The standard windows size for one memory page.
+constexpr size_t kOneMemPage = 4096;
+// The IPC and Policy shared memory sizes.
+constexpr size_t kIPCMemSize = kOneMemPage * 2;
+constexpr size_t kPolMemSize = kOneMemPage * 14;
+
+// Helper function to allocate space (on the heap) for policy.
+sandbox::PolicyGlobal* MakeBrokerPolicyMemory() {
+ const size_t kTotalPolicySz = kPolMemSize;
+ sandbox::PolicyGlobal* policy =
+ static_cast<sandbox::PolicyGlobal*>(::operator new(kTotalPolicySz));
+ DCHECK(policy);
+ memset(policy, 0, kTotalPolicySz);
+ policy->data_size = kTotalPolicySz - sizeof(sandbox::PolicyGlobal);
+ return policy;
+}
+
+bool IsInheritableHandle(HANDLE handle) {
+ if (!handle)
+ return false;
+ if (handle == INVALID_HANDLE_VALUE)
+ return false;
+ // File handles (FILE_TYPE_DISK) and pipe handles are known to be
+ // inheritable. Console handles (FILE_TYPE_CHAR) are not
+ // inheritable via PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
+ DWORD handle_type = GetFileType(handle);
+ return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE;
+}
+
+} // namespace
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level;
+SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations;
+
+// Initializes static members. alternate_desktop_handle_ is a desktop on
+// alternate_winstation_handle_, alternate_desktop_local_winstation_handle_ is a
+// desktop on the same winstation as the parent process.
+HWINSTA PolicyBase::alternate_winstation_handle_ = nullptr;
+HDESK PolicyBase::alternate_desktop_handle_ = nullptr;
+HDESK PolicyBase::alternate_desktop_local_winstation_handle_ = nullptr;
+IntegrityLevel PolicyBase::alternate_desktop_integrity_level_label_ =
+ INTEGRITY_LEVEL_SYSTEM;
+IntegrityLevel
+ PolicyBase::alternate_desktop_local_winstation_integrity_level_label_ =
+ INTEGRITY_LEVEL_SYSTEM;
+
+PolicyBase::PolicyBase()
+ : ref_count(1),
+ lockdown_level_(USER_LOCKDOWN),
+ initial_level_(USER_LOCKDOWN),
+ job_level_(JOB_LOCKDOWN),
+ ui_exceptions_(0),
+ memory_limit_(0),
+ use_alternate_desktop_(false),
+ use_alternate_winstation_(false),
+ file_system_init_(false),
+ relaxed_interceptions_(true),
+ stdout_handle_(INVALID_HANDLE_VALUE),
+ stderr_handle_(INVALID_HANDLE_VALUE),
+ integrity_level_(INTEGRITY_LEVEL_LAST),
+ delayed_integrity_level_(INTEGRITY_LEVEL_LAST),
+ mitigations_(0),
+ delayed_mitigations_(0),
+ is_csrss_connected_(true),
+ policy_maker_(nullptr),
+ policy_(nullptr),
+ lowbox_sid_(nullptr),
+ lockdown_default_dacl_(false),
+ add_restricting_random_sid_(false),
+ enable_opm_redirection_(false),
+ effective_token_(nullptr) {
+ ::InitializeCriticalSection(&lock_);
+ dispatcher_.reset(new TopLevelDispatcher(this));
+}
+
+PolicyBase::~PolicyBase() {
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ TargetProcess* target = (*it);
+ delete target;
+ }
+ delete policy_maker_;
+ delete policy_;
+
+ if (lowbox_sid_)
+ ::LocalFree(lowbox_sid_);
+
+ ::DeleteCriticalSection(&lock_);
+}
+
+void PolicyBase::AddRef() {
+ ::InterlockedIncrement(&ref_count);
+}
+
+void PolicyBase::Release() {
+ if (0 == ::InterlockedDecrement(&ref_count))
+ delete this;
+}
+
+ResultCode PolicyBase::SetTokenLevel(TokenLevel initial, TokenLevel lockdown) {
+ if (initial < lockdown) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ initial_level_ = initial;
+ lockdown_level_ = lockdown;
+ return SBOX_ALL_OK;
+}
+
+TokenLevel PolicyBase::GetInitialTokenLevel() const {
+ return initial_level_;
+}
+
+TokenLevel PolicyBase::GetLockdownTokenLevel() const {
+ return lockdown_level_;
+}
+
+void PolicyBase::SetDoNotUseRestrictingSIDs() {
+ use_restricting_sids_ = false;
+}
+
+ResultCode PolicyBase::SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) {
+ if (memory_limit_ && job_level == JOB_NONE) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ job_level_ = job_level;
+ ui_exceptions_ = ui_exceptions;
+ return SBOX_ALL_OK;
+}
+
+JobLevel PolicyBase::GetJobLevel() const {
+ return job_level_;
+}
+
+ResultCode PolicyBase::SetJobMemoryLimit(size_t memory_limit) {
+ memory_limit_ = memory_limit;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetAlternateDesktop(bool alternate_winstation) {
+ use_alternate_desktop_ = true;
+ use_alternate_winstation_ = alternate_winstation;
+ return CreateAlternateDesktop(alternate_winstation);
+}
+
+std::wstring PolicyBase::GetAlternateDesktop() const {
+ // No alternate desktop or winstation. Return an empty string.
+ if (!use_alternate_desktop_ && !use_alternate_winstation_) {
+ return std::wstring();
+ }
+
+ if (use_alternate_winstation_) {
+ // The desktop and winstation should have been created by now.
+ // If we hit this scenario, it means that the user ignored the failure
+ // during SetAlternateDesktop, so we ignore it here too.
+ if (!alternate_desktop_handle_ || !alternate_winstation_handle_)
+ return std::wstring();
+
+ return GetFullDesktopName(alternate_winstation_handle_,
+ alternate_desktop_handle_);
+ }
+
+ if (!alternate_desktop_local_winstation_handle_)
+ return std::wstring();
+
+ return GetFullDesktopName(nullptr,
+ alternate_desktop_local_winstation_handle_);
+}
+
+ResultCode PolicyBase::CreateAlternateDesktop(bool alternate_winstation) {
+ if (alternate_winstation) {
+ // Check if it's already created.
+ if (alternate_winstation_handle_ && alternate_desktop_handle_)
+ return SBOX_ALL_OK;
+
+ DCHECK(!alternate_winstation_handle_);
+ // Create the window station.
+ ResultCode result = CreateAltWindowStation(&alternate_winstation_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_winstation_handle_ ||
+ base::win::GetWindowObjectName(alternate_winstation_handle_).empty())
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+
+ // Create the destkop.
+ result = CreateAltDesktop(alternate_winstation_handle_,
+ &alternate_desktop_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_handle_ ||
+ base::win::GetWindowObjectName(alternate_desktop_handle_).empty()) {
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+ } else {
+ // Check if it already exists.
+ if (alternate_desktop_local_winstation_handle_)
+ return SBOX_ALL_OK;
+
+ // Create the destkop.
+ ResultCode result =
+ CreateAltDesktop(nullptr, &alternate_desktop_local_winstation_handle_);
+ if (SBOX_ALL_OK != result)
+ return result;
+
+ // Verify that everything is fine.
+ if (!alternate_desktop_local_winstation_handle_ ||
+ base::win::GetWindowObjectName(
+ alternate_desktop_local_winstation_handle_)
+ .empty()) {
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+ }
+
+ return SBOX_ALL_OK;
+}
+
+void PolicyBase::DestroyAlternateDesktop() {
+ if (use_alternate_winstation_) {
+ if (alternate_desktop_handle_) {
+ ::CloseDesktop(alternate_desktop_handle_);
+ alternate_desktop_handle_ = nullptr;
+ }
+
+ if (alternate_winstation_handle_) {
+ ::CloseWindowStation(alternate_winstation_handle_);
+ alternate_winstation_handle_ = nullptr;
+ }
+ } else {
+ if (alternate_desktop_local_winstation_handle_) {
+ ::CloseDesktop(alternate_desktop_local_winstation_handle_);
+ alternate_desktop_local_winstation_handle_ = nullptr;
+ }
+ }
+}
+
+ResultCode PolicyBase::SetIntegrityLevel(IntegrityLevel integrity_level) {
+ if (app_container_profile_)
+ return SBOX_ERROR_BAD_PARAMS;
+ integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+IntegrityLevel PolicyBase::GetIntegrityLevel() const {
+ return integrity_level_;
+}
+
+ResultCode PolicyBase::SetDelayedIntegrityLevel(
+ IntegrityLevel integrity_level) {
+ delayed_integrity_level_ = integrity_level;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetLowBox(const wchar_t* sid) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ DCHECK(sid);
+ if (lowbox_sid_ || app_container_profile_)
+ return SBOX_ERROR_BAD_PARAMS;
+
+ if (!ConvertStringSidToSid(sid, &lowbox_sid_))
+ return SBOX_ERROR_INVALID_LOWBOX_SID;
+
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetProcessMitigations(MitigationFlags flags) {
+ // Prior to Win10 RS5 CreateProcess fails when AppContainer and mitigation
+ // flags are enabled. Return an error on downlevel platforms if trying to
+ // set new mitigations.
+ if (app_container_profile_ &&
+ base::win::GetVersion() < base::win::Version::WIN10_RS5) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ if (!CanSetProcessMitigationsPreStartup(flags))
+ return SBOX_ERROR_BAD_PARAMS;
+ mitigations_ = flags;
+ return SBOX_ALL_OK;
+}
+
+MitigationFlags PolicyBase::GetProcessMitigations() {
+ return mitigations_;
+}
+
+ResultCode PolicyBase::SetDelayedProcessMitigations(MitigationFlags flags) {
+ if (!CanSetProcessMitigationsPostStartup(flags))
+ return SBOX_ERROR_BAD_PARAMS;
+ delayed_mitigations_ = flags;
+ return SBOX_ALL_OK;
+}
+
+MitigationFlags PolicyBase::GetDelayedProcessMitigations() const {
+ return delayed_mitigations_;
+}
+
+void PolicyBase::SetStrictInterceptions() {
+ relaxed_interceptions_ = false;
+}
+
+ResultCode PolicyBase::SetStdoutHandle(HANDLE handle) {
+ if (!IsInheritableHandle(handle))
+ return SBOX_ERROR_BAD_PARAMS;
+ stdout_handle_ = handle;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::SetStderrHandle(HANDLE handle) {
+ if (!IsInheritableHandle(handle))
+ return SBOX_ERROR_BAD_PARAMS;
+ stderr_handle_ = handle;
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddRule(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) {
+ ResultCode result = AddRuleInternal(subsystem, semantics, pattern);
+ LOG_IF(ERROR, result != SBOX_ALL_OK)
+ << "Failed to add sandbox rule."
+ << " error = " << result << ", subsystem = " << subsystem
+ << ", semantics = " << semantics << ", pattern = '" << pattern << "'";
+ return result;
+}
+
+ResultCode PolicyBase::AddDllToUnload(const wchar_t* dll_name) {
+ blocklisted_dlls_.push_back(dll_name);
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) {
+ return handle_closer_.AddHandle(handle_type, handle_name);
+}
+
+void PolicyBase::AddHandleToShare(HANDLE handle) {
+ CHECK(handle);
+ CHECK_NE(handle, INVALID_HANDLE_VALUE);
+
+ // Ensure the handle can be inherited.
+ bool result =
+ SetHandleInformation(handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+ PCHECK(result);
+
+ handles_to_share_.push_back(handle);
+}
+
+void PolicyBase::SetLockdownDefaultDacl() {
+ lockdown_default_dacl_ = true;
+}
+
+void PolicyBase::AddRestrictingRandomSid() {
+ add_restricting_random_sid_ = true;
+}
+
+const base::HandlesToInheritVector& PolicyBase::GetHandlesBeingShared() {
+ return handles_to_share_;
+}
+
+ResultCode PolicyBase::MakeJobObject(base::win::ScopedHandle* job) {
+ if (job_level_ == JOB_NONE) {
+ job->Close();
+ return SBOX_ALL_OK;
+ }
+
+ // Create the windows job object.
+ Job job_obj;
+ DWORD result =
+ job_obj.Init(job_level_, nullptr, ui_exceptions_, memory_limit_);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_INIT_JOB;
+
+ *job = job_obj.Take();
+ return SBOX_ALL_OK;
+}
+
+ResultCode PolicyBase::MakeTokens(base::win::ScopedHandle* initial,
+ base::win::ScopedHandle* lockdown,
+ base::win::ScopedHandle* lowbox) {
+ Sid random_sid = Sid::GenerateRandomSid();
+ PSID random_sid_ptr = nullptr;
+ if (add_restricting_random_sid_)
+ random_sid_ptr = random_sid.GetPSID();
+
+ // Create the 'naked' token. This will be the permanent token associated
+ // with the process and therefore with any thread that is not impersonating.
+ DWORD result = CreateRestrictedToken(
+ effective_token_, lockdown_level_, integrity_level_, PRIMARY,
+ lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, lockdown);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN;
+
+ // If we're launching on the alternate desktop we need to make sure the
+ // integrity label on the object is no higher than the sandboxed process's
+ // integrity level. So, we lower the label on the desktop process if it's
+ // not already low enough for our process.
+ if (use_alternate_desktop_ && integrity_level_ != INTEGRITY_LEVEL_LAST) {
+ // Integrity label enum is reversed (higher level is a lower value).
+ static_assert(INTEGRITY_LEVEL_SYSTEM < INTEGRITY_LEVEL_UNTRUSTED,
+ "Integrity level ordering reversed.");
+ HDESK desktop_handle = nullptr;
+ IntegrityLevel desktop_integrity_level_label;
+ if (use_alternate_winstation_) {
+ desktop_handle = alternate_desktop_handle_;
+ desktop_integrity_level_label = alternate_desktop_integrity_level_label_;
+ } else {
+ desktop_handle = alternate_desktop_local_winstation_handle_;
+ desktop_integrity_level_label =
+ alternate_desktop_local_winstation_integrity_level_label_;
+ }
+ // If the desktop_handle hasn't been created for any reason, skip this.
+ if (desktop_handle && desktop_integrity_level_label < integrity_level_) {
+ result =
+ SetObjectIntegrityLabel(desktop_handle, SE_WINDOW_OBJECT, L"",
+ GetIntegrityLevelString(integrity_level_));
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_SET_DESKTOP_INTEGRITY;
+
+ if (use_alternate_winstation_) {
+ alternate_desktop_integrity_level_label_ = integrity_level_;
+ } else {
+ alternate_desktop_local_winstation_integrity_level_label_ =
+ integrity_level_;
+ }
+ }
+ }
+
+ if (lowbox_sid_) {
+ 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;
+
+ 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_CANNOT_CREATE_LOWBOX_TOKEN;
+ }
+
+ if (!ReplacePackageSidInDacl(lowbox->Get(), SE_KERNEL_OBJECT, package_sid,
+ TOKEN_ALL_ACCESS)) {
+ return SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL;
+ }
+ }
+
+ // Create the 'better' token. We use this token as the one that the main
+ // thread uses when booting up the process. It should contain most of
+ // what we need (before reaching main( ))
+ result = CreateRestrictedToken(
+ effective_token_, initial_level_, integrity_level_, IMPERSONATION,
+ lockdown_default_dacl_, random_sid_ptr, use_restricting_sids_, initial);
+ if (ERROR_SUCCESS != result)
+ return SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN;
+
+ return SBOX_ALL_OK;
+}
+
+PSID PolicyBase::GetLowBoxSid() const {
+ return lowbox_sid_;
+}
+
+ResultCode PolicyBase::AddTarget(TargetProcess* target) {
+ if (policy_)
+ policy_maker_->Done();
+
+ if (!ApplyProcessMitigationsToSuspendedProcess(target->Process(),
+ mitigations_)) {
+ return SBOX_ERROR_APPLY_ASLR_MITIGATIONS;
+ }
+
+ ResultCode ret = SetupAllInterceptions(target);
+
+ if (ret != SBOX_ALL_OK)
+ return ret;
+
+ if (!SetupHandleCloser(target))
+ return SBOX_ERROR_SETUP_HANDLE_CLOSER;
+
+ DWORD win_error = ERROR_SUCCESS;
+ // Initialize the sandbox infrastructure for the target.
+ // TODO(wfh) do something with win_error code here.
+ ret = target->Init(dispatcher_.get(), policy_, kIPCMemSize, kPolMemSize,
+ &win_error);
+
+ if (ret != SBOX_ALL_OK)
+ return ret;
+
+ g_shared_delayed_integrity_level = delayed_integrity_level_;
+ ret = target->TransferVariable("g_shared_delayed_integrity_level",
+ &g_shared_delayed_integrity_level,
+ sizeof(g_shared_delayed_integrity_level));
+ g_shared_delayed_integrity_level = INTEGRITY_LEVEL_LAST;
+ if (SBOX_ALL_OK != ret)
+ return ret;
+
+ // Add in delayed mitigations and pseudo-mitigations enforced at startup.
+ g_shared_delayed_mitigations =
+ delayed_mitigations_ | FilterPostStartupProcessMitigations(mitigations_);
+ if (!CanSetProcessMitigationsPostStartup(g_shared_delayed_mitigations))
+ return SBOX_ERROR_BAD_PARAMS;
+
+ ret = target->TransferVariable("g_shared_delayed_mitigations",
+ &g_shared_delayed_mitigations,
+ sizeof(g_shared_delayed_mitigations));
+ g_shared_delayed_mitigations = 0;
+ if (SBOX_ALL_OK != ret)
+ return ret;
+
+ AutoLock lock(&lock_);
+ targets_.push_back(target);
+ return SBOX_ALL_OK;
+}
+
+bool PolicyBase::OnJobEmpty(HANDLE job) {
+ AutoLock lock(&lock_);
+ TargetSet::iterator it;
+ for (it = targets_.begin(); it != targets_.end(); ++it) {
+ if ((*it)->Job() == job)
+ break;
+ }
+ if (it == targets_.end()) {
+ return false;
+ }
+ TargetProcess* target = *it;
+ targets_.erase(it);
+ delete target;
+ return true;
+}
+
+ResultCode PolicyBase::SetDisconnectCsrss() {
+// Does not work on 32-bit, and the ASAN runtime falls over with the
+// CreateThread EAT patch used when this is enabled.
+// See https://crbug.com/783296#c27.
+#if defined(_WIN64) && !defined(ADDRESS_SANITIZER)
+ if (base::win::GetVersion() >= base::win::Version::WIN10) {
+ is_csrss_connected_ = false;
+ return AddKernelObjectToClose(L"ALPC Port", nullptr);
+ }
+#endif // !defined(_WIN64)
+ return SBOX_ALL_OK;
+}
+
+EvalResult PolicyBase::EvalPolicy(IpcTag service,
+ CountedParameterSetBase* params) {
+ if (policy_) {
+ if (!policy_->entry[static_cast<size_t>(service)]) {
+ // There is no policy for this particular service. This is not a big
+ // deal.
+ return DENY_ACCESS;
+ }
+ for (size_t i = 0; i < params->count; i++) {
+ if (!params->parameters[i].IsValid()) {
+ NOTREACHED();
+ return SIGNAL_ALARM;
+ }
+ }
+ PolicyProcessor pol_evaluator(policy_->entry[static_cast<size_t>(service)]);
+ PolicyResult result =
+ pol_evaluator.Evaluate(kShortEval, params->parameters, params->count);
+ if (POLICY_MATCH == result)
+ return pol_evaluator.GetAction();
+
+ DCHECK(POLICY_ERROR != result);
+ }
+
+ return DENY_ACCESS;
+}
+
+HANDLE PolicyBase::GetStdoutHandle() {
+ return stdout_handle_;
+}
+
+HANDLE PolicyBase::GetStderrHandle() {
+ return stderr_handle_;
+}
+
+void PolicyBase::SetEnableOPMRedirection() {
+ enable_opm_redirection_ = true;
+}
+
+bool PolicyBase::GetEnableOPMRedirection() {
+ return enable_opm_redirection_;
+}
+
+ResultCode PolicyBase::AddAppContainerProfile(const wchar_t* package_name,
+ bool create_profile) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return SBOX_ERROR_UNSUPPORTED;
+
+ DCHECK(package_name);
+ if (lowbox_sid_ || app_container_profile_ ||
+ integrity_level_ != INTEGRITY_LEVEL_LAST) {
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+
+ if (create_profile) {
+ app_container_profile_ = AppContainerProfileBase::Create(
+ package_name, L"Chrome Sandbox", L"Profile for Chrome Sandbox");
+ } else {
+ app_container_profile_ = AppContainerProfileBase::Open(package_name);
+ }
+ if (!app_container_profile_)
+ return SBOX_ERROR_CREATE_APPCONTAINER_PROFILE;
+
+ // A bug exists in CreateProcess where enabling an AppContainer profile and
+ // passing a set of mitigation flags will generate ERROR_INVALID_PARAMETER.
+ // Apply best efforts here and convert set mitigations to delayed mitigations.
+ // This bug looks to have been fixed in Win10 RS5, so exit early if possible.
+ if (base::win::GetVersion() >= base::win::Version::WIN10_RS5)
+ return SBOX_ALL_OK;
+
+ delayed_mitigations_ =
+ mitigations_ & GetAllowedPostStartupProcessMitigations();
+ DCHECK(delayed_mitigations_ ==
+ (mitigations_ & ~(MITIGATION_SEHOP |
+ MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION)));
+ mitigations_ = 0;
+ return SBOX_ALL_OK;
+}
+
+scoped_refptr<AppContainerProfile> PolicyBase::GetAppContainerProfile() {
+ return GetAppContainerProfileBase();
+}
+
+void PolicyBase::SetEffectiveToken(HANDLE token) {
+ CHECK(token);
+ effective_token_ = token;
+}
+
+scoped_refptr<AppContainerProfileBase>
+PolicyBase::GetAppContainerProfileBase() {
+ return app_container_profile_;
+}
+
+ResultCode PolicyBase::SetupAllInterceptions(TargetProcess* target) {
+ InterceptionManager manager(target, relaxed_interceptions_);
+
+ if (policy_) {
+ for (size_t i = 0; i < kMaxIpcTag; i++) {
+ if (policy_->entry[i] &&
+ !dispatcher_->SetupService(&manager, static_cast<IpcTag>(i)))
+ return SBOX_ERROR_SETUP_INTERCEPTION_SERVICE;
+ }
+ }
+
+ for (const std::wstring& dll : blocklisted_dlls_)
+ manager.AddToUnloadModules(dll.c_str());
+
+ if (!SetupBasicInterceptions(&manager, is_csrss_connected_))
+ return SBOX_ERROR_SETUP_BASIC_INTERCEPTIONS;
+
+ ResultCode rc = manager.InitializeInterceptions();
+ if (rc != SBOX_ALL_OK)
+ return rc;
+
+ // Finally, setup imports on the target so the interceptions can work.
+ if (!SetupNtdllImports(target))
+ return SBOX_ERROR_SETUP_NTDLL_IMPORTS;
+
+ return SBOX_ALL_OK;
+}
+
+bool PolicyBase::SetupHandleCloser(TargetProcess* target) {
+ return handle_closer_.InitializeTargetHandles(target);
+}
+
+ResultCode PolicyBase::AddRuleInternal(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) {
+ if (!policy_) {
+ policy_ = MakeBrokerPolicyMemory();
+ DCHECK(policy_);
+ policy_maker_ = new LowLevelPolicy(policy_);
+ DCHECK(policy_maker_);
+ }
+
+ switch (subsystem) {
+ case SUBSYS_FILES: {
+ if (!file_system_init_) {
+ if (!FileSystemPolicy::SetInitialRules(policy_maker_))
+ return SBOX_ERROR_BAD_PARAMS;
+ file_system_init_ = true;
+ }
+ if (!FileSystemPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_SYNC: {
+ if (!SyncPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_PROCESS: {
+ if (lockdown_level_ < USER_INTERACTIVE &&
+ TargetPolicy::PROCESS_ALL_EXEC == semantics) {
+ // This is unsupported. This is a huge security risk to give full access
+ // to a process handle.
+ return SBOX_ERROR_UNSUPPORTED;
+ }
+ if (!ProcessPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_NAMED_PIPES: {
+ if (!NamedPipePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_REGISTRY: {
+ if (!RegistryPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+ case SUBSYS_HANDLES: {
+ if (!HandlePolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+
+ case SUBSYS_WIN32K_LOCKDOWN: {
+ // Win32k intercept rules only supported on Windows 8 and above. This must
+ // match the version checks in process_mitigations.cc for consistency.
+ if (base::win::GetVersion() >= base::win::Version::WIN8) {
+ DCHECK_EQ(MITIGATION_WIN32K_DISABLE,
+ mitigations_ & MITIGATION_WIN32K_DISABLE)
+ << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy "
+ "rules.";
+ if (!ProcessMitigationsWin32KLockdownPolicy::GenerateRules(
+ pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ }
+ break;
+ }
+ case SUBSYS_SIGNED_BINARY: {
+ // Signed intercept rules only supported on Windows 10 TH2 and above. This
+ // must match the version checks in process_mitigations.cc for
+ // consistency.
+ if (base::win::GetVersion() >= base::win::Version::WIN10_TH2) {
+ DCHECK_EQ(MITIGATION_FORCE_MS_SIGNED_BINS,
+ (mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS) | (delayed_mitigations_ & MITIGATION_FORCE_MS_SIGNED_BINS))
+ << "Enable MITIGATION_FORCE_MS_SIGNED_BINS before adding signed "
+ "policy rules.";
+ if (!SignedPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ }
+ break;
+ }
+ case SUBSYS_LINE_BREAK: {
+ if (!LineBreakPolicy::GenerateRules(pattern, semantics, policy_maker_)) {
+ NOTREACHED();
+ return SBOX_ERROR_BAD_PARAMS;
+ }
+ break;
+ }
+
+ default: { return SBOX_ERROR_UNSUPPORTED; }
+ }
+
+ return SBOX_ALL_OK;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
new file mode 100644
index 0000000000..18268e230d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_policy_base.h
@@ -0,0 +1,198 @@
+// Copyright (c) 2011 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_WIN_SRC_SANDBOX_POLICY_BASE_H_
+#define SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
+
+#include <windows.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/process/launch.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/app_container_profile_base.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/handle_closer.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_engine_params.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+class LowLevelPolicy;
+class PolicyDiagnostic;
+class TargetProcess;
+struct PolicyGlobal;
+
+class PolicyBase final : public TargetPolicy {
+ public:
+ PolicyBase();
+
+ // TargetPolicy:
+ void AddRef() override;
+ void Release() override;
+ ResultCode SetTokenLevel(TokenLevel initial, TokenLevel lockdown) override;
+ TokenLevel GetInitialTokenLevel() const override;
+ TokenLevel GetLockdownTokenLevel() const override;
+ void SetDoNotUseRestrictingSIDs() final;
+ ResultCode SetJobLevel(JobLevel job_level, uint32_t ui_exceptions) override;
+ JobLevel GetJobLevel() const override;
+ ResultCode SetJobMemoryLimit(size_t memory_limit) override;
+ ResultCode SetAlternateDesktop(bool alternate_winstation) override;
+ std::wstring GetAlternateDesktop() const override;
+ ResultCode CreateAlternateDesktop(bool alternate_winstation) override;
+ void DestroyAlternateDesktop() override;
+ ResultCode SetIntegrityLevel(IntegrityLevel integrity_level) override;
+ IntegrityLevel GetIntegrityLevel() const override;
+ ResultCode SetDelayedIntegrityLevel(IntegrityLevel integrity_level) override;
+ ResultCode SetLowBox(const wchar_t* sid) override;
+ ResultCode SetProcessMitigations(MitigationFlags flags) override;
+ MitigationFlags GetProcessMitigations() override;
+ ResultCode SetDelayedProcessMitigations(MitigationFlags flags) override;
+ MitigationFlags GetDelayedProcessMitigations() const override;
+ ResultCode SetDisconnectCsrss() override;
+ void SetStrictInterceptions() override;
+ ResultCode SetStdoutHandle(HANDLE handle) override;
+ ResultCode SetStderrHandle(HANDLE handle) override;
+ ResultCode AddRule(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern) override;
+ ResultCode AddDllToUnload(const wchar_t* dll_name) override;
+ ResultCode AddKernelObjectToClose(const wchar_t* handle_type,
+ const wchar_t* handle_name) override;
+ void AddHandleToShare(HANDLE handle) override;
+ void SetLockdownDefaultDacl() override;
+ void AddRestrictingRandomSid() override;
+ void SetEnableOPMRedirection() override;
+ bool GetEnableOPMRedirection() override;
+ ResultCode AddAppContainerProfile(const wchar_t* package_name,
+ bool create_profile) override;
+ scoped_refptr<AppContainerProfile> GetAppContainerProfile() override;
+ void SetEffectiveToken(HANDLE token) override;
+
+ // Get the AppContainer profile as its internal type.
+ scoped_refptr<AppContainerProfileBase> GetAppContainerProfileBase();
+
+ // Creates a Job object with the level specified in a previous call to
+ // SetJobLevel().
+ ResultCode MakeJobObject(base::win::ScopedHandle* job);
+
+ // Creates the two tokens with the levels specified in a previous call to
+ // SetTokenLevel(). Also creates a lowbox token if specified based on the
+ // lowbox SID.
+ ResultCode MakeTokens(base::win::ScopedHandle* initial,
+ base::win::ScopedHandle* lockdown,
+ base::win::ScopedHandle* lowbox);
+
+ PSID GetLowBoxSid() const;
+
+ // Adds a target process to the internal list of targets. Internally a
+ // call to TargetProcess::Init() is issued.
+ ResultCode AddTarget(TargetProcess* target);
+
+ // Called when there are no more active processes in a Job.
+ // Removes a Job object associated with this policy and the target associated
+ // with the job.
+ bool OnJobEmpty(HANDLE job);
+
+ EvalResult EvalPolicy(IpcTag service, CountedParameterSetBase* params);
+
+ HANDLE GetStdoutHandle();
+ HANDLE GetStderrHandle();
+
+ // Returns the list of handles being shared with the target process.
+ const base::HandlesToInheritVector& GetHandlesBeingShared();
+
+ private:
+ // Allow PolicyInfo to snapshot PolicyBase for diagnostics.
+ friend class PolicyDiagnostic;
+ ~PolicyBase();
+
+ // Sets up interceptions for a new target.
+ ResultCode SetupAllInterceptions(TargetProcess* target);
+
+ // Sets up the handle closer for a new target.
+ bool SetupHandleCloser(TargetProcess* target);
+
+ ResultCode AddRuleInternal(SubSystem subsystem,
+ Semantics semantics,
+ const wchar_t* pattern);
+
+ // This lock synchronizes operations on the targets_ collection.
+ CRITICAL_SECTION lock_;
+ // Maintains the list of target process associated with this policy.
+ // The policy takes ownership of them.
+ typedef std::list<TargetProcess*> TargetSet;
+ TargetSet targets_;
+ // Standard object-lifetime reference counter.
+ volatile LONG ref_count;
+ // The user-defined global policy settings.
+ TokenLevel lockdown_level_;
+ TokenLevel initial_level_;
+ bool use_restricting_sids_ = true;
+ JobLevel job_level_;
+ uint32_t ui_exceptions_;
+ size_t memory_limit_;
+ bool use_alternate_desktop_;
+ bool use_alternate_winstation_;
+ // Helps the file system policy initialization.
+ bool file_system_init_;
+ bool relaxed_interceptions_;
+ HANDLE stdout_handle_;
+ HANDLE stderr_handle_;
+ IntegrityLevel integrity_level_;
+ IntegrityLevel delayed_integrity_level_;
+ MitigationFlags mitigations_;
+ MitigationFlags delayed_mitigations_;
+ bool is_csrss_connected_;
+ // Object in charge of generating the low level policy.
+ LowLevelPolicy* policy_maker_;
+ // Memory structure that stores the low level policy.
+ PolicyGlobal* policy_;
+ // The list of dlls to unload in the target process.
+ std::vector<std::wstring> blocklisted_dlls_;
+ // This is a map of handle-types to names that we need to close in the
+ // target process. A null set means we need to close all handles of the
+ // given type.
+ HandleCloser handle_closer_;
+ PSID lowbox_sid_;
+ base::win::ScopedHandle lowbox_directory_;
+ std::unique_ptr<Dispatcher> dispatcher_;
+ bool lockdown_default_dacl_;
+ bool add_restricting_random_sid_;
+
+ static HDESK alternate_desktop_handle_;
+ static HWINSTA alternate_winstation_handle_;
+ static HDESK alternate_desktop_local_winstation_handle_;
+ static IntegrityLevel alternate_desktop_integrity_level_label_;
+ static IntegrityLevel
+ alternate_desktop_local_winstation_integrity_level_label_;
+
+ // Contains the list of handles being shared with the target process.
+ // This list contains handles other than the stderr/stdout handles which are
+ // shared with the target at times.
+ base::HandlesToInheritVector handles_to_share_;
+ bool enable_opm_redirection_;
+
+ scoped_refptr<AppContainerProfileBase> app_container_profile_;
+
+ HANDLE effective_token_;
+
+ DISALLOW_COPY_AND_ASSIGN(PolicyBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_POLICY_BASE_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc
new file mode 100644
index 0000000000..9bc8634ec6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sandbox_rand.h"
+
+#include <windows.h>
+
+// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the
+// "Community Additions" comment on MSDN here:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
+#define SystemFunction036 NTAPI SystemFunction036
+#include <ntsecapi.h>
+#undef SystemFunction036
+
+namespace sandbox {
+
+bool GetRandom(unsigned int* random_value) {
+ return RtlGenRandom(random_value, sizeof(unsigned int)) != false;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_rand.h b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.h
new file mode 100644
index 0000000000..f4662f9a84
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_rand.h
@@ -0,0 +1,15 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SANDBOX_RAND_H_
+#define SANDBOX_SRC_SANDBOX_RAND_H_
+
+namespace sandbox {
+
+// Generate a random value in |random_value|. Returns true on success.
+bool GetRandom(unsigned int* random_value);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_RAND_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_types.h b/security/sandbox/chromium/sandbox/win/src/sandbox_types.h
new file mode 100644
index 0000000000..782541863d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_types.h
@@ -0,0 +1,199 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+#define SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
+
+#include "base/process/kill.h"
+#include "base/process/launch.h"
+
+namespace sandbox {
+
+#ifdef __MINGW32__
+// Map Microsoft's proprietary more-safe version of copy() back to
+// the std::basic_string method
+#define _Copy_s copy
+#endif
+
+// Operation result codes returned by the sandbox API.
+//
+// Note: These codes are listed in a histogram and any new codes should be added
+// at the end. If the underlying type is changed then the forward declaration in
+// sandbox_init.h must be updated.
+//
+enum ResultCode : int {
+ SBOX_ALL_OK = 0,
+ // Error is originating on the win32 layer. Call GetlastError() for more
+ // information.
+ SBOX_ERROR_GENERIC = 1,
+ // An invalid combination of parameters was given to the API.
+ SBOX_ERROR_BAD_PARAMS = 2,
+ // The desired operation is not supported at this time.
+ SBOX_ERROR_UNSUPPORTED = 3,
+ // The request requires more memory that allocated or available.
+ SBOX_ERROR_NO_SPACE = 4,
+ // The ipc service requested does not exist.
+ SBOX_ERROR_INVALID_IPC = 5,
+ // The ipc service did not complete.
+ SBOX_ERROR_FAILED_IPC = 6,
+ // The requested handle was not found.
+ SBOX_ERROR_NO_HANDLE = 7,
+ // This function was not expected to be called at this time.
+ SBOX_ERROR_UNEXPECTED_CALL = 8,
+ // WaitForAllTargets is already called.
+ SBOX_ERROR_WAIT_ALREADY_CALLED = 9,
+ // A channel error prevented DoCall from executing.
+ SBOX_ERROR_CHANNEL_ERROR = 10,
+ // Failed to create the alternate desktop.
+ SBOX_ERROR_CANNOT_CREATE_DESKTOP = 11,
+ // Failed to create the alternate window station.
+ SBOX_ERROR_CANNOT_CREATE_WINSTATION = 12,
+ // Failed to switch back to the interactive window station.
+ SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION = 13,
+ // The supplied AppContainer is not valid.
+ SBOX_ERROR_INVALID_APP_CONTAINER = 14,
+ // The supplied capability is not valid.
+ SBOX_ERROR_INVALID_CAPABILITY = 15,
+ // There is a failure initializing the AppContainer.
+ SBOX_ERROR_CANNOT_INIT_APPCONTAINER = 16,
+ // Initializing or updating ProcThreadAttributes failed.
+ SBOX_ERROR_PROC_THREAD_ATTRIBUTES = 17,
+ // Error in creating process.
+ SBOX_ERROR_CREATE_PROCESS = 18,
+ // Failure calling delegate PreSpawnTarget.
+ SBOX_ERROR_DELEGATE_PRE_SPAWN = 19,
+ // Could not assign process to job object.
+ SBOX_ERROR_ASSIGN_PROCESS_TO_JOB_OBJECT = 20,
+ // Could not assign process to job object.
+ SBOX_ERROR_SET_THREAD_TOKEN = 21,
+ // Could not get thread context of new process.
+ SBOX_ERROR_GET_THREAD_CONTEXT = 22,
+ // Could not duplicate target info of new process.
+ SBOX_ERROR_DUPLICATE_TARGET_INFO = 23,
+ // Could not set low box token.
+ SBOX_ERROR_SET_LOW_BOX_TOKEN = 24,
+ // Could not create file mapping for IPC dispatcher.
+ SBOX_ERROR_CREATE_FILE_MAPPING = 25,
+ // Could not duplicate shared section into target process for IPC dispatcher.
+ SBOX_ERROR_DUPLICATE_SHARED_SECTION = 26,
+ // Could not map view of shared memory in broker.
+ SBOX_ERROR_MAP_VIEW_OF_SHARED_SECTION = 27,
+ // Could not apply ASLR mitigations to target process.
+ SBOX_ERROR_APPLY_ASLR_MITIGATIONS = 28,
+ // Could not setup one of the required interception services.
+ SBOX_ERROR_SETUP_BASIC_INTERCEPTIONS = 29,
+ // Could not setup basic interceptions.
+ SBOX_ERROR_SETUP_INTERCEPTION_SERVICE = 30,
+ // Could not initialize interceptions. This usually means 3rd party software
+ // is stomping on our hooks, or can sometimes mean the syscall format has
+ // changed.
+ SBOX_ERROR_INITIALIZE_INTERCEPTIONS = 31,
+ // Could not setup the imports for ntdll in target process.
+ SBOX_ERROR_SETUP_NTDLL_IMPORTS = 32,
+ // Could not setup the handle closer in target process.
+ SBOX_ERROR_SETUP_HANDLE_CLOSER = 33,
+ // Cannot get the current Window Station.
+ SBOX_ERROR_CANNOT_GET_WINSTATION = 34,
+ // Cannot query the security attributes of the current Window Station.
+ SBOX_ERROR_CANNOT_QUERY_WINSTATION_SECURITY = 35,
+ // Cannot get the current Desktop.
+ SBOX_ERROR_CANNOT_GET_DESKTOP = 36,
+ // Cannot query the security attributes of the current Desktop.
+ SBOX_ERROR_CANNOT_QUERY_DESKTOP_SECURITY = 37,
+ // Cannot setup the interception manager config buffer.
+ SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_CONFIG_BUFFER = 38,
+ // Cannot copy data to the child process.
+ SBOX_ERROR_CANNOT_COPY_DATA_TO_CHILD = 39,
+ // Cannot setup the interception thunk.
+ SBOX_ERROR_CANNOT_SETUP_INTERCEPTION_THUNK = 40,
+ // Cannot resolve the interception thunk.
+ SBOX_ERROR_CANNOT_RESOLVE_INTERCEPTION_THUNK = 41,
+ // Cannot write interception thunk to child process.
+ SBOX_ERROR_CANNOT_WRITE_INTERCEPTION_THUNK = 42,
+ // Cannot find the base address of the new process.
+ SBOX_ERROR_CANNOT_FIND_BASE_ADDRESS = 43,
+ // Cannot create the AppContainer profile.
+ SBOX_ERROR_CREATE_APPCONTAINER_PROFILE = 44,
+ // Cannot create the AppContainer as the main executable can't be accessed.
+ SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_ACCESS_CHECK = 45,
+ // Cannot create the AppContainer as adding a capability failed.
+ SBOX_ERROR_CREATE_APPCONTAINER_PROFILE_CAPABILITY = 46,
+ // Cannot initialize a job object.
+ SBOX_ERROR_CANNOT_INIT_JOB = 47,
+ // Invalid LowBox SID string.
+ SBOX_ERROR_INVALID_LOWBOX_SID = 48,
+ // Cannot create restricted token.
+ SBOX_ERROR_CANNOT_CREATE_RESTRICTED_TOKEN = 49,
+ // Cannot set the integrity level on a desktop object.
+ SBOX_ERROR_CANNOT_SET_DESKTOP_INTEGRITY = 50,
+ // Cannot create a LowBox token.
+ SBOX_ERROR_CANNOT_CREATE_LOWBOX_TOKEN = 51,
+ // Cannot modify LowBox token's DACL.
+ SBOX_ERROR_CANNOT_MODIFY_LOWBOX_TOKEN_DACL = 52,
+ // Cannot create restricted impersonation token.
+ SBOX_ERROR_CANNOT_CREATE_RESTRICTED_IMP_TOKEN = 53,
+ // Cannot duplicate target process handle.
+ SBOX_ERROR_CANNOT_DUPLICATE_PROCESS_HANDLE = 54,
+ // Cannot load executable for variable transfer.
+ SBOX_ERROR_CANNOT_LOADLIBRARY_EXECUTABLE = 55,
+ // Cannot find variable address for transfer.
+ SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS = 56,
+ // Cannot write variable value.
+ SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE = 57,
+ // Short write to variable.
+ SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE = 58,
+ // Cannot initialize BrokerServices.
+ SBOX_ERROR_CANNOT_INIT_BROKERSERVICES = 59,
+ // Placeholder for last item of the enum.
+ SBOX_ERROR_LAST
+};
+
+// If the sandbox cannot create a secure environment for the target, the
+// target will be forcibly terminated. These are the process exit codes.
+enum TerminationCodes {
+ SBOX_FATAL_INTEGRITY = 7006, // Could not set the integrity level.
+ SBOX_FATAL_DROPTOKEN = 7007, // Could not lower the token.
+ SBOX_FATAL_FLUSHANDLES = 7008, // Failed to flush registry handles.
+ SBOX_FATAL_CACHEDISABLE = 7009, // Failed to forbid HCKU caching.
+ SBOX_FATAL_CLOSEHANDLES = 7010, // Failed to close pending handles.
+ SBOX_FATAL_MITIGATION = 7011, // Could not set the mitigation policy.
+ SBOX_FATAL_MEMORY_EXCEEDED = 7012, // Exceeded the job memory limit.
+ SBOX_FATAL_WARMUP = 7013, // Failed to warmup.
+ SBOX_FATAL_LAST
+};
+
+#if !defined(SANDBOX_FUZZ_TARGET)
+static_assert(SBOX_FATAL_MEMORY_EXCEEDED ==
+ base::win::kSandboxFatalMemoryExceeded,
+ "Value for SBOX_FATAL_MEMORY_EXCEEDED must match base.");
+#endif // !defined(SANDBOX_FUZZ_TARGET)
+
+class BrokerServices;
+class TargetServices;
+
+// Contains the pointer to a target or broker service.
+struct SandboxInterfaceInfo {
+ BrokerServices* broker_services;
+ TargetServices* target_services;
+};
+
+#if SANDBOX_EXPORTS
+#define SANDBOX_INTERCEPT extern "C" __declspec(dllexport)
+#else
+#define SANDBOX_INTERCEPT extern "C"
+#endif
+
+enum InterceptionType {
+ INTERCEPTION_INVALID = 0,
+ INTERCEPTION_SERVICE_CALL, // Trampoline of an NT native call
+ INTERCEPTION_EAT,
+ INTERCEPTION_SIDESTEP, // Preamble patch
+ INTERCEPTION_SMART_SIDESTEP, // Preamble patch but bypass internal calls
+ INTERCEPTION_UNLOAD_MODULE, // Unload the module (don't patch)
+ INTERCEPTION_LAST // Placeholder for last item in the enumeration
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SANDBOX_TYPES_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc
new file mode 100644
index 0000000000..f34daa7d3e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 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/sandbox_utils.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "sandbox/win/src/internal_types.h"
+
+namespace sandbox {
+
+void InitObjectAttribs(const std::wstring& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name,
+ SECURITY_QUALITY_OF_SERVICE* security_qos) {
+ static RtlInitUnicodeStringFunction RtlInitUnicodeString;
+ if (!RtlInitUnicodeString) {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+ RtlInitUnicodeString = reinterpret_cast<RtlInitUnicodeStringFunction>(
+ GetProcAddress(ntdll, "RtlInitUnicodeString"));
+ DCHECK(RtlInitUnicodeString);
+ }
+ RtlInitUnicodeString(uni_name, name.c_str());
+ InitializeObjectAttributes(obj_attr, uni_name, attributes, root, nullptr);
+ obj_attr->SecurityQualityOfService = security_qos;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h
new file mode 100644
index 0000000000..580d1298f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sandbox_utils.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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_SANDBOX_UTILS_H_
+#define SANDBOX_SRC_SANDBOX_UTILS_H_
+
+#include <windows.h>
+#include <string>
+
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+void InitObjectAttribs(const std::wstring& name,
+ ULONG attributes,
+ HANDLE root,
+ OBJECT_ATTRIBUTES* obj_attr,
+ UNICODE_STRING* uni_name,
+ SECURITY_QUALITY_OF_SERVICE* security_qos);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SANDBOX_UTILS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/security_capabilities.cc b/security/sandbox/chromium/sandbox/win/src/security_capabilities.cc
new file mode 100644
index 0000000000..0df446b5be
--- /dev/null
+++ b/security/sandbox/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/security/sandbox/chromium/sandbox/win/src/security_capabilities.h b/security/sandbox/chromium/sandbox/win/src/security_capabilities.h
new file mode 100644
index 0000000000..7a66e59743
--- /dev/null
+++ b/security/sandbox/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/security/sandbox/chromium/sandbox/win/src/security_level.h b/security/sandbox/chromium/sandbox/win/src/security_level.h
new file mode 100644
index 0000000000..8e81cf21c9
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/security_level.h
@@ -0,0 +1,300 @@
+// Copyright (c) 2006-2008 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_LEVEL_H_
+#define SANDBOX_SRC_SECURITY_LEVEL_H_
+
+#include <stdint.h>
+
+namespace sandbox {
+
+// List of all the integrity levels supported in the sandbox.
+// The integrity level of the sandboxed process can't be set to a level higher
+// than the broker process.
+//
+// Note: These levels map to SIDs under the hood.
+// INTEGRITY_LEVEL_SYSTEM: "S-1-16-16384" System Mandatory Level
+// INTEGRITY_LEVEL_HIGH: "S-1-16-12288" High Mandatory Level
+// INTEGRITY_LEVEL_MEDIUM: "S-1-16-8192" Medium Mandatory Level
+// INTEGRITY_LEVEL_MEDIUM_LOW: "S-1-16-6144"
+// INTEGRITY_LEVEL_LOW: "S-1-16-4096" Low Mandatory Level
+// INTEGRITY_LEVEL_BELOW_LOW: "S-1-16-2048"
+// INTEGRITY_LEVEL_UNTRUSTED: "S-1-16-0" Untrusted Mandatory Level
+//
+// Not defined: "S-1-16-20480" Protected Process Mandatory Level
+// Not defined: "S-1-16-28672" Secure Process Mandatory Level
+enum IntegrityLevel {
+ INTEGRITY_LEVEL_SYSTEM,
+ INTEGRITY_LEVEL_HIGH,
+ INTEGRITY_LEVEL_MEDIUM,
+ INTEGRITY_LEVEL_MEDIUM_LOW,
+ INTEGRITY_LEVEL_LOW,
+ INTEGRITY_LEVEL_BELOW_LOW,
+ INTEGRITY_LEVEL_UNTRUSTED,
+ INTEGRITY_LEVEL_LAST
+};
+
+// The Token level specifies a set of security profiles designed to
+// provide the bulk of the security of sandbox.
+//
+// TokenLevel |Restricting |Deny Only |Privileges|
+// |Sids |Sids | |
+// ----------------------------|--------------|----------------|----------|
+// USER_LOCKDOWN | Null Sid | All | None |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED | RESTRICTED | All | Traverse |
+// ----------------------------|--------------|----------------|----------|
+// USER_LIMITED | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | RESTRICTED | Everyone | |
+// | | Interactive | |
+// ----------------------------|--------------|----------------|----------|
+// USER_INTERACTIVE | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | RESTRICTED | Everyone | |
+// | Owner | Interactive | |
+// | | Local | |
+// | | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED_NON_ADMIN | Users | All except: | Traverse |
+// | Everyone | Users | |
+// | Interactive | Everyone | |
+// | Local | Interactive | |
+// | Authent-users| Local | |
+// | User | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_NON_ADMIN | None | All except: | Traverse |
+// | | Users | |
+// | | Everyone | |
+// | | Interactive | |
+// | | Local | |
+// | | Authent-users | |
+// | | User | |
+// ----------------------------|--------------|----------------|----------|
+// USER_RESTRICTED_SAME_ACCESS | All | None | All |
+// ----------------------------|--------------|----------------|----------|
+// USER_UNPROTECTED | None | None | All |
+// ----------------------------|--------------|----------------|----------|
+//
+// The above restrictions are actually a transformation that is applied to
+// the existing broker process token. The resulting token that will be
+// applied to the target process depends both on the token level selected
+// and on the broker token itself.
+//
+// The LOCKDOWN and RESTRICTED are designed to allow access to almost
+// nothing that has security associated with and they are the recommended
+// levels to run sandboxed code specially if there is a chance that the
+// broker is process might be started by a user that belongs to the Admins
+// or power users groups.
+enum TokenLevel {
+ USER_LOCKDOWN = 0,
+ USER_RESTRICTED,
+ USER_LIMITED,
+ USER_INTERACTIVE,
+ USER_RESTRICTED_NON_ADMIN,
+ USER_NON_ADMIN,
+ USER_RESTRICTED_SAME_ACCESS,
+ USER_UNPROTECTED,
+ USER_LAST
+};
+
+// The Job level specifies a set of decreasing security profiles for the
+// Job object that the target process will be placed into.
+// This table summarizes the security associated with each level:
+//
+// JobLevel |General |Quota |
+// |restrictions |restrictions |
+// -----------------|---------------------------------- |--------------------|
+// JOB_NONE | No job is assigned to the | None |
+// | sandboxed process. | |
+// -----------------|---------------------------------- |--------------------|
+// JOB_UNPROTECTED | None | *Kill on Job close.|
+// -----------------|---------------------------------- |--------------------|
+// JOB_INTERACTIVE | *Forbid system-wide changes using | |
+// | SystemParametersInfo(). | *Kill on Job close.|
+// | *Forbid the creation/switch of | |
+// | Desktops. | |
+// | *Forbids calls to ExitWindows(). | |
+// -----------------|---------------------------------- |--------------------|
+// JOB_LIMITED_USER | Same as INTERACTIVE_USER plus: | *One active process|
+// | *Forbid changes to the display | limit. |
+// | settings. | *Kill on Job close.|
+// -----------------|---------------------------------- |--------------------|
+// JOB_RESTRICTED | Same as LIMITED_USER plus: | *One active process|
+// | * No read/write to the clipboard. | limit. |
+// | * No access to User Handles that | *Kill on Job close.|
+// | belong to other processes. | |
+// | * Forbid message broadcasts. | |
+// | * Forbid setting global hooks. | |
+// | * No access to the global atoms | |
+// | table. | |
+// -----------------|-----------------------------------|--------------------|
+// JOB_LOCKDOWN | Same as RESTRICTED | *One active process|
+// | | limit. |
+// | | *Kill on Job close.|
+// | | *Kill on unhandled |
+// | | exception. |
+// | | |
+// In the context of the above table, 'user handles' refers to the handles of
+// windows, bitmaps, menus, etc. Files, treads and registry handles are kernel
+// handles and are not affected by the job level settings.
+enum JobLevel {
+ JOB_LOCKDOWN = 0,
+ JOB_RESTRICTED,
+ JOB_LIMITED_USER,
+ JOB_INTERACTIVE,
+ JOB_UNPROTECTED,
+ JOB_NONE
+};
+
+// These flags correspond to various process-level mitigations (eg. ASLR and
+// DEP). Most are implemented via UpdateProcThreadAttribute() plus flags for
+// the PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY attribute argument; documented
+// here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms686880
+// Some mitigations are implemented directly by the sandbox or emulated to
+// the greatest extent possible when not directly supported by the OS.
+// Flags that are unsupported for the target OS will be silently ignored.
+// Flags that are invalid for their application (pre or post startup) will
+// return SBOX_ERROR_BAD_PARAMS.
+typedef uint64_t MitigationFlags;
+
+// Permanently enables DEP for the target process. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_DEP_ENABLE.
+const MitigationFlags MITIGATION_DEP = 0x00000001;
+
+// Permanently Disables ATL thunk emulation when DEP is enabled. Valid
+// only when MITIGATION_DEP is passed. Corresponds to not passing
+// PROCESS_CREATION_MITIGATION_POLICY_DEP_ATL_THUNK_ENABLE.
+const MitigationFlags MITIGATION_DEP_NO_ATL_THUNK = 0x00000002;
+
+// Enables Structured exception handling override prevention. Must be
+// enabled prior to process start. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_SEHOP_ENABLE.
+const MitigationFlags MITIGATION_SEHOP = 0x00000004;
+
+// Forces ASLR on all images in the child process. In debug builds, must be
+// enabled after startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON .
+const MitigationFlags MITIGATION_RELOCATE_IMAGE = 0x00000008;
+
+// Refuses to load DLLs that cannot support ASLR. In debug builds, must be
+// enabled after startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_ON_REQ_RELOCS.
+const MitigationFlags MITIGATION_RELOCATE_IMAGE_REQUIRED = 0x00000010;
+
+// Terminates the process on Windows heap corruption. Coresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_HEAP_TERMINATE_ALWAYS_ON.
+const MitigationFlags MITIGATION_HEAP_TERMINATE = 0x00000020;
+
+// Sets a random lower bound as the minimum user address. Must be
+// enabled prior to process start. On 32-bit processes this is
+// emulated to a much smaller degree. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_ON.
+const MitigationFlags MITIGATION_BOTTOM_UP_ASLR = 0x00000040;
+
+// Increases the randomness range of bottom-up ASLR to up to 1TB. Must be
+// enabled prior to process start and with MITIGATION_BOTTOM_UP_ASLR.
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_HIGH_ENTROPY_ASLR_ALWAYS_ON
+const MitigationFlags MITIGATION_HIGH_ENTROPY_ASLR = 0x00000080;
+
+// Immediately raises an exception on a bad handle reference. Must be
+// enabled after startup. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_STRICT_HANDLE_CHECKS_ALWAYS_ON.
+const MitigationFlags MITIGATION_STRICT_HANDLE_CHECKS = 0x00000100;
+
+// Sets the DLL search order to LOAD_LIBRARY_SEARCH_DEFAULT_DIRS. Additional
+// directories can be added via the Windows AddDllDirectory() function.
+// http://msdn.microsoft.com/en-us/library/windows/desktop/hh310515
+// Must be enabled after startup.
+const MitigationFlags MITIGATION_DLL_SEARCH_ORDER = 0x00000200;
+
+// Changes the mandatory integrity level policy on the current process' token
+// to enable no-read and no-execute up. This prevents a lower IL process from
+// opening the process token for impersonate/duplicate/assignment.
+const MitigationFlags MITIGATION_HARDEN_TOKEN_IL_POLICY = 0x00000400;
+
+// Prevents the process from making Win32k calls. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON.
+//
+// Applications linked to user32.dll or gdi32.dll make Win32k calls during
+// setup, even if Win32k is not otherwise used. So they also need to add a rule
+// with SUBSYS_WIN32K_LOCKDOWN and semantics FAKE_USER_GDI_INIT to allow the
+// initialization to succeed.
+const MitigationFlags MITIGATION_WIN32K_DISABLE = 0x00000800;
+
+// Prevents certain built-in third party extension points from being used.
+// - App_Init DLLs
+// - Winsock Layered Service Providers (LSPs)
+// - Global Windows Hooks (NOT thread-targeted hooks)
+// - Legacy Input Method Editors (IMEs)
+// I.e.: Disable legacy hooking mechanisms. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_EXTENSION_POINT_DISABLE_ALWAYS_ON.
+const MitigationFlags MITIGATION_EXTENSION_POINT_DISABLE = 0x00001000;
+
+// Prevents the process from generating dynamic code or modifying executable
+// code. Second option to allow thread-specific opt-out.
+// - VirtualAlloc with PAGE_EXECUTE_*
+// - VirtualProtect with PAGE_EXECUTE_*
+// - MapViewOfFile with FILE_MAP_EXECUTE | FILE_MAP_WRITE
+// - SetProcessValidCallTargets for CFG
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON and
+// PROCESS_CREATION_MITIGATION_POLICY_PROHIBIT_DYNAMIC_CODE_ALWAYS_ON_ALLOW_OPT_OUT.
+const MitigationFlags MITIGATION_DYNAMIC_CODE_DISABLE = 0x00002000;
+const MitigationFlags MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT = 0x00004000;
+// The following per-thread flag can be used with the
+// ApplyMitigationsToCurrentThread API. Requires the above process mitigation
+// to be set on the current process.
+const MitigationFlags MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD = 0x00008000;
+
+// Prevents the process from loading non-system fonts into GDI.
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_FONT_DISABLE_ALWAYS_ON
+const MitigationFlags MITIGATION_NONSYSTEM_FONT_DISABLE = 0x00010000;
+
+// Prevents the process from loading binaries NOT signed by MS.
+// Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON
+const MitigationFlags MITIGATION_FORCE_MS_SIGNED_BINS = 0x00020000;
+
+// Blocks mapping of images from remote devices. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_REMOTE_ALWAYS_ON.
+const MitigationFlags MITIGATION_IMAGE_LOAD_NO_REMOTE = 0x00040000;
+
+// Blocks mapping of images that have the low manditory label. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_NO_LOW_LABEL_ALWAYS_ON.
+const MitigationFlags MITIGATION_IMAGE_LOAD_NO_LOW_LABEL = 0x00080000;
+
+// Forces image load preference to prioritize the Windows install System32
+// folder before dll load dir, application dir and any user dirs set.
+// - Affects IAT resolution standard search path only, NOT direct LoadLibrary or
+// executable search path.
+// PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON.
+const MitigationFlags MITIGATION_IMAGE_LOAD_PREFER_SYS32 = 0x00100000;
+
+// Prevents hyperthreads from interfering with indirect branch predictions.
+// (SPECTRE Variant 2 mitigation.) Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY2_RESTRICT_INDIRECT_BRANCH_PREDICTION_ALWAYS_ON.
+const MitigationFlags MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION =
+ 0x00200000;
+
+// Begin Mozilla-added flags.
+// Working down from the high bit to avoid conflict with new upstream flags.
+
+// Disable Control Flow Guard. This may seem more like an anti-mitigation, but
+// this flag allows code to make targeted changes to CFG to avoid bugs, while
+// leaving it enabled in the common case. Corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY_CONTROL_FLOW_GUARD_ALWAYS_ON.
+const MitigationFlags MITIGATION_CONTROL_FLOW_GUARD_DISABLE = 0x80000000;
+
+// This enables CET User Shadow Stack for compatible modules and corresponds to
+// PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_ALWAYS_ON.
+const MitigationFlags MITIGATION_CET_COMPAT_MODE = 0x40000000;
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SECURITY_LEVEL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver.cc
new file mode 100644
index 0000000000..f26322dd63
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver.cc
@@ -0,0 +1,47 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::ResolveInterceptor(
+ const void* interceptor_module,
+ const char* interceptor_name,
+ const void** address) {
+ // After all, we are using a locally mapped version of the exe, so the
+ // action is the same as for a target function.
+ return ResolveTarget(interceptor_module, interceptor_name,
+ const_cast<void**>(address));
+}
+
+// In this case all the work is done from the parent, so resolve is
+// just a simple GetProcAddress.
+NTSTATUS ServiceResolverThunk::ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) {
+ if (!module)
+ return STATUS_UNSUCCESSFUL;
+
+ base::win::PEImage module_image(module);
+ *address =
+ reinterpret_cast<void*>(module_image.GetProcAddress(function_name));
+
+ if (!*address) {
+ NOTREACHED_NT();
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+void ServiceResolverThunk::AllowLocalPatches() {
+ ntdll_base_ = ::GetModuleHandle(kNtdllName);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver.h b/security/sandbox/chromium/sandbox/win/src/service_resolver.h
new file mode 100644
index 0000000000..ee292f6a37
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SERVICE_RESOLVER_H__
+#define SANDBOX_SRC_SERVICE_RESOLVER_H__
+
+#include <stddef.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/resolver.h"
+
+namespace sandbox {
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll.
+class ServiceResolverThunk : public ResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ ServiceResolverThunk(HANDLE process, bool relaxed)
+ : ntdll_base_(nullptr),
+ process_(process),
+ relaxed_(relaxed),
+ relative_jump_(0) {}
+ ~ServiceResolverThunk() override {}
+
+ // Implementation of Resolver::Setup.
+ NTSTATUS Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) override;
+
+ // Implementation of Resolver::ResolveInterceptor.
+ NTSTATUS ResolveInterceptor(const void* module,
+ const char* function_name,
+ const void** address) override;
+
+ // Implementation of Resolver::ResolveTarget.
+ NTSTATUS ResolveTarget(const void* module,
+ const char* function_name,
+ void** address) override;
+
+ // Implementation of Resolver::GetThunkSize.
+ size_t GetThunkSize() const override;
+
+ // Call this to set up ntdll_base_ which will allow for local patches.
+ virtual void AllowLocalPatches();
+
+ // Verifies that the function specified by |target_name| in |target_module| is
+ // a service and copies the data from that function into |thunk_storage|. If
+ // |storage_bytes| is too small, then the method fails.
+ virtual NTSTATUS CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used);
+
+ protected:
+ // The unit test will use this member to allow local patch on a buffer.
+ HMODULE ntdll_base_;
+
+ // Handle of the child process.
+ HANDLE process_;
+
+ private:
+ // Returns true if the code pointer by target_ corresponds to the expected
+ // type of function. Saves that code on the first part of the thunk pointed
+ // by local_thunk (should be directly accessible from the parent).
+ virtual bool IsFunctionAService(void* local_thunk) const;
+
+ // Performs the actual patch of target_.
+ // local_thunk must be already fully initialized, and the first part must
+ // contain the original code. The real type of this buffer is ServiceFullThunk
+ // (yes, private). remote_thunk (real type ServiceFullThunk), must be
+ // allocated on the child, and will contain the thunk data, after this call.
+ // Returns the apropriate status code.
+ virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk);
+
+ // Provides basically the same functionality as IsFunctionAService but it
+ // continues even if it does not recognize the function code. remote_thunk
+ // is the address of our memory on the child.
+ bool SaveOriginalFunction(void* local_thunk, void* remote_thunk);
+
+ // true if we are allowed to patch already-patched functions.
+ bool relaxed_;
+ ULONG relative_jump_;
+
+ DISALLOW_COPY_AND_ASSIGN(ServiceResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on WOW64 (32 bit ntdll on 64 bit Vista).
+class Wow64ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Wow64ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ ~Wow64ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64ResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on WOW64 for Windows 8.
+class Wow64W8ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Wow64W8ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ ~Wow64W8ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64W8ResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on Windows 8.
+class Win8ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Win8ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ ~Win8ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Win8ResolverThunk);
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll on WOW64 for Windows 10.
+class Wow64W10ResolverThunk : public ServiceResolverThunk {
+ public:
+ // The service resolver needs a child process to write to.
+ Wow64W10ResolverThunk(HANDLE process, bool relaxed)
+ : ServiceResolverThunk(process, relaxed) {}
+ ~Wow64W10ResolverThunk() override {}
+
+ private:
+ bool IsFunctionAService(void* local_thunk) const override;
+
+ DISALLOW_COPY_AND_ASSIGN(Wow64W10ResolverThunk);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SERVICE_RESOLVER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc
new file mode 100644
index 0000000000..d95ce0086b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_32.cc
@@ -0,0 +1,476 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/bit_cast.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+#pragma pack(push, 1)
+
+const BYTE kMovEax = 0xB8;
+const BYTE kMovEdx = 0xBA;
+const USHORT kMovEdxEsp = 0xD48B;
+const USHORT kCallPtrEdx = 0x12FF;
+const USHORT kCallEdx = 0xD2FF;
+const BYTE kCallEip = 0xE8;
+const BYTE kRet = 0xC2;
+const BYTE kRet2 = 0xC3;
+const USHORT kJmpEdx = 0xE2FF;
+const USHORT kXorEcx = 0xC933;
+const ULONG kLeaEdx = 0x0424548D;
+const ULONG kCallFs1 = 0xC015FF64;
+const USHORT kCallFs2 = 0;
+const BYTE kCallFs3 = 0;
+const BYTE kAddEsp1 = 0x83;
+const USHORT kAddEsp2 = 0x4C4;
+const BYTE kJmp32 = 0xE9;
+const USHORT kSysenter = 0x340F;
+
+// Service code for 32 bit systems.
+// NOTE: on win2003 "call dword ptr [edx]" is "call edx".
+struct ServiceEntry {
+ // This struct contains roughly the following code:
+ // 00 mov eax,25h
+ // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
+ // 0a call dword ptr [edx]
+ // 0c ret 2Ch
+ // 0f nop
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ BYTE mov_edx; // = BA
+ ULONG stub;
+ USHORT call_ptr_edx; // = FF 12
+ BYTE ret; // = C2
+ USHORT num_params;
+ BYTE nop;
+};
+
+// Service code for 32 bit Windows 8.
+struct ServiceEntryW8 {
+ // This struct contains the following code:
+ // 00 b825000000 mov eax,25h
+ // 05 e803000000 call eip+3
+ // 0a c22c00 ret 2Ch
+ // 0d 8bd4 mov edx,esp
+ // 0f 0f34 sysenter
+ // 11 c3 ret
+ // 12 8bff mov edi,edi
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ BYTE call_eip; // = E8
+ ULONG call_offset;
+ BYTE ret_p; // = C2
+ USHORT num_params;
+ USHORT mov_edx_esp; // = BD D4
+ USHORT sysenter; // = 0F 34
+ BYTE ret; // = C3
+ USHORT nop;
+};
+
+// Service code for a 32 bit process running on a 64 bit os.
+struct Wow64Entry {
+ // This struct may contain one of two versions of code:
+ // 1. For XP, Vista and 2K3:
+ // 00 b825000000 mov eax, 25h
+ // 05 33c9 xor ecx, ecx
+ // 07 8d542404 lea edx, [esp + 4]
+ // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 12 c22c00 ret 2Ch
+ //
+ // 2. For Windows 7:
+ // 00 b825000000 mov eax, 25h
+ // 05 33c9 xor ecx, ecx
+ // 07 8d542404 lea edx, [esp + 4]
+ // 0b 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 12 83c404 add esp, 4
+ // 15 c22c00 ret 2Ch
+ //
+ // So we base the structure on the bigger one:
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ USHORT xor_ecx; // = 33 C9
+ ULONG lea_edx; // = 8D 54 24 04
+ ULONG call_fs1; // = 64 FF 15 C0
+ USHORT call_fs2; // = 00 00
+ BYTE call_fs3; // = 00
+ BYTE add_esp1; // = 83 or ret
+ USHORT add_esp2; // = C4 04 or num_params
+ BYTE ret; // = C2
+ USHORT num_params;
+};
+
+// Service code for a 32 bit process running on 64 bit Windows 8.
+struct Wow64EntryW8 {
+ // 00 b825000000 mov eax, 25h
+ // 05 64ff15c0000000 call dword ptr fs:[0C0h]
+ // 0b c22c00 ret 2Ch
+ // 0f 90 nop
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ ULONG call_fs1; // = 64 FF 15 C0
+ USHORT call_fs2; // = 00 00
+ BYTE call_fs3; // = 00
+ BYTE ret; // = C2
+ USHORT num_params;
+ BYTE nop;
+};
+
+// Service code for a 32 bit process running on 64 bit Windows 10.
+struct Wow64EntryW10 {
+ // 00 b828000000 mov eax, 28h
+ // 05 bab0d54877 mov edx, 7748D5B0h
+ // 09 ffd2 call edx
+ // 0b c22800 ret 28h
+ BYTE mov_eax; // = B8
+ ULONG service_id;
+ BYTE mov_edx; // = BA
+ ULONG mov_edx_param;
+ USHORT call_edx; // = FF D2
+ BYTE ret; // = C2
+ USHORT num_params;
+};
+
+// Make sure that relaxed patching works as expected.
+const size_t kMinServiceSize = offsetof(ServiceEntry, ret);
+static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize,
+ "wrong service length");
+static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length");
+static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length");
+
+struct ServiceFullThunk {
+ union {
+ ServiceEntry original;
+ ServiceEntryW8 original_w8;
+ Wow64Entry wow_64;
+ Wow64EntryW8 wow_64_w8;
+ };
+ int internal_thunk; // Dummy member to the beginning of the internal thunk.
+};
+
+#pragma pack(pop)
+
+} // namespace
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret =
+ Init(target_module, interceptor_module, target_name, interceptor_name,
+ interceptor_entry_point, thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ relative_jump_ = 0;
+ size_t thunk_bytes = GetThunkSize();
+ std::unique_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
+ ServiceFullThunk* thunk =
+ reinterpret_cast<ServiceFullThunk*>(thunk_buffer.get());
+
+ if (!IsFunctionAService(&thunk->original) &&
+ (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
+ return STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ ret = PerformPatch(thunk, thunk_storage);
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+size_t ServiceResolverThunk::GetThunkSize() const {
+ return offsetof(ServiceFullThunk, internal_thunk) + GetInternalThunkSize();
+}
+
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original) &&
+ (!relaxed_ || !SaveOriginalFunction(&thunk->original, thunk_storage))) {
+ return STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceEntry function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kMovEdx != function_code.mov_edx ||
+ (kCallPtrEdx != function_code.call_ptr_edx &&
+ kCallEdx != function_code.call_ptr_edx) ||
+ kRet != function_code.ret) {
+ return false;
+ }
+
+ // Find the system call pointer if we don't already have it.
+ if (kCallEdx != function_code.call_ptr_edx) {
+ DWORD ki_system_call;
+ if (!::ReadProcessMemory(process_,
+ bit_cast<const void*>(function_code.stub),
+ &ki_system_call, sizeof(ki_system_call), &read)) {
+ return false;
+ }
+
+ if (sizeof(ki_system_call) != read)
+ return false;
+
+ HMODULE module_1, module_2;
+ // last check, call_stub should point to a KiXXSystemCall function on ntdll
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ bit_cast<const wchar_t*>(ki_system_call),
+ &module_1)) {
+ return false;
+ }
+
+ if (ntdll_base_) {
+ // This path is only taken when running the unit tests. We want to be
+ // able to patch a buffer in memory, so target_ is not inside ntdll.
+ module_2 = ntdll_base_;
+ } else {
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<const wchar_t*>(target_),
+ &module_2))
+ return false;
+ }
+
+ if (module_1 != module_2)
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
+ void* remote_thunk) {
+ ServiceEntry intercepted_code;
+ size_t bytes_to_write = sizeof(intercepted_code);
+ ServiceFullThunk* full_local_thunk =
+ reinterpret_cast<ServiceFullThunk*>(local_thunk);
+ ServiceFullThunk* full_remote_thunk =
+ reinterpret_cast<ServiceFullThunk*>(remote_thunk);
+
+ // patch the original code
+ memcpy(&intercepted_code, &full_local_thunk->original,
+ sizeof(intercepted_code));
+ intercepted_code.mov_eax = kMovEax;
+ intercepted_code.service_id = full_local_thunk->original.service_id;
+ intercepted_code.mov_edx = kMovEdx;
+ intercepted_code.stub = bit_cast<ULONG>(&full_remote_thunk->internal_thunk);
+ intercepted_code.call_ptr_edx = kJmpEdx;
+ bytes_to_write = kMinServiceSize;
+
+ if (relative_jump_) {
+ intercepted_code.mov_eax = kJmp32;
+ intercepted_code.service_id = relative_jump_;
+ bytes_to_write = offsetof(ServiceEntry, mov_edx);
+ }
+
+ // setup the thunk
+ SetInternalThunk(&full_local_thunk->internal_thunk, GetInternalThunkSize(),
+ remote_thunk, interceptor_);
+
+ size_t thunk_size = GetThunkSize();
+
+ // copy the local thunk buffer to the child
+ SIZE_T written;
+ if (!::WriteProcessMemory(process_, remote_thunk, local_thunk, thunk_size,
+ &written)) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ if (thunk_size != written)
+ return STATUS_UNSUCCESSFUL;
+
+ // and now change the function to intercept, on the child
+ if (ntdll_base_) {
+ // running a unit test
+ if (!::WriteProcessMemory(process_, target_, &intercepted_code,
+ bytes_to_write, &written))
+ return STATUS_UNSUCCESSFUL;
+ } else {
+ if (!WriteProtectedChildMemory(process_, target_, &intercepted_code,
+ bytes_to_write))
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk,
+ void* remote_thunk) {
+ ServiceEntry function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kJmp32 == function_code.mov_eax) {
+ // Plain old entry point patch. The relative jump address follows it.
+ ULONG relative = function_code.service_id;
+
+ // First, fix our copy of their patch.
+ relative += bit_cast<ULONG>(target_) - bit_cast<ULONG>(remote_thunk);
+
+ function_code.service_id = relative;
+
+ // And now, remember how to re-patch it.
+ ServiceFullThunk* full_thunk =
+ reinterpret_cast<ServiceFullThunk*>(remote_thunk);
+
+ const ULONG kJmp32Size = 5;
+
+ relative_jump_ = bit_cast<ULONG>(&full_thunk->internal_thunk) -
+ bit_cast<ULONG>(target_) - kJmp32Size;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ Wow64Entry function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx ||
+ kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 ||
+ kCallFs2 != function_code.call_fs2 ||
+ kCallFs3 != function_code.call_fs3) {
+ return false;
+ }
+
+ if ((kAddEsp1 == function_code.add_esp1 &&
+ kAddEsp2 == function_code.add_esp2 && kRet == function_code.ret) ||
+ kRet == function_code.add_esp1) {
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+ return true;
+ }
+
+ return false;
+}
+
+bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ Wow64EntryW8 function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 ||
+ kCallFs2 != function_code.call_fs2 ||
+ kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) {
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+ return true;
+}
+
+bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceEntryW8 function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip ||
+ function_code.call_offset != 3 || kRet != function_code.ret_p ||
+ kMovEdxEsp != function_code.mov_edx_esp ||
+ kSysenter != function_code.sysenter || kRet2 != function_code.ret) {
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+bool Wow64W10ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ Wow64EntryW10 function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read)) {
+ return false;
+ }
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (kMovEax != function_code.mov_eax || kMovEdx != function_code.mov_edx ||
+ kCallEdx != function_code.call_edx || kRet != function_code.ret) {
+ return false;
+ }
+
+ // Save the verified code
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc
new file mode 100644
index 0000000000..f692d0ea4f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_64.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+#if defined(_M_X64)
+#pragma pack(push, 1)
+
+const ULONG kMmovR10EcxMovEax = 0xB8D18B4C;
+const USHORT kSyscall = 0x050F;
+const BYTE kRetNp = 0xC3;
+const ULONG64 kMov1 = 0x54894808244C8948;
+const ULONG64 kMov2 = 0x4C182444894C1024;
+const ULONG kMov3 = 0x20244C89;
+const USHORT kTestByte = 0x04F6;
+const BYTE kPtr = 0x25;
+const BYTE kRet = 0xC3;
+const USHORT kJne = 0x0375;
+
+// Service code for 64 bit systems.
+struct ServiceEntry {
+ // This struct contains roughly the following code:
+ // 00 mov r10,rcx
+ // 03 mov eax,52h
+ // 08 syscall
+ // 0a ret
+ // 0b xchg ax,ax
+ // 0e xchg ax,ax
+
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ BYTE pad; // = 66
+ USHORT xchg_ax_ax1; // = 66 90
+ USHORT xchg_ax_ax2; // = 66 90
+};
+
+// Service code for 64 bit Windows 8.
+struct ServiceEntryW8 {
+ // This struct contains the following code:
+ // 00 48894c2408 mov [rsp+8], rcx
+ // 05 4889542410 mov [rsp+10], rdx
+ // 0a 4c89442418 mov [rsp+18], r8
+ // 0f 4c894c2420 mov [rsp+20], r9
+ // 14 4c8bd1 mov r10,rcx
+ // 17 b825000000 mov eax,25h
+ // 1c 0f05 syscall
+ // 1e c3 ret
+ // 1f 90 nop
+
+ ULONG64 mov_1; // = 48 89 4C 24 08 48 89 54
+ ULONG64 mov_2; // = 24 10 4C 89 44 24 18 4C
+ ULONG mov_3; // = 89 4C 24 20
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ BYTE nop; // = 90
+};
+
+// Service code for 64 bit systems with int 2e fallback.
+struct ServiceEntryWithInt2E {
+ // This struct contains roughly the following code:
+ // 00 4c8bd1 mov r10,rcx
+ // 03 b855000000 mov eax,52h
+ // 08 f604250803fe7f01 test byte ptr SharedUserData!308, 1
+ // 10 7503 jne [over syscall]
+ // 12 0f05 syscall
+ // 14 c3 ret
+ // 15 cd2e int 2e
+ // 17 c3 ret
+
+ ULONG mov_r10_rcx_mov_eax; // = 4C 8B D1 B8
+ ULONG service_id;
+ USHORT test_byte; // = F6 04
+ BYTE ptr; // = 25
+ ULONG user_shared_data_ptr;
+ BYTE one; // = 01
+ USHORT jne_over_syscall; // = 75 03
+ USHORT syscall; // = 0F 05
+ BYTE ret; // = C3
+ USHORT int2e; // = CD 2E
+ BYTE ret2; // = C3
+};
+
+// We don't have an internal thunk for x64.
+struct ServiceFullThunk {
+ union {
+ ServiceEntry original;
+ ServiceEntryW8 original_w8;
+ ServiceEntryWithInt2E original_int2e_fallback;
+ };
+};
+
+#pragma pack(pop)
+
+bool IsService(const void* source) {
+ const ServiceEntry* service = reinterpret_cast<const ServiceEntry*>(source);
+
+ return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax &&
+ kSyscall == service->syscall && kRetNp == service->ret);
+}
+
+bool IsServiceW8(const void* source) {
+ const ServiceEntryW8* service =
+ reinterpret_cast<const ServiceEntryW8*>(source);
+
+ return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax &&
+ kMov1 == service->mov_1 && kMov2 == service->mov_2 &&
+ kMov3 == service->mov_3);
+}
+
+bool IsServiceWithInt2E(const void* source) {
+ const ServiceEntryWithInt2E* service =
+ reinterpret_cast<const ServiceEntryWithInt2E*>(source);
+
+ return (kMmovR10EcxMovEax == service->mov_r10_rcx_mov_eax &&
+ kTestByte == service->test_byte && kPtr == service->ptr &&
+ kJne == service->jne_over_syscall && kSyscall == service->syscall &&
+ kRet == service->ret && kRet == service->ret2);
+}
+
+bool IsAnyService(const void* source) {
+ return IsService(source) || IsServiceW8(source) || IsServiceWithInt2E(source);
+}
+
+#elif defined(_M_ARM64)
+#pragma pack(push, 4)
+
+const ULONG kSvc = 0xD4000001;
+const ULONG kRetNp = 0xD65F03C0;
+const ULONG kServiceIdMask = 0x001FFFE0;
+
+struct ServiceEntry {
+ ULONG svc;
+ ULONG ret;
+ ULONG64 unused;
+};
+
+struct ServiceFullThunk {
+ ServiceEntry original;
+};
+
+#pragma pack(pop)
+
+bool IsService(const void* source) {
+ const ServiceEntry* service = reinterpret_cast<const ServiceEntry*>(source);
+
+ return (kSvc == (service->svc & ~kServiceIdMask) && kRetNp == service->ret &&
+ 0 == service->unused);
+}
+
+bool IsAnyService(const void* source) {
+ return IsService(source);
+}
+
+#else
+#error "Unsupported Windows 64-bit Arch"
+#endif
+
+} // namespace
+
+namespace sandbox {
+
+NTSTATUS ServiceResolverThunk::Setup(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret =
+ Init(target_module, interceptor_module, target_name, interceptor_name,
+ interceptor_entry_point, thunk_storage, storage_bytes);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ std::unique_ptr<char[]> thunk_buffer(new char[thunk_bytes]);
+ ServiceFullThunk* thunk =
+ reinterpret_cast<ServiceFullThunk*>(thunk_buffer.get());
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_OBJECT_NAME_COLLISION;
+
+ ret = PerformPatch(thunk, thunk_storage);
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+size_t ServiceResolverThunk::GetThunkSize() const {
+ return sizeof(ServiceFullThunk);
+}
+
+NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module,
+ const char* target_name,
+ BYTE* thunk_storage,
+ size_t storage_bytes,
+ size_t* storage_used) {
+ NTSTATUS ret = ResolveTarget(target_module, target_name, &target_);
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ size_t thunk_bytes = GetThunkSize();
+ if (storage_bytes < thunk_bytes)
+ return STATUS_UNSUCCESSFUL;
+
+ ServiceFullThunk* thunk = reinterpret_cast<ServiceFullThunk*>(thunk_storage);
+
+ if (!IsFunctionAService(&thunk->original))
+ return STATUS_OBJECT_NAME_COLLISION;
+
+ if (storage_used)
+ *storage_used = thunk_bytes;
+
+ return ret;
+}
+
+bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const {
+ ServiceFullThunk function_code;
+ SIZE_T read;
+ if (!::ReadProcessMemory(process_, target_, &function_code,
+ sizeof(function_code), &read))
+ return false;
+
+ if (sizeof(function_code) != read)
+ return false;
+
+ if (!IsAnyService(&function_code))
+ return false;
+
+ // Save the verified code.
+ memcpy(local_thunk, &function_code, sizeof(function_code));
+
+ return true;
+}
+
+NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk,
+ void* remote_thunk) {
+ // Patch the original code.
+ ServiceEntry local_service;
+ DCHECK_NT(GetInternalThunkSize() <= sizeof(local_service));
+ if (!SetInternalThunk(&local_service, sizeof(local_service), nullptr,
+ interceptor_))
+ return STATUS_UNSUCCESSFUL;
+
+ // Copy the local thunk buffer to the child.
+ SIZE_T actual;
+ if (!::WriteProcessMemory(process_, remote_thunk, local_thunk,
+ sizeof(ServiceFullThunk), &actual))
+ return STATUS_UNSUCCESSFUL;
+
+ if (sizeof(ServiceFullThunk) != actual)
+ return STATUS_UNSUCCESSFUL;
+
+ // And now change the function to intercept, on the child.
+ if (ntdll_base_) {
+ // Running a unit test.
+ if (!::WriteProcessMemory(process_, target_, &local_service,
+ sizeof(local_service), &actual))
+ return STATUS_UNSUCCESSFUL;
+ } else {
+ if (!WriteProtectedChildMemory(process_, target_, &local_service,
+ sizeof(local_service)))
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const {
+ NOTREACHED_NT();
+ return false;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc
new file mode 100644
index 0000000000..63ea26dcbf
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/service_resolver_unittest.cc
@@ -0,0 +1,278 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains unit tests for ServiceResolverThunk.
+
+#include "sandbox/win/src/service_resolver.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "base/bit_cast.h"
+#include "base/macros.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/resolver.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class ResolverThunkTest {
+ public:
+ virtual ~ResolverThunkTest() {}
+
+ virtual sandbox::ServiceResolverThunk* resolver() = 0;
+
+ // Sets the interception target to the desired address.
+ void set_target(void* target) { fake_target_ = target; }
+
+ protected:
+ // Holds the address of the fake target.
+ void* fake_target_;
+};
+
+// This is the concrete resolver used to perform service-call type functions
+// inside ntdll.dll.
+template <typename T>
+class ResolverThunkTestImpl : public T, public ResolverThunkTest {
+ public:
+ // The service resolver needs a child process to write to.
+ explicit ResolverThunkTestImpl(bool relaxed)
+ : T(::GetCurrentProcess(), relaxed) {}
+
+ sandbox::ServiceResolverThunk* resolver() { return this; }
+
+ protected:
+ // Overrides Resolver::Init
+ virtual NTSTATUS Init(const void* target_module,
+ const void* interceptor_module,
+ const char* target_name,
+ const char* interceptor_name,
+ const void* interceptor_entry_point,
+ void* thunk_storage,
+ size_t storage_bytes) {
+ NTSTATUS ret = STATUS_SUCCESS;
+ ret = T::Init(target_module, interceptor_module, target_name,
+ interceptor_name, interceptor_entry_point, thunk_storage,
+ storage_bytes);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+
+ this->target_ = fake_target_;
+
+ return ret;
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ResolverThunkTestImpl);
+};
+
+typedef ResolverThunkTestImpl<sandbox::ServiceResolverThunk> WinXpResolverTest;
+
+#if !defined(_WIN64)
+typedef ResolverThunkTestImpl<sandbox::Win8ResolverThunk> Win8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64ResolverThunk> Wow64ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W8ResolverThunk>
+ Wow64W8ResolverTest;
+typedef ResolverThunkTestImpl<sandbox::Wow64W10ResolverThunk>
+ Wow64W10ResolverTest;
+#endif
+
+const BYTE kJump32 = 0xE9;
+
+void CheckJump(void* source, void* target) {
+#pragma pack(push)
+#pragma pack(1)
+ struct Code {
+ BYTE jump;
+ ULONG delta;
+ };
+#pragma pack(pop)
+
+#if defined(_WIN64)
+ FAIL() << "Running 32-bit codepath";
+#else
+ Code* patched = reinterpret_cast<Code*>(source);
+ EXPECT_EQ(kJump32, patched->jump);
+
+ ULONG source_addr = bit_cast<ULONG>(source);
+ ULONG target_addr = bit_cast<ULONG>(target);
+ EXPECT_EQ(target_addr + 19 - source_addr, patched->delta);
+#endif
+}
+
+NTSTATUS PatchNtdllWithResolver(const char* function,
+ bool relaxed,
+ ResolverThunkTest* thunk_test) {
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ EXPECT_TRUE(ntdll_base);
+
+ void* target =
+ reinterpret_cast<void*>(::GetProcAddress(ntdll_base, function));
+ EXPECT_TRUE(target);
+ if (!target)
+ return STATUS_UNSUCCESSFUL;
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+
+ thunk_test->set_target(service);
+
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ std::unique_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ resolver->AllowLocalPatches();
+
+ NTSTATUS ret =
+ resolver->Setup(ntdll_base, nullptr, function, nullptr, function_entry,
+ thunk.get(), thunk_size, &used);
+ if (NT_SUCCESS(ret)) {
+ EXPECT_EQ(thunk_size, used);
+ EXPECT_NE(0, memcmp(service, target, sizeof(service)));
+ EXPECT_NE(kJump32, service[0]);
+
+ if (relaxed) {
+ // It's already patched, let's patch again, and simulate a direct patch.
+ service[0] = kJump32;
+ ret = resolver->Setup(ntdll_base, nullptr, function, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ CheckJump(service, thunk.get());
+ }
+ }
+
+ return ret;
+}
+
+std::unique_ptr<ResolverThunkTest> GetTestResolver(bool relaxed) {
+#if defined(_WIN64)
+ return std::make_unique<WinXpResolverTest>(relaxed);
+#else
+ base::win::OSInfo* os_info = base::win::OSInfo::GetInstance();
+ if (os_info->wow64_status() == base::win::OSInfo::WOW64_ENABLED) {
+ if (os_info->version() >= base::win::Version::WIN10)
+ return std::make_unique<Wow64W10ResolverTest>(relaxed);
+ if (os_info->version() >= base::win::Version::WIN8)
+ return std::make_unique<Wow64W8ResolverTest>(relaxed);
+ return std::make_unique<Wow64ResolverTest>(relaxed);
+ }
+
+ if (os_info->version() >= base::win::Version::WIN8)
+ return std::make_unique<Win8ResolverTest>(relaxed);
+
+ return std::make_unique<WinXpResolverTest>(relaxed);
+#endif
+}
+
+NTSTATUS PatchNtdll(const char* function, bool relaxed) {
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(relaxed);
+ return PatchNtdllWithResolver(function, relaxed, thunk_test.get());
+}
+
+TEST(ServiceResolverTest, PatchesServices) {
+ NTSTATUS ret = PatchNtdll("NtClose", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", false);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+}
+
+TEST(ServiceResolverTest, FailsIfNotService) {
+#if !defined(_WIN64)
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("RtlUlongByteSwap", false));
+#endif
+
+ EXPECT_NE(STATUS_SUCCESS, PatchNtdll("LdrLoadDll", false));
+}
+
+TEST(ServiceResolverTest, PatchesPatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ NTSTATUS ret = PatchNtdll("NtClose", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateFile", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtCreateMutant", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdll("NtMapViewOfSection", true);
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+#endif
+}
+
+TEST(ServiceResolverTest, MultiplePatchedServices) {
+// We don't support "relaxed mode" for Win64 apps.
+#if !defined(_WIN64)
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
+ NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateFile", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateFile, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtCreateMutant", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtCreateMutant, last error: " << ::GetLastError();
+
+ ret = PatchNtdllWithResolver("NtMapViewOfSection", true, thunk_test.get());
+ EXPECT_EQ(STATUS_SUCCESS, ret)
+ << "NtMapViewOfSection, last error: " << ::GetLastError();
+#endif
+}
+
+TEST(ServiceResolverTest, LocalPatchesAllowed) {
+ std::unique_ptr<ResolverThunkTest> thunk_test = GetTestResolver(true);
+
+ HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll");
+ ASSERT_TRUE(ntdll_base);
+
+ const char kFunctionName[] = "NtClose";
+
+ void* target =
+ reinterpret_cast<void*>(::GetProcAddress(ntdll_base, kFunctionName));
+ ASSERT_TRUE(target);
+
+ BYTE service[50];
+ memcpy(service, target, sizeof(service));
+ thunk_test->set_target(service);
+
+ sandbox::ServiceResolverThunk* resolver = thunk_test->resolver();
+ // Any pointer will do as an interception_entry_point
+ void* function_entry = resolver;
+ size_t thunk_size = resolver->GetThunkSize();
+ std::unique_ptr<char[]> thunk(new char[thunk_size]);
+ size_t used;
+
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+
+ // First try patching without having allowed local patches.
+ ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ EXPECT_FALSE(NT_SUCCESS(ret));
+
+ // Now allow local patches and check that things work.
+ resolver->AllowLocalPatches();
+ ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr,
+ function_entry, thunk.get(), thunk_size, &used);
+ EXPECT_EQ(STATUS_SUCCESS, ret);
+}
+
+} // namespace
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc
new file mode 100644
index 0000000000..3ddea76350
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.cc
@@ -0,0 +1,193 @@
+// Copyright (c) 2006-2008 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/sharedmem_ipc_client.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_types.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+namespace {
+
+DWORD SignalObjectAndWaitWrapper(HANDLE object_to_signal,
+ HANDLE object_to_wait_on,
+ DWORD millis,
+ BOOL alertable) {
+ // Not running in a sandboxed process so can call directly.
+ if (!g_nt.SignalAndWaitForSingleObject)
+ return SignalObjectAndWait(object_to_signal, object_to_wait_on, millis,
+ alertable);
+ // Don't support alertable.
+ CHECK_NT(!alertable);
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = millis * -10000LL;
+ NTSTATUS status = g_nt.SignalAndWaitForSingleObject(
+ object_to_signal, object_to_wait_on, alertable,
+ millis == INFINITE ? nullptr : &timeout);
+ if (!NT_SUCCESS(status))
+ return WAIT_FAILED;
+ return status;
+}
+
+DWORD WaitForSingleObjectWrapper(HANDLE handle, DWORD millis) {
+ // Not running in a sandboxed process so can call directly.
+ if (!g_nt.WaitForSingleObject)
+ return WaitForSingleObject(handle, millis);
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = millis * -10000LL;
+ NTSTATUS status = g_nt.WaitForSingleObject(
+ handle, FALSE, millis == INFINITE ? nullptr : &timeout);
+ if (!NT_SUCCESS(status))
+ return WAIT_FAILED;
+ return status;
+}
+
+} // namespace
+
+// Get the base of the data buffer of the channel; this is where the input
+// parameters get serialized. Since they get serialized directly into the
+// channel we avoid one copy.
+void* SharedMemIPCClient::GetBuffer() {
+ bool failure = false;
+ size_t ix = LockFreeChannel(&failure);
+ if (failure)
+ return nullptr;
+ return reinterpret_cast<char*>(control_) +
+ control_->channels[ix].channel_base;
+}
+
+// If we need to cancel an IPC before issuing DoCall
+// our client should call FreeBuffer with the same pointer
+// returned by GetBuffer.
+void SharedMemIPCClient::FreeBuffer(void* buffer) {
+ size_t num = ChannelIndexFromBuffer(buffer);
+ ChannelControl* channel = control_->channels;
+ LONG result = ::InterlockedExchange(&channel[num].state, kFreeChannel);
+ DCHECK_NE(kFreeChannel, static_cast<ChannelState>(result));
+}
+
+// The constructor simply casts the shared memory to the internal
+// structures. This is a cheap step that is why this IPC object can
+// and should be constructed per call.
+SharedMemIPCClient::SharedMemIPCClient(void* shared_mem)
+ : control_(reinterpret_cast<IPCControl*>(shared_mem)) {
+ first_base_ =
+ reinterpret_cast<char*>(shared_mem) + control_->channels[0].channel_base;
+ // There must be at least one channel.
+ DCHECK(0 != control_->channels_count);
+}
+
+// Do the IPC. At this point the channel should have already been
+// filled with the serialized input parameters.
+// We follow the pattern explained in the header file.
+ResultCode SharedMemIPCClient::DoCall(CrossCallParams* params,
+ CrossCallReturn* answer) {
+ if (!control_->server_alive)
+ return SBOX_ERROR_CHANNEL_ERROR;
+
+ size_t num = ChannelIndexFromBuffer(params->GetBuffer());
+ ChannelControl* channel = control_->channels;
+ // Note that the IPC tag goes outside the buffer as well inside
+ // the buffer. This should enable the server to prioritize based on
+ // IPC tags without having to de-serialize the entire message.
+ channel[num].ipc_tag = params->GetTag();
+
+ // Wait for the server to service this IPC call. After kIPCWaitTimeOut1
+ // we check if the server_alive mutex was abandoned which will indicate
+ // that the server has died.
+
+ // While the atomic signaling and waiting is not a requirement, it
+ // is nice because we save a trip to kernel.
+ DWORD wait = SignalObjectAndWaitWrapper(channel[num].ping_event,
+ channel[num].pong_event,
+ kIPCWaitTimeOut1, false);
+ if (WAIT_TIMEOUT == wait) {
+ // The server is taking too long. Enter a loop were we check if the
+ // server_alive mutex has been abandoned which would signal a server crash
+ // or else we keep waiting for a response.
+ while (true) {
+ wait = WaitForSingleObjectWrapper(control_->server_alive, 0);
+ if (WAIT_TIMEOUT == wait) {
+ // Server seems still alive. We already signaled so here we just wait.
+ wait = WaitForSingleObjectWrapper(channel[num].pong_event,
+ kIPCWaitTimeOut1);
+ if (WAIT_OBJECT_0 == wait) {
+ // The server took a long time but responded.
+ break;
+ } else if (WAIT_TIMEOUT == wait) {
+ continue;
+ } else {
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+ } else {
+ // The server has crashed and windows has signaled the mutex as
+ // abandoned.
+ ::InterlockedExchange(&channel[num].state, kAbandonedChannel);
+ control_->server_alive = 0;
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+ }
+ } else if (WAIT_OBJECT_0 != wait) {
+ // Probably the server crashed before the kIPCWaitTimeOut1 occurred.
+ return SBOX_ERROR_CHANNEL_ERROR;
+ }
+
+ // The server has returned an answer, copy it and free the channel.
+ memcpy_wrapper(answer, params->GetCallReturn(), sizeof(CrossCallReturn));
+
+ // Return the IPC state It can indicate that while the IPC has
+ // completed some error in the Broker has caused to not return valid
+ // results.
+ return answer->call_outcome;
+}
+
+// Locking a channel is a simple as looping over all the channels
+// looking for one that is has state = kFreeChannel and atomically
+// swapping it to kBusyChannel.
+// If there is no free channel, then we must back off so some other
+// thread makes progress and frees a channel. To back off we sleep.
+size_t SharedMemIPCClient::LockFreeChannel(bool* severe_failure) {
+ if (0 == control_->channels_count) {
+ *severe_failure = true;
+ return 0;
+ }
+ ChannelControl* channel = control_->channels;
+ do {
+ for (size_t ix = 0; ix != control_->channels_count; ++ix) {
+ if (kFreeChannel == ::InterlockedCompareExchange(
+ &channel[ix].state, kBusyChannel, kFreeChannel)) {
+ *severe_failure = false;
+ return ix;
+ }
+ }
+ // We did not find any available channel, maybe the server is dead.
+ DWORD wait =
+ WaitForSingleObjectWrapper(control_->server_alive, kIPCWaitTimeOut2);
+ if (WAIT_TIMEOUT != wait) {
+ // The server is dead and we outlive it enough to get in trouble.
+ *severe_failure = true;
+ return 0;
+ }
+ } while (true);
+}
+
+// Find out which channel we are from the pointer returned by GetBuffer.
+size_t SharedMemIPCClient::ChannelIndexFromBuffer(const void* buffer) {
+ ptrdiff_t d = reinterpret_cast<const char*>(buffer) - first_base_;
+ size_t num = d / kIPCChannelSize;
+ DCHECK_LT(num, control_->channels_count);
+ return (num);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h
new file mode 100644
index 0000000000..73f739dece
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_client.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2006-2008 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_SHAREDMEM_IPC_CLIENT_H__
+#define SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox.h"
+
+// IPC transport implementation that uses shared memory.
+// This is the client side
+//
+// The shared memory is divided on blocks called channels, and potentially
+// it can perform as many concurrent IPC calls as channels. The IPC over
+// each channel is strictly synchronous for the client.
+//
+// Each channel as a channel control section associated with. Each control
+// section has two kernel events (known as ping and pong) and a integer
+// variable that maintains a state
+//
+// this is the state diagram of a channel:
+//
+// locked in service
+// kFreeChannel---------->BusyChannel-------------->kAckChannel
+// ^ |
+// |_________________________________________________|
+// answer ready
+//
+// The protocol is as follows:
+// 1) client finds a free channel: state = kFreeChannel
+// 2) does an atomic compare-and-swap, now state = BusyChannel
+// 3) client writes the data into the channel buffer
+// 4) client signals the ping event and waits (blocks) on the pong event
+// 5) eventually the server signals the pong event
+// 6) the client awakes and reads the answer from the same channel
+// 7) the client updates its InOut parameters with the new data from the
+// shared memory section.
+// 8) the client atomically sets the state = kFreeChannel
+//
+// In the shared memory the layout is as follows:
+//
+// [ channel count ]
+// [ channel control 0]
+// [ channel control 1]
+// [ channel control N]
+// [ channel buffer 0 ] 1024 bytes
+// [ channel buffer 1 ] 1024 bytes
+// [ channel buffer N ] 1024 bytes
+//
+// By default each channel buffer is 1024 bytes
+namespace sandbox {
+
+// the possible channel states as described above
+enum ChannelState {
+ // channel is free
+ kFreeChannel = 1,
+ // IPC in progress client side
+ kBusyChannel,
+ // IPC in progress server side
+ kAckChannel,
+ // not used right now
+ kReadyChannel,
+ // IPC abandoned by client side
+ kAbandonedChannel
+};
+
+// The next two constants control the time outs for the IPC.
+const DWORD kIPCWaitTimeOut1 = 1000; // Milliseconds.
+const DWORD kIPCWaitTimeOut2 = 50; // Milliseconds.
+
+// the channel control structure
+struct ChannelControl {
+ // points to be beginning of the channel buffer, where data goes
+ size_t channel_base;
+ // maintains the state from the ChannelState enumeration
+ volatile LONG state;
+ // the ping event is signaled by the client when the IPC data is ready on
+ // the buffer
+ HANDLE ping_event;
+ // the client waits on the pong event for the IPC answer back
+ HANDLE pong_event;
+ // the IPC unique identifier
+ IpcTag ipc_tag;
+};
+
+struct IPCControl {
+ // total number of channels available, some might be busy at a given time
+ size_t channels_count;
+ // handle to a shared mutex to detect when the server is dead
+ HANDLE server_alive;
+ // array of channel control structures
+ ChannelControl channels[1];
+};
+
+// the actual shared memory IPC implementation class. This object is designed
+// to be lightweight so it can be constructed on-site (at the calling place)
+// wherever an IPC call is needed.
+class SharedMemIPCClient {
+ public:
+ // Creates the IPC client.
+ // as parameter it takes the base address of the shared memory
+ explicit SharedMemIPCClient(void* shared_mem);
+
+ // locks a free channel and returns the channel buffer memory base. This call
+ // blocks until there is a free channel
+ void* GetBuffer();
+
+ // releases the lock on the channel, for other to use. call this if you have
+ // called GetBuffer and you want to abort but have not called yet DoCall()
+ void FreeBuffer(void* buffer);
+
+ // Performs the actual IPC call.
+ // params: The blob of packed input parameters.
+ // answer: upon IPC completion, it contains the server answer to the IPC.
+ // If the return value is not SBOX_ERROR_CHANNEL_ERROR, the caller has to free
+ // the channel.
+ // returns ALL_OK if the IPC mechanism successfully delivered. You still need
+ // to check on the answer structure to see the actual IPC result.
+ ResultCode DoCall(CrossCallParams* params, CrossCallReturn* answer);
+
+ private:
+ // Returns the index of the first free channel. It sets 'severe_failure'
+ // to true if there is an unrecoverable error that does not allow to
+ // find a channel.
+ size_t LockFreeChannel(bool* severe_failure);
+ // Return the channel index given the address of the buffer.
+ size_t ChannelIndexFromBuffer(const void* buffer);
+ IPCControl* control_;
+ // point to the first channel base
+ char* first_base_;
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SHAREDMEM_IPC_CLIENT_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc
new file mode 100644
index 0000000000..74e5c171e0
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.cc
@@ -0,0 +1,346 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_args.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+
+namespace {
+// This handle must not be closed.
+volatile HANDLE g_alive_mutex = nullptr;
+} // namespace
+
+namespace sandbox {
+
+SharedMemIPCServer::ServerControl::ServerControl() {}
+
+SharedMemIPCServer::ServerControl::~ServerControl() {}
+
+SharedMemIPCServer::SharedMemIPCServer(HANDLE target_process,
+ DWORD target_process_id,
+ ThreadProvider* thread_provider,
+ Dispatcher* dispatcher)
+ : client_control_(nullptr),
+ thread_provider_(thread_provider),
+ target_process_(target_process),
+ target_process_id_(target_process_id),
+ call_dispatcher_(dispatcher) {
+ // We create a initially owned mutex. If the server dies unexpectedly,
+ // the thread that owns it will fail to release the lock and windows will
+ // report to the target (when it tries to acquire it) that the wait was
+ // abandoned. Note: We purposely leak the local handle because we want it to
+ // be closed by Windows itself so it is properly marked as abandoned if the
+ // server dies.
+ if (!g_alive_mutex) {
+ HANDLE mutex = ::CreateMutexW(nullptr, true, nullptr);
+ if (::InterlockedCompareExchangePointer(&g_alive_mutex, mutex, nullptr)) {
+ // We lost the race to create the mutex.
+ ::CloseHandle(mutex);
+ }
+ }
+}
+
+SharedMemIPCServer::~SharedMemIPCServer() {
+ // Free the wait handles associated with the thread pool.
+ if (!thread_provider_->UnRegisterWaits(this)) {
+ // Better to leak than to crash.
+ return;
+ }
+ server_contexts_.clear();
+
+ if (client_control_)
+ ::UnmapViewOfFile(client_control_);
+}
+
+bool SharedMemIPCServer::Init(void* shared_mem,
+ uint32_t shared_size,
+ uint32_t channel_size) {
+ // The shared memory needs to be at least as big as a channel.
+ if (shared_size < channel_size) {
+ return false;
+ }
+ // The channel size should be aligned.
+ if (0 != (channel_size % 32)) {
+ return false;
+ }
+
+ // Calculate how many channels we can fit in the shared memory.
+ shared_size -= offsetof(IPCControl, channels);
+ size_t channel_count = shared_size / (sizeof(ChannelControl) + channel_size);
+
+ // If we cannot fit even one channel we bail out.
+ if (0 == channel_count) {
+ return false;
+ }
+ // Calculate the start of the first channel.
+ size_t base_start =
+ (sizeof(ChannelControl) * channel_count) + offsetof(IPCControl, channels);
+
+ client_control_ = reinterpret_cast<IPCControl*>(shared_mem);
+ client_control_->channels_count = 0;
+
+ // This is the initialization that we do per-channel. Basically:
+ // 1) make two events (ping & pong)
+ // 2) create handles to the events for the client and the server.
+ // 3) initialize the channel (client_context) with the state.
+ // 4) initialize the server side of the channel (service_context).
+ // 5) call the thread provider RegisterWait to register the ping events.
+ for (size_t ix = 0; ix != channel_count; ++ix) {
+ ChannelControl* client_context = &client_control_->channels[ix];
+ ServerControl* service_context = new ServerControl;
+ server_contexts_.push_back(base::WrapUnique(service_context));
+
+ if (!MakeEvents(&service_context->ping_event, &service_context->pong_event,
+ &client_context->ping_event, &client_context->pong_event)) {
+ return false;
+ }
+
+ client_context->channel_base = base_start;
+ client_context->state = kFreeChannel;
+
+ // Note that some of these values are available as members of this object
+ // but we put them again into the service_context because we will be called
+ // on a static method (ThreadPingEventReady). In particular, target_process_
+ // is a raw handle that is not owned by this object (it's owned by the
+ // owner of this object), and we are storing it in multiple places.
+ service_context->shared_base = reinterpret_cast<char*>(shared_mem);
+ service_context->channel_size = channel_size;
+ service_context->channel = client_context;
+ service_context->channel_buffer =
+ service_context->shared_base + client_context->channel_base;
+ service_context->dispatcher = call_dispatcher_;
+ service_context->target_info.process = target_process_;
+ service_context->target_info.process_id = target_process_id_;
+ // Advance to the next channel.
+ base_start += channel_size;
+ // Register the ping event with the threadpool.
+ thread_provider_->RegisterWait(this, service_context->ping_event.Get(),
+ ThreadPingEventReady, service_context);
+ }
+ if (!::DuplicateHandle(::GetCurrentProcess(), g_alive_mutex, target_process_,
+ &client_control_->server_alive,
+ SYNCHRONIZE | EVENT_MODIFY_STATE, false, 0)) {
+ return false;
+ }
+ // This last setting indicates to the client all is setup.
+ client_control_->channels_count = channel_count;
+ return true;
+}
+
+bool SharedMemIPCServer::InvokeCallback(const ServerControl* service_context,
+ void* ipc_buffer,
+ CrossCallReturn* call_result) {
+ // Set the default error code;
+ SetCallError(SBOX_ERROR_INVALID_IPC, call_result);
+ uint32_t output_size = 0;
+ // Parse, verify and copy the message. The handler operates on a copy
+ // of the message so the client cannot play dirty tricks by changing the
+ // data in the channel while the IPC is being processed.
+ std::unique_ptr<CrossCallParamsEx> params(CrossCallParamsEx::CreateFromBuffer(
+ ipc_buffer, service_context->channel_size, &output_size));
+ if (!params.get())
+ return false;
+
+ IpcTag tag = params->GetTag();
+ static_assert(0 == INVALID_TYPE, "incorrect type enum");
+ IPCParams ipc_params = {tag};
+
+ void* args[kMaxIpcParams];
+ if (!GetArgs(params.get(), &ipc_params, args))
+ return false;
+
+ IPCInfo ipc_info = {tag};
+ ipc_info.client_info = &service_context->target_info;
+ Dispatcher* dispatcher = service_context->dispatcher;
+ DCHECK(dispatcher);
+ bool error = true;
+ Dispatcher* handler = nullptr;
+
+ Dispatcher::CallbackGeneric callback_generic;
+ handler = dispatcher->OnMessageReady(&ipc_params, &callback_generic);
+ if (handler) {
+ switch (params->GetParamsCount()) {
+ case 0: {
+ // Ask the IPC dispatcher if it can service this IPC.
+ Dispatcher::Callback0 callback =
+ reinterpret_cast<Dispatcher::Callback0>(callback_generic);
+ if (!(handler->*callback)(&ipc_info))
+ break;
+ error = false;
+ break;
+ }
+ case 1: {
+ Dispatcher::Callback1 callback =
+ reinterpret_cast<Dispatcher::Callback1>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0]))
+ break;
+ error = false;
+ break;
+ }
+ case 2: {
+ Dispatcher::Callback2 callback =
+ reinterpret_cast<Dispatcher::Callback2>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1]))
+ break;
+ error = false;
+ break;
+ }
+ case 3: {
+ Dispatcher::Callback3 callback =
+ reinterpret_cast<Dispatcher::Callback3>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2]))
+ break;
+ error = false;
+ break;
+ }
+ case 4: {
+ Dispatcher::Callback4 callback =
+ reinterpret_cast<Dispatcher::Callback4>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2],
+ args[3]))
+ break;
+ error = false;
+ break;
+ }
+ case 5: {
+ Dispatcher::Callback5 callback =
+ reinterpret_cast<Dispatcher::Callback5>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4]))
+ break;
+ error = false;
+ break;
+ }
+ case 6: {
+ Dispatcher::Callback6 callback =
+ reinterpret_cast<Dispatcher::Callback6>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5]))
+ break;
+ error = false;
+ break;
+ }
+ case 7: {
+ Dispatcher::Callback7 callback =
+ reinterpret_cast<Dispatcher::Callback7>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6]))
+ break;
+ error = false;
+ break;
+ }
+ case 8: {
+ Dispatcher::Callback8 callback =
+ reinterpret_cast<Dispatcher::Callback8>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7]))
+ break;
+ error = false;
+ break;
+ }
+ case 9: {
+ Dispatcher::Callback9 callback =
+ reinterpret_cast<Dispatcher::Callback9>(callback_generic);
+ if (!(handler->*callback)(&ipc_info, args[0], args[1], args[2], args[3],
+ args[4], args[5], args[6], args[7], args[8]))
+ break;
+ error = false;
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ }
+
+ if (error) {
+ if (handler)
+ SetCallError(SBOX_ERROR_FAILED_IPC, call_result);
+ } else {
+ memcpy(call_result, &ipc_info.return_info, sizeof(*call_result));
+ SetCallSuccess(call_result);
+ if (params->IsInOut()) {
+ // Maybe the params got changed by the broker. We need to upadte the
+ // memory section.
+ memcpy(ipc_buffer, params.get(), output_size);
+ }
+ }
+
+ ReleaseArgs(&ipc_params, args);
+
+ return !error;
+}
+
+// This function gets called by a thread from the thread pool when a
+// ping event fires. The context is the same as passed in the RegisterWait()
+// call above.
+void __stdcall SharedMemIPCServer::ThreadPingEventReady(void* context,
+ unsigned char) {
+ if (!context) {
+ DCHECK(false);
+ return;
+ }
+ ServerControl* service_context = reinterpret_cast<ServerControl*>(context);
+ // Since the event fired, the channel *must* be busy. Change to kAckChannel
+ // while we service it.
+ LONG last_state = ::InterlockedCompareExchange(
+ &service_context->channel->state, kAckChannel, kBusyChannel);
+ if (kBusyChannel != last_state) {
+ DCHECK(false);
+ return;
+ }
+
+ // Prepare the result structure. At this point we will return some result
+ // even if the IPC is invalid, malformed or has no handler.
+ CrossCallReturn call_result = {0};
+ void* buffer = service_context->channel_buffer;
+
+ InvokeCallback(service_context, buffer, &call_result);
+
+ // Copy the answer back into the channel and signal the pong event. This
+ // should wake up the client so it can finish the ipc cycle.
+ CrossCallParams* call_params = reinterpret_cast<CrossCallParams*>(buffer);
+ memcpy(call_params->GetCallReturn(), &call_result, sizeof(call_result));
+ ::InterlockedExchange(&service_context->channel->state, kAckChannel);
+ ::SetEvent(service_context->pong_event.Get());
+}
+
+bool SharedMemIPCServer::MakeEvents(base::win::ScopedHandle* server_ping,
+ base::win::ScopedHandle* server_pong,
+ HANDLE* client_ping,
+ HANDLE* client_pong) {
+ // Note that the IPC client has no right to delete the events. That would
+ // cause problems. The server *owns* the events.
+ const DWORD kDesiredAccess = SYNCHRONIZE | EVENT_MODIFY_STATE;
+
+ // The events are auto reset, and start not signaled.
+ server_ping->Set(::CreateEventW(nullptr, false, false, nullptr));
+ if (!::DuplicateHandle(::GetCurrentProcess(), server_ping->Get(),
+ target_process_, client_ping, kDesiredAccess, false,
+ 0)) {
+ return false;
+ }
+
+ server_pong->Set(::CreateEventW(nullptr, false, false, nullptr));
+ if (!::DuplicateHandle(::GetCurrentProcess(), server_pong->Get(),
+ target_process_, client_pong, kDesiredAccess, false,
+ 0)) {
+ return false;
+ }
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h
new file mode 100644
index 0000000000..fce52c6ad0
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sharedmem_ipc_server.h
@@ -0,0 +1,137 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
+#define SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
+
+#include <stdint.h>
+
+#include <list>
+#include <memory>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_params.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+
+// IPC transport implementation that uses shared memory.
+// This is the server side
+//
+// The server side has knowledge about the layout of the shared memory
+// and the state transitions. Both are explained in sharedmem_ipc_client.h
+//
+// As opposed to SharedMemIPClient, the Server object should be one for the
+// entire lifetime of the target process. The server is in charge of creating
+// the events (ping, pong) both for the client and for the target that are used
+// to signal the IPC and also in charge of setting the initial state of the
+// channels.
+//
+// When an IPC is ready, the server relies on being called by on the
+// ThreadPingEventReady callback. The IPC server then retrieves the buffer,
+// marshals it into a CrossCallParam object and calls the Dispatcher, who is in
+// charge of fulfilling the IPC request.
+namespace sandbox {
+
+// the shared memory implementation of the IPC server. There should be one
+// of these objects per target (IPC client) process
+class SharedMemIPCServer {
+ public:
+ // Creates the IPC server.
+ // target_process: handle to the target process. It must be suspended. It is
+ // unfortunate to receive a raw handle (and store it inside this object) as
+ // that dilutes ownership of the process, but in practice a SharedMemIPCServer
+ // is owned by TargetProcess, which calls this method, and owns the handle, so
+ // everything is safe. If that changes, we should break this dependency and
+ // duplicate the handle instead.
+ // target_process_id: process id of the target process.
+ // thread_provider: a thread provider object.
+ // dispatcher: an object that can service IPC calls.
+ SharedMemIPCServer(HANDLE target_process,
+ DWORD target_process_id,
+ ThreadProvider* thread_provider,
+ Dispatcher* dispatcher);
+
+ ~SharedMemIPCServer();
+
+ // Initializes the server structures, shared memory structures and
+ // creates the kernels events used to signal the IPC.
+ bool Init(void* shared_mem, uint32_t shared_size, uint32_t channel_size);
+
+ private:
+ // Allow tests to be marked DISABLED_. Note that FLAKY_ and FAILS_ prefixes
+ // do not work with sandbox tests.
+ FRIEND_TEST_ALL_PREFIXES(IPCTest, SharedMemServerTests);
+ // When an event fires (IPC request). A thread from the ThreadProvider
+ // will call this function. The context parameter should be the same as
+ // provided when ThreadProvider::RegisterWait was called.
+ static void __stdcall ThreadPingEventReady(void* context, unsigned char);
+
+ // Makes the client and server events. This function is called once
+ // per channel.
+ bool MakeEvents(base::win::ScopedHandle* server_ping,
+ base::win::ScopedHandle* server_pong,
+ HANDLE* client_ping,
+ HANDLE* client_pong);
+
+ // A copy this structure is maintained per channel.
+ // Note that a lot of the fields are just the same of what we have in the IPC
+ // object itself. It is better to have the copies since we can dispatch in the
+ // static method without worrying about converting back to a member function
+ // call or about threading issues.
+ struct ServerControl {
+ ServerControl();
+ ~ServerControl();
+
+ // This channel server ping event.
+ base::win::ScopedHandle ping_event;
+ // This channel server pong event.
+ base::win::ScopedHandle pong_event;
+ // The size of this channel.
+ uint32_t channel_size;
+ // The pointer to the actual channel data.
+ char* channel_buffer;
+ // The pointer to the base of the shared memory.
+ char* shared_base;
+ // A pointer to this channel's client-side control structure this structure
+ // lives in the shared memory.
+ ChannelControl* channel;
+ // the IPC dispatcher associated with this channel.
+ Dispatcher* dispatcher;
+ // The target process information associated with this channel.
+ ClientInfo target_info;
+ };
+
+ // Looks for the appropriate handler for this IPC and invokes it.
+ static bool InvokeCallback(const ServerControl* service_context,
+ void* ipc_buffer,
+ CrossCallReturn* call_result);
+
+ // Points to the shared memory channel control which lives at
+ // the start of the shared section.
+ IPCControl* client_control_;
+
+ // Keeps track of the server side objects that are used to answer an IPC.
+ std::list<std::unique_ptr<ServerControl>> server_contexts_;
+
+ // The thread provider provides the threads that call back into this object
+ // when the IPC events fire.
+ ThreadProvider* thread_provider_;
+
+ // The IPC object is associated with a target process.
+ HANDLE target_process_;
+
+ // The target process id associated with the IPC object.
+ DWORD target_process_id_;
+
+ // The dispatcher handles 'ready' IPC calls.
+ Dispatcher* call_dispatcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(SharedMemIPCServer);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SHAREDMEM_IPC_SERVER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sid.cc b/security/sandbox/chromium/sandbox/win/src/sid.cc
new file mode 100644
index 0000000000..a97ca340ed
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sid.cc
@@ -0,0 +1,163 @@
+// Copyright (c) 2006-2008 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/sid.h"
+
+#include <memory>
+
+#include <sddl.h>
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+DWORD WellKnownCapabilityToRid(WellKnownCapabilities capability) {
+ switch (capability) {
+ case kInternetClient:
+ return SECURITY_CAPABILITY_INTERNET_CLIENT;
+ case kInternetClientServer:
+ return SECURITY_CAPABILITY_INTERNET_CLIENT_SERVER;
+ case kPrivateNetworkClientServer:
+ 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:
+ break;
+ }
+ return 0;
+}
+
+} // namespace
+
+Sid::Sid() : sid_() {}
+
+Sid::Sid(PSID sid) : sid_() {
+ ::CopySid(SECURITY_MAX_SID_SIZE, sid_, sid);
+}
+
+Sid::Sid(const SID* sid) : sid_() {
+ ::CopySid(SECURITY_MAX_SID_SIZE, sid_, const_cast<SID*>(sid));
+}
+
+Sid::Sid(WELL_KNOWN_SID_TYPE type) {
+ DWORD size_sid = SECURITY_MAX_SID_SIZE;
+ bool result = ::CreateWellKnownSid(type, nullptr, sid_, &size_sid);
+ DCHECK(result);
+ (void)result;
+}
+
+Sid Sid::FromKnownCapability(WellKnownCapabilities 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) {
+ 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();
+
+ UNICODE_STRING name = {};
+ init_unicode_string(&name, capability_name);
+ Sid capability_sid;
+ Sid group_sid;
+
+ NTSTATUS status =
+ derive_capability_sids(&name, group_sid.sid_, capability_sid.sid_);
+ if (!NT_SUCCESS(status))
+ return Sid();
+
+ return capability_sid;
+}
+
+Sid Sid::FromSddlString(const wchar_t* sddl_sid) {
+ PSID converted_sid;
+ if (!::ConvertStringSidToSid(sddl_sid, &converted_sid))
+ return Sid();
+
+ return Sid(converted_sid);
+}
+
+Sid Sid::FromSubAuthorities(PSID_IDENTIFIER_AUTHORITY identifier_authority,
+ BYTE sub_authority_count,
+ PDWORD sub_authorities) {
+ Sid sid;
+ if (!::InitializeSid(sid.sid_, identifier_authority, sub_authority_count))
+ return Sid();
+
+ for (DWORD index = 0; index < sub_authority_count; ++index) {
+ PDWORD sub_authority = GetSidSubAuthority(sid.sid_, index);
+ *sub_authority = sub_authorities[index];
+ }
+ return sid;
+}
+
+Sid Sid::AllRestrictedApplicationPackages() {
+ SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_APP_PACKAGE_AUTHORITY};
+ DWORD sub_authorities[] = {SECURITY_APP_PACKAGE_BASE_RID,
+ SECURITY_BUILTIN_PACKAGE_ANY_RESTRICTED_PACKAGE};
+ return FromSubAuthorities(&package_authority, 2, sub_authorities);
+}
+
+Sid Sid::GenerateRandomSid() {
+ SID_IDENTIFIER_AUTHORITY package_authority = {SECURITY_NULL_SID_AUTHORITY};
+ DWORD sub_authorities[4] = {};
+ base::RandBytes(&sub_authorities, sizeof(sub_authorities));
+ return FromSubAuthorities(&package_authority, _countof(sub_authorities),
+ sub_authorities);
+}
+
+PSID Sid::GetPSID() const {
+ return const_cast<BYTE*>(sid_);
+}
+
+bool Sid::IsValid() const {
+ return !!::IsValidSid(GetPSID());
+}
+
+// Converts the SID to an SDDL format string.
+bool Sid::ToSddlString(std::wstring* sddl_string) const {
+ LPWSTR sid = nullptr;
+ if (!::ConvertSidToStringSid(GetPSID(), &sid))
+ return false;
+ std::unique_ptr<void, LocalFreeDeleter> sid_ptr(sid);
+ *sddl_string = sid;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sid.h b/security/sandbox/chromium/sandbox/win/src/sid.h
new file mode 100644
index 0000000000..745f471054
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sid.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2006-2008 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_SID_H_
+#define SANDBOX_SRC_SID_H_
+
+#include <windows.h>
+
+#include <string>
+
+namespace sandbox {
+
+// Known capabilities defined in Windows 8.
+enum WellKnownCapabilities {
+ kInternetClient,
+ kInternetClientServer,
+ kPrivateNetworkClientServer,
+ kPicturesLibrary,
+ kVideosLibrary,
+ kMusicLibrary,
+ kDocumentsLibrary,
+ kEnterpriseAuthentication,
+ kSharedUserCertificates,
+ kRemovableStorage,
+ kAppointments,
+ kContacts,
+ kMaxWellKnownCapability
+};
+
+// This class is used to hold and generate SIDS.
+class Sid {
+ public:
+ // As PSID is just a void* make it explicit.
+ explicit Sid(PSID sid);
+ // Constructors initializing the object with the SID passed.
+ // This is a converting constructor. It is not explicit.
+ Sid(const SID* sid);
+ Sid(WELL_KNOWN_SID_TYPE type);
+
+ // 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. 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);
+ // Create a Sid from a set of sub authorities.
+ static Sid FromSubAuthorities(PSID_IDENTIFIER_AUTHORITY identifier_authority,
+ BYTE sub_authority_count,
+ PDWORD sub_authorities);
+ // Create the restricted all application packages sid.
+ static Sid AllRestrictedApplicationPackages();
+ // Generate a random SID value.
+ static Sid GenerateRandomSid();
+
+ // Returns sid_.
+ PSID GetPSID() const;
+
+ // Gets whether the sid is valid.
+ bool IsValid() const;
+
+ // Converts the SID to a SDDL format string.
+ bool ToSddlString(std::wstring* sddl_string) const;
+
+ private:
+ Sid();
+ BYTE sid_[SECURITY_MAX_SID_SIZE];
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SID_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sid_unittest.cc b/security/sandbox/chromium/sandbox/win/src/sid_unittest.cc
new file mode 100644
index 0000000000..81186cd0be
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sid_unittest.cc
@@ -0,0 +1,182 @@
+// Copyright (c) 2006-2008 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.
+
+// This file contains unit tests for the sid class.
+
+#include "sandbox/win/src/sid.h"
+
+#include <sddl.h>
+
+#include "base/win/atl.h"
+#include "base/win/windows_version.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+bool EqualSid(const Sid& sid, const ATL::CSid& compare_sid) {
+ if (!sid.IsValid())
+ return false;
+ return !!::EqualSid(sid.GetPSID(), const_cast<SID*>(compare_sid.GetPSID()));
+}
+
+bool EqualSid(const Sid& sid, const wchar_t* sddl_sid) {
+ PSID compare_sid;
+ if (!sid.IsValid())
+ return false;
+ if (!::ConvertStringSidToSid(sddl_sid, &compare_sid))
+ return false;
+ bool equal = !!::EqualSid(sid.GetPSID(), compare_sid);
+ ::LocalFree(compare_sid);
+ return equal;
+}
+
+struct KnownCapabilityTestEntry {
+ WellKnownCapabilities capability;
+ const wchar_t* sddl_sid;
+};
+
+struct NamedCapabilityTestEntry {
+ const wchar_t* capability_name;
+ const wchar_t* sddl_sid;
+};
+
+} // namespace
+
+// Tests the creation of a Sid.
+TEST(SidTest, Constructors) {
+ ATL::CSid sid_world = ATL::Sids::World();
+ PSID sid_world_pointer = const_cast<SID*>(sid_world.GetPSID());
+
+ // Check the SID* constructor.
+ Sid sid_sid_star(sid_world_pointer);
+ ASSERT_TRUE(EqualSid(sid_sid_star, sid_world));
+
+ // Check the copy constructor.
+ Sid sid_copy(sid_sid_star);
+ ASSERT_TRUE(EqualSid(sid_copy, sid_world));
+
+ Sid sid_sddl = Sid::FromSddlString(L"S-1-1-0");
+ ASSERT_TRUE(sid_sddl.IsValid());
+ ASSERT_TRUE(EqualSid(sid_sddl, sid_world));
+
+ Sid sid_sddl_invalid = Sid::FromSddlString(L"X-1-1-0");
+ ASSERT_FALSE(sid_sddl_invalid.IsValid());
+
+ Sid sid_sddl_empty = Sid::FromSddlString(L"");
+ ASSERT_FALSE(sid_sddl_empty.IsValid());
+
+ // Note that the WELL_KNOWN_SID_TYPE constructor is tested in the GetPSID
+ // test. AppContainer related constructors are tested in AppContainer.
+}
+
+// Tests the method GetPSID
+TEST(SidTest, GetPSID) {
+ // Check for non-null result;
+ ASSERT_NE(nullptr, Sid(::WinLocalSid).GetPSID());
+ ASSERT_NE(nullptr, Sid(::WinCreatorOwnerSid).GetPSID());
+ ASSERT_NE(nullptr, Sid(::WinBatchSid).GetPSID());
+
+ ASSERT_TRUE(EqualSid(Sid(::WinNullSid), ATL::Sids::Null()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinWorldSid), ATL::Sids::World()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinDialupSid), ATL::Sids::Dialup()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinNetworkSid), ATL::Sids::Network()));
+
+ ASSERT_TRUE(
+ EqualSid(Sid(::WinBuiltinAdministratorsSid), ATL::Sids::Admins()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinBuiltinUsersSid), ATL::Sids::Users()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinBuiltinGuestsSid), ATL::Sids::Guests()));
+
+ ASSERT_TRUE(EqualSid(Sid(::WinProxySid), ATL::Sids::Proxy()));
+}
+
+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"},
+ };
+
+ 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);
+ EXPECT_FALSE(sid_nullptr.IsValid());
+
+ Sid sid_empty = Sid::FromNamedCapability(L"");
+ EXPECT_FALSE(sid_empty.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;
+ }
+}
+
+TEST(SidTest, Sddl) {
+ Sid sid_sddl = Sid::FromSddlString(L"S-1-1-0");
+ ASSERT_TRUE(sid_sddl.IsValid());
+ std::wstring sddl_str;
+ ASSERT_TRUE(sid_sddl.ToSddlString(&sddl_str));
+ ASSERT_EQ(L"S-1-1-0", sddl_str);
+}
+
+TEST(SidTest, SubAuthorities) {
+ DWORD world_subauthorities[] = {0};
+ SID_IDENTIFIER_AUTHORITY world_authority = {SECURITY_WORLD_SID_AUTHORITY};
+ Sid sid_world =
+ Sid::FromSubAuthorities(&world_authority, 1, world_subauthorities);
+ ASSERT_TRUE(EqualSid(sid_world, ATL::Sids::World()));
+ ASSERT_TRUE(Sid::FromSubAuthorities(&world_authority, 0, nullptr).IsValid());
+
+ DWORD admin_subauthorities[] = {32, 544};
+ SID_IDENTIFIER_AUTHORITY nt_authority = {SECURITY_NT_AUTHORITY};
+ Sid sid_admin =
+ Sid::FromSubAuthorities(&nt_authority, 2, admin_subauthorities);
+ ASSERT_TRUE(EqualSid(sid_admin, ATL::Sids::Admins()));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc
new file mode 100644
index 0000000000..49e7cbe946
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.cc
@@ -0,0 +1,68 @@
+// Copyright 2019 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/signed_dispatcher.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/strings/string_util.h"
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/signed_interception.h"
+#include "sandbox/win/src/signed_policy.h"
+
+namespace sandbox {
+
+SignedDispatcher::SignedDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::NTCREATESECTION, {VOIDPTR_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&SignedDispatcher::CreateSection)};
+
+ ipc_calls_.push_back(create_params);
+}
+
+bool SignedDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (service == IpcTag::NTCREATESECTION)
+ return INTERCEPT_NT(manager, NtCreateSection, CREATE_SECTION_ID, 32);
+ return false;
+}
+
+bool SignedDispatcher::CreateSection(IPCInfo* ipc, HANDLE file_handle) {
+ // Duplicate input handle from target to broker.
+ HANDLE local_file_handle = nullptr;
+ if (!::DuplicateHandle((*ipc->client_info).process, file_handle,
+ ::GetCurrentProcess(), &local_file_handle,
+ FILE_MAP_EXECUTE, false, 0)) {
+ return false;
+ }
+
+ base::win::ScopedHandle local_handle(local_file_handle);
+ std::wstring path;
+ if (!GetPathFromHandle(local_handle.Get(), &path))
+ return false;
+ const wchar_t* module_name = path.c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(module_name);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::NTCREATESECTION, params.GetBase());
+
+ // Return operation status on the IPC.
+ HANDLE section_handle = nullptr;
+ ipc->return_info.nt_status = SignedPolicy::CreateSectionAction(
+ result, *ipc->client_info, local_handle, &section_handle);
+ ipc->return_info.handle = section_handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h
new file mode 100644
index 0000000000..38435481f6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_dispatcher.h
@@ -0,0 +1,37 @@
+// Copyright 2019 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_WIN_SRC_SIGNED_DISPATCHER_H_
+#define SANDBOX_WIN_SRC_SIGNED_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles signed-binary related IPC calls.
+class SignedDispatcher : public Dispatcher {
+ public:
+ explicit SignedDispatcher(PolicyBase* policy_base);
+ ~SignedDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to CreateSection in the target.
+ bool CreateSection(IPCInfo* ipc, HANDLE file_handle);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(SignedDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SIGNED_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_interception.cc b/security/sandbox/chromium/sandbox/win/src/signed_interception.cc
new file mode 100644
index 0000000000..4f4310d605
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_interception.cc
@@ -0,0 +1,97 @@
+// Copyright 2019 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/signed_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+NTSTATUS WINAPI
+TargetNtCreateSection(NtCreateSectionFunction orig_CreateSection,
+ PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle) {
+ do {
+ // The section only needs to have SECTION_MAP_EXECUTE, but the permissions
+ // vary depending on the OS. Windows 1903 and higher requests (SECTION_QUERY
+ // | SECTION_MAP_READ | SECTION_MAP_EXECUTE) while previous OS versions also
+ // request SECTION_MAP_WRITE. Just check for EXECUTE.
+ if (!(desired_access & SECTION_MAP_EXECUTE))
+ break;
+ if (object_attributes)
+ break;
+ if (maximum_size)
+ break;
+ if (section_page_protection != PAGE_EXECUTE)
+ break;
+ if (allocation_attributes != SEC_IMAGE)
+ break;
+
+ mozilla::sandboxing::LogBlocked("NtCreateSection");
+
+ // IPC must be fully started.
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> path;
+
+ if (!NtGetPathFromHandle(file_handle, &path))
+ break;
+
+ const wchar_t* const_name = path.get();
+
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(const_name);
+
+ // Check if this will be sent to the broker.
+ if (!QueryBroker(IpcTag::NTCREATESECTION, params.GetBase()))
+ break;
+
+ if (!ValidParameter(section_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = STATUS_INVALID_IMAGE_HASH;
+ SharedMemIPCClient ipc(memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::NTCREATESECTION, file_handle, &answer);
+
+ if (code != SBOX_ALL_OK)
+ break;
+
+ if (!NT_SUCCESS(answer.nt_status))
+ break;
+
+ __try {
+ *section_handle = answer.handle;
+ mozilla::sandboxing::LogAllowed("NtCreateSection");
+ return answer.nt_status;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ } while (false);
+
+ // Fall back to the original API in all failure cases.
+ return orig_CreateSection(section_handle, desired_access, object_attributes,
+ maximum_size, section_page_protection,
+ allocation_attributes, file_handle);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_interception.h b/security/sandbox/chromium/sandbox/win/src/signed_interception.h
new file mode 100644
index 0000000000..a50ec38222
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_interception.h
@@ -0,0 +1,30 @@
+// Copyright 2019 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_WIN_SRC_SIGNED_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_SIGNED_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interceptor for NtCreateSection
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateSection(NtCreateSectionFunction orig_CreateSection,
+ PHANDLE section_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ PLARGE_INTEGER maximum_size,
+ ULONG section_page_protection,
+ ULONG allocation_attributes,
+ HANDLE file_handle);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SIGNED_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_policy.cc b/security/sandbox/chromium/sandbox/win/src/signed_policy.cc
new file mode 100644
index 0000000000..ccf11defe7
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_policy.cc
@@ -0,0 +1,102 @@
+// Copyright 2019 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/signed_policy.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace {
+
+bool IsValidNtPath(const base::FilePath& name) {
+ UNICODE_STRING uni_name;
+ OBJECT_ATTRIBUTES obj_attr;
+ sandbox::InitObjectAttribs(name.value(), OBJ_CASE_INSENSITIVE, nullptr,
+ &obj_attr, &uni_name, nullptr);
+
+ NtQueryAttributesFileFunction NtQueryAttributesFile = nullptr;
+ ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
+ FILE_BASIC_INFORMATION file_info;
+ return NtQueryAttributesFile &&
+ NT_SUCCESS(NtQueryAttributesFile(&obj_attr, &file_info));
+}
+
+} // namespace
+
+namespace sandbox {
+
+bool SignedPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ // Only support one semantic.
+ if (TargetPolicy::SIGNED_ALLOW_LOAD != semantics) {
+ return false;
+ }
+
+ base::FilePath file_path(name);
+ base::FilePath nt_filename;
+ std::wstring nt_path_name;
+ if (GetNtPathFromWin32Path(file_path.DirName().value().c_str(),
+ &nt_path_name)) {
+ base::FilePath nt_path(nt_path_name);
+ nt_filename = nt_path.Append(file_path.BaseName());
+ } else if (IsValidNtPath(file_path)) {
+ nt_filename = std::move(file_path);
+ } else {
+ return false;
+ }
+
+ // Create a rule to ASK_BROKER if name matches.
+ PolicyRule signed_policy(ASK_BROKER);
+ if (!signed_policy.AddStringMatch(
+ IF, NameBased::NAME, nt_filename.value().c_str(), CASE_INSENSITIVE)) {
+ return false;
+ }
+ if (!policy->AddRule(IpcTag::NTCREATESECTION, &signed_policy)) {
+ return false;
+ }
+
+ return true;
+}
+
+NTSTATUS SignedPolicy::CreateSectionAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::win::ScopedHandle& local_file_handle,
+ HANDLE* target_section_handle) {
+ NtCreateSectionFunction NtCreateSection = nullptr;
+ ResolveNTFunctionPtr("NtCreateSection", &NtCreateSection);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // section as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE local_section_handle = nullptr;
+ NTSTATUS status = NtCreateSection(&local_section_handle,
+ SECTION_QUERY | SECTION_MAP_WRITE |
+ SECTION_MAP_READ | SECTION_MAP_EXECUTE,
+ nullptr, 0, PAGE_EXECUTE, SEC_IMAGE,
+ local_file_handle.Get());
+ if (!local_section_handle)
+ return status;
+
+ // Duplicate section handle back to the target.
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_section_handle,
+ client_info.process, target_section_handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/signed_policy.h b/security/sandbox/chromium/sandbox/win/src/signed_policy.h
new file mode 100644
index 0000000000..d22af4d4dc
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/signed_policy.h
@@ -0,0 +1,39 @@
+// Copyright 2019 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_WIN_SRC_SIGNED_POLICY_H_
+#define SANDBOX_WIN_SRC_SIGNED_POLICY_H_
+
+#include <stdint.h>
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to signed policy
+class SignedPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a request.
+ // client_info is the target process that is making the request and
+ // eval_result is the desired policy action to accomplish.
+ static NTSTATUS CreateSectionAction(
+ EvalResult eval_result,
+ const ClientInfo& client_info,
+ const base::win::ScopedHandle& local_file_handle,
+ HANDLE* section_handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SIGNED_POLICY_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc
new file mode 100644
index 0000000000..d728e1d831
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.cc
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/sync_dispatcher.h"
+
+#include <stdint.h>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/interceptors.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_broker.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/sync_policy.h"
+
+namespace sandbox {
+
+SyncDispatcher::SyncDispatcher(PolicyBase* policy_base)
+ : policy_base_(policy_base) {
+ static const IPCCall create_params = {
+ {IpcTag::CREATEEVENT, {WCHAR_TYPE, UINT32_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&SyncDispatcher::CreateEvent)};
+
+ static const IPCCall open_params = {
+ {IpcTag::OPENEVENT, {WCHAR_TYPE, UINT32_TYPE}},
+ reinterpret_cast<CallbackGeneric>(&SyncDispatcher::OpenEvent)};
+
+ ipc_calls_.push_back(create_params);
+ ipc_calls_.push_back(open_params);
+}
+
+bool SyncDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (service == IpcTag::CREATEEVENT) {
+ return INTERCEPT_NT(manager, NtCreateEvent, CREATE_EVENT_ID, 24);
+ }
+ return (service == IpcTag::OPENEVENT) &&
+ INTERCEPT_NT(manager, NtOpenEvent, OPEN_EVENT_ID, 16);
+}
+
+bool SyncDispatcher::CreateEvent(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t event_type,
+ uint32_t initial_state) {
+ const wchar_t* event_name = name->c_str();
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(event_name);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::CREATEEVENT, params.GetBase());
+ HANDLE handle = nullptr;
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = SyncPolicy::CreateEventAction(
+ result, *ipc->client_info, *name, event_type, initial_state, &handle);
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+bool SyncDispatcher::OpenEvent(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t desired_access) {
+ const wchar_t* event_name = name->c_str();
+
+ CountedParameterSet<OpenEventParams> params;
+ params[OpenEventParams::NAME] = ParamPickerMake(event_name);
+ params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access);
+
+ EvalResult result =
+ policy_base_->EvalPolicy(IpcTag::OPENEVENT, params.GetBase());
+ HANDLE handle = nullptr;
+ // Return operation status on the IPC.
+ ipc->return_info.nt_status = SyncPolicy::OpenEventAction(
+ result, *ipc->client_info, *name, desired_access, &handle);
+ ipc->return_info.handle = handle;
+ return true;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h
new file mode 100644
index 0000000000..d4bc025a0b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_dispatcher.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 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_SYNC_DISPATCHER_H_
+#define SANDBOX_SRC_SYNC_DISPATCHER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// This class handles sync-related IPC calls.
+class SyncDispatcher : public Dispatcher {
+ public:
+ explicit SyncDispatcher(PolicyBase* policy_base);
+ ~SyncDispatcher() override {}
+
+ // Dispatcher interface.
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Processes IPC requests coming from calls to CreateEvent in the target.
+ bool CreateEvent(IPCInfo* ipc,
+ std::wstring* name,
+ uint32_t event_type,
+ uint32_t initial_state);
+
+ // Processes IPC requests coming from calls to OpenEvent in the target.
+ bool OpenEvent(IPCInfo* ipc, std::wstring* name, uint32_t desired_access);
+
+ PolicyBase* policy_base_;
+ DISALLOW_COPY_AND_ASSIGN(SyncDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_DISPATCHER_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_interception.cc b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc
new file mode 100644
index 0000000000..9ec680e2fd
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_interception.cc
@@ -0,0 +1,177 @@
+// Copyright (c) 2006-2008 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/sync_interception.h"
+
+#include <stdint.h>
+
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/policy_target.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+#include "sandbox/win/src/target_services.h"
+#include "mozilla/sandboxing/sandboxLogging.h"
+
+namespace sandbox {
+
+ResultCode ProxyCreateEvent(LPCWSTR name,
+ uint32_t initial_state,
+ EVENT_TYPE event_type,
+ void* ipc_memory,
+ CrossCallReturn* answer) {
+ CountedParameterSet<NameBased> params;
+ params[NameBased::NAME] = ParamPickerMake(name);
+
+ if (!QueryBroker(IpcTag::CREATEEVENT, params.GetBase()))
+ return SBOX_ERROR_GENERIC;
+
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code = CrossCall(ipc, IpcTag::CREATEEVENT, name, event_type,
+ initial_state, answer);
+ return code;
+}
+
+ResultCode ProxyOpenEvent(LPCWSTR name,
+ uint32_t desired_access,
+ void* ipc_memory,
+ CrossCallReturn* answer) {
+ CountedParameterSet<OpenEventParams> params;
+ params[OpenEventParams::NAME] = ParamPickerMake(name);
+ params[OpenEventParams::ACCESS] = ParamPickerMake(desired_access);
+
+ if (!QueryBroker(IpcTag::OPENEVENT, params.GetBase()))
+ return SBOX_ERROR_GENERIC;
+
+ SharedMemIPCClient ipc(ipc_memory);
+ ResultCode code =
+ CrossCall(ipc, IpcTag::OPENEVENT, name, desired_access, answer);
+
+ return code;
+}
+
+NTSTATUS WINAPI TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state) {
+ NTSTATUS status =
+ orig_CreateEvent(event_handle, desired_access, object_attributes,
+ event_type, initial_state);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtCreatEvent",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes;
+ // The RootDirectory points to BaseNamedObjects. We can ignore it.
+ object_attribs_copy.RootDirectory = nullptr;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(&object_attribs_copy, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = status;
+ ResultCode code = ProxyCreateEvent(name.get(), initial_state, event_type,
+ memory, &answer);
+
+ if (code != SBOX_ALL_OK) {
+ status = answer.nt_status;
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtCreateEvent",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+NTSTATUS WINAPI TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes) {
+ NTSTATUS status =
+ orig_OpenEvent(event_handle, desired_access, object_attributes);
+ if (status != STATUS_ACCESS_DENIED || !object_attributes)
+ return status;
+
+ mozilla::sandboxing::LogBlocked("NtOpenEvent",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+
+ // We don't trust that the IPC can work this early.
+ if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
+ return status;
+
+ do {
+ if (!ValidParameter(event_handle, sizeof(HANDLE), WRITE))
+ break;
+
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ break;
+
+ OBJECT_ATTRIBUTES object_attribs_copy = *object_attributes;
+ // The RootDirectory points to BaseNamedObjects. We can ignore it.
+ object_attribs_copy.RootDirectory = nullptr;
+
+ std::unique_ptr<wchar_t, NtAllocDeleter> name;
+ uint32_t attributes = 0;
+ NTSTATUS ret =
+ AllocAndCopyName(&object_attribs_copy, &name, &attributes, nullptr);
+ if (!NT_SUCCESS(ret) || !name)
+ break;
+
+ CrossCallReturn answer = {0};
+ answer.nt_status = status;
+ ResultCode code =
+ ProxyOpenEvent(name.get(), desired_access, memory, &answer);
+
+ if (code != SBOX_ALL_OK) {
+ status = answer.nt_status;
+ break;
+ }
+ __try {
+ *event_handle = answer.handle;
+ status = STATUS_SUCCESS;
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ break;
+ }
+ mozilla::sandboxing::LogAllowed("NtOpenEvent",
+ object_attributes->ObjectName->Buffer,
+ object_attributes->ObjectName->Length);
+ } while (false);
+
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_interception.h b/security/sandbox/chromium/sandbox/win/src/sync_interception.h
new file mode 100644
index 0000000000..c21e7ee7b2
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_interception.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2006-2008 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_WIN_SRC_SYNC_INTERCEPTION_H_
+#define SANDBOX_WIN_SRC_SYNC_INTERCEPTION_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+typedef NTSTATUS(WINAPI* NtCreateEventFunction)(
+ PHANDLE EventHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes,
+ EVENT_TYPE EventType,
+ BOOLEAN InitialState);
+
+typedef NTSTATUS(WINAPI* NtOpenEventFunction)(
+ PHANDLE EventHandle,
+ ACCESS_MASK DesiredAccess,
+ POBJECT_ATTRIBUTES ObjectAttributes);
+
+// Interceptors for NtCreateEvent/NtOpenEvent
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtCreateEvent(NtCreateEventFunction orig_CreateEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes,
+ EVENT_TYPE event_type,
+ BOOLEAN initial_state);
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtOpenEvent(NtOpenEventFunction orig_OpenEvent,
+ PHANDLE event_handle,
+ ACCESS_MASK desired_access,
+ POBJECT_ATTRIBUTES object_attributes);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SYNC_INTERCEPTION_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy.cc b/security/sandbox/chromium/sandbox/win/src/sync_policy.cc
new file mode 100644
index 0000000000..cdc34dd241
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2006-2008 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/sync_policy.h"
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_engine_opcodes.h"
+#include "sandbox/win/src/policy_params.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sandbox_utils.h"
+#include "sandbox/win/src/sync_interception.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+// Provides functionality to resolve a symbolic link within the object
+// directory passed in.
+NTSTATUS ResolveSymbolicLink(const std::wstring& directory_name,
+ const std::wstring& name,
+ std::wstring* target) {
+ NtOpenDirectoryObjectFunction NtOpenDirectoryObject = nullptr;
+ ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject);
+
+ NtQuerySymbolicLinkObjectFunction NtQuerySymbolicLinkObject = nullptr;
+ ResolveNTFunctionPtr("NtQuerySymbolicLinkObject", &NtQuerySymbolicLinkObject);
+
+ NtOpenSymbolicLinkObjectFunction NtOpenSymbolicLinkObject = nullptr;
+ ResolveNTFunctionPtr("NtOpenSymbolicLinkObject", &NtOpenSymbolicLinkObject);
+
+ NtCloseFunction NtClose = nullptr;
+ ResolveNTFunctionPtr("NtClose", &NtClose);
+
+ OBJECT_ATTRIBUTES symbolic_link_directory_attributes = {};
+ UNICODE_STRING symbolic_link_directory_string = {};
+ InitObjectAttribs(directory_name, OBJ_CASE_INSENSITIVE, nullptr,
+ &symbolic_link_directory_attributes,
+ &symbolic_link_directory_string, nullptr);
+
+ HANDLE symbolic_link_directory = nullptr;
+ NTSTATUS status =
+ NtOpenDirectoryObject(&symbolic_link_directory, DIRECTORY_QUERY,
+ &symbolic_link_directory_attributes);
+ if (!NT_SUCCESS(status))
+ return status;
+
+ OBJECT_ATTRIBUTES symbolic_link_attributes = {};
+ UNICODE_STRING name_string = {};
+ InitObjectAttribs(name, OBJ_CASE_INSENSITIVE, symbolic_link_directory,
+ &symbolic_link_attributes, &name_string, nullptr);
+
+ HANDLE symbolic_link = nullptr;
+ status = NtOpenSymbolicLinkObject(&symbolic_link, GENERIC_READ,
+ &symbolic_link_attributes);
+ CHECK(NT_SUCCESS(NtClose(symbolic_link_directory)));
+ if (!NT_SUCCESS(status))
+ return status;
+
+ UNICODE_STRING target_path = {};
+ unsigned long target_length = 0;
+ status =
+ NtQuerySymbolicLinkObject(symbolic_link, &target_path, &target_length);
+ if (status != STATUS_BUFFER_TOO_SMALL) {
+ CHECK(NT_SUCCESS(NtClose(symbolic_link)));
+ return status;
+ }
+
+ target_path.Length = 0;
+ target_path.MaximumLength = static_cast<USHORT>(target_length);
+ target_path.Buffer = new wchar_t[target_path.MaximumLength + 1];
+ status =
+ NtQuerySymbolicLinkObject(symbolic_link, &target_path, &target_length);
+ if (NT_SUCCESS(status))
+ target->assign(target_path.Buffer, target_length);
+
+ CHECK(NT_SUCCESS(NtClose(symbolic_link)));
+ delete[] target_path.Buffer;
+ return status;
+}
+
+NTSTATUS GetBaseNamedObjectsDirectory(HANDLE* directory) {
+ static HANDLE base_named_objects_handle = nullptr;
+ if (base_named_objects_handle) {
+ *directory = base_named_objects_handle;
+ return STATUS_SUCCESS;
+ }
+
+ NtOpenDirectoryObjectFunction NtOpenDirectoryObject = nullptr;
+ ResolveNTFunctionPtr("NtOpenDirectoryObject", &NtOpenDirectoryObject);
+
+ DWORD session_id = 0;
+ ProcessIdToSessionId(::GetCurrentProcessId(), &session_id);
+
+ std::wstring base_named_objects_path;
+
+ NTSTATUS status = ResolveSymbolicLink(L"\\Sessions\\BNOLINKS",
+ base::StringPrintf(L"%d", session_id),
+ &base_named_objects_path);
+ if (!NT_SUCCESS(status)) {
+ DLOG(ERROR) << "Failed to resolve BaseNamedObjects path. Error: " << status;
+ return status;
+ }
+
+ UNICODE_STRING directory_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(base_named_objects_path, OBJ_CASE_INSENSITIVE, nullptr,
+ &object_attributes, &directory_name, nullptr);
+ status = NtOpenDirectoryObject(&base_named_objects_handle,
+ DIRECTORY_ALL_ACCESS, &object_attributes);
+ if (NT_SUCCESS(status))
+ *directory = base_named_objects_handle;
+ return status;
+}
+
+bool SyncPolicy::GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy) {
+ std::wstring mod_name(name);
+ if (mod_name.empty()) {
+ return false;
+ }
+
+ if (TargetPolicy::EVENTS_ALLOW_ANY != semantics &&
+ TargetPolicy::EVENTS_ALLOW_READONLY != semantics) {
+ // Other flags are not valid for sync policy yet.
+ NOTREACHED();
+ return false;
+ }
+
+ // Add the open rule.
+ EvalResult result = ASK_BROKER;
+ PolicyRule open(result);
+
+ if (!open.AddStringMatch(IF, OpenEventParams::NAME, name, CASE_INSENSITIVE))
+ return false;
+
+ if (TargetPolicy::EVENTS_ALLOW_READONLY == semantics) {
+ // We consider all flags that are not known to be readonly as potentially
+ // used for write.
+ uint32_t allowed_flags = SYNCHRONIZE | GENERIC_READ | READ_CONTROL;
+ uint32_t restricted_flags = ~allowed_flags;
+ open.AddNumberMatch(IF_NOT, OpenEventParams::ACCESS, restricted_flags, AND);
+ }
+
+ if (!policy->AddRule(IpcTag::OPENEVENT, &open))
+ return false;
+
+ // If it's not a read only, add the create rule.
+ if (TargetPolicy::EVENTS_ALLOW_READONLY != semantics) {
+ PolicyRule create(result);
+ if (!create.AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE))
+ return false;
+
+ if (!policy->AddRule(IpcTag::CREATEEVENT, &create))
+ return false;
+ }
+
+ return true;
+}
+
+NTSTATUS SyncPolicy::CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t event_type,
+ uint32_t initial_state,
+ HANDLE* handle) {
+ NtCreateEventFunction NtCreateEvent = nullptr;
+ ResolveNTFunctionPtr("NtCreateEvent", &NtCreateEvent);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // file as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE object_directory = nullptr;
+ NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory);
+ if (status != STATUS_SUCCESS)
+ return status;
+
+ UNICODE_STRING unicode_event_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory,
+ &object_attributes, &unicode_event_name, nullptr);
+
+ HANDLE local_handle = nullptr;
+ status = NtCreateEvent(&local_handle, EVENT_ALL_ACCESS, &object_attributes,
+ static_cast<EVENT_TYPE>(event_type),
+ static_cast<BOOLEAN>(initial_state != 0));
+ if (!local_handle)
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+NTSTATUS SyncPolicy::OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t desired_access,
+ HANDLE* handle) {
+ NtOpenEventFunction NtOpenEvent = nullptr;
+ ResolveNTFunctionPtr("NtOpenEvent", &NtOpenEvent);
+
+ // The only action supported is ASK_BROKER which means create the requested
+ // event as specified.
+ if (ASK_BROKER != eval_result)
+ return false;
+
+ HANDLE object_directory = nullptr;
+ NTSTATUS status = GetBaseNamedObjectsDirectory(&object_directory);
+ if (status != STATUS_SUCCESS)
+ return status;
+
+ UNICODE_STRING unicode_event_name = {};
+ OBJECT_ATTRIBUTES object_attributes = {};
+ InitObjectAttribs(event_name, OBJ_CASE_INSENSITIVE, object_directory,
+ &object_attributes, &unicode_event_name, nullptr);
+
+ HANDLE local_handle = nullptr;
+ status = NtOpenEvent(&local_handle, desired_access, &object_attributes);
+ if (!local_handle)
+ return status;
+
+ if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
+ client_info.process, handle, 0, false,
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
+ return STATUS_ACCESS_DENIED;
+ }
+ return status;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy.h b/security/sandbox/chromium/sandbox/win/src/sync_policy.h
new file mode 100644
index 0000000000..2eb4124a82
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2006-2008 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_SYNC_POLICY_H__
+#define SANDBOX_SRC_SYNC_POLICY_H__
+
+#include <stdint.h>
+
+#include <string>
+
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/sandbox_policy.h"
+
+namespace sandbox {
+
+// This class centralizes most of the knowledge related to sync policy
+class SyncPolicy {
+ public:
+ // Creates the required low-level policy rules to evaluate a high-level
+ // policy rule for sync calls, in particular open or create actions.
+ // name is the sync object name, semantics is the desired semantics for the
+ // open or create and policy is the policy generator to which the rules are
+ // going to be added.
+ static bool GenerateRules(const wchar_t* name,
+ TargetPolicy::Semantics semantics,
+ LowLevelPolicy* policy);
+
+ // Performs the desired policy action on a request.
+ // client_info is the target process that is making the request and
+ // eval_result is the desired policy action to accomplish.
+ static NTSTATUS CreateEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t event_type,
+ uint32_t initial_state,
+ HANDLE* handle);
+ static NTSTATUS OpenEventAction(EvalResult eval_result,
+ const ClientInfo& client_info,
+ const std::wstring& event_name,
+ uint32_t desired_access,
+ HANDLE* handle);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_SYNC_POLICY_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc
new file mode 100644
index 0000000000..97f60d359f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.cc
@@ -0,0 +1,145 @@
+// Copyright (c) 2011 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/sync_policy_test.h"
+
+#include "base/win/scoped_handle.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_policy.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ DWORD desired_access = SYNCHRONIZE;
+ if (L'f' == argv[0][0])
+ desired_access = EVENT_ALL_ACCESS;
+
+ base::win::ScopedHandle event_open(
+ ::OpenEvent(desired_access, false, argv[1]));
+ DWORD error_open = ::GetLastError();
+
+ if (event_open.IsValid())
+ return SBOX_TEST_SUCCEEDED;
+
+ if (ERROR_ACCESS_DENIED == error_open || ERROR_BAD_PATHNAME == error_open ||
+ ERROR_FILE_NOT_FOUND == error_open)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+SBOX_TESTS_COMMAND int Event_CreateOpen(int argc, wchar_t** argv) {
+ if (argc < 2 || argc > 3)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ wchar_t* event_name = nullptr;
+ if (3 == argc)
+ event_name = argv[2];
+
+ bool manual_reset = false;
+ bool initial_state = false;
+ if (L't' == argv[0][0])
+ manual_reset = true;
+ if (L't' == argv[1][0])
+ initial_state = true;
+
+ base::win::ScopedHandle event_create(
+ ::CreateEvent(nullptr, manual_reset, initial_state, event_name));
+ DWORD error_create = ::GetLastError();
+ base::win::ScopedHandle event_open;
+ if (event_name)
+ event_open.Set(::OpenEvent(EVENT_ALL_ACCESS, false, event_name));
+
+ if (event_create.IsValid()) {
+ DWORD wait = ::WaitForSingleObject(event_create.Get(), 0);
+ if (initial_state && WAIT_OBJECT_0 != wait)
+ return SBOX_TEST_FAILED;
+
+ if (!initial_state && WAIT_TIMEOUT != wait)
+ return SBOX_TEST_FAILED;
+ }
+
+ if (event_name) {
+ // Both event_open and event_create have to be valid.
+ if (event_open.IsValid() && event_create.IsValid())
+ return SBOX_TEST_SUCCEEDED;
+
+ if ((event_open.IsValid() && !event_create.IsValid()) ||
+ (!event_open.IsValid() && event_create.IsValid())) {
+ return SBOX_TEST_FAILED;
+ }
+ } else {
+ // Only event_create has to be valid.
+ if (event_create.Get())
+ return SBOX_TEST_SUCCEEDED;
+ }
+
+ if (ERROR_ACCESS_DENIED == error_create || ERROR_BAD_PATHNAME == error_create)
+ return SBOX_TEST_DENIED;
+
+ return SBOX_TEST_FAILED;
+}
+
+// Tests the creation of events using all the possible combinations.
+TEST(SyncPolicyTest, TestEvent) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"test1"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"test2"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f test2"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f t test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t t test2"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test4"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test4"));
+}
+
+// Tests opening events with read only access.
+TEST(SyncPolicyTest, TestEventReadOnly) {
+ TestRunner runner;
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test1"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test2"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test5"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_READONLY, L"test6"));
+
+ base::win::ScopedHandle handle1(
+ ::CreateEvent(nullptr, false, false, L"test1"));
+ base::win::ScopedHandle handle2(
+ ::CreateEvent(nullptr, false, false, L"test2"));
+ base::win::ScopedHandle handle3(
+ ::CreateEvent(nullptr, false, false, L"test3"));
+ base::win::ScopedHandle handle4(
+ ::CreateEvent(nullptr, false, false, L"test4"));
+
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen f f"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_CreateOpen t f"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test1"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"Event_Open s test2"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open f test3"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_Open s test4"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f f test5"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t f test6"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen f t test5"));
+ EXPECT_EQ(SBOX_TEST_DENIED, runner.RunTest(L"Event_CreateOpen t t test6"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/sync_policy_test.h b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.h
new file mode 100644
index 0000000000..4f354b35be
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/sync_policy_test.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
+#define SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
+
+#include "sandbox/win/tests/common/controller.h"
+
+namespace sandbox {
+
+// Opens the named event received on argv[1]. The requested access is
+// EVENT_ALL_ACCESS if argv[0] starts with 'f', or SYNCHRONIZE otherwise.
+SBOX_TESTS_COMMAND int Event_Open(int argc, wchar_t **argv);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_SYNC_POLICY_TEST_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/target_interceptions.cc b/security/sandbox/chromium/sandbox/win/src/target_interceptions.cc
new file mode 100644
index 0000000000..1b467814c6
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_interceptions.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2006-2008 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/target_interceptions.h"
+
+#include "base/win/static_constants.h"
+#include "sandbox/win/src/interception_agent.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+const char KERNEL32_DLL_NAME[] = "kernel32.dll";
+
+enum SectionLoadState {
+ kBeforeKernel32,
+ kAfterKernel32,
+};
+
+// Hooks NtMapViewOfSection to detect the load of DLLs. If hot patching is
+// required for this dll, this functions patches it.
+NTSTATUS WINAPI
+TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection,
+ HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect) {
+ NTSTATUS ret = orig_MapViewOfSection(section, process, base, zero_bits,
+ commit_size, offset, view_size, inherit,
+ allocation_type, protect);
+ static SectionLoadState s_state = kBeforeKernel32;
+
+ do {
+ if (!NT_SUCCESS(ret))
+ break;
+
+ if (!IsSameProcess(process))
+ break;
+
+ // Only check for verifier.dll or kernel32.dll loading if we haven't moved
+ // past that state yet.
+ if (s_state == kBeforeKernel32) {
+ const char* ansi_module_name =
+ GetAnsiImageInfoFromModule(reinterpret_cast<HMODULE>(*base));
+
+ // _strnicmp below may hit read access violations for some sections. We
+ // find what looks like a valid export directory for a PE module but the
+ // pointer to the module name will be pointing to invalid memory.
+ __try {
+ // Don't initialize the heap if verifier.dll is being loaded. This
+ // indicates Application Verifier is enabled and we should wait until
+ // the next module is loaded.
+ if (ansi_module_name &&
+ (g_nt._strnicmp(
+ ansi_module_name, base::win::kApplicationVerifierDllName,
+ g_nt.strlen(base::win::kApplicationVerifierDllName) + 1) == 0))
+ break;
+
+ if (ansi_module_name &&
+ (g_nt._strnicmp(ansi_module_name, KERNEL32_DLL_NAME,
+ sizeof(KERNEL32_DLL_NAME)) == 0)) {
+ s_state = kAfterKernel32;
+ }
+ } __except (EXCEPTION_EXECUTE_HANDLER) {
+ }
+ }
+
+ if (!InitHeap())
+ break;
+
+ if (!IsValidImageSection(section, base, offset, view_size))
+ break;
+
+ UINT image_flags;
+ UNICODE_STRING* module_name =
+ GetImageInfoFromModule(reinterpret_cast<HMODULE>(*base), &image_flags);
+ UNICODE_STRING* file_name = GetBackingFilePath(*base);
+
+ if ((!module_name) && (image_flags & MODULE_HAS_CODE)) {
+ // If the module has no exports we retrieve the module name from the
+ // full path of the mapped section.
+ module_name = ExtractModuleName(file_name);
+ }
+
+ InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent();
+
+ if (agent) {
+ if (!agent->OnDllLoad(file_name, module_name, *base)) {
+ // Interception agent is demanding to un-map the module.
+ g_nt.UnmapViewOfSection(process, *base);
+ *base = nullptr;
+ ret = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ if (module_name)
+ operator delete(module_name, NT_ALLOC);
+
+ if (file_name)
+ operator delete(file_name, NT_ALLOC);
+
+ } while (false);
+
+ return ret;
+}
+
+NTSTATUS WINAPI
+TargetNtUnmapViewOfSection(NtUnmapViewOfSectionFunction orig_UnmapViewOfSection,
+ HANDLE process,
+ PVOID base) {
+ NTSTATUS ret = orig_UnmapViewOfSection(process, base);
+
+ if (!NT_SUCCESS(ret))
+ return ret;
+
+ if (!IsSameProcess(process))
+ return ret;
+
+ InterceptionAgent* agent = InterceptionAgent::GetInterceptionAgent();
+
+ if (agent)
+ agent->OnDllUnload(base);
+
+ return ret;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/target_interceptions.h b/security/sandbox/chromium/sandbox/win/src/target_interceptions.h
new file mode 100644
index 0000000000..fac1c65c27
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_interceptions.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2006-2008 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_WIN_SRC_TARGET_INTERCEPTIONS_H_
+#define SANDBOX_WIN_SRC_TARGET_INTERCEPTIONS_H_
+
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+extern "C" {
+
+// Interception of NtMapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being loaded, so we can patch them if needed.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtMapViewOfSection(NtMapViewOfSectionFunction orig_MapViewOfSection,
+ HANDLE section,
+ HANDLE process,
+ PVOID* base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect);
+
+// Interception of NtUnmapViewOfSection on the child process.
+// It should never be called directly. This function provides the means to
+// detect dlls being unloaded, so we can clean up our interceptions.
+SANDBOX_INTERCEPT NTSTATUS WINAPI
+TargetNtUnmapViewOfSection(NtUnmapViewOfSectionFunction orig_UnmapViewOfSection,
+ HANDLE process,
+ PVOID base);
+
+} // extern "C"
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_TARGET_INTERCEPTIONS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.cc b/security/sandbox/chromium/sandbox/win/src/target_process.cc
new file mode 100644
index 0000000000..4661267d1e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_process.cc
@@ -0,0 +1,393 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/target_process.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/memory/free_deleter.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/process/environment_internal.h"
+#include "base/win/startup_information.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/policy_low_level.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sharedmem_ipc_server.h"
+#include "sandbox/win/src/sid.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+namespace {
+
+void CopyPolicyToTarget(const void* source, size_t size, void* dest) {
+ if (!source || !size)
+ return;
+ memcpy(dest, source, size);
+ sandbox::PolicyGlobal* policy =
+ reinterpret_cast<sandbox::PolicyGlobal*>(dest);
+
+ size_t offset = reinterpret_cast<size_t>(source);
+
+ for (size_t i = 0; i < sandbox::kMaxServiceCount; i++) {
+ size_t buffer = reinterpret_cast<size_t>(policy->entry[i]);
+ if (buffer) {
+ buffer -= offset;
+ policy->entry[i] = reinterpret_cast<sandbox::PolicyBuffer*>(buffer);
+ }
+ }
+}
+
+bool GetTokenAppContainerSid(HANDLE token_handle,
+ std::unique_ptr<Sid>* app_container_sid) {
+ std::vector<char> app_container_info(sizeof(TOKEN_APPCONTAINER_INFORMATION) +
+ SECURITY_MAX_SID_SIZE);
+ DWORD return_length;
+
+ if (!::GetTokenInformation(
+ token_handle, TokenAppContainerSid, app_container_info.data(),
+ base::checked_cast<DWORD>(app_container_info.size()),
+ &return_length)) {
+ return false;
+ }
+
+ PTOKEN_APPCONTAINER_INFORMATION info =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(
+ app_container_info.data());
+ if (!info->TokenAppContainer)
+ return false;
+ *app_container_sid = std::unique_ptr<Sid>(new Sid(info->TokenAppContainer));
+ return true;
+}
+
+bool GetProcessAppContainerSid(HANDLE process,
+ std::unique_ptr<Sid>* app_container_sid) {
+ HANDLE token_handle;
+ if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle))
+ return false;
+ base::win::ScopedHandle process_token(token_handle);
+
+ return GetTokenAppContainerSid(process_token.Get(), app_container_sid);
+}
+
+bool GetAppContainerImpersonationToken(
+ HANDLE process,
+ HANDLE initial_token,
+ const std::vector<Sid>& capabilities,
+ base::win::ScopedHandle* impersonation_token) {
+ std::unique_ptr<Sid> app_container_sid;
+ if (!GetProcessAppContainerSid(process, &app_container_sid)) {
+ return false;
+ }
+ SecurityCapabilities security_caps(*app_container_sid, capabilities);
+ return CreateLowBoxToken(initial_token, IMPERSONATION, &security_caps,
+ nullptr, 0, impersonation_token) == ERROR_SUCCESS;
+}
+
+} // namespace
+
+SANDBOX_INTERCEPT HANDLE g_shared_section;
+SANDBOX_INTERCEPT size_t g_shared_IPC_size;
+SANDBOX_INTERCEPT size_t g_shared_policy_size;
+
+TargetProcess::TargetProcess(base::win::ScopedHandle initial_token,
+ base::win::ScopedHandle lockdown_token,
+ HANDLE job,
+ ThreadProvider* thread_pool,
+ const std::vector<Sid>& impersonation_capabilities)
+ // This object owns everything initialized here except thread_pool and
+ // the job_ handle. The Job handle is closed by BrokerServices and results
+ // eventually in a call to our dtor.
+ : lockdown_token_(std::move(lockdown_token)),
+ initial_token_(std::move(initial_token)),
+ job_(job),
+ thread_pool_(thread_pool),
+ base_address_(nullptr),
+ impersonation_capabilities_(impersonation_capabilities) {}
+
+TargetProcess::~TargetProcess() {
+ // Give a chance to the process to die. In most cases the JOB_KILL_ON_CLOSE
+ // will take effect only when the context changes. As far as the testing went,
+ // this wait was enough to switch context and kill the processes in the job.
+ // If this process is already dead, the function will return without waiting.
+ // For now, this wait is there only to do a best effort to prevent some leaks
+ // from showing up in purify.
+ if (sandbox_process_info_.IsValid()) {
+ ::WaitForSingleObject(sandbox_process_info_.process_handle(), 50);
+ // Terminate the process if it's still alive, as its IPC server is going
+ // away. 1 is RESULT_CODE_KILLED.
+ ::TerminateProcess(sandbox_process_info_.process_handle(), 1);
+ }
+
+ // ipc_server_ references our process handle, so make sure the former is shut
+ // down before the latter is closed (by ScopedProcessInformation).
+ ipc_server_.reset();
+}
+
+// Creates the target (child) process suspended and assigns it to the job
+// object.
+ResultCode TargetProcess::Create(
+ const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info,
+ base::EnvironmentMap& env_changes,
+ DWORD* win_error) {
+ exe_name_.reset(_wcsdup(exe_path));
+
+ // the command line needs to be writable by CreateProcess().
+ std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
+
+ // Start the target process suspended.
+ DWORD flags =
+ CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
+
+ if (startup_info.has_extended_startup_info())
+ flags |= EXTENDED_STARTUPINFO_PRESENT;
+
+ if (job_ && base::win::GetVersion() < base::win::Version::WIN8) {
+ // Windows 8 implements nested jobs, but for older systems we need to
+ // break out of any job we're in to enforce our restrictions.
+ flags |= CREATE_BREAKAWAY_FROM_JOB;
+ }
+
+ LPTCH original_environment = GetEnvironmentStrings();
+ base::NativeEnvironmentString new_environment =
+ base::internal::AlterEnvironment(original_environment, env_changes);
+ // Ignore return value? What can we do?
+ FreeEnvironmentStrings(original_environment);
+ LPVOID new_env_ptr = (void*)new_environment.data();
+
+ PROCESS_INFORMATION temp_process_info = {};
+ if (!::CreateProcessAsUserW(lockdown_token_.Get(), exe_path, cmd_line.get(),
+ nullptr, // No security attribute.
+ nullptr, // No thread attribute.
+ inherit_handles, flags,
+ new_env_ptr,
+ nullptr, // Use current directory of the caller.
+ startup_info.startup_info(),
+ &temp_process_info)) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_CREATE_PROCESS;
+ }
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+
+ if (job_) {
+ // Assign the suspended target to the windows job object.
+ if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
+ *win_error = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_ASSIGN_PROCESS_TO_JOB_OBJECT;
+ }
+ }
+
+ if (initial_token_.IsValid()) {
+ HANDLE impersonation_token = initial_token_.Get();
+ base::win::ScopedHandle app_container_token;
+ if (GetAppContainerImpersonationToken(
+ process_info.process_handle(), impersonation_token,
+ impersonation_capabilities_, &app_container_token)) {
+ impersonation_token = app_container_token.Get();
+ }
+
+ // Change the token of the main thread of the new process for the
+ // impersonation token with more rights. This allows the target to start;
+ // otherwise it will crash too early for us to help.
+ HANDLE temp_thread = process_info.thread_handle();
+ if (!::SetThreadToken(&temp_thread, impersonation_token)) {
+ *win_error = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_SET_THREAD_TOKEN;
+ }
+ initial_token_.Close();
+ }
+
+ if (!target_info->DuplicateFrom(process_info)) {
+ *win_error = ::GetLastError(); // This may or may not be correct.
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_DUPLICATE_TARGET_INFO;
+ }
+
+ base_address_ = GetProcessBaseAddress(process_info.process_handle());
+ DCHECK(base_address_);
+ if (!base_address_) {
+ *win_error = ::GetLastError();
+ ::TerminateProcess(process_info.process_handle(), 0);
+ return SBOX_ERROR_CANNOT_FIND_BASE_ADDRESS;
+ }
+
+ sandbox_process_info_.Set(process_info.Take());
+ return SBOX_ALL_OK;
+}
+
+ResultCode TargetProcess::TransferVariable(const char* name,
+ void* address,
+ size_t size) {
+ if (!sandbox_process_info_.IsValid())
+ return SBOX_ERROR_UNEXPECTED_CALL;
+
+ void* child_var = address;
+
+#if SANDBOX_EXPORTS
+ HMODULE module = ::LoadLibrary(exe_name_.get());
+ if (!module)
+ return SBOX_ERROR_CANNOT_LOADLIBRARY_EXECUTABLE;
+
+ child_var = reinterpret_cast<void*>(::GetProcAddress(module, name));
+ ::FreeLibrary(module);
+
+ if (!child_var)
+ return SBOX_ERROR_CANNOT_FIND_VARIABLE_ADDRESS;
+
+ size_t offset =
+ reinterpret_cast<char*>(child_var) - reinterpret_cast<char*>(module);
+ child_var = reinterpret_cast<char*>(MainModule()) + offset;
+#endif
+
+ SIZE_T written;
+ if (!::WriteProcessMemory(sandbox_process_info_.process_handle(), child_var,
+ address, size, &written))
+ return SBOX_ERROR_CANNOT_WRITE_VARIABLE_VALUE;
+
+ if (written != size)
+ return SBOX_ERROR_INVALID_WRITE_VARIABLE_SIZE;
+
+ return SBOX_ALL_OK;
+}
+
+// Construct the IPC server and the IPC dispatcher. When the target does
+// an IPC it will eventually call the dispatcher.
+ResultCode TargetProcess::Init(Dispatcher* ipc_dispatcher,
+ void* policy,
+ uint32_t shared_IPC_size,
+ uint32_t shared_policy_size,
+ DWORD* win_error) {
+ // We need to map the shared memory on the target. This is necessary for
+ // any IPC that needs to take place, even if the target has not yet hit
+ // the main( ) function or even has initialized the CRT. So here we set
+ // the handle to the shared section. The target on the first IPC must do
+ // the rest, which boils down to calling MapViewofFile()
+
+ // We use this single memory pool for IPC and for policy.
+ DWORD shared_mem_size =
+ static_cast<DWORD>(shared_IPC_size + shared_policy_size);
+ shared_section_.Set(::CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr,
+ PAGE_READWRITE | SEC_COMMIT, 0,
+ shared_mem_size, nullptr));
+ if (!shared_section_.IsValid()) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_CREATE_FILE_MAPPING;
+ }
+
+ void* shared_memory = ::MapViewOfFile(
+ shared_section_.Get(), FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, 0);
+ if (!shared_memory) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_MAP_VIEW_OF_SHARED_SECTION;
+ }
+
+ CopyPolicyToTarget(policy, shared_policy_size,
+ reinterpret_cast<char*>(shared_memory) + shared_IPC_size);
+
+ ResultCode ret;
+ // Set the global variables in the target. These are not used on the broker.
+ g_shared_IPC_size = shared_IPC_size;
+ ret = TransferVariable("g_shared_IPC_size", &g_shared_IPC_size,
+ sizeof(g_shared_IPC_size));
+ g_shared_IPC_size = 0;
+ if (SBOX_ALL_OK != ret) {
+ *win_error = ::GetLastError();
+ return ret;
+ }
+ g_shared_policy_size = shared_policy_size;
+ ret = TransferVariable("g_shared_policy_size", &g_shared_policy_size,
+ sizeof(g_shared_policy_size));
+ g_shared_policy_size = 0;
+ if (SBOX_ALL_OK != ret) {
+ *win_error = ::GetLastError();
+ return ret;
+ }
+
+ ipc_server_.reset(new SharedMemIPCServer(
+ sandbox_process_info_.process_handle(),
+ sandbox_process_info_.process_id(), thread_pool_, ipc_dispatcher));
+
+ if (!ipc_server_->Init(shared_memory, shared_IPC_size, kIPCChannelSize))
+ return SBOX_ERROR_NO_SPACE;
+
+ DWORD access = FILE_MAP_READ | FILE_MAP_WRITE | SECTION_QUERY;
+ HANDLE target_shared_section;
+ if (!::DuplicateHandle(::GetCurrentProcess(), shared_section_.Get(),
+ sandbox_process_info_.process_handle(),
+ &target_shared_section, access, false, 0)) {
+ *win_error = ::GetLastError();
+ return SBOX_ERROR_DUPLICATE_SHARED_SECTION;
+ }
+
+ g_shared_section = target_shared_section;
+ ret = TransferVariable("g_shared_section", &g_shared_section,
+ sizeof(g_shared_section));
+ g_shared_section = nullptr;
+ if (SBOX_ALL_OK != ret) {
+ *win_error = ::GetLastError();
+ return ret;
+ }
+
+ // After this point we cannot use this handle anymore.
+ ::CloseHandle(sandbox_process_info_.TakeThreadHandle());
+
+ return SBOX_ALL_OK;
+}
+
+void TargetProcess::Terminate() {
+ if (!sandbox_process_info_.IsValid())
+ return;
+
+ ::TerminateProcess(sandbox_process_info_.process_handle(), 0);
+}
+
+ResultCode TargetProcess::AssignLowBoxToken(
+ const base::win::ScopedHandle& token) {
+ if (!token.IsValid())
+ return SBOX_ALL_OK;
+ PROCESS_ACCESS_TOKEN process_access_token = {};
+ process_access_token.token = token.Get();
+
+ NtSetInformationProcess SetInformationProcess = nullptr;
+ ResolveNTFunctionPtr("NtSetInformationProcess", &SetInformationProcess);
+
+ NTSTATUS status = SetInformationProcess(
+ sandbox_process_info_.process_handle(),
+ static_cast<PROCESS_INFORMATION_CLASS>(NtProcessInformationAccessToken),
+ &process_access_token, sizeof(process_access_token));
+ if (!NT_SUCCESS(status)) {
+ ::SetLastError(GetLastErrorFromNtStatus(status));
+ return SBOX_ERROR_SET_LOW_BOX_TOKEN;
+ }
+ return SBOX_ALL_OK;
+}
+
+TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address) {
+ TargetProcess* target =
+ new TargetProcess(base::win::ScopedHandle(), base::win::ScopedHandle(),
+ nullptr, nullptr, std::vector<Sid>());
+ PROCESS_INFORMATION process_info = {};
+ process_info.hProcess = process;
+ target->sandbox_process_info_.Set(process_info);
+ target->base_address_ = base_address;
+ return target;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/target_process.h b/security/sandbox/chromium/sandbox/win/src/target_process.h
new file mode 100644
index 0000000000..c489ed7bc1
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_process.h
@@ -0,0 +1,143 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_WIN_SRC_TARGET_PROCESS_H_
+#define SANDBOX_WIN_SRC_TARGET_PROCESS_H_
+
+#include <windows.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/environment.h"
+#include "base/memory/free_deleter.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace base {
+namespace win {
+
+class StartupInformation;
+
+} // namespace win
+} // namespace base
+
+namespace sandbox {
+
+class SharedMemIPCServer;
+class Sid;
+class ThreadProvider;
+
+// TargetProcess models a target instance (child process). Objects of this
+// class are owned by the Policy used to create them.
+class TargetProcess {
+ public:
+ // The constructor takes ownership of |initial_token| and |lockdown_token|
+ TargetProcess(base::win::ScopedHandle initial_token,
+ base::win::ScopedHandle lockdown_token,
+ HANDLE job,
+ ThreadProvider* thread_pool,
+ const std::vector<Sid>& impersonation_capabilities);
+ ~TargetProcess();
+
+ // TODO(cpu): Currently there does not seem to be a reason to implement
+ // reference counting for this class since is internal, but kept the
+ // the same interface so the interception framework does not need to be
+ // touched at this point.
+ void AddRef() {}
+ void Release() {}
+
+ // Creates the new target process. The process is created suspended.
+ ResultCode Create(const wchar_t* exe_path,
+ const wchar_t* command_line,
+ bool inherit_handles,
+ const base::win::StartupInformation& startup_info,
+ base::win::ScopedProcessInformation* target_info,
+ base::EnvironmentMap& env_map,
+ DWORD* win_error);
+
+ // Assign a new lowbox token to the process post creation. The process
+ // must still be in its initial suspended state, however this still
+ // might fail in the presence of third-party software.
+ ResultCode AssignLowBoxToken(const base::win::ScopedHandle& token);
+
+ // Destroys the target process.
+ void Terminate();
+
+ // Creates the IPC objects such as the BrokerDispatcher and the
+ // IPC server. The IPC server uses the services of the thread_pool.
+ ResultCode Init(Dispatcher* ipc_dispatcher,
+ void* policy,
+ uint32_t shared_IPC_size,
+ uint32_t shared_policy_size,
+ DWORD* win_error);
+
+ // Returns the handle to the target process.
+ HANDLE Process() const { return sandbox_process_info_.process_handle(); }
+
+ // Returns the handle to the job object that the target process belongs to.
+ HANDLE Job() const { return job_; }
+
+ // Returns the address of the target main exe. This is used by the
+ // interceptions framework.
+ HMODULE MainModule() const {
+ return reinterpret_cast<HMODULE>(base_address_);
+ }
+
+ // Returns the name of the executable.
+ const wchar_t* Name() const { return exe_name_.get(); }
+
+ // Returns the process id.
+ DWORD ProcessId() const { return sandbox_process_info_.process_id(); }
+
+ // Returns the handle to the main thread.
+ HANDLE MainThread() const { return sandbox_process_info_.thread_handle(); }
+
+ // Transfers variable at |address| of |size| bytes from broker to target.
+ ResultCode TransferVariable(const char* name, void* address, size_t size);
+
+ private:
+ // Details of the target process.
+ base::win::ScopedProcessInformation sandbox_process_info_;
+ // The token associated with the process. It provides the core of the
+ // sbox security.
+ base::win::ScopedHandle lockdown_token_;
+ // The token given to the initial thread so that the target process can
+ // start. It has more powers than the lockdown_token.
+ base::win::ScopedHandle initial_token_;
+ // Kernel handle to the shared memory used by the IPC server.
+ base::win::ScopedHandle shared_section_;
+ // Job object containing the target process.
+ HANDLE job_;
+ // Reference to the IPC subsystem.
+ std::unique_ptr<SharedMemIPCServer> ipc_server_;
+ // Provides the threads used by the IPC. This class does not own this pointer.
+ ThreadProvider* thread_pool_;
+ // Base address of the main executable
+ void* base_address_;
+ // Full name of the target executable.
+ std::unique_ptr<wchar_t, base::FreeDeleter> exe_name_;
+ /// List of capability sids for use when impersonating in an AC process.
+ std::vector<Sid> impersonation_capabilities_;
+
+ // Function used for testing.
+ friend TargetProcess* MakeTestTargetProcess(HANDLE process,
+ HMODULE base_address);
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TargetProcess);
+};
+
+// Creates a mock TargetProcess used for testing interceptions.
+// TODO(cpu): It seems that this method is not going to be used anymore.
+TargetProcess* MakeTestTargetProcess(HANDLE process, HMODULE base_address);
+
+} // namespace sandbox
+
+#endif // SANDBOX_WIN_SRC_TARGET_PROCESS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.cc b/security/sandbox/chromium/sandbox/win/src/target_services.cc
new file mode 100644
index 0000000000..a80e0106ef
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_services.cc
@@ -0,0 +1,264 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/target_services.h"
+
+#include <new>
+
+#include <process.h>
+#include <stdint.h>
+
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/crosscall_client.h"
+#include "sandbox/win/src/handle_closer_agent.h"
+#include "sandbox/win/src/handle_interception.h"
+#include "sandbox/win/src/heap_helper.h"
+#include "sandbox/win/src/line_break_interception.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/process_mitigations.h"
+#include "sandbox/win/src/restricted_token_utils.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+#include "sandbox/win/src/sandbox_types.h"
+#include "sandbox/win/src/sharedmem_ipc_client.h"
+
+namespace sandbox {
+namespace {
+
+// Flushing a cached key is triggered by just opening the key and closing the
+// resulting handle. RegDisablePredefinedCache() is the documented way to flush
+// HKCU so do not use it with this function.
+bool FlushRegKey(HKEY root) {
+ HKEY key;
+ if (ERROR_SUCCESS ==
+ ::RegOpenKeyExW(root, nullptr, 0, MAXIMUM_ALLOWED, &key)) {
+ if (ERROR_SUCCESS != ::RegCloseKey(key))
+ return false;
+ }
+ return true;
+}
+
+// This function forces advapi32.dll to release some internally cached handles
+// that were made during calls to RegOpenkey and RegOpenKeyEx if it is called
+// with a more restrictive token. Returns true if the flushing is succesful
+// although this behavior is undocumented and there is no guarantee that in
+// fact this will happen in future versions of windows.
+bool FlushCachedRegHandles() {
+ return (FlushRegKey(HKEY_LOCAL_MACHINE) && FlushRegKey(HKEY_CLASSES_ROOT) &&
+ FlushRegKey(HKEY_USERS));
+}
+
+// Cleans up this process if CSRSS will be disconnected, as this disconnection
+// is not supported Windows behavior.
+// Currently, this step requires closing a heap that this shared with csrss.exe.
+// Closing the ALPC Port handle to csrss.exe leaves this heap in an invalid
+// state. This causes problems if anyone enumerates the heap.
+bool CsrssDisconnectCleanup() {
+ HANDLE csr_port_heap = FindCsrPortHeap();
+ if (!csr_port_heap) {
+ DLOG(ERROR) << "Failed to find CSR Port heap handle";
+ return false;
+ }
+ HeapDestroy(csr_port_heap);
+ return true;
+}
+
+// Used by EnumSystemLocales for warming up.
+static BOOL CALLBACK EnumLocalesProcEx(LPWSTR lpLocaleString,
+ DWORD dwFlags,
+ LPARAM lParam) {
+ return TRUE;
+}
+
+// Additional warmup done just when CSRSS is being disconnected.
+bool CsrssDisconnectWarmup() {
+ return ::EnumSystemLocalesEx(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
+}
+
+// Checks if we have handle entries pending and runs the closer.
+// Updates is_csrss_connected based on which handle types are closed.
+bool CloseOpenHandles(bool* is_csrss_connected) {
+ if (HandleCloserAgent::NeedsHandlesClosed()) {
+ HandleCloserAgent handle_closer;
+ handle_closer.InitializeHandlesToClose(is_csrss_connected);
+ if (!*is_csrss_connected) {
+ if (!CsrssDisconnectWarmup() || !CsrssDisconnectCleanup()) {
+ return false;
+ }
+ }
+ if (!handle_closer.CloseHandles())
+ return false;
+ }
+ return true;
+}
+
+// Warm up language subsystems before the sandbox is turned on.
+// Tested on Win8.1 x64:
+// This needs to happen after RevertToSelf() is called, because (at least) in
+// the case of GetUserDefaultLCID() it checks the TEB to see if the process is
+// impersonating (TEB!IsImpersonating). If it is, the cached locale information
+// is not used, nor is it set. Therefore, calls after RevertToSelf() will not
+// have warmed-up values to use.
+bool WarmupWindowsLocales() {
+ // NOTE(liamjm): When last checked (Win 8.1 x64) it wasn't necessary to
+ // warmup all of these functions, but let's not assume that.
+ ::GetUserDefaultLangID();
+ ::GetUserDefaultLCID();
+ wchar_t localeName[LOCALE_NAME_MAX_LENGTH] = {0};
+ return (0 != ::GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH));
+}
+
+// Used as storage for g_target_services, because other allocation facilities
+// are not available early. We can't use a regular function static because on
+// VS2015, because the CRT tries to acquire a lock to guard initialization, but
+// this code runs before the CRT is initialized.
+char g_target_services_memory[sizeof(TargetServicesBase)];
+TargetServicesBase* g_target_services = nullptr;
+
+} // namespace
+
+SANDBOX_INTERCEPT IntegrityLevel g_shared_delayed_integrity_level =
+ INTEGRITY_LEVEL_LAST;
+SANDBOX_INTERCEPT MitigationFlags g_shared_delayed_mitigations = 0;
+
+TargetServicesBase::TargetServicesBase() {}
+
+ResultCode TargetServicesBase::Init() {
+ process_state_.SetInitCalled();
+ return SBOX_ALL_OK;
+}
+
+// Failure here is a breach of security so the process is terminated.
+void TargetServicesBase::LowerToken() {
+ if (ERROR_SUCCESS !=
+ SetProcessIntegrityLevel(g_shared_delayed_integrity_level))
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_INTEGRITY);
+ process_state_.SetRevertedToSelf();
+ // If the client code as called RegOpenKey, advapi32.dll has cached some
+ // handles. The following code gets rid of them.
+ if (!::RevertToSelf())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_DROPTOKEN);
+ if (!FlushCachedRegHandles())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_FLUSHANDLES);
+ if (ERROR_SUCCESS != ::RegDisablePredefinedCache())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CACHEDISABLE);
+ if (!WarmupWindowsLocales())
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_WARMUP);
+ bool is_csrss_connected = true;
+ if (!CloseOpenHandles(&is_csrss_connected))
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_CLOSEHANDLES);
+ process_state_.SetCsrssConnected(is_csrss_connected);
+ // Enabling mitigations must happen last otherwise handle closing breaks
+ if (g_shared_delayed_mitigations &&
+ !ApplyProcessMitigationsToCurrentProcess(g_shared_delayed_mitigations))
+ ::TerminateProcess(::GetCurrentProcess(), SBOX_FATAL_MITIGATION);
+}
+
+ProcessState* TargetServicesBase::GetState() {
+ return &process_state_;
+}
+
+TargetServicesBase* TargetServicesBase::GetInstance() {
+ // Leak on purpose TargetServicesBase.
+ if (!g_target_services)
+ g_target_services = new (g_target_services_memory) TargetServicesBase;
+ return g_target_services;
+}
+
+// The broker services a 'test' IPC service with the PING tag.
+bool TargetServicesBase::TestIPCPing(int version) {
+ void* memory = GetGlobalIPCMemory();
+ if (!memory)
+ return false;
+ SharedMemIPCClient ipc(memory);
+ CrossCallReturn answer = {0};
+
+ if (1 == version) {
+ uint32_t tick1 = ::GetTickCount();
+ uint32_t cookie = 717115;
+ ResultCode code = CrossCall(ipc, IpcTag::PING1, cookie, &answer);
+
+ if (SBOX_ALL_OK != code) {
+ return false;
+ }
+ // We should get two extended returns values from the IPC, one is the
+ // tick count on the broker and the other is the cookie times two.
+ if ((answer.extended_count != 2)) {
+ return false;
+ }
+ // We test the first extended answer to be within the bounds of the tick
+ // count only if there was no tick count wraparound.
+ uint32_t tick2 = ::GetTickCount();
+ if (tick2 >= tick1) {
+ if ((answer.extended[0].unsigned_int < tick1) ||
+ (answer.extended[0].unsigned_int > tick2)) {
+ return false;
+ }
+ }
+
+ if (answer.extended[1].unsigned_int != cookie * 2) {
+ return false;
+ }
+ } else if (2 == version) {
+ uint32_t cookie = 717111;
+ InOutCountedBuffer counted_buffer(&cookie, sizeof(cookie));
+ ResultCode code = CrossCall(ipc, IpcTag::PING2, counted_buffer, &answer);
+
+ if (SBOX_ALL_OK != code) {
+ return false;
+ }
+ if (cookie != 717111 * 3) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
+
+ProcessState::ProcessState()
+ : process_state_(ProcessStateInternal::NONE), csrss_connected_(true) {}
+
+bool ProcessState::InitCalled() const {
+ return process_state_ >= ProcessStateInternal::INIT_CALLED;
+}
+
+bool ProcessState::RevertedToSelf() const {
+ return process_state_ >= ProcessStateInternal::REVERTED_TO_SELF;
+}
+
+bool ProcessState::IsCsrssConnected() const {
+ return csrss_connected_;
+}
+
+void ProcessState::SetInitCalled() {
+ if (process_state_ < ProcessStateInternal::INIT_CALLED)
+ process_state_ = ProcessStateInternal::INIT_CALLED;
+}
+
+void ProcessState::SetRevertedToSelf() {
+ if (process_state_ < ProcessStateInternal::REVERTED_TO_SELF)
+ process_state_ = ProcessStateInternal::REVERTED_TO_SELF;
+}
+
+void ProcessState::SetCsrssConnected(bool csrss_connected) {
+ csrss_connected_ = csrss_connected;
+}
+
+ResultCode TargetServicesBase::DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) {
+ return sandbox::DuplicateHandleProxy(source_handle, target_process_id,
+ target_handle, desired_access, options);
+}
+
+ResultCode TargetServicesBase::GetComplexLineBreaks(const WCHAR* text,
+ uint32_t length,
+ uint8_t* break_before) {
+ return sandbox::GetComplexLineBreaksProxy(text, length, break_before);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/target_services.h b/security/sandbox/chromium/sandbox/win/src/target_services.h
new file mode 100644
index 0000000000..1d70d4cd34
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/target_services.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_TARGET_SERVICES_H__
+#define SANDBOX_SRC_TARGET_SERVICES_H__
+
+#include "base/macros.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+class ProcessState {
+ public:
+ ProcessState();
+ // Returns true if main has been called.
+ bool InitCalled() const;
+ // Returns true if LowerToken has been called.
+ bool RevertedToSelf() const;
+ // Returns true if Csrss is connected.
+ bool IsCsrssConnected() const;
+ // Set the current state.
+ void SetInitCalled();
+ void SetRevertedToSelf();
+ void SetCsrssConnected(bool csrss_connected);
+
+ private:
+ enum class ProcessStateInternal { NONE = 0, INIT_CALLED, REVERTED_TO_SELF };
+
+ ProcessStateInternal process_state_;
+ bool csrss_connected_;
+ DISALLOW_COPY_AND_ASSIGN(ProcessState);
+};
+
+// This class is an implementation of the TargetServices.
+// Look in the documentation of sandbox::TargetServices for more info.
+// Do NOT add a destructor to this class without changing the implementation of
+// the factory method.
+class TargetServicesBase : public TargetServices {
+ public:
+ TargetServicesBase();
+
+ // Public interface of TargetServices.
+ ResultCode Init() override;
+ void LowerToken() override;
+ ProcessState* GetState() override;
+ ResultCode DuplicateHandle(HANDLE source_handle,
+ DWORD target_process_id,
+ HANDLE* target_handle,
+ DWORD desired_access,
+ DWORD options) override;
+ ResultCode GetComplexLineBreaks(const WCHAR* text, uint32_t length,
+ uint8_t* break_before) final;
+
+ // Factory method.
+ static TargetServicesBase* GetInstance();
+
+ // Sends a simple IPC Message that has a well-known answer. Returns true
+ // if the IPC was successful and false otherwise. There are 2 versions of
+ // this test: 1 and 2. The first one send a simple message while the
+ // second one send a message with an in/out param.
+ bool TestIPCPing(int version);
+
+ private:
+ ~TargetServicesBase() {}
+ ProcessState process_state_;
+ DISALLOW_COPY_AND_ASSIGN(TargetServicesBase);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_TARGET_SERVICES_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc b/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc
new file mode 100644
index 0000000000..3f951b761b
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/threadpool_unittest.cc
@@ -0,0 +1,97 @@
+// Copyright (c) 2006-2008 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/win2k_threadpool.h"
+
+#include <stdint.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+void __stdcall EmptyCallBack(void*, unsigned char) {}
+
+void __stdcall TestCallBack(void* context, unsigned char) {
+ HANDLE event = reinterpret_cast<HANDLE>(context);
+ ::SetEvent(event);
+}
+
+namespace sandbox {
+
+// Test that register and unregister work, part 1.
+TEST(IPCTest, ThreadPoolRegisterTest1) {
+ Win2kThreadPool thread_pool;
+
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ HANDLE event1 = ::CreateEventW(nullptr, false, false, nullptr);
+ HANDLE event2 = ::CreateEventW(nullptr, false, false, nullptr);
+
+ uint32_t context = 0;
+ EXPECT_FALSE(thread_pool.RegisterWait(0, event1, EmptyCallBack, &context));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event1, EmptyCallBack, &context));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event2, EmptyCallBack, &context));
+ EXPECT_EQ(2u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(this));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(::CloseHandle(event1));
+ EXPECT_TRUE(::CloseHandle(event2));
+}
+
+// Test that register and unregister work, part 2.
+TEST(IPCTest, ThreadPoolRegisterTest2) {
+ Win2kThreadPool thread_pool;
+
+ HANDLE event1 = ::CreateEventW(nullptr, false, false, nullptr);
+ HANDLE event2 = ::CreateEventW(nullptr, false, false, nullptr);
+
+ uint32_t context = 0;
+ uint32_t c1 = 0;
+ uint32_t c2 = 0;
+
+ EXPECT_TRUE(thread_pool.RegisterWait(&c1, event1, EmptyCallBack, &context));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.RegisterWait(&c2, event2, EmptyCallBack, &context));
+ EXPECT_EQ(2u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c2));
+ EXPECT_EQ(1u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(&c1));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_TRUE(::CloseHandle(event1));
+ EXPECT_TRUE(::CloseHandle(event2));
+}
+
+// Test that the thread pool has at least a thread that services an event.
+// Test that when the event is un-registered is no longer serviced.
+TEST(IPCTest, ThreadPoolSignalAndWaitTest) {
+ Win2kThreadPool thread_pool;
+
+ // The events are auto reset and start not signaled.
+ HANDLE event1 = ::CreateEventW(nullptr, false, false, nullptr);
+ HANDLE event2 = ::CreateEventW(nullptr, false, false, nullptr);
+
+ EXPECT_TRUE(thread_pool.RegisterWait(this, event1, TestCallBack, event2));
+
+ EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, false));
+ EXPECT_EQ(WAIT_OBJECT_0, ::SignalObjectAndWait(event1, event2, 5000, false));
+
+ EXPECT_TRUE(thread_pool.UnRegisterWaits(this));
+ EXPECT_EQ(0u, thread_pool.OutstandingWaits());
+
+ EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT),
+ ::SignalObjectAndWait(event1, event2, 1000, false));
+
+ EXPECT_TRUE(::CloseHandle(event1));
+ EXPECT_TRUE(::CloseHandle(event2));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
new file mode 100644
index 0000000000..3c8f8e25e5
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.cc
@@ -0,0 +1,178 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/top_level_dispatcher.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "base/logging.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/filesystem_dispatcher.h"
+#include "sandbox/win/src/handle_dispatcher.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/line_break_dispatcher.h"
+#include "sandbox/win/src/named_pipe_dispatcher.h"
+#include "sandbox/win/src/process_mitigations_win32k_dispatcher.h"
+#include "sandbox/win/src/process_thread_dispatcher.h"
+#include "sandbox/win/src/registry_dispatcher.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+#include "sandbox/win/src/signed_dispatcher.h"
+#include "sandbox/win/src/sync_dispatcher.h"
+
+namespace sandbox {
+
+TopLevelDispatcher::TopLevelDispatcher(PolicyBase* policy) : policy_(policy) {
+ // Initialize the IPC dispatcher array.
+ memset(ipc_targets_, 0, sizeof(ipc_targets_));
+ Dispatcher* dispatcher;
+
+ dispatcher = new FilesystemDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTCREATEFILE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENFILE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTSETINFO_RENAME)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTQUERYATTRIBUTESFILE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTQUERYFULLATTRIBUTESFILE)] =
+ dispatcher;
+ filesystem_dispatcher_.reset(dispatcher);
+
+ dispatcher = new NamedPipeDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATENAMEDPIPEW)] = dispatcher;
+ named_pipe_dispatcher_.reset(dispatcher);
+
+ dispatcher = new ThreadProcessDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENTHREAD)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENPROCESS)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATEPROCESSW)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENPROCESSTOKEN)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENPROCESSTOKENEX)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATETHREAD)] = dispatcher;
+ thread_process_dispatcher_.reset(dispatcher);
+
+ dispatcher = new SyncDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::CREATEEVENT)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::OPENEVENT)] = dispatcher;
+ sync_dispatcher_.reset(dispatcher);
+
+ dispatcher = new RegistryDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTCREATEKEY)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::NTOPENKEY)] = dispatcher;
+ registry_dispatcher_.reset(dispatcher);
+
+ dispatcher = new HandleDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::DUPLICATEHANDLEPROXY)] = dispatcher;
+ handle_dispatcher_.reset(dispatcher);
+
+ dispatcher = new ProcessMitigationsWin32KDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GDIDLLINITIALIZE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETSTOCKOBJECT)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_REGISTERCLASSW)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_ENUMDISPLAYMONITORS)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_ENUMDISPLAYDEVICES)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::USER_GETMONITORINFO)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_CREATEOPMPROTECTEDOUTPUTS)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETCERTIFICATE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETCERTIFICATESIZE)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_DESTROYOPMPROTECTEDOUTPUT)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_CONFIGUREOPMPROTECTEDOUTPUT)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETOPMINFORMATION)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(IpcTag::GDI_GETOPMRANDOMNUMBER)] =
+ dispatcher;
+ ipc_targets_[static_cast<size_t>(
+ IpcTag::GDI_GETSUGGESTEDOPMPROTECTEDOUTPUTARRAYSIZE)] = dispatcher;
+ ipc_targets_[static_cast<size_t>(
+ IpcTag::GDI_SETOPMSIGNINGKEYANDSEQUENCENUMBERS)] = dispatcher;
+ process_mitigations_win32k_dispatcher_.reset(dispatcher);
+
+ dispatcher = new SignedDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::NTCREATESECTION)] = dispatcher;
+ signed_dispatcher_.reset(dispatcher);
+
+ dispatcher = new LineBreakDispatcher(policy_);
+ ipc_targets_[static_cast<size_t>(IpcTag::GETCOMPLEXLINEBREAKS)] = dispatcher;
+ line_break_dispatcher_.reset(dispatcher);
+}
+
+TopLevelDispatcher::~TopLevelDispatcher() {}
+
+// When an IPC is ready in any of the targets we get called. We manage an array
+// of IPC dispatchers which are keyed on the IPC tag so we normally delegate
+// to the appropriate dispatcher unless we can handle the IPC call ourselves.
+Dispatcher* TopLevelDispatcher::OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) {
+ DCHECK(callback);
+ static const IPCParams ping1 = {IpcTag::PING1, {UINT32_TYPE}};
+ static const IPCParams ping2 = {IpcTag::PING2, {INOUTPTR_TYPE}};
+
+ if (ping1.Matches(ipc) || ping2.Matches(ipc)) {
+ *callback = reinterpret_cast<CallbackGeneric>(
+ static_cast<Callback1>(&TopLevelDispatcher::Ping));
+ return this;
+ }
+
+ Dispatcher* dispatcher = GetDispatcher(ipc->ipc_tag);
+ if (!dispatcher) {
+ NOTREACHED();
+ return nullptr;
+ }
+ return dispatcher->OnMessageReady(ipc, callback);
+}
+
+// Delegate to the appropriate dispatcher.
+bool TopLevelDispatcher::SetupService(InterceptionManager* manager,
+ IpcTag service) {
+ if (IpcTag::PING1 == service || IpcTag::PING2 == service)
+ return true;
+
+ Dispatcher* dispatcher = GetDispatcher(service);
+ if (!dispatcher) {
+ NOTREACHED();
+ return false;
+ }
+ return dispatcher->SetupService(manager, service);
+}
+
+// We service PING message which is a way to test a round trip of the
+// IPC subsystem. We receive a integer cookie and we are expected to return the
+// cookie times two (or three) and the current tick count.
+bool TopLevelDispatcher::Ping(IPCInfo* ipc, void* arg1) {
+ switch (ipc->ipc_tag) {
+ case IpcTag::PING1: {
+ IPCInt ipc_int(arg1);
+ uint32_t cookie = ipc_int.As32Bit();
+ ipc->return_info.extended_count = 2;
+ ipc->return_info.extended[0].unsigned_int = ::GetTickCount();
+ ipc->return_info.extended[1].unsigned_int = 2 * cookie;
+ return true;
+ }
+ case IpcTag::PING2: {
+ CountedBuffer* io_buffer = reinterpret_cast<CountedBuffer*>(arg1);
+ if (sizeof(uint32_t) != io_buffer->Size())
+ return false;
+
+ uint32_t* cookie = reinterpret_cast<uint32_t*>(io_buffer->Buffer());
+ *cookie = (*cookie) * 3;
+ return true;
+ }
+ default:
+ return false;
+ }
+}
+
+Dispatcher* TopLevelDispatcher::GetDispatcher(IpcTag ipc_tag) {
+ if (ipc_tag >= IpcTag::LAST || ipc_tag <= IpcTag::UNUSED)
+ return nullptr;
+
+ return ipc_targets_[static_cast<size_t>(ipc_tag)];
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h
new file mode 100644
index 0000000000..a80ce1ef5d
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/top_level_dispatcher.h
@@ -0,0 +1,54 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_TOP_LEVEL_DISPATCHER_H__
+#define SANDBOX_SRC_TOP_LEVEL_DISPATCHER_H__
+
+#include <memory>
+
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+#include "sandbox/win/src/interception.h"
+#include "sandbox/win/src/ipc_tags.h"
+#include "sandbox/win/src/sandbox_policy_base.h"
+
+namespace sandbox {
+
+// Top level dispatcher which hands requests to the appropriate service
+// dispatchers.
+class TopLevelDispatcher : public Dispatcher {
+ public:
+ // |policy| must outlive this class.
+ explicit TopLevelDispatcher(PolicyBase* policy);
+ ~TopLevelDispatcher() override;
+
+ Dispatcher* OnMessageReady(IPCParams* ipc,
+ CallbackGeneric* callback) override;
+ bool SetupService(InterceptionManager* manager, IpcTag service) override;
+
+ private:
+ // Test IPC provider.
+ bool Ping(IPCInfo* ipc, void* cookie);
+
+ // Returns a dispatcher from ipc_targets_.
+ Dispatcher* GetDispatcher(IpcTag ipc_tag);
+
+ PolicyBase* policy_;
+ std::unique_ptr<Dispatcher> filesystem_dispatcher_;
+ std::unique_ptr<Dispatcher> named_pipe_dispatcher_;
+ std::unique_ptr<Dispatcher> thread_process_dispatcher_;
+ std::unique_ptr<Dispatcher> sync_dispatcher_;
+ std::unique_ptr<Dispatcher> registry_dispatcher_;
+ std::unique_ptr<Dispatcher> handle_dispatcher_;
+ std::unique_ptr<Dispatcher> process_mitigations_win32k_dispatcher_;
+ std::unique_ptr<Dispatcher> signed_dispatcher_;
+ std::unique_ptr<Dispatcher> line_break_dispatcher_;
+ Dispatcher* ipc_targets_[kMaxIpcTag];
+
+ DISALLOW_COPY_AND_ASSIGN(TopLevelDispatcher);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_TOP_LEVEL_DISPATCHER_H__
diff --git a/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc b/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc
new file mode 100644
index 0000000000..0acb178987
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/unload_dll_test.cc
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/win/scoped_handle.h"
+#include "build/build_config.h"
+#include "sandbox/win/src/sandbox.h"
+#include "sandbox/win/src/sandbox_factory.h"
+#include "sandbox/win/src/target_services.h"
+#include "sandbox/win/tests/common/controller.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+// Loads and or unloads a DLL passed in the second parameter of argv.
+// The first parameter of argv is 'L' = load, 'U' = unload or 'B' for both.
+SBOX_TESTS_COMMAND int UseOneDLL(int argc, wchar_t** argv) {
+ if (argc != 2)
+ return SBOX_TEST_FAILED_TO_RUN_TEST;
+ int rv = SBOX_TEST_FAILED_TO_RUN_TEST;
+
+ wchar_t option = (argv[0])[0];
+ if ((option == L'L') || (option == L'B')) {
+ HMODULE module1 = ::LoadLibraryW(argv[1]);
+ rv = (!module1) ? SBOX_TEST_FAILED : SBOX_TEST_SUCCEEDED;
+ }
+
+ if ((option == L'U') || (option == L'B')) {
+ HMODULE module2 = ::GetModuleHandleW(argv[1]);
+ rv = ::FreeLibrary(module2) ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+ }
+ return rv;
+}
+
+// Opens an event passed as the first parameter of argv.
+SBOX_TESTS_COMMAND int SimpleOpenEvent(int argc, wchar_t** argv) {
+ if (argc != 1)
+ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND;
+
+ base::win::ScopedHandle event_open(::OpenEvent(SYNCHRONIZE, false, argv[0]));
+ return event_open.Get() ? SBOX_TEST_SUCCEEDED : SBOX_TEST_FAILED;
+}
+
+// Fails on Windows ARM64: https://crbug.com/905526
+#if defined(ARCH_CPU_ARM64)
+#define MAYBE_BaselineAvicapDll DISABLED_BaselineAvicapDll
+#else
+#define MAYBE_BaselineAvicapDll BaselineAvicapDll
+#endif
+TEST(UnloadDllTest, MAYBE_BaselineAvicapDll) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ runner.SetTimeout(2000);
+ // Add a sync rule, because that ensures that the interception agent has
+ // more than one item in its internal table.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"t0001"));
+
+ // Note for the puzzled: avicap32.dll is a 64-bit dll in 64-bit versions of
+ // windows so this test and the others just work.
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"UseOneDLL B avicap32.dll"));
+}
+
+TEST(UnloadDllTest, UnloadAviCapDllNoPatching) {
+ TestRunner runner;
+ runner.SetTestState(BEFORE_REVERT);
+ runner.SetTimeout(2000);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->AddDllToUnload(L"avicap32.dll");
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL B avicap32.dll"));
+}
+
+TEST(UnloadDllTest, UnloadAviCapDllWithPatching) {
+ TestRunner runner;
+ runner.SetTimeout(2000);
+ runner.SetTestState(BEFORE_REVERT);
+ sandbox::TargetPolicy* policy = runner.GetPolicy();
+ policy->AddDllToUnload(L"avicap32.dll");
+
+ base::win::ScopedHandle handle1(
+ ::CreateEvent(nullptr, false, false, L"tst0001"));
+
+ // Add a couple of rules that ensures that the interception agent add EAT
+ // patching on the client which makes sure that the unload dll record does
+ // not interact badly with them.
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_REGISTRY,
+ TargetPolicy::REG_ALLOW_ANY,
+ L"HKEY_LOCAL_MACHINE\\Software\\Microsoft"));
+ EXPECT_TRUE(runner.AddRule(TargetPolicy::SUBSYS_SYNC,
+ TargetPolicy::EVENTS_ALLOW_ANY, L"tst0001"));
+
+ EXPECT_EQ(SBOX_TEST_FAILED, runner.RunTest(L"UseOneDLL L avicap32.dll"));
+
+ runner.SetTestState(AFTER_REVERT);
+ EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"SimpleOpenEvent tst0001"));
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc
new file mode 100644
index 0000000000..49cc68bb00
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "sandbox/win/src/win2k_threadpool.h"
+
+#include <stddef.h>
+
+#include "sandbox/win/src/win_utils.h"
+
+namespace sandbox {
+
+Win2kThreadPool::Win2kThreadPool() {
+ ::InitializeCriticalSection(&lock_);
+}
+
+bool Win2kThreadPool::RegisterWait(const void* cookie,
+ HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) {
+ if (0 == cookie) {
+ return false;
+ }
+ HANDLE pool_object = nullptr;
+ // create a wait for a kernel object, with no timeout
+ if (!::RegisterWaitForSingleObject(&pool_object, waitable_object, callback,
+ context, INFINITE, WT_EXECUTEDEFAULT)) {
+ return false;
+ }
+ PoolObject pool_obj = {cookie, pool_object};
+ AutoLock lock(&lock_);
+ pool_objects_.push_back(pool_obj);
+ return true;
+}
+
+bool Win2kThreadPool::UnRegisterWaits(void* cookie) {
+ if (0 == cookie) {
+ return false;
+ }
+ AutoLock lock(&lock_);
+ bool success = true;
+ PoolObjects::iterator it = pool_objects_.begin();
+ while (it != pool_objects_.end()) {
+ if (it->cookie == cookie) {
+ HANDLE wait = it->wait;
+ it = pool_objects_.erase(it);
+ success &= (::UnregisterWaitEx(wait, INVALID_HANDLE_VALUE) != 0);
+ } else {
+ ++it;
+ }
+ }
+ return success;
+}
+
+size_t Win2kThreadPool::OutstandingWaits() {
+ AutoLock lock(&lock_);
+ return pool_objects_.size();
+}
+
+Win2kThreadPool::~Win2kThreadPool() {
+ // Here we used to unregister all the pool wait handles. Now, following the
+ // rest of the code we avoid lengthy or blocking calls given that the process
+ // is being torn down.
+ ::DeleteCriticalSection(&lock_);
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h
new file mode 100644
index 0000000000..c4d539dd7f
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win2k_threadpool.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SANDBOX_SRC_WIN2K_THREADPOOL_H_
+#define SANDBOX_SRC_WIN2K_THREADPOOL_H_
+
+#include <stddef.h>
+
+#include <algorithm>
+#include <list>
+#include "base/macros.h"
+#include "sandbox/win/src/crosscall_server.h"
+
+namespace sandbox {
+
+// Win2kThreadPool a simple implementation of a thread provider as required
+// for the sandbox IPC subsystem. See sandbox\crosscall_server.h for the details
+// and requirements of this interface.
+//
+// Implementing the thread provider as a thread pool is desirable in the case
+// of shared memory IPC because it can generate a large number of waitable
+// events: as many as channels. A thread pool does not create a thread per
+// event, instead maintains a few idle threads but can create more if the need
+// arises.
+//
+// This implementation simply thunks to the nice thread pool API of win2k.
+class Win2kThreadPool : public ThreadProvider {
+ public:
+ Win2kThreadPool();
+ ~Win2kThreadPool() override;
+
+ bool RegisterWait(const void* cookie,
+ HANDLE waitable_object,
+ CrossCallIPCCallback callback,
+ void* context) override;
+
+ bool UnRegisterWaits(void* cookie) override;
+
+ // Returns the total number of wait objects associated with
+ // the thread pool.
+ size_t OutstandingWaits();
+
+ private:
+ // record to keep track of a wait and its associated cookie.
+ struct PoolObject {
+ const void* cookie;
+ HANDLE wait;
+ };
+ // The list of pool wait objects.
+ typedef std::list<PoolObject> PoolObjects;
+ PoolObjects pool_objects_;
+ // This lock protects the list of pool wait objects.
+ CRITICAL_SECTION lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(Win2kThreadPool);
+};
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WIN2K_THREADPOOL_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.cc b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
new file mode 100644
index 0000000000..8fb345a239
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win_utils.cc
@@ -0,0 +1,619 @@
+// Copyright (c) 2011 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/win_utils.h"
+
+#include <psapi.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/numerics/safe_math.h"
+#include "base/stl_util.h"
+#include "base/strings/string_util.h"
+#include "base/win/pe_image.h"
+#include "sandbox/win/src/internal_types.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/src/sandbox_nt_util.h"
+
+namespace {
+
+const size_t kDriveLetterLen = 3;
+
+constexpr wchar_t kNTDotPrefix[] = L"\\\\.\\";
+const size_t kNTDotPrefixLen = base::size(kNTDotPrefix) - 1;
+
+// Holds the information about a known registry key.
+struct KnownReservedKey {
+ const wchar_t* name;
+ HKEY key;
+};
+
+// Contains all the known registry key by name and by handle.
+const KnownReservedKey kKnownKey[] = {
+ {L"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT},
+ {L"HKEY_CURRENT_USER", HKEY_CURRENT_USER},
+ {L"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE},
+ {L"HKEY_USERS", HKEY_USERS},
+ {L"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA},
+ {L"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT},
+ {L"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT},
+ {L"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG},
+ {L"HKEY_DYN_DATA", HKEY_DYN_DATA}};
+
+// These functions perform case independent path comparisons.
+bool EqualPath(const std::wstring& first, const std::wstring& second) {
+ return _wcsicmp(first.c_str(), second.c_str()) == 0;
+}
+
+bool EqualPath(const std::wstring& first,
+ size_t first_offset,
+ const std::wstring& second,
+ size_t second_offset) {
+ return _wcsicmp(first.c_str() + first_offset,
+ second.c_str() + second_offset) == 0;
+}
+
+bool EqualPath(const std::wstring& first,
+ const wchar_t* second,
+ size_t second_len) {
+ return _wcsnicmp(first.c_str(), second, second_len) == 0;
+}
+
+bool EqualPath(const std::wstring& first,
+ size_t first_offset,
+ const wchar_t* second,
+ size_t second_len) {
+ return _wcsnicmp(first.c_str() + first_offset, second, second_len) == 0;
+}
+
+// Returns true if |path| starts with "\??\" and returns a path without that
+// component.
+bool IsNTPath(const std::wstring& path, std::wstring* trimmed_path) {
+ if ((path.size() < sandbox::kNTPrefixLen) ||
+ !EqualPath(path, sandbox::kNTPrefix, sandbox::kNTPrefixLen)) {
+ *trimmed_path = path;
+ return false;
+ }
+
+ *trimmed_path = path.substr(sandbox::kNTPrefixLen);
+ return true;
+}
+
+// Returns true if |path| starts with "\Device\" and returns a path without that
+// component.
+bool IsDevicePath(const std::wstring& path, std::wstring* trimmed_path) {
+ if ((path.size() < sandbox::kNTDevicePrefixLen) ||
+ (!EqualPath(path, sandbox::kNTDevicePrefix,
+ sandbox::kNTDevicePrefixLen))) {
+ *trimmed_path = path;
+ return false;
+ }
+
+ *trimmed_path = path.substr(sandbox::kNTDevicePrefixLen);
+ return true;
+}
+
+// Returns the offset to the path seperator following
+// "\Device\HarddiskVolumeX" in |path|.
+size_t PassHarddiskVolume(const std::wstring& path) {
+ static constexpr wchar_t pattern[] = L"\\Device\\HarddiskVolume";
+ const size_t patternLen = base::size(pattern) - 1;
+
+ // First, check for |pattern|.
+ if ((path.size() < patternLen) || (!EqualPath(path, pattern, patternLen)))
+ return std::wstring::npos;
+
+ // Find the next path separator, after the pattern match.
+ return path.find_first_of(L'\\', patternLen - 1);
+}
+
+// Returns true if |path| starts with "\Device\HarddiskVolumeX\" and returns a
+// path without that component. |removed| will hold the prefix removed.
+bool IsDeviceHarddiskPath(const std::wstring& path,
+ std::wstring* trimmed_path,
+ std::wstring* removed) {
+ size_t offset = PassHarddiskVolume(path);
+ if (offset == std::wstring::npos)
+ return false;
+
+ // Remove up to and including the path separator.
+ *removed = path.substr(0, offset + 1);
+ // Remaining path starts after the path separator.
+ *trimmed_path = path.substr(offset + 1);
+ return true;
+}
+
+bool StartsWithDriveLetter(const std::wstring& path) {
+ if (path.size() < kDriveLetterLen)
+ return false;
+
+ if (path[1] != L':' || path[2] != L'\\')
+ return false;
+
+ return base::IsAsciiAlpha(path[0]);
+}
+
+// Removes "\\\\.\\" from the path.
+void RemoveImpliedDevice(std::wstring* path) {
+ if (EqualPath(*path, kNTDotPrefix, kNTDotPrefixLen))
+ *path = path->substr(kNTDotPrefixLen);
+}
+
+} // namespace
+
+namespace sandbox {
+
+// Returns true if the provided path points to a pipe.
+bool IsPipe(const std::wstring& path) {
+ size_t start = 0;
+ if (EqualPath(path, sandbox::kNTPrefix, sandbox::kNTPrefixLen))
+ start = sandbox::kNTPrefixLen;
+
+ const wchar_t kPipe[] = L"pipe\\";
+ if (path.size() < start + base::size(kPipe) - 1)
+ return false;
+
+ return EqualPath(path, start, kPipe, base::size(kPipe) - 1);
+}
+
+HKEY GetReservedKeyFromName(const std::wstring& name) {
+ for (size_t i = 0; i < base::size(kKnownKey); ++i) {
+ if (name == kKnownKey[i].name)
+ return kKnownKey[i].key;
+ }
+
+ return nullptr;
+}
+
+bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name) {
+ for (size_t i = 0; i < base::size(kKnownKey); ++i) {
+ if (name.find(kKnownKey[i].name) == 0) {
+ HKEY key;
+ DWORD disposition;
+ if (ERROR_SUCCESS != ::RegCreateKeyEx(kKnownKey[i].key, L"", 0, nullptr,
+ 0, MAXIMUM_ALLOWED, nullptr, &key,
+ &disposition))
+ return false;
+
+ bool result = GetPathFromHandle(key, resolved_name);
+ ::RegCloseKey(key);
+
+ if (!result)
+ return false;
+
+ *resolved_name += name.substr(wcslen(kKnownKey[i].name));
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// |full_path| can have any of the following forms:
+// \??\c:\some\foo\bar
+// \Device\HarddiskVolume0\some\foo\bar
+// \??\HarddiskVolume0\some\foo\bar
+// \??\UNC\SERVER\Share\some\foo\bar
+DWORD IsReparsePoint(const std::wstring& full_path) {
+ // Check if it's a pipe. We can't query the attributes of a pipe.
+ if (IsPipe(full_path))
+ return ERROR_NOT_A_REPARSE_POINT;
+
+ std::wstring path;
+ bool nt_path = IsNTPath(full_path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+ bool is_device_path = IsDevicePath(path, &path);
+
+ if (!has_drive && !is_device_path && !nt_path)
+ return ERROR_INVALID_NAME;
+
+ if (!has_drive) {
+ // Add Win32 device namespace prefix, required for some Windows APIs.
+ path.insert(0, kNTDotPrefix);
+ }
+
+ // Ensure that volume path matches start of path.
+ wchar_t vol_path[MAX_PATH];
+ if (!::GetVolumePathNameW(path.c_str(), vol_path, MAX_PATH)) {
+ // This will fail if this is a device that isn't volume related, which can't
+ // then be a reparse point.
+ return is_device_path ? ERROR_NOT_A_REPARSE_POINT : ERROR_INVALID_NAME;
+ }
+
+ // vol_path includes a trailing slash, so reduce size for path and loop check.
+ size_t vol_path_len = wcslen(vol_path) - 1;
+ if (!EqualPath(path, vol_path, vol_path_len)) {
+ return ERROR_INVALID_NAME;
+ }
+
+ do {
+ DWORD attributes = ::GetFileAttributes(path.c_str());
+ if (INVALID_FILE_ATTRIBUTES == attributes) {
+ DWORD error = ::GetLastError();
+ if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND &&
+ error != ERROR_INVALID_FUNCTION &&
+ error != ERROR_INVALID_NAME) {
+ // Unexpected error.
+ return error;
+ }
+ } else if (FILE_ATTRIBUTE_REPARSE_POINT & attributes) {
+ // This is a reparse point.
+ return ERROR_SUCCESS;
+ }
+
+ path.resize(path.rfind(L'\\'));
+ } while (path.size() > vol_path_len); // Skip root dir.
+
+ return ERROR_NOT_A_REPARSE_POINT;
+}
+
+// We get a |full_path| of the forms accepted by IsReparsePoint(), and the name
+// we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
+bool SameObject(HANDLE handle, const wchar_t* full_path) {
+ // Check if it's a pipe.
+ if (IsPipe(full_path))
+ return true;
+
+ std::wstring actual_path;
+ if (!GetPathFromHandle(handle, &actual_path))
+ return false;
+
+ std::wstring path(full_path);
+ DCHECK_NT(!path.empty());
+
+ // This may end with a backslash.
+ if (path.back() == L'\\') {
+ path.pop_back();
+ }
+
+ // Perfect match (case-insensitive check).
+ if (EqualPath(actual_path, path))
+ return true;
+
+ bool nt_path = IsNTPath(path, &path);
+ bool has_drive = StartsWithDriveLetter(path);
+
+ if (!has_drive && nt_path) {
+ std::wstring simple_actual_path;
+ if (IsDevicePath(path, &path)) {
+ if (IsDevicePath(actual_path, &simple_actual_path)) {
+ // Perfect match (case-insensitive check).
+ return (EqualPath(simple_actual_path, path));
+ } else {
+ return false;
+ }
+ } else {
+ // Add Win32 device namespace for GetVolumePathName.
+ path.insert(0, kNTDotPrefix);
+ }
+ }
+
+ // Get the volume path in the same format as actual_path.
+ wchar_t vol_path[MAX_PATH];
+ if (!::GetVolumePathName(path.c_str(), vol_path, MAX_PATH)) {
+ return false;
+ }
+ size_t vol_path_len = wcslen(vol_path);
+ base::string16 nt_vol;
+ if (!GetNtPathFromWin32Path(vol_path, &nt_vol)) {
+ return false;
+ }
+
+ // The two paths should be the same length.
+ if (nt_vol.size() + path.size() - vol_path_len != actual_path.size()) {
+ return false;
+ }
+
+ // Check the volume matches.
+ if (!EqualPath(actual_path, nt_vol.c_str(), nt_vol.size())) {
+ return false;
+ }
+
+ // Check the path after the volume matches.
+ if (!EqualPath(actual_path, nt_vol.size(), path, vol_path_len)) {
+ return false;
+ }
+
+ return true;
+}
+
+// Just make a best effort here. There are lots of corner cases that we're
+// not expecting - and will fail to make long.
+bool ConvertToLongPath(std::wstring* native_path,
+ const std::wstring* drive_letter) {
+ if (IsPipe(*native_path))
+ return true;
+
+ bool is_device_harddisk_path = false;
+ bool is_nt_path = false;
+ bool added_implied_device = false;
+ std::wstring temp_path;
+ std::wstring to_restore;
+
+ // Process a few prefix types.
+ if (IsNTPath(*native_path, &temp_path)) {
+ // "\??\"
+ if (!StartsWithDriveLetter(temp_path)) {
+ // Prepend with "\\.\".
+ temp_path = std::wstring(kNTDotPrefix) + temp_path;
+ added_implied_device = true;
+ }
+ is_nt_path = true;
+ } else if (IsDeviceHarddiskPath(*native_path, &temp_path, &to_restore)) {
+ // "\Device\HarddiskVolumeX\" - hacky attempt making ::GetLongPathName
+ // work for native device paths. Remove "\Device\HarddiskVolumeX\" and
+ // replace with drive letter.
+
+ // Nothing we can do if we don't have a drive letter. Leave |native_path|
+ // as is.
+ if (!drive_letter || drive_letter->empty())
+ return false;
+ temp_path = *drive_letter + temp_path;
+ is_device_harddisk_path = true;
+ } else if (IsDevicePath(*native_path, &temp_path)) {
+ // "\Device\" - there's nothing we can do to convert to long here.
+ return false;
+ }
+
+ DWORD size = MAX_PATH;
+ std::unique_ptr<wchar_t[]> long_path_buf(new wchar_t[size]);
+
+ DWORD return_value =
+ ::GetLongPathName(temp_path.c_str(), long_path_buf.get(), size);
+ while (return_value >= size) {
+ size *= 2;
+ long_path_buf.reset(new wchar_t[size]);
+ return_value =
+ ::GetLongPathName(temp_path.c_str(), long_path_buf.get(), size);
+ }
+
+ DWORD last_error = ::GetLastError();
+ if (0 == return_value && (ERROR_FILE_NOT_FOUND == last_error ||
+ ERROR_PATH_NOT_FOUND == last_error ||
+ ERROR_INVALID_NAME == last_error)) {
+ // The file does not exist, but maybe a sub path needs to be expanded.
+ std::wstring::size_type last_slash = temp_path.rfind(L'\\');
+ if (std::wstring::npos == last_slash)
+ return false;
+
+ std::wstring begin = temp_path.substr(0, last_slash);
+ std::wstring end = temp_path.substr(last_slash);
+ if (!ConvertToLongPath(&begin))
+ return false;
+
+ // Ok, it worked. Let's reset the return value.
+ temp_path = begin + end;
+ return_value = 1;
+ } else if (0 != return_value) {
+ temp_path = long_path_buf.get();
+ }
+
+ // If successful, re-apply original namespace prefix before returning.
+ if (return_value != 0) {
+ if (added_implied_device)
+ RemoveImpliedDevice(&temp_path);
+
+ if (is_nt_path) {
+ *native_path = kNTPrefix;
+ *native_path += temp_path;
+ } else if (is_device_harddisk_path) {
+ // Remove the added drive letter.
+ temp_path = temp_path.substr(kDriveLetterLen);
+ *native_path = to_restore;
+ *native_path += temp_path;
+ } else {
+ *native_path = temp_path;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+bool GetPathFromHandle(HANDLE handle, std::wstring* path) {
+ NtQueryObjectFunction NtQueryObject = nullptr;
+ ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject);
+
+ OBJECT_NAME_INFORMATION initial_buffer;
+ OBJECT_NAME_INFORMATION* name = &initial_buffer;
+ ULONG size = sizeof(initial_buffer);
+ // Query the name information a first time to get the size of the name.
+ // Windows XP requires that the size of the buffer passed in here be != 0.
+ NTSTATUS status =
+ NtQueryObject(handle, ObjectNameInformation, name, size, &size);
+
+ std::unique_ptr<BYTE[]> name_ptr;
+ if (size) {
+ name_ptr.reset(new BYTE[size]);
+ name = reinterpret_cast<OBJECT_NAME_INFORMATION*>(name_ptr.get());
+
+ // Query the name information a second time to get the name of the
+ // object referenced by the handle.
+ status = NtQueryObject(handle, ObjectNameInformation, name, size, &size);
+ }
+
+ if (STATUS_SUCCESS != status)
+ return false;
+
+ path->assign(name->ObjectName.Buffer,
+ name->ObjectName.Length / sizeof(name->ObjectName.Buffer[0]));
+ return true;
+}
+
+bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path) {
+ HANDLE file = ::CreateFileW(
+ path.c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+ bool rv = GetPathFromHandle(file, nt_path);
+ ::CloseHandle(file);
+ return rv;
+}
+
+bool WriteProtectedChildMemory(HANDLE child_process,
+ void* address,
+ const void* buffer,
+ size_t length,
+ DWORD writeProtection) {
+ // First, remove the protections.
+ DWORD old_protection;
+ if (!::VirtualProtectEx(child_process, address, length, writeProtection,
+ &old_protection))
+ return false;
+
+ SIZE_T written;
+ bool ok =
+ ::WriteProcessMemory(child_process, address, buffer, length, &written) &&
+ (length == written);
+
+ // Always attempt to restore the original protection.
+ if (!::VirtualProtectEx(child_process, address, length, old_protection,
+ &old_protection))
+ return false;
+
+ return ok;
+}
+
+bool CopyToChildMemory(HANDLE child,
+ const void* local_buffer,
+ size_t buffer_bytes,
+ void** remote_buffer) {
+ DCHECK(remote_buffer);
+ if (0 == buffer_bytes) {
+ *remote_buffer = nullptr;
+ return true;
+ }
+
+ // Allocate memory in the target process without specifying the address
+ void* remote_data = ::VirtualAllocEx(child, nullptr, buffer_bytes, MEM_COMMIT,
+ PAGE_READWRITE);
+ if (!remote_data)
+ return false;
+
+ SIZE_T bytes_written;
+ bool success = ::WriteProcessMemory(child, remote_data, local_buffer,
+ buffer_bytes, &bytes_written);
+ if (!success || bytes_written != buffer_bytes) {
+ ::VirtualFreeEx(child, remote_data, 0, MEM_RELEASE);
+ return false;
+ }
+
+ *remote_buffer = remote_data;
+
+ return true;
+}
+
+DWORD GetLastErrorFromNtStatus(NTSTATUS status) {
+ RtlNtStatusToDosErrorFunction NtStatusToDosError = nullptr;
+ ResolveNTFunctionPtr("RtlNtStatusToDosError", &NtStatusToDosError);
+ return NtStatusToDosError(status);
+}
+
+// This function uses the undocumented PEB ImageBaseAddress field to extract
+// the base address of the new process.
+void* GetProcessBaseAddress(HANDLE process) {
+ NtQueryInformationProcessFunction query_information_process = nullptr;
+ ResolveNTFunctionPtr("NtQueryInformationProcess", &query_information_process);
+ if (!query_information_process)
+ return nullptr;
+ PROCESS_BASIC_INFORMATION process_basic_info = {};
+ NTSTATUS status = query_information_process(
+ process, ProcessBasicInformation, &process_basic_info,
+ sizeof(process_basic_info), nullptr);
+ if (STATUS_SUCCESS != status)
+ return nullptr;
+
+ PEB peb = {};
+ SIZE_T bytes_read = 0;
+ if (!::ReadProcessMemory(process, process_basic_info.PebBaseAddress, &peb,
+ sizeof(peb), &bytes_read) ||
+ (sizeof(peb) != bytes_read)) {
+ return nullptr;
+ }
+
+ void* base_address = peb.ImageBaseAddress;
+ char magic[2] = {};
+ if (!::ReadProcessMemory(process, base_address, magic, sizeof(magic),
+ &bytes_read) ||
+ (sizeof(magic) != bytes_read)) {
+ return nullptr;
+ }
+
+ if (magic[0] != 'M' || magic[1] != 'Z')
+ return nullptr;
+
+#if defined(_M_ARM64)
+ // Windows 10 on ARM64 has multi-threaded DLL loading that does not work with
+ // the sandbox. (On x86 this gets disabled by hook detection code that was not
+ // ported to ARM64). This overwrites the LoaderThreads value in the process
+ // parameters part of the PEB, if it is set to the default of 0 (which
+ // actually means it defaults to 4 loading threads). This is an undocumented
+ // field so there is a, probably small, risk that it might change or move in
+ // the future. In order to slightly guard against that we only update if the
+ // value is currently 0.
+ auto processParameters = reinterpret_cast<uint8_t*>(peb.ProcessParameters);
+ const uint32_t loaderThreadsOffset = 0x40c;
+ uint32_t maxLoaderThreads = 0;
+ BOOL memoryRead = ::ReadProcessMemory(
+ process, processParameters + loaderThreadsOffset, &maxLoaderThreads,
+ sizeof(maxLoaderThreads), &bytes_read);
+ if (memoryRead && (sizeof(maxLoaderThreads) == bytes_read) &&
+ (maxLoaderThreads == 0)) {
+ maxLoaderThreads = 1;
+ WriteProtectedChildMemory(process, processParameters + loaderThreadsOffset,
+ &maxLoaderThreads, sizeof(maxLoaderThreads),
+ PAGE_READWRITE);
+ }
+#endif
+
+ return base_address;
+}
+
+DWORD GetTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS info_class,
+ std::unique_ptr<BYTE[]>* buffer) {
+ // Get the required buffer size.
+ DWORD size = 0;
+ ::GetTokenInformation(token, info_class, nullptr, 0, &size);
+ if (!size) {
+ return ::GetLastError();
+ }
+
+ auto temp_buffer = std::make_unique<BYTE[]>(size);
+ if (!::GetTokenInformation(token, info_class, temp_buffer.get(), size,
+ &size)) {
+ return ::GetLastError();
+ }
+
+ *buffer = std::move(temp_buffer);
+ return ERROR_SUCCESS;
+}
+
+} // namespace sandbox
+
+void ResolveNTFunctionPtr(const char* name, void* ptr) {
+ static volatile HMODULE ntdll = nullptr;
+
+ if (!ntdll) {
+ HMODULE ntdll_local = ::GetModuleHandle(sandbox::kNtdllName);
+ // Use PEImage to sanity-check that we have a valid ntdll handle.
+ base::win::PEImage ntdll_peimage(ntdll_local);
+ CHECK_NT(ntdll_peimage.VerifyMagic());
+ // Race-safe way to set static ntdll.
+ ::InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID volatile*>(&ntdll), ntdll_local, nullptr);
+ }
+
+ CHECK_NT(ntdll);
+ FARPROC* function_ptr = reinterpret_cast<FARPROC*>(ptr);
+ *function_ptr = ::GetProcAddress(ntdll, name);
+ CHECK_NT(*function_ptr);
+}
diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils.h b/security/sandbox/chromium/sandbox/win/src/win_utils.h
new file mode 100644
index 0000000000..7c848f2f08
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win_utils.h
@@ -0,0 +1,156 @@
+// Copyright (c) 2006-2010 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_WIN_UTILS_H_
+#define SANDBOX_SRC_WIN_UTILS_H_
+
+#include <stddef.h>
+#include <windows.h>
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/stl_util.h"
+#include "sandbox/win/src/nt_internals.h"
+
+namespace sandbox {
+
+// Prefix for path used by NT calls.
+const wchar_t kNTPrefix[] = L"\\??\\";
+const size_t kNTPrefixLen = base::size(kNTPrefix) - 1;
+
+const wchar_t kNTDevicePrefix[] = L"\\Device\\";
+const size_t kNTDevicePrefixLen = base::size(kNTDevicePrefix) - 1;
+
+// Automatically acquires and releases a lock when the object is
+// is destroyed.
+class AutoLock {
+ public:
+ // Acquires the lock.
+ explicit AutoLock(CRITICAL_SECTION* lock) : lock_(lock) {
+ ::EnterCriticalSection(lock);
+ }
+
+ // Releases the lock;
+ ~AutoLock() { ::LeaveCriticalSection(lock_); }
+
+ private:
+ CRITICAL_SECTION* lock_;
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AutoLock);
+};
+
+// Basic implementation of a singleton which calls the destructor
+// when the exe is shutting down or the DLL is being unloaded.
+template <typename Derived>
+class SingletonBase {
+ public:
+ static Derived* GetInstance() {
+ static Derived* instance = nullptr;
+ if (!instance) {
+ instance = new Derived();
+ // Microsoft CRT extension. In an exe this this called after
+ // winmain returns, in a dll is called in DLL_PROCESS_DETACH
+ _onexit(OnExit);
+ }
+ return instance;
+ }
+
+ private:
+ // this is the function that gets called by the CRT when the
+ // process is shutting down.
+ static int __cdecl OnExit() {
+ delete GetInstance();
+ return 0;
+ }
+};
+
+// Function object which invokes LocalFree on its parameter, which must be
+// a pointer. Can be used to store LocalAlloc pointers in std::unique_ptr:
+//
+// std::unique_ptr<int, sandbox::LocalFreeDeleter> foo_ptr(
+// static_cast<int*>(LocalAlloc(LMEM_FIXED, sizeof(int))));
+struct LocalFreeDeleter {
+ inline void operator()(void* ptr) const { ::LocalFree(ptr); }
+};
+
+// Convert a short path (C:\path~1 or \\??\\c:\path~1) to the long version of
+// the path. If the path is not a valid filesystem path, the function returns
+// false and argument is not modified.
+// - If passing in a short native device path (\Device\HarddiskVolumeX\path~1),
+// a drive letter string (c:\) must also be provided.
+bool ConvertToLongPath(std::wstring* path,
+ const std::wstring* drive_letter = nullptr);
+
+// Returns ERROR_SUCCESS if the path contains a reparse point,
+// ERROR_NOT_A_REPARSE_POINT if there's no reparse point in this path, or an
+// error code when the function fails.
+// This function is not smart. It looks for each element in the path and
+// returns true if any of them is a reparse point.
+DWORD IsReparsePoint(const std::wstring& full_path);
+
+// Returns true if the handle corresponds to the object pointed by this path.
+bool SameObject(HANDLE handle, const wchar_t* full_path);
+
+// Resolves a handle to an nt path. Returns true if the handle can be resolved.
+bool GetPathFromHandle(HANDLE handle, std::wstring* path);
+
+// Resolves a win32 path to an nt path using GetPathFromHandle. The path must
+// exist. Returs true if the translation was succesful.
+bool GetNtPathFromWin32Path(const std::wstring& path, std::wstring* nt_path);
+
+// Translates a reserved key name to its handle.
+// For example "HKEY_LOCAL_MACHINE" returns HKEY_LOCAL_MACHINE.
+// Returns nullptr if the name does not represent any reserved key name.
+HKEY GetReservedKeyFromName(const std::wstring& name);
+
+// Resolves a user-readable registry path to a system-readable registry path.
+// For example, HKEY_LOCAL_MACHINE\\Software\\microsoft is translated to
+// \\registry\\machine\\software\\microsoft. Returns false if the path
+// cannot be resolved.
+bool ResolveRegistryName(std::wstring name, std::wstring* resolved_name);
+
+// Writes |length| bytes from the provided |buffer| into the address space of
+// |child_process|, at the specified |address|, preserving the original write
+// protection attributes. Returns true on success.
+bool WriteProtectedChildMemory(HANDLE child_process,
+ void* address,
+ const void* buffer,
+ size_t length,
+ DWORD writeProtection = PAGE_WRITECOPY);
+
+// Allocates |buffer_bytes| in child (PAGE_READWRITE) and copies data
+// from |local_buffer| in this process into |child|. |remote_buffer|
+// contains the address in the chile. If a zero byte copy is
+// requested |true| is returned and no allocation or copying is
+// attempted. Returns false if allocation or copying fails. If
+// copying fails, the allocation will be reversed.
+bool CopyToChildMemory(HANDLE child,
+ const void* local_buffer,
+ size_t buffer_bytes,
+ void** remote_buffer);
+
+// Returns true if the provided path points to a pipe.
+bool IsPipe(const std::wstring& path);
+
+// Converts a NTSTATUS code to a Win32 error code.
+DWORD GetLastErrorFromNtStatus(NTSTATUS status);
+
+// Returns the address of the main exe module in memory taking in account
+// address space layout randomization. This uses the process' PEB to extract
+// the base address. This should only be called on new, suspended processes.
+void* GetProcessBaseAddress(HANDLE process);
+
+// Calls GetTokenInformation with the desired |info_class| and returns a
+// |buffer| and the Win32 error code.
+DWORD GetTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS info_class,
+ std::unique_ptr<BYTE[]>* buffer);
+
+} // namespace sandbox
+
+// Resolves a function name in NTDLL to a function pointer. The second parameter
+// is a pointer to the function pointer.
+void ResolveNTFunctionPtr(const char* name, void* ptr);
+
+#endif // SANDBOX_SRC_WIN_UTILS_H_
diff --git a/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc b/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc
new file mode 100644
index 0000000000..36e7d4dd1e
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/win_utils_unittest.cc
@@ -0,0 +1,258 @@
+// Copyright (c) 2011 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/win_utils.h"
+
+#include <windows.h>
+
+#include <psapi.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/path_service.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/scoped_process_information.h"
+#include "sandbox/win/src/nt_internals.h"
+#include "sandbox/win/tests/common/test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+class ScopedTerminateProcess {
+ public:
+ ScopedTerminateProcess(HANDLE process) : process_(process) {}
+
+ ~ScopedTerminateProcess() { ::TerminateProcess(process_, 0); }
+
+ private:
+ HANDLE process_;
+};
+
+bool GetModuleList(HANDLE process, std::vector<HMODULE>* result) {
+ std::vector<HMODULE> modules(256);
+ DWORD size_needed = 0;
+ if (EnumProcessModules(
+ process, &modules[0],
+ base::checked_cast<DWORD>(modules.size() * sizeof(HMODULE)),
+ &size_needed)) {
+ result->assign(modules.begin(),
+ modules.begin() + (size_needed / sizeof(HMODULE)));
+ return true;
+ }
+ modules.resize(size_needed / sizeof(HMODULE));
+ if (EnumProcessModules(
+ process, &modules[0],
+ base::checked_cast<DWORD>(modules.size() * sizeof(HMODULE)),
+ &size_needed)) {
+ result->assign(modules.begin(),
+ modules.begin() + (size_needed / sizeof(HMODULE)));
+ return true;
+ }
+ return false;
+}
+
+} // namespace
+
+TEST(WinUtils, IsReparsePoint) {
+ using sandbox::IsReparsePoint;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t my_folder[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(my_folder));
+ ASSERT_TRUE(::CreateDirectory(my_folder, nullptr));
+
+ EXPECT_EQ(static_cast<DWORD>(ERROR_NOT_A_REPARSE_POINT),
+ IsReparsePoint(my_folder));
+
+ std::wstring not_found = std::wstring(my_folder) + L"\\foo\\bar";
+ EXPECT_EQ(static_cast<DWORD>(ERROR_NOT_A_REPARSE_POINT),
+ IsReparsePoint(not_found));
+
+ std::wstring new_file = std::wstring(my_folder) + L"\\foo";
+ EXPECT_EQ(static_cast<DWORD>(ERROR_NOT_A_REPARSE_POINT),
+ IsReparsePoint(new_file));
+
+ // Replace the directory with a reparse point to %temp%.
+ HANDLE dir = ::CreateFile(my_folder, FILE_ALL_ACCESS,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+ EXPECT_NE(INVALID_HANDLE_VALUE, dir);
+
+ std::wstring temp_dir_nt = std::wstring(L"\\??\\") + temp_directory;
+ EXPECT_TRUE(SetReparsePoint(dir, temp_dir_nt.c_str()));
+
+ EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS), IsReparsePoint(new_file));
+
+ EXPECT_TRUE(DeleteReparsePoint(dir));
+ EXPECT_TRUE(::CloseHandle(dir));
+ EXPECT_TRUE(::RemoveDirectory(my_folder));
+}
+
+TEST(WinUtils, SameObject) {
+ using sandbox::SameObject;
+
+ // Create a temp file because we need write access to it.
+ wchar_t temp_directory[MAX_PATH];
+ wchar_t my_folder[MAX_PATH];
+ ASSERT_NE(::GetTempPath(MAX_PATH, temp_directory), 0u);
+ ASSERT_NE(::GetTempFileName(temp_directory, L"test", 0, my_folder), 0u);
+
+ // Delete the file and create a directory instead.
+ ASSERT_TRUE(::DeleteFile(my_folder));
+ ASSERT_TRUE(::CreateDirectory(my_folder, nullptr));
+
+ std::wstring folder(my_folder);
+ std::wstring file_name = folder + L"\\foo.txt";
+ const ULONG kSharing = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE;
+ base::win::ScopedHandle file(CreateFile(file_name.c_str(), GENERIC_WRITE,
+ kSharing, nullptr, CREATE_ALWAYS,
+ FILE_FLAG_DELETE_ON_CLOSE, nullptr));
+
+ EXPECT_TRUE(file.IsValid());
+ std::wstring file_name_nt1 = std::wstring(L"\\??\\") + file_name;
+ std::wstring file_name_nt2 = std::wstring(L"\\??\\") + folder + L"\\FOO.txT";
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt1.c_str()));
+ EXPECT_TRUE(SameObject(file.Get(), file_name_nt2.c_str()));
+
+ file.Close();
+ EXPECT_TRUE(::RemoveDirectory(my_folder));
+}
+
+TEST(WinUtils, IsPipe) {
+ using sandbox::IsPipe;
+
+ std::wstring pipe_name = L"\\??\\pipe\\mypipe";
+ EXPECT_TRUE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\PiPe\\mypipe";
+ EXPECT_TRUE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\pipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\_pipe_\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\??\\ABCD\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ // Written as two strings to prevent trigraph '?' '?' '/'.
+ pipe_name =
+ L"/?"
+ L"?/pipe/mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\XX\\pipe\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+
+ pipe_name = L"\\Device\\NamedPipe\\mypipe";
+ EXPECT_FALSE(IsPipe(pipe_name));
+}
+
+TEST(WinUtils, NtStatusToWin32Error) {
+ using sandbox::GetLastErrorFromNtStatus;
+ EXPECT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ GetLastErrorFromNtStatus(STATUS_SUCCESS));
+ EXPECT_EQ(static_cast<DWORD>(ERROR_NOT_SUPPORTED),
+ GetLastErrorFromNtStatus(STATUS_NOT_SUPPORTED));
+ EXPECT_EQ(static_cast<DWORD>(ERROR_ALREADY_EXISTS),
+ GetLastErrorFromNtStatus(STATUS_OBJECT_NAME_COLLISION));
+ EXPECT_EQ(static_cast<DWORD>(ERROR_ACCESS_DENIED),
+ GetLastErrorFromNtStatus(STATUS_ACCESS_DENIED));
+}
+
+TEST(WinUtils, GetProcessBaseAddress) {
+ using sandbox::GetProcessBaseAddress;
+ STARTUPINFO start_info = {};
+ PROCESS_INFORMATION proc_info = {};
+ WCHAR command_line[] = L"notepad";
+ start_info.cb = sizeof(start_info);
+ start_info.dwFlags = STARTF_USESHOWWINDOW;
+ start_info.wShowWindow = SW_HIDE;
+ ASSERT_TRUE(::CreateProcessW(nullptr, command_line, nullptr, nullptr, false,
+ CREATE_SUSPENDED, nullptr, nullptr, &start_info,
+ &proc_info));
+ base::win::ScopedProcessInformation scoped_proc_info(proc_info);
+ ScopedTerminateProcess process_terminate(scoped_proc_info.process_handle());
+ void* base_address = GetProcessBaseAddress(scoped_proc_info.process_handle());
+ ASSERT_NE(nullptr, base_address);
+ ASSERT_NE(static_cast<DWORD>(-1),
+ ::ResumeThread(scoped_proc_info.thread_handle()));
+ ::WaitForInputIdle(scoped_proc_info.process_handle(), 1000);
+ ASSERT_NE(static_cast<DWORD>(-1),
+ ::SuspendThread(scoped_proc_info.thread_handle()));
+
+ std::vector<HMODULE> modules;
+ // Compare against the loader's module list (which should now be initialized).
+ ASSERT_TRUE(GetModuleList(scoped_proc_info.process_handle(), &modules));
+ ASSERT_GT(modules.size(), 0U);
+ EXPECT_EQ(base_address, modules[0]);
+}
+
+// This test requires an elevated prompt to setup.
+TEST(WinUtils, ConvertToLongPath) {
+ // Test setup.
+ base::FilePath orig_path;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &orig_path));
+ orig_path = orig_path.Append(L"calc.exe");
+
+ base::FilePath temp_path;
+ ASSERT_TRUE(base::PathService::Get(base::DIR_PROGRAM_FILES, &temp_path));
+ temp_path = temp_path.Append(L"test_calc.exe");
+
+ ASSERT_TRUE(base::CopyFile(orig_path, temp_path));
+ // No more asserts until cleanup.
+
+ // WIN32 long path: "c:\Program Files\test_calc.exe"
+ wchar_t short_path[MAX_PATH] = {};
+ DWORD size =
+ ::GetShortPathNameW(temp_path.value().c_str(), short_path, MAX_PATH);
+ EXPECT_TRUE(size > 0 && size < MAX_PATH);
+ // WIN32 short path: "C:\PROGRA~1\TEST_C~1.exe"
+
+ // Sanity check that we actually got a short path above! Small chance
+ // it was disabled in the filesystem setup.
+ EXPECT_NE(temp_path.value().length(), ::wcslen(short_path));
+
+ std::wstring short_form_native_path;
+ EXPECT_TRUE(sandbox::GetNtPathFromWin32Path(std::wstring(short_path),
+ &short_form_native_path));
+ // NT short path: "\Device\HarddiskVolume4\PROGRA~1\TEST_C~1.EXE"
+
+ // Test 1: convert win32 short path to long:
+ std::wstring test1(short_path);
+ EXPECT_TRUE(sandbox::ConvertToLongPath(&test1));
+ EXPECT_TRUE(::wcsicmp(temp_path.value().c_str(), test1.c_str()) == 0);
+ // Expected result: "c:\Program Files\test_calc.exe"
+
+ // Test 2: convert native short path to long:
+ std::wstring drive_letter = temp_path.value().substr(0, 3);
+ std::wstring test2(short_form_native_path);
+ EXPECT_TRUE(sandbox::ConvertToLongPath(&test2, &drive_letter));
+
+ size_t index = short_form_native_path.find_first_of(
+ L'\\', ::wcslen(L"\\Device\\HarddiskVolume"));
+ EXPECT_TRUE(index != std::wstring::npos);
+ std::wstring expected_result = short_form_native_path.substr(0, index + 1);
+ expected_result.append(temp_path.value().substr(3));
+ EXPECT_TRUE(::wcsicmp(expected_result.c_str(), test2.c_str()) == 0);
+ // Expected result: "\Device\HarddiskVolumeX\Program Files\test_calc.exe"
+
+ // clean up
+ EXPECT_TRUE(base::DeleteFileW(temp_path, false));
+
+ return;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/window.cc b/security/sandbox/chromium/sandbox/win/src/window.cc
new file mode 100644
index 0000000000..27645a9006
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/window.cc
@@ -0,0 +1,147 @@
+// Copyright (c) 2011 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/window.h"
+
+#include <aclapi.h>
+
+#include <memory>
+
+#include "base/logging.h"
+#include "base/win/win_util.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/sid.h"
+
+namespace {
+
+// Gets the security attributes of a window object referenced by |handle|. The
+// lpSecurityDescriptor member of the SECURITY_ATTRIBUTES parameter returned
+// must be freed using LocalFree by the caller.
+bool GetSecurityAttributes(HANDLE handle, SECURITY_ATTRIBUTES* attributes) {
+ attributes->bInheritHandle = false;
+ attributes->nLength = sizeof(SECURITY_ATTRIBUTES);
+
+ PACL dacl = nullptr;
+ DWORD result = ::GetSecurityInfo(
+ handle, SE_WINDOW_OBJECT, DACL_SECURITY_INFORMATION, nullptr, nullptr,
+ &dacl, nullptr, &attributes->lpSecurityDescriptor);
+ if (ERROR_SUCCESS == result)
+ return true;
+
+ return false;
+}
+
+} // namespace
+
+namespace sandbox {
+
+ResultCode CreateAltWindowStation(HWINSTA* winsta) {
+ // Get the security attributes from the current window station; we will
+ // use this as the base security attributes for the new window station.
+ HWINSTA current_winsta = ::GetProcessWindowStation();
+ if (!current_winsta)
+ return SBOX_ERROR_CANNOT_GET_WINSTATION;
+
+ SECURITY_ATTRIBUTES attributes = {0};
+ if (!GetSecurityAttributes(current_winsta, &attributes))
+ return SBOX_ERROR_CANNOT_QUERY_WINSTATION_SECURITY;
+
+ // Create the window station using nullptr for the name to ask the os to
+ // generate it.
+ *winsta = ::CreateWindowStationW(
+ nullptr, 0, GENERIC_READ | WINSTA_CREATEDESKTOP, &attributes);
+ if (!*winsta && ::GetLastError() == ERROR_ACCESS_DENIED) {
+ *winsta = ::CreateWindowStationW(
+ nullptr, 0, WINSTA_READATTRIBUTES | WINSTA_CREATEDESKTOP, &attributes);
+ }
+ LocalFree(attributes.lpSecurityDescriptor);
+
+ if (*winsta)
+ return SBOX_ALL_OK;
+
+ return SBOX_ERROR_CANNOT_CREATE_WINSTATION;
+}
+
+ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop) {
+ std::wstring desktop_name = L"sbox_alternate_desktop_";
+
+ if (!winsta) {
+ desktop_name += L"local_winstation_";
+ }
+
+ // Append the current PID to the desktop name.
+ wchar_t buffer[16];
+ _snwprintf_s(buffer, sizeof(buffer) / sizeof(wchar_t), L"0x%X",
+ ::GetCurrentProcessId());
+ desktop_name += buffer;
+
+ HDESK current_desktop = GetThreadDesktop(GetCurrentThreadId());
+
+ if (!current_desktop)
+ return SBOX_ERROR_CANNOT_GET_DESKTOP;
+
+ // Get the security attributes from the current desktop, we will use this as
+ // the base security attributes for the new desktop.
+ SECURITY_ATTRIBUTES attributes = {0};
+ if (!GetSecurityAttributes(current_desktop, &attributes))
+ return SBOX_ERROR_CANNOT_QUERY_DESKTOP_SECURITY;
+
+ // Back up the current window station, in case we need to switch it.
+ HWINSTA current_winsta = ::GetProcessWindowStation();
+
+ if (winsta) {
+ // We need to switch to the alternate window station before creating the
+ // desktop.
+ if (!::SetProcessWindowStation(winsta)) {
+ ::LocalFree(attributes.lpSecurityDescriptor);
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+ }
+ }
+
+ // Create the destkop.
+ *desktop = ::CreateDesktop(desktop_name.c_str(), nullptr, nullptr, 0,
+ DESKTOP_CREATEWINDOW | DESKTOP_READOBJECTS |
+ READ_CONTROL | WRITE_DAC | WRITE_OWNER,
+ &attributes);
+ ::LocalFree(attributes.lpSecurityDescriptor);
+
+ if (winsta) {
+ // Revert to the right window station.
+ if (!::SetProcessWindowStation(current_winsta)) {
+ return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION;
+ }
+ }
+
+ if (*desktop) {
+ // Replace the DACL on the new Desktop with a reduced privilege version.
+ // We can soft fail on this for now, as it's just an extra mitigation.
+ static const ACCESS_MASK kDesktopDenyMask =
+ WRITE_DAC | WRITE_OWNER | DELETE | DESKTOP_CREATEMENU |
+ DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | DESKTOP_JOURNALPLAYBACK |
+ DESKTOP_JOURNALRECORD | DESKTOP_SWITCHDESKTOP;
+ AddKnownSidToObject(*desktop, SE_WINDOW_OBJECT, Sid(WinRestrictedCodeSid),
+ DENY_ACCESS, kDesktopDenyMask);
+ return SBOX_ALL_OK;
+ }
+
+ return SBOX_ERROR_CANNOT_CREATE_DESKTOP;
+}
+
+std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop) {
+ if (!desktop) {
+ NOTREACHED();
+ return std::wstring();
+ }
+
+ std::wstring name;
+ if (winsta) {
+ name = base::win::GetWindowObjectName(winsta);
+ name += L'\\';
+ }
+
+ name += base::win::GetWindowObjectName(desktop);
+ return name;
+}
+
+} // namespace sandbox
diff --git a/security/sandbox/chromium/sandbox/win/src/window.h b/security/sandbox/chromium/sandbox/win/src/window.h
new file mode 100644
index 0000000000..1fe5b6cc72
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/window.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2009 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_WINDOW_H_
+#define SANDBOX_SRC_WINDOW_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "sandbox/win/src/sandbox_types.h"
+
+namespace sandbox {
+
+// Creates a window station. The name is generated by the OS. The security
+// descriptor is based on the security descriptor of the current window
+// station.
+ResultCode CreateAltWindowStation(HWINSTA* winsta);
+
+// Creates a desktop. The name is a static string followed by the pid of the
+// current process. The security descriptor on the new desktop is based on the
+// security descriptor of the desktop associated with the current thread.
+// If a winsta is specified, the function will switch to it before creating
+// the desktop. If the functions fails the switch back to the current winsta,
+// the function will return SBOX_ERROR_FAILED_TO_SWITCH_BACK_WINSTATION.
+ResultCode CreateAltDesktop(HWINSTA winsta, HDESK* desktop);
+
+// Returns the name of the desktop referenced by |desktop|. If a window
+// station is specified, the name is prepended with the window station name,
+// followed by a backslash. This name can be used as the lpDesktop parameter
+// to CreateProcess.
+std::wstring GetFullDesktopName(HWINSTA winsta, HDESK desktop);
+
+} // namespace sandbox
+
+#endif // SANDBOX_SRC_WINDOW_H_