chromium/chrome/updater/test/unit_test_util_win.cc

// Copyright 2022 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/updater/test/unit_test_util_win.h"

#include <windows.h>

#include <string>

#include "base/base_paths_win.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_timeouts.h"
#include "base/win/atl.h"
#include "base/win/registry.h"
#include "chrome/updater/test/unit_test_util.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/win_constants.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace updater::test {

namespace {

void CreateAppCommandRegistryHelper(UpdaterScope scope,
                                    const std::wstring& app_id,
                                    const std::wstring& cmd_id,
                                    const std::wstring& cmd_line,
                                    bool auto_run_on_os_upgrade) {
  CreateAppClientKey(scope, app_id);
  base::win::RegKey command_key;
  EXPECT_EQ(command_key.Create(UpdaterScopeToHKeyRoot(scope),
                               GetAppCommandKey(app_id, cmd_id).c_str(),
                               Wow6432(KEY_WRITE)),
            ERROR_SUCCESS);
  EXPECT_EQ(command_key.WriteValue(kRegValueCommandLine, cmd_line.c_str()),
            ERROR_SUCCESS);
  if (auto_run_on_os_upgrade) {
    EXPECT_EQ(command_key.WriteValue(kRegValueAutoRunOnOSUpgrade, 1U),
              ERROR_SUCCESS);
  }
}

}  // namespace

base::win::RegKey CreateAppClientKey(UpdaterScope scope,
                                     const std::wstring& app_id) {
  base::win::RegKey client_key;
  EXPECT_EQ(
      client_key.Create(UpdaterScopeToHKeyRoot(scope),
                        GetAppClientsKey(app_id).c_str(), Wow6432(KEY_WRITE)),
      ERROR_SUCCESS);
  return client_key;
}

void DeleteAppClientKey(UpdaterScope scope, const std::wstring& app_id) {
  base::win::RegKey(UpdaterScopeToHKeyRoot(scope), L"", Wow6432(DELETE))
      .DeleteKey(GetAppClientsKey(app_id).c_str());
}

base::win::RegKey CreateAppClientStateKey(UpdaterScope scope,
                                          const std::wstring& app_id) {
  base::win::RegKey client_state_key;
  EXPECT_EQ(client_state_key.Create(UpdaterScopeToHKeyRoot(scope),
                                    GetAppClientStateKey(app_id).c_str(),
                                    Wow6432(KEY_WRITE)),
            ERROR_SUCCESS);
  return client_state_key;
}

void DeleteAppClientStateKey(UpdaterScope scope, const std::wstring& app_id) {
  base::win::RegKey(UpdaterScopeToHKeyRoot(scope), L"", Wow6432(DELETE))
      .DeleteKey(GetAppClientStateKey(app_id).c_str());
}

void CreateLaunchCmdElevatedRegistry(const std::wstring& app_id,
                                     const std::wstring& name,
                                     const std::wstring& pv,
                                     const std::wstring& command_id,
                                     const std::wstring& command_line) {
  base::win::RegKey client_key =
      CreateAppClientKey(UpdaterScope::kSystem, app_id);
  EXPECT_EQ(client_key.WriteValue(kRegValueName, name.c_str()), ERROR_SUCCESS);
  EXPECT_EQ(client_key.WriteValue(kRegValuePV, pv.c_str()), ERROR_SUCCESS);
  EXPECT_EQ(client_key.WriteValue(command_id.c_str(), command_line.c_str()),
            ERROR_SUCCESS);
}

void CreateAppCommandRegistry(UpdaterScope scope,
                              const std::wstring& app_id,
                              const std::wstring& cmd_id,
                              const std::wstring& cmd_line) {
  CreateAppCommandRegistryHelper(scope, app_id, cmd_id, cmd_line, false);
}

void CreateAppCommandOSUpgradeRegistry(UpdaterScope scope,
                                       const std::wstring& app_id,
                                       const std::wstring& cmd_id,
                                       const std::wstring& cmd_line) {
  CreateAppCommandRegistryHelper(scope, app_id, cmd_id, cmd_line, true);
}

void SetupCmdExe(UpdaterScope scope,
                 base::CommandLine& cmd_exe_command_line,
                 base::ScopedTempDir& temp_programfiles_dir) {
  constexpr wchar_t kCmdExe[] = L"cmd.exe";

  base::FilePath system_path;
  ASSERT_TRUE(base::PathService::Get(base::DIR_SYSTEM, &system_path));

  const base::FilePath cmd_exe_system_path = system_path.Append(kCmdExe);
  if (!IsSystemInstall(scope)) {
    cmd_exe_command_line = base::CommandLine(cmd_exe_system_path);
    return;
  }

  base::FilePath programfiles_path;
  ASSERT_TRUE(
      base::PathService::Get(base::DIR_PROGRAM_FILES, &programfiles_path));
  ASSERT_TRUE(
      temp_programfiles_dir.CreateUniqueTempDirUnderPath(programfiles_path));
  base::FilePath cmd_exe_path;
  cmd_exe_path = temp_programfiles_dir.GetPath().Append(kCmdExe);

  ASSERT_TRUE(base::CopyFile(cmd_exe_system_path, cmd_exe_path));
  cmd_exe_command_line = base::CommandLine(cmd_exe_path);
}

[[nodiscard]] bool CreateService(const std::wstring& service_name,
                                 const std::wstring& display_name,
                                 const std::wstring& command_line) {
  ScopedScHandle scm(::OpenSCManager(
      nullptr, nullptr, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE));
  if (!scm.IsValid()) {
    return false;
  }

  ScopedScHandle service(::CreateService(
      scm.Get(), service_name.c_str(), display_name.c_str(),
      DELETE | SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG,
      SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
      command_line.c_str(), nullptr, nullptr, nullptr, nullptr, nullptr));
  return service.IsValid() || ::GetLastError() == ERROR_SERVICE_EXISTS;
}

CSecurityDesc GetEveryoneDaclSecurityDescriptor(ACCESS_MASK accessmask) {
  CSecurityDesc sd;
  CDacl dacl;
  dacl.AddAllowedAce(Sids::System(), accessmask);
  dacl.AddAllowedAce(Sids::Admins(), accessmask);
  dacl.AddAllowedAce(Sids::Interactive(), accessmask);

  sd.SetDacl(dacl);
  sd.MakeAbsolute();
  return sd;
}

test::EventHolder CreateEveryoneWaitableEventForTest() {
  const std::wstring event_name =
      base::StrCat({base::UTF8ToWide(test::GetTestName()), L" ",
                    base::NumberToWString(::GetCurrentProcessId())});
  CSecurityAttributes sa(GetEveryoneDaclSecurityDescriptor(GENERIC_ALL));
  return {base::WaitableEvent(base::win::ScopedHandle(
              ::CreateEvent(&sa, FALSE, FALSE, event_name.c_str()))),
          event_name};
}

int RunVPythonCommand(const base::CommandLine& command_line) {
  base::CommandLine python_command = command_line;
  python_command.PrependWrapper(FILE_PATH_LITERAL("vpython3.bat"));

  int exit_code = -1;
  base::Process process = base::LaunchProcess(python_command, {});
  EXPECT_TRUE(process.IsValid());
  EXPECT_TRUE(process.WaitForExitWithTimeout(TestTimeouts::action_timeout(),
                                             &exit_code));
  return exit_code;
}

}  // namespace updater::test