| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/chrome_elf/chrome_elf_security.h" |
| |
| // clang-format off |
| #include <windows.h> // Must be included before versionhelpers.h |
| #include <versionhelpers.h> |
| // clang-format on |
| |
| #include <assert.h> |
| #include <ntstatus.h> |
| |
| #include <optional> |
| |
| #include "base/check.h" |
| #include "base/file_version_info.h" |
| #include "base/logging.h" |
| #include "base/threading/thread_checker.h" |
| #include "base/win/current_module.h" |
| #include "chrome/chrome_elf/chrome_elf_constants.h" |
| #include "chrome/chrome_elf/nt_registry/nt_registry.h" |
| #include "chrome/install_static/install_util.h" |
| |
| namespace elf_security { |
| |
| namespace { |
| |
| // Used to turn off validation in tests |
| static bool g_validate_not_exe_for_testing = true; |
| |
| // Used to ensure we are calling a method defined in the correct module. |
| // In particular, there are often issues where the exe version is called |
| // instead of the dll version. |
| void MaybeValidateNotCallingFromExe() { |
| HMODULE module; |
| |
| // NULL returns the exe we're running in |
| DCHECK(::GetModuleHandleExW(0, NULL, &module)); |
| DCHECK(CURRENT_MODULE() != module); |
| } |
| |
| class ExtensionPointDisableSet { |
| public: |
| ~ExtensionPointDisableSet() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| } |
| |
| ExtensionPointDisableSet(const ExtensionPointDisableSet&) = delete; |
| ExtensionPointDisableSet& operator=(const ExtensionPointDisableSet&) = delete; |
| |
| static ExtensionPointDisableSet* GetInstance() { |
| static ExtensionPointDisableSet* instance = nullptr; |
| if (!instance) { |
| instance = new ExtensionPointDisableSet(); |
| } |
| |
| return instance; |
| } |
| |
| void SetExtensionPointDisabled(bool set) { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| CHECK(!extension_point_disable_set_.has_value()); |
| extension_point_disable_set_ = set; |
| } |
| |
| bool GetExtensionPointDisabled() { |
| DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); |
| |
| if (extension_point_disable_set_.has_value()) |
| return extension_point_disable_set_.value(); |
| |
| return false; |
| } |
| |
| private: |
| ExtensionPointDisableSet() { DETACH_FROM_THREAD(thread_checker_); } |
| |
| THREAD_CHECKER(thread_checker_); |
| std::optional<bool> extension_point_disable_set_ |
| GUARDED_BY_CONTEXT(thread_checker_); |
| }; |
| |
| } // namespace |
| |
| void EarlyBrowserSecurity() { |
| // This function is called from within DllMain. |
| // Don't do anything naughty while we have the loader lock. |
| NTSTATUS ret_val = STATUS_SUCCESS; |
| HANDLE handle = INVALID_HANDLE_VALUE; |
| |
| // Check for kRegBrowserExtensionPointKeyName. We only disable extension |
| // points when this exists, for devices that have been vetted by our |
| // heuristic. |
| if (!nt::OpenRegKey(nt::HKCU, |
| install_static::GetRegistryPath() |
| .append(elf_sec::kRegBrowserExtensionPointKeyName) |
| .c_str(), |
| KEY_QUERY_VALUE, &handle, &ret_val)) { |
| #ifdef _DEBUG |
| // The only failure expected is for the path not existing. |
| if (ret_val != STATUS_OBJECT_NAME_NOT_FOUND) |
| assert(false); |
| #endif |
| return; |
| } |
| |
| nt::CloseRegKey(handle); |
| |
| // Disable extension points (legacy hooking) in this process. |
| PROCESS_MITIGATION_EXTENSION_POINT_DISABLE_POLICY policy = {}; |
| policy.DisableExtensionPoints = true; |
| SetProcessMitigationPolicy(ProcessExtensionPointDisablePolicy, &policy, |
| sizeof(policy)); |
| ExtensionPointDisableSet::GetInstance()->SetExtensionPointDisabled(true); |
| |
| return; |
| } |
| |
| void ValidateExeForTesting(bool on) { |
| g_validate_not_exe_for_testing = on; |
| } |
| |
| bool IsExtensionPointDisableSet() { |
| if (g_validate_not_exe_for_testing) |
| MaybeValidateNotCallingFromExe(); |
| |
| return ExtensionPointDisableSet::GetInstance()->GetExtensionPointDisabled(); |
| } |
| |
| } // namespace elf_security |