chromium/chrome/chrome_elf/third_party_dlls/main.cc

// Copyright 2017 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/third_party_dlls/main.h"

#include <windows.h>

#include <assert.h>
#include <versionhelpers.h>

#include <limits>

#include "chrome/chrome_elf/nt_registry/nt_registry.h"
#include "chrome/chrome_elf/third_party_dlls/hook.h"
#include "chrome/chrome_elf/third_party_dlls/logs.h"
#include "chrome/chrome_elf/third_party_dlls/packed_list_file.h"
#include "chrome/chrome_elf/third_party_dlls/packed_list_format.h"
#include "chrome/chrome_elf/third_party_dlls/status_codes.h"
#include "chrome/install_static/install_util.h"

namespace third_party_dlls {
namespace {

// Record if all the third-party DLL management code was successfully
// initialized, so processes can easily determine if it is enabled for them.
bool g_third_party_initialized = false;

//------------------------------------------------------------------------------
// Private functions
//------------------------------------------------------------------------------

// Clear all status codes.
bool ResetStatusCodes() {
  HANDLE key_handle = nullptr;

  // If the ThirdParty registry key does not exist, it will be created now.
  if (!nt::CreateRegKey(nt::HKCU,
                        install_static::GetRegistryPath()
                            .append(kThirdPartyRegKeyName)
                            .c_str(),
                        KEY_WRITE, &key_handle)) {
    return false;
  }

  bool success = nt::SetRegKeyValue(key_handle, kStatusCodesRegValue,
                                    REG_BINARY, nullptr, 0);
  nt::CloseRegKey(key_handle);

  return success;
}

// Store a status code for later consumption.
void AddStatusCode(ThirdPartyStatus code) {
  HANDLE key_handle = nullptr;

  if (!nt::CreateRegKey(nt::HKCU,
                        install_static::GetRegistryPath()
                            .append(kThirdPartyRegKeyName)
                            .c_str(),
                        KEY_WRITE | KEY_READ, &key_handle)) {
    return;
  }

  std::vector<BYTE> value_bytes;
  ULONG value_type = REG_NONE;
  // Query for the existing value and sanity check any existing content.
  // Note: If non-existent, or corrupt, carry on and overwrite.
  if (!nt::QueryRegKeyValue(key_handle, kStatusCodesRegValue, &value_type,
                            &value_bytes) ||
      value_type != REG_BINARY) {
    value_bytes.clear();
  }

  AddStatusCodeToBuffer(code, &value_bytes);

  assert(value_bytes.size() < std::numeric_limits<DWORD>::max());
  nt::SetRegKeyValue(key_handle, kStatusCodesRegValue, REG_BINARY,
                     value_bytes.data(),
                     static_cast<DWORD>(value_bytes.size()));
  nt::CloseRegKey(key_handle);

  return;
}

}  // namespace

//------------------------------------------------------------------------------
// Public defines & functions
//------------------------------------------------------------------------------

bool Init() {
  // Debug check: Init should not be called more than once.
  assert(!g_third_party_initialized);

  // Sanity check: third_party_dlls should only be enabled in the browser
  // process at this time.
  if (!install_static::IsBrowserProcess())
    return false;

  // Zero tolerance for unsupported versions of Windows.  Third-party control
  // is too entwined with the operating system.
  if (!::IsWindows7OrGreater())
    return false;

  if (!ResetStatusCodes())
    AddStatusCode(ThirdPartyStatus::kStatusCodeResetFailure);

  // 1) Initialize the blocklist from file
  ThirdPartyStatus status = InitFromFile();
  if (status != ThirdPartyStatus::kSuccess) {
    AddStatusCode(status);
    // A few status codes are considered acceptable here.
    if (!IsStatusCodeSuccessful(status))
      return false;
  }

  // 2) InitLogs
  status = InitLogs();
  if (status != ThirdPartyStatus::kSuccess) {
    AddStatusCode(status);
    DeinitFromFile();
    return false;
  }

  // 3) Apply the hook only after everything else is successfully set up.
  status = ApplyHook();
  if (status != ThirdPartyStatus::kSuccess) {
    AddStatusCode(status);
    DeinitLogs();
    DeinitFromFile();
    return false;
  }

  // Record initialization.
  g_third_party_initialized = true;

  return true;
}

//------------------------------------------------------------------------------
// Testing-only access to status code APIs.
//------------------------------------------------------------------------------
bool ResetStatusCodesForTesting() {
  return ResetStatusCodes();
}

void AddStatusCodeForTesting(ThirdPartyStatus code) {
  AddStatusCode(code);
}

}  // namespace third_party_dlls

bool IsThirdPartyInitialized() {
  return third_party_dlls::g_third_party_initialized;
}