chromium/chrome/updater/policy/win/group_policy_manager.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 "chrome/updater/policy/win/group_policy_manager.h"

#include <userenv.h>

#include <optional>
#include <ostream>
#include <string>

#include "base/check.h"
#include "base/enterprise_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/scoped_generic.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "base/win/registry.h"
#include "chrome/updater/win/win_constants.h"

namespace updater {

namespace {

struct ScopedHCriticalPolicySectionTraits {
  static HANDLE InvalidValue() { return nullptr; }
  static void Free(HANDLE handle) {
    if (handle != InvalidValue()) {
      ::LeaveCriticalPolicySection(handle);
    }
  }
};

// Manages the lifetime of critical policy section handle allocated by
// ::EnterCriticalPolicySection.
using scoped_hpolicy =
    base::ScopedGeneric<HANDLE, updater::ScopedHCriticalPolicySectionTraits>;

struct PolicySectionEvents
    : public base::RefCountedThreadSafe<PolicySectionEvents> {
  base::WaitableEvent enter_policy_section;
  base::WaitableEvent leave_policy_section;

 private:
  friend class base::RefCountedThreadSafe<PolicySectionEvents>;
  virtual ~PolicySectionEvents() = default;
};

base::Value::Dict LoadGroupPolicies(bool should_take_policy_critical_section) {
  base::ScopedClosureRunner leave_policy_section_closure;

  if (should_take_policy_critical_section && base::IsManagedDevice()) {
    // Only for managed machines, a best effort is made to take the Group Policy
    // critical section. Lock acquisition can take a long time in the worst case
    // scenarios, hence a short timed wait is used.

    auto events = base::MakeRefCounted<PolicySectionEvents>();
    leave_policy_section_closure.ReplaceClosure(base::BindOnce(
        [](scoped_refptr<PolicySectionEvents> events) {
          events->leave_policy_section.Signal();
        },
        events));

    base::ThreadPool::PostTask(
        FROM_HERE, {base::MayBlock(), base::WithBaseSyncPrimitives()},
        base::BindOnce(
            [](scoped_refptr<PolicySectionEvents> events) {
              scoped_hpolicy policy_lock(::EnterCriticalPolicySection(true));

              events->enter_policy_section.Signal();

              events->leave_policy_section.Wait();
            },
            events));

    if (!events->enter_policy_section.TimedWait(base::Seconds(30))) {
      VLOG(1) << "Timed out trying to get the policy critical section.";
    }
  }

  base::Value::Dict policies;

  for (base::win::RegistryValueIterator it(HKEY_LOCAL_MACHINE,
                                           UPDATER_POLICIES_KEY);
       it.Valid(); ++it) {
    const std::string key_name =
        base::ToLowerASCII(base::SysWideToUTF8(it.Name()));
    switch (it.Type()) {
      case REG_SZ:
        policies.Set(key_name, base::SysWideToUTF8(it.Value()));
        break;

      case REG_DWORD:
        policies.Set(key_name, *(reinterpret_cast<const int*>(it.Value())));
        break;

      default:
        // Ignore all types that are not used by updater policies.
        break;
    }
  }

  return policies;
}

}  // namespace

GroupPolicyManager::GroupPolicyManager(
    bool should_take_policy_critical_section,
    const std::optional<bool>& override_is_managed_device)
    : PolicyManager(LoadGroupPolicies(should_take_policy_critical_section)),
      is_managed_device_(override_is_managed_device.value_or(
          base::IsManagedOrEnterpriseDevice())) {}

GroupPolicyManager::~GroupPolicyManager() = default;

bool GroupPolicyManager::HasActiveDevicePolicies() const {
  return is_managed_device_ && PolicyManager::HasActiveDevicePolicies();
}

std::string GroupPolicyManager::source() const {
  return kSourceGroupPolicyManager;
}

}  // namespace updater