chromium/chrome/updater/activity_impl_win.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/functional/function_ref.h"
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "base/win/windows_types.h"
#include "chrome/updater/activity.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/user_info.h"
#include "chrome/updater/win/win_constants.h"

namespace updater {
namespace {

using ProcessActiveBitUnderKeyCallback =
    base::FunctionRef<bool(HKEY, const std::wstring&)>;

constexpr wchar_t kDidRun[] = L"dr";

bool GetActiveBitUnderKey(HKEY rootkey, const std::wstring& key_name) {
  base::win::RegKey key;
  if (key.Open(rootkey, key_name.c_str(), Wow6432(KEY_QUERY_VALUE)) ==
      ERROR_SUCCESS) {
    // We support both string and DWORD formats for backward compatibility.
    std::wstring value;
    if ((key.ReadValue(kDidRun, &value) == ERROR_SUCCESS) && (value == L"1")) {
      return true;
    }

    DWORD value_dw = 0;
    if ((key.ReadValueDW(kDidRun, &value_dw) == ERROR_SUCCESS) &&
        (value_dw == 1)) {
      return true;
    }
  }
  return false;
}

// Always returns false to avoid the short circuit in ProcessActiveBit and
// the early return in ProcessSystemActiveBit.
bool ClearActiveBitUnderKey(HKEY rootkey, const std::wstring& key_name) {
  base::win::RegKey key;
  if (key.Open(rootkey, key_name.c_str(),
               Wow6432(KEY_QUERY_VALUE | KEY_SET_VALUE)) != ERROR_SUCCESS) {
    VLOG(3) << "Failed to open activity key with write for " << key_name;
    return false;
  }

  if (!key.HasValue(kDidRun)) {
    return false;
  }

  // We always clear the value as a string "0".
  const LONG result = key.WriteValue(kDidRun, L"0");
  VLOG_IF(2, result) << "Failed to clear activity key for " << key_name << ": "
                     << result;
  return false;
}

bool ProcessActiveBit(ProcessActiveBitUnderKeyCallback callback,
                      HKEY rootkey,
                      const std::wstring& sid,
                      const std::string& id) {
  const std::wstring rootkey_suffix =
      (rootkey == HKEY_USERS) ? base::StrCat({sid, L"\\"}) : L"";
  const bool process_success = callback(
      rootkey, base::StrCat({rootkey_suffix, GetAppClientStateKey(id)}));

  // For Google Toolbar and similar apps that run at low integrity, we need to
  // also look at the low integrity IE key. Note that we cannot use the
  // IEGetWriteableHKCU function since this function assumes that we are
  // running with the user's credentials. The path is as follows:
  // USER_REG_VISTA_LOW_INTEGRITY_HKCU\\SID
  // \\GOOPDATE_REG_RELATIVE_CLIENT_STATE\\app_guid
  const std::wstring low_integrity_key_name =
      base::StrCat({rootkey_suffix, USER_REG_VISTA_LOW_INTEGRITY_HKCU, L"\\",
                    sid, L"\\", GetAppClientStateKey(id)});

  return callback(rootkey, low_integrity_key_name) || process_success;
}

bool ProcessUserActiveBit(ProcessActiveBitUnderKeyCallback callback,
                          const std::string& id) {
  // Clear the active bit under HKCU.
  std::wstring sid;
  const HRESULT hr = GetProcessUser(nullptr, nullptr, &sid);
  if (FAILED(hr)) {
    VLOG(2) << "Failed to GetProcessUser " << hr;
    return false;
  }

  return ProcessActiveBit(callback, HKEY_CURRENT_USER, sid, id);
}

bool ProcessSystemActiveBit(ProcessActiveBitUnderKeyCallback callback,
                            const std::string& id) {
  // Clear the active bit under each user in HKU\<sid>.
  for (base::win::RegistryKeyIterator it(HKEY_USERS, L"", KEY_WOW64_32KEY);
       it.Valid(); ++it) {
    const std::wstring sid = it.Name();
    if (ProcessActiveBit(callback, HKEY_USERS, sid, id)) {
      return true;
    }
  }

  return false;
}

bool GetUserActiveBit(const std::string& id) {
  // Read the active bit under HKCU.
  return ProcessUserActiveBit(&GetActiveBitUnderKey, id);
}

void ClearUserActiveBit(const std::string& id) {
  // Clear the active bit under HKCU.
  ProcessUserActiveBit(&ClearActiveBitUnderKey, id);
}

bool GetSystemActiveBit(const std::string& id) {
  // Read the active bit under each user in HKU\<sid>.
  return ProcessSystemActiveBit(&GetActiveBitUnderKey, id);
}

void ClearSystemActiveBit(const std::string& id) {
  // Clear the active bit under each user in HKU\<sid>.
  ProcessSystemActiveBit(&ClearActiveBitUnderKey, id);
}

}  // namespace

bool GetActiveBit(UpdaterScope scope, const std::string& id) {
  switch (scope) {
    case UpdaterScope::kUser:
      return GetUserActiveBit(id);
    case UpdaterScope::kSystem:
      return GetSystemActiveBit(id);
  }
}

void ClearActiveBit(UpdaterScope scope, const std::string& id) {
  switch (scope) {
    case UpdaterScope::kUser:
      ClearUserActiveBit(id);
      break;
    case UpdaterScope::kSystem:
      ClearSystemActiveBit(id);
      break;
  }
}

}  // namespace updater