Add back registry brokering.

This was removed from the chromium codebase, but we still require it.
The stand-alone files have been added to chromium-shim. This patch covers the
wiring back into other chromium code.

diff --git a/sandbox/win/src/interceptors.h b/sandbox/win/src/interceptors.h
--- a/sandbox/win/src/interceptors.h
+++ b/sandbox/win/src/interceptors.h
@@ -31,14 +31,16 @@ enum InterceptorId {
   SET_INFO_FILE_ID,
   // Process-thread dispatcher:
   CREATE_THREAD_ID,
+  // Registry dispatcher:
+  CREATE_KEY_ID,
+  OPEN_KEY_ID,
+  OPEN_KEY_EX_ID,
   // Process mitigations Win32k dispatcher:
   GDIINITIALIZE_ID,
   GETSTOCKOBJECT_ID,
   REGISTERCLASSW_ID,
   // Signed dispatcher:
   CREATE_SECTION_ID,
-  // Unittests (fake Registry dispatcher):
-  OPEN_KEY_ID,
   INTERCEPTOR_MAX_ID
 };
 
diff --git a/sandbox/win/src/interceptors_64.cc b/sandbox/win/src/interceptors_64.cc
--- a/sandbox/win/src/interceptors_64.cc
+++ b/sandbox/win/src/interceptors_64.cc
@@ -10,6 +10,7 @@
 #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"
@@ -213,6 +214,36 @@ TargetCreateThread64(LPSECURITY_ATTRIBUTES thread_attributes,
 
 // -----------------------------------------------------------------------
 
+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.functions[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.functions[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.functions[OPEN_KEY_EX_ID]);
+  return TargetNtOpenKeyEx(orig_fn, key, desired_access, object_attributes,
+                           open_options);
+}
+
+// -----------------------------------------------------------------------
+
 SANDBOX_INTERCEPT BOOL WINAPI TargetGdiDllInitialize64(HANDLE dll,
                                                        DWORD reason) {
   GdiDllInitializeFunction orig_fn = reinterpret_cast<GdiDllInitializeFunction>(
diff --git a/sandbox/win/src/interceptors_64.h b/sandbox/win/src/interceptors_64.h
--- a/sandbox/win/src/interceptors_64.h
+++ b/sandbox/win/src/interceptors_64.h
@@ -141,6 +141,25 @@ TargetCreateThread64(LPSECURITY_ATTRIBUTES thread_attributes,
                      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 process mitigations win32k lockdown code.
 
diff --git a/sandbox/win/src/internal_types.h b/sandbox/win/src/internal_types.h
--- a/sandbox/win/src/internal_types.h
+++ b/sandbox/win/src/internal_types.h
@@ -23,6 +23,7 @@ enum ArgType {
   WCHAR_TYPE,
   UINT32_TYPE,
   VOIDPTR_TYPE,
+  INPTR_TYPE,
   INOUTPTR_TYPE,
   LAST_TYPE
 };
diff --git a/sandbox/win/src/ipc_tags.h b/sandbox/win/src/ipc_tags.h
--- a/sandbox/win/src/ipc_tags.h
+++ b/sandbox/win/src/ipc_tags.h
@@ -22,6 +22,8 @@ enum class IpcTag : uint32_t {
   NTSETINFO_RENAME,
   NTOPENTHREAD,
   NTOPENPROCESSTOKENEX,
+  NTCREATEKEY,
+  NTOPENKEY,
   GDI_GDIDLLINITIALIZE,
   GDI_GETSTOCKOBJECT,
   USER_REGISTERCLASSW,
diff --git a/sandbox/win/src/nt_internals.h b/sandbox/win/src/nt_internals.h
--- a/sandbox/win/src/nt_internals.h
+++ b/sandbox/win/src/nt_internals.h
@@ -205,6 +205,23 @@ typedef NTSTATUS(WINAPI* NtOpenProcessTokenExFunction)(
     IN ULONG HandleAttributes,
     OUT PHANDLE TokenHandle);
 
+// -----------------------------------------------------------------------
+// Registry
+
+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);
+
 // -----------------------------------------------------------------------
 // Memory
 
diff --git a/sandbox/win/src/policy_params.h b/sandbox/win/src/policy_params.h
--- a/sandbox/win/src/policy_params.h
+++ b/sandbox/win/src/policy_params.h
@@ -30,6 +30,12 @@ POLPARAMS_BEGIN(NameBased)
   POLPARAM(NAME)
 POLPARAMS_END(NameBased)
 
+// Policy Parameters for reg open / create.
+POLPARAMS_BEGIN(OpenKey)
+POLPARAM(NAME)
+POLPARAM(ACCESS)
+POLPARAMS_END(OpenKey)
+
 }  // namespace sandbox
 
 #endif  // SANDBOX_WIN_SRC_POLICY_PARAMS_H_
diff --git a/sandbox/win/src/sandbox_nt_util.cc b/sandbox/win/src/sandbox_nt_util.cc
--- a/sandbox/win/src/sandbox_nt_util.cc
+++ b/sandbox/win/src/sandbox_nt_util.cc
@@ -332,6 +332,95 @@ NTSTATUS CopyData(void* destination, const void* source, size_t bytes) {
   return ret;
 }

+NtHeapWString::NtHeapWString(std::initializer_list<std::wstring_view> parts) {
+  size_t total = 0;
+  for (auto part : parts) {
+    total += part.size();
+  }
+
+  data_.reset(new (NT_ALLOC) wchar_t[total + 1]);
+  if (!data_) {
+    return;
+  }
+
+  wchar_t* dst = data_.get();
+  for (auto part : parts) {
+    NTSTATUS ret = CopyData(dst, part.data(), part.size() * sizeof(wchar_t));
+    if (!NT_SUCCESS(ret)) {
+      data_.reset();
+      return;
+    }
+    dst += part.size();
+  }
+  *dst = L'\0';
+  length_ = total;
+}
+
+bool NtHeapWString::is_valid() const {
+  return data_ != nullptr;
+}
+
+std::wstring_view NtHeapWString::view() const {
+  return {data_.get(), length_};
+}
+
+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);
+}
+
+NtHeapWString AllocAndGetFullPath(HANDLE root, const std::wstring_view& path) {
+  if (!InitHeap()) return {};
+
+  DCHECK_NT(!path.empty());
+  NtHeapWString result;
+  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 (!NT_SUCCESS(ret)) break;
+
+      size_t handle_name_len = handle_name->Name.Length / sizeof(wchar_t);
+      result = NtHeapWString(
+          {{handle_name->Name.Buffer, handle_name_len}, L"\\", path});
+    } while (false);
+  } __except (EXCEPTION_EXECUTE_HANDLER) {
+  }
+
+  return result;
+}
+
 NTSTATUS GetProcessId(HANDLE process, DWORD* process_id) {
   PROCESS_BASIC_INFORMATION proc_info;
   ULONG bytes_returned;
diff --git a/sandbox/win/src/sandbox_nt_util.h b/sandbox/win/src/sandbox_nt_util.h
--- a/sandbox/win/src/sandbox_nt_util.h
+++ b/sandbox/win/src/sandbox_nt_util.h
@@ -123,6 +123,30 @@ 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);

+// A simple immutable wide string allocated from the sandbox's private heap,
+// built from a list of string_view parts. Intended for constructing NT object
+// path strings in interception code where standard allocators are unavailable.
+class NtHeapWString {
+ public:
+  NtHeapWString() = default;
+  explicit NtHeapWString(std::initializer_list<std::wstring_view> parts);
+
+  bool is_valid() const;
+  std::wstring_view view() const;
+
+ private:
+  std::unique_ptr<wchar_t, NtAllocDeleter> data_;
+  size_t length_ = 0;
+};
+
+// 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);
+
+// Determine full path name from object root and path. Returns an invalid
+// NtHeapWString on failure.
+NtHeapWString AllocAndGetFullPath(HANDLE root, const std::wstring_view& path);
+
 // Initializes our ntdll level heap
 bool InitHeap();
 
diff --git a/sandbox/win/src/sandbox_policy.h b/sandbox/win/src/sandbox_policy.h
--- a/sandbox/win/src/sandbox_policy.h
+++ b/sandbox/win/src/sandbox_policy.h
@@ -164,6 +164,15 @@ class [[clang::lto_visibility_public]] TargetConfig {
   [[nodiscard]] virtual ResultCode AllowFileAccess(FileSemantics semantics,
                                                    const wchar_t* pattern) = 0;
 
+  // Adds a policy rule effective for processes spawned using this policy.
+  // Registry entries matching `pattern` (see AllowFileAccess) can be opened
+  // for read access.
+  //
+  // Note: Do not add new uses of this function - instead proxy registry handles
+  // into your process via normal Chrome IPC.
+  [[nodiscard]] virtual ResultCode AllowRegistryRead(
+      const wchar_t* pattern) = 0;
+
   // Adds a policy rule effective for processes spawned using this policy.
   // Modules patching `path` exactly can still be loaded under
   // Code-Integrity Guard (MITIGATION_FORCE_MS_SIGNED_BINS).
diff --git a/sandbox/win/src/sandbox_policy_base.cc b/sandbox/win/src/sandbox_policy_base.cc
--- a/sandbox/win/src/sandbox_policy_base.cc
+++ b/sandbox/win/src/sandbox_policy_base.cc
@@ -40,6 +40,7 @@
 #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_policy_diagnostic.h"
@@ -263,6 +264,13 @@ ResultCode ConfigBase::AllowFileAccess(FileSemantics semantics,
   return SBOX_ALL_OK;
 }
 
+ResultCode ConfigBase::AllowRegistryRead(const wchar_t* pattern) {
+  if (!RegistryPolicy::GenerateRules(pattern, PolicyMaker())) {
+    return SBOX_ERROR_BAD_PARAMS;
+  }
+  return SBOX_ALL_OK;
+}
+
 ResultCode ConfigBase::SetFakeGdiInit() {
   DCHECK_EQ(MITIGATION_WIN32K_DISABLE, mitigations_ & MITIGATION_WIN32K_DISABLE)
       << "Enable MITIGATION_WIN32K_DISABLE before adding win32k policy "
diff --git a/sandbox/win/src/sandbox_policy_base.h b/sandbox/win/src/sandbox_policy_base.h
--- a/sandbox/win/src/sandbox_policy_base.h
+++ b/sandbox/win/src/sandbox_policy_base.h
@@ -61,6 +61,7 @@ class ConfigBase final : public TargetConfig {
   void SetJobMemoryLimit(size_t memory_limit) override;
   ResultCode AllowFileAccess(FileSemantics semantics,
                              const wchar_t* pattern) override;
+  ResultCode AllowRegistryRead(const wchar_t* pattern) final;
   ResultCode AllowExtraDll(const wchar_t* path) override;
   ResultCode SetFakeGdiInit() override;
   void AddDllToUnload(const wchar_t* dll_name) override;
diff --git a/sandbox/win/src/top_level_dispatcher.cc b/sandbox/win/src/top_level_dispatcher.cc
--- a/sandbox/win/src/top_level_dispatcher.cc
+++ b/sandbox/win/src/top_level_dispatcher.cc
@@ -17,6 +17,7 @@
 #include "sandbox/win/src/ipc_tags.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"
 
@@ -55,6 +56,17 @@ TopLevelDispatcher::TopLevelDispatcher(PolicyBase* policy) : policy_(policy) {
     }
   }
 
+  for (IpcTag service : {IpcTag::NTCREATEKEY, IpcTag::NTOPENKEY}) {
+    if (config->NeedsIpc(service)) {
+      if (!registry_dispatcher_) {
+        registry_dispatcher_ =
+            std::make_unique<RegistryDispatcher>(policy_);
+      }
+      UNSAFE_TODO(ipc_targets_[static_cast<size_t>(service)]) =
+          registry_dispatcher_.get();
+    }
+  }
+
   for (IpcTag service :
        {IpcTag::GDI_GDIDLLINITIALIZE, IpcTag::GDI_GETSTOCKOBJECT,
         IpcTag::USER_REGISTERCLASSW}) {
diff --git a/sandbox/win/src/top_level_dispatcher.h b/sandbox/win/src/top_level_dispatcher.h
--- a/sandbox/win/src/top_level_dispatcher.h
+++ b/sandbox/win/src/top_level_dispatcher.h
@@ -46,6 +46,7 @@ class TopLevelDispatcher : public Dispatcher {
   // Dispatchers below are only created if they are needed.
   std::unique_ptr<Dispatcher> filesystem_dispatcher_;
   std::unique_ptr<Dispatcher> thread_process_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_;

