chromium/chrome/chrome_elf/chrome_elf_security.cc

// 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