chromium/chrome/credential_provider/gaiacp/reg_utils.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chrome/credential_provider/gaiacp/reg_utils.h"

#include "base/base64.h"
#include "base/strings/strcat_win.h"
#include "base/strings/string_number_conversions_win.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/atl.h"
#include "base/win/registry.h"
#include "base/win/win_util.h"
#include "build/branding_buildflags.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
#include "chrome/credential_provider/gaiacp/logging.h"

namespace credential_provider {

// Root registry key for GCPW configuration and state.
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#define CREDENTIAL_PROVIDER_REGISTRY_KEY L"Software\\Google\\GCPW"
#else
#define CREDENTIAL_PROVIDER_REGISTRY_KEY L"Software\\Chromium\\GCPW"
#endif  // BUILDFLAG(GOOGLE_CHROME_BRANDING)

const wchar_t kGcpRootKeyName[] = CREDENTIAL_PROVIDER_REGISTRY_KEY;

const wchar_t kGcpUsersRootKeyName[] =
    CREDENTIAL_PROVIDER_REGISTRY_KEY L"\\Users";

const wchar_t kWinlogonUserListRegKey[] =
    L"SOFTWARE\\Microsoft\\Windows NT"
    L"\\CurrentVersion\\Winlogon\\SpecialAccounts\\UserList";

const wchar_t kLogonUiUserTileRegKey[] =
    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Authentication\\LogonUI"
    L"\\UserTile";

const wchar_t kMicrosoftCryptographyRegKey[] =
    L"SOFTWARE\\Microsoft\\Cryptography";
const wchar_t kMicrosoftCryptographyMachineGuidRegKey[] = L"MachineGuid";

constexpr wchar_t kRegUserDeviceResourceId[] = L"device_resource_id";
constexpr wchar_t kRegGlsPath[] = L"gls_path";
constexpr wchar_t kRegEnableVerboseLogging[] = L"enable_verbose_logging";
constexpr wchar_t kRegLogFilePath[] = L"log_file_path";
constexpr wchar_t kRegLogFileAppend[] = L"log_file_append";
constexpr wchar_t kRegInitializeCrashReporting[] = L"enable_crash_reporting";
constexpr wchar_t kRegMdmUrl[] = L"mdm";
constexpr wchar_t kRegEnableDmEnrollment[] = L"enable_dm_enrollment";
constexpr wchar_t kRegDisablePasswordSync[] = L"disable_password_sync";
constexpr wchar_t kRegMdmSupportsMultiUser[] = L"enable_multi_user_login";
constexpr wchar_t kRegMdmAllowConsumerAccounts[] = L"enable_consumer_accounts ";
constexpr wchar_t kRegMdmEnableForcePasswordReset[] =
    L"enable_force_reset_password_option";
constexpr wchar_t kRegDeviceDetailsUploadStatus[] =
    L"device_details_upload_status";
constexpr wchar_t kRegDeviceDetailsUploadFailures[] =
    L"device_details_upload_failures";
constexpr wchar_t kRegDeveloperMode[] = L"developer_mode";
constexpr wchar_t kRegUpdateCredentialsOnChange[] =
    L"update_credentials_on_change";
constexpr wchar_t kRegUseShorterAccountName[] = L"use_shorter_account_name";
constexpr wchar_t kEmailDomainsKey[] = L"ed";  // deprecated.
constexpr wchar_t kEmailDomainsKeyNew[] = L"domains_allowed_to_login";

const wchar_t kLastUserPolicyRefreshTimeRegKey[] = L"last_policy_refresh_time";
const wchar_t kLastUserExperimentsRefreshTimeRegKey[] =
    L"last_experiments_refresh_time";

namespace {

constexpr wchar_t kAccountPicturesRootRegKey[] =
    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\AccountPicture\\Users";
constexpr wchar_t kImageRegKey[] = L"Image";

// Registry entry that controls whether GCPW is the default
// Credential Provider or not.
constexpr wchar_t kMakeGcpwDefaultCredProvider[] = L"set_gcpw_as_default_cp";
// Windows OS defined registry entry used to configure the
// default credential provider CLSID.
constexpr wchar_t kDefaultCredProviderPath[] =
    L"Software\\Policies\\Microsoft\\Windows\\System";
constexpr wchar_t kDefaultCredProviderKey[] = L"DefaultCredentialProvider";

constexpr wchar_t kEnrollmentRegKey[] = L"SOFTWARE\\Google\\Enrollment";
constexpr wchar_t kDmTokenRegKey[] = L"dmtoken";

HRESULT SetMachineRegBinaryInternal(const std::wstring& key_name,
                                    const std::wstring& name,
                                    const std::string& value,
                                    REGSAM sam_desired) {
  base::win::RegKey key;
  LONG sts = key.Create(HKEY_LOCAL_MACHINE, key_name.c_str(), sam_desired);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  if (value.empty()) {
    sts = key.DeleteValue(name.c_str());
    if (sts == ERROR_FILE_NOT_FOUND)
      sts = ERROR_SUCCESS;
  } else {
    sts =
        key.WriteValue(name.c_str(), value.c_str(), value.length(), REG_BINARY);
  }

  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  return S_OK;
}

std::wstring GetImageRegKeyForSpecificSize(int image_size) {
  return kImageRegKey + base::NumberToWString(image_size);
}

std::wstring GetAccountPictureRegPathForUSer(const std::wstring& user_sid) {
  return base::StrCat({kAccountPicturesRootRegKey, L"\\", user_sid});
}

}  // namespace

HRESULT SetMachineRegDWORD(const std::wstring& key_name,
                           const std::wstring& name,
                           DWORD value) {
  base::win::RegKey key;
  LONG sts = key.Create(HKEY_LOCAL_MACHINE, key_name.c_str(), KEY_WRITE);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  sts = key.WriteValue(name.c_str(), value);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  return S_OK;
}

HRESULT MakeGcpwDefaultCP() {
  if (GetGlobalFlagOrDefault(kMakeGcpwDefaultCredProvider, 1))
    return SetMachineRegString(
        kDefaultCredProviderPath, kDefaultCredProviderKey,
        base::win::WStringFromGUID(CLSID_GaiaCredentialProvider));

  return S_OK;
}

HRESULT SetMachineRegString(const std::wstring& key_name,
                            const std::wstring& name,
                            const std::wstring& value) {
  base::win::RegKey key;
  LONG sts = key.Create(HKEY_LOCAL_MACHINE, key_name.c_str(), KEY_WRITE);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  if (value.empty()) {
    sts = key.DeleteValue(name.c_str());
    if (sts == ERROR_FILE_NOT_FOUND)
      sts = ERROR_SUCCESS;
  } else {
    sts = key.WriteValue(name.c_str(), value.c_str());
  }

  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  return S_OK;
}

HRESULT GetMachineRegDWORD(const std::wstring& key_name,
                           const std::wstring& name,
                           DWORD* value) {
  base::win::RegKey key;
  LONG sts = key.Open(HKEY_LOCAL_MACHINE, key_name.c_str(), KEY_READ);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  sts = key.ReadValueDW(name.c_str(), value);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  return S_OK;
}

HRESULT GetMachineRegString(const std::wstring& key_name,
                            const std::wstring& name,
                            wchar_t* value,
                            ULONG* length) {
  DCHECK(value);
  DCHECK(length);
  DCHECK_GT(*length, 0u);

  base::win::RegKey key;
  LONG sts = key.Open(HKEY_LOCAL_MACHINE, key_name.c_str(), KEY_READ);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  // read one less character that specified in |length| so that the returned
  // string can always be null terminated.  Note that string registry values
  // are not guaranteed to be null terminated.
  DWORD type;
  ULONG local_length = (*length - 1) * sizeof(decltype(value[0]));
  sts = key.ReadValue(name.c_str(), value, &local_length, &type);
  if (type != REG_SZ)
    return HRESULT_FROM_WIN32(ERROR_CANTREAD);

  // When using this overload of the ReadValue() method, the returned length
  // is in bytes.  The caller expects the length in characters.
  local_length /= sizeof(decltype(value[0]));

  if (sts != ERROR_SUCCESS) {
    if (sts == ERROR_MORE_DATA)
      *length = local_length;
    return HRESULT_FROM_WIN32(sts);
  }

  value[local_length] = 0;
  *length = local_length;
  return S_OK;
}

HRESULT GetMachineRegBinaryInternal(const std::wstring& key_name,
                                    const std::wstring& name,
                                    std::string* val,
                                    REGSAM sam_desired) {
  DCHECK(val);

  base::win::RegKey key;
  LONG sts = key.Open(HKEY_LOCAL_MACHINE, key_name.c_str(), sam_desired);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  DWORD type;
  DWORD size = 0;

  sts = key.ReadValue(name.c_str(), nullptr, &size, &type);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  if (type != REG_BINARY)
    return HRESULT_FROM_WIN32(ERROR_CANTREAD);

  std::vector<char> buffer(size);

  sts = key.ReadValue(name.c_str(), const_cast<char*>(buffer.data()), &size,
                      &type);
  if (sts != ERROR_SUCCESS) {
    if (sts == ERROR_MORE_DATA)
      return HRESULT_FROM_WIN32(sts);
  }

  val->assign(buffer.data(), buffer.size());

  return S_OK;
}

HRESULT GetAccountPictureRegString(const std::wstring& user_sid,
                                   int image_size,
                                   wchar_t* value,
                                   ULONG* length) {
  return GetMachineRegString(GetAccountPictureRegPathForUSer(user_sid),
                             GetImageRegKeyForSpecificSize(image_size), value,
                             length);
}

// Sets a specific account picture registry key in HKEY_LOCAL_MACHINE
HRESULT SetAccountPictureRegString(const std::wstring& user_sid,
                                   int image_size,
                                   const std::wstring& value) {
  return SetMachineRegString(GetAccountPictureRegPathForUSer(user_sid),
                             GetImageRegKeyForSpecificSize(image_size), value);
}

HRESULT GetGlobalFlag(const std::wstring& name, DWORD* value) {
  return GetMachineRegDWORD(kGcpRootKeyName, name, value);
}

HRESULT GetGlobalFlag(const std::wstring& name, wchar_t* value, ULONG* length) {
  return GetMachineRegString(kGcpRootKeyName, name, value, length);
}

std::wstring GetGlobalFlagOrDefault(const std::wstring& reg_key,
                                    const std::wstring& default_value) {
  wchar_t reg_value_buffer[256];
  ULONG length = std::size(reg_value_buffer);
  HRESULT hr = GetGlobalFlag(reg_key, reg_value_buffer, &length);
  if (FAILED(hr))
    return default_value;

  return reg_value_buffer;
}

DWORD GetGlobalFlagOrDefault(const std::wstring& reg_key,
                             const DWORD& default_value) {
  DWORD value;
  HRESULT hr = GetGlobalFlag(reg_key, &value);
  return SUCCEEDED(hr) ? value : default_value;
}

HRESULT SetGlobalFlag(const std::wstring& name, DWORD value) {
  return SetMachineRegDWORD(kGcpRootKeyName, name, value);
}

HRESULT SetGlobalFlag(const std::wstring& name, const std::wstring& value) {
  return SetMachineRegString(kGcpRootKeyName, name, value);
}

HRESULT SetGlobalFlagForTesting(const std::wstring& name,
                                const std::wstring& value) {
  return SetMachineRegString(kGcpRootKeyName, name, value);
}

HRESULT SetGlobalFlagForTesting(const std::wstring& name, DWORD value) {
  return SetMachineRegDWORD(kGcpRootKeyName, name, value);
}

HRESULT SetUpdaterClientsAppPathFlag(const std::wstring& name, DWORD value) {
  base::win::RegKey key;
  LONG sts = key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientsAppPath,
                        KEY_WRITE | KEY_WOW64_32KEY);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  sts = key.WriteValue(name.c_str(), value);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  return S_OK;
}

HRESULT GetUpdaterClientsAppPathFlag(const std::wstring& name, DWORD* value) {
  base::win::RegKey key;
  LONG sts = key.Open(HKEY_LOCAL_MACHINE, kRegUpdaterClientsAppPath,
                      KEY_READ | KEY_WOW64_32KEY);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  sts = key.ReadValueDW(name.c_str(), value);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  return S_OK;
}

DWORD GetUpdaterClientsAppPathFlagOrDefault(const std::wstring& reg_key,
                                            const DWORD& default_value) {
  DWORD value;
  HRESULT hr = GetUpdaterClientsAppPathFlag(reg_key, &value);
  return SUCCEEDED(hr) ? value : default_value;
}

HRESULT GetUserProperty(const std::wstring& sid,
                        const std::wstring& name,
                        DWORD* value) {
  wchar_t key_name[128];
  swprintf_s(key_name, std::size(key_name), L"%s\\%s", kGcpUsersRootKeyName,
             sid.c_str());
  return GetMachineRegDWORD(key_name, name, value);
}

HRESULT GetUserProperty(const std::wstring& sid,
                        const std::wstring& name,
                        wchar_t* value,
                        ULONG* length) {
  wchar_t key_name[128];
  swprintf_s(key_name, std::size(key_name), L"%s\\%s", kGcpUsersRootKeyName,
             sid.c_str());
  return GetMachineRegString(key_name, name, value, length);
}

HRESULT SetUserProperty(const std::wstring& sid,
                        const std::wstring& name,
                        DWORD value) {
  wchar_t key_name[128];
  swprintf_s(key_name, std::size(key_name), L"%s\\%s", kGcpUsersRootKeyName,
             sid.c_str());
  return SetMachineRegDWORD(key_name, name, value);
}

HRESULT SetUserProperty(const std::wstring& sid,
                        const std::wstring& name,
                        const std::wstring& value) {
  wchar_t key_name[128];
  swprintf_s(key_name, std::size(key_name), L"%s\\%s", kGcpUsersRootKeyName,
             sid.c_str());
  return SetMachineRegString(key_name, name, value);
}

HRESULT RemoveAllUserProperties(const std::wstring& sid) {
  base::win::RegKey key;
  LONG sts = key.Open(HKEY_LOCAL_MACHINE, kGcpUsersRootKeyName, KEY_WRITE);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  sts = key.DeleteKey(sid.c_str());
  return sts != ERROR_SUCCESS ? HRESULT_FROM_WIN32(sts) : S_OK;
}

HRESULT GetUserTokenHandles(
    std::map<std::wstring, UserTokenHandleInfo>* sid_to_handle_info) {
  DCHECK(sid_to_handle_info);
  sid_to_handle_info->clear();

  base::win::RegistryKeyIterator iter(HKEY_LOCAL_MACHINE, kGcpUsersRootKeyName);
  for (; iter.Valid(); ++iter) {
    const wchar_t* sid = iter.Name();
    wchar_t gaia_id[256];
    ULONG length = std::size(gaia_id);
    HRESULT gaia_id_hr = GetUserProperty(sid, kUserId, gaia_id, &length);
    wchar_t token_handle[256];
    length = std::size(token_handle);
    HRESULT token_handle_hr =
        GetUserProperty(sid, kUserTokenHandle, token_handle, &length);
    wchar_t email_address[256];
    length = std::size(email_address);
    HRESULT email_address_hr =
        GetUserProperty(sid, kUserEmail, email_address, &length);
    sid_to_handle_info->emplace(
        sid,
        UserTokenHandleInfo{SUCCEEDED(gaia_id_hr) ? gaia_id : L"",
                            SUCCEEDED(email_address_hr) ? email_address : L"",
                            SUCCEEDED(token_handle_hr) ? token_handle : L""});
  }
  return S_OK;
}

HRESULT GetSidFromKey(const wchar_t* key,
                      const std::wstring& value,
                      wchar_t* sid,
                      ULONG length) {
  DCHECK(sid);
  bool result_found = false;
  base::win::RegistryKeyIterator iter(HKEY_LOCAL_MACHINE, kGcpUsersRootKeyName);
  for (; iter.Valid(); ++iter) {
    const wchar_t* user_sid = iter.Name();
    wchar_t result[256];
    ULONG result_length = std::size(result);
    HRESULT hr = GetUserProperty(user_sid, key, result, &result_length);
    if (SUCCEEDED(hr) && value == result) {
      // Make sure there are not 2 users with the same SID.
      if (result_found)
        return HRESULT_FROM_WIN32(ERROR_USER_EXISTS);

      wcsncpy_s(sid, length, user_sid, wcslen(user_sid));
      result_found = true;
    }
  }

  return result_found ? S_OK : HRESULT_FROM_WIN32(ERROR_NONE_MAPPED);
}

HRESULT GetSidFromEmail(const std::wstring& email, wchar_t* sid, ULONG length) {
  return GetSidFromKey(kUserEmail, email, sid, length);
}

HRESULT GetSidFromId(const std::wstring& id, wchar_t* sid, ULONG length) {
  return GetSidFromKey(kUserId, id, sid, length);
}

HRESULT GetSidFromDomainAccountInfo(const std::wstring& domain,
                                    const std::wstring& username,
                                    wchar_t* sid,
                                    ULONG length) {
  // Max SID length is 256 characters.
  // https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-offlineuseraccounts-offlinedomainaccounts-offlinedomainaccount-sid
  wchar_t sid1[256];
  wchar_t sid2[256];

  if (SUCCEEDED(GetSidFromKey(base::UTF8ToWide(kKeyDomain).c_str(), domain,
                              sid1, length)) &&
      SUCCEEDED(GetSidFromKey(base::UTF8ToWide(kKeyUsername).c_str(), username,
                              sid2, length)) &&
      wcsicmp(sid1, sid2) == 0) {
    wcscpy_s(sid, length, sid1);
    return S_OK;
  } else {
    return E_FAIL;
  }
}

HRESULT GetIdFromSid(const wchar_t* sid, std::wstring* id) {
  DCHECK(id);

  base::win::RegistryKeyIterator iter(HKEY_LOCAL_MACHINE, kGcpUsersRootKeyName);
  for (; iter.Valid(); ++iter) {
    const wchar_t* user_sid = iter.Name();

    if (wcscmp(sid, user_sid) == 0) {
      wchar_t user_id[256];
      ULONG user_length = std::size(user_id);
      HRESULT hr = GetUserProperty(user_sid, kUserId, user_id, &user_length);
      if (SUCCEEDED(hr)) {
        *id = user_id;
        return S_OK;
      }
    }
  }
  return HRESULT_FROM_WIN32(ERROR_NONE_MAPPED);
}

std::string GetUserEmailFromSid(const std::wstring& sid) {
  wchar_t email_id[512];
  ULONG email_id_size = std::size(email_id);
  HRESULT hr = GetUserProperty(sid, kUserEmail, email_id, &email_id_size);

  std::wstring email_id_str;
  if (SUCCEEDED(hr) && email_id_size > 0)
    email_id_str = std::wstring(email_id, email_id_size - 1);

  return base::WideToUTF8(email_id_str);
}

void GetChildrenAtPath(const wchar_t* path,
                       std::vector<std::wstring>& children) {
  base::win::RegistryKeyIterator iter(HKEY_LOCAL_MACHINE, path);
  for (; iter.Valid(); ++iter) {
    const wchar_t* child = iter.Name();
    children.push_back(std::wstring(child));
  }
}

HRESULT SetUserWinlogonUserListEntry(const std::wstring& username,
                                     DWORD visible) {
  // Sets the value of the key that will hide the user from all credential
  // providers.
  base::win::RegKey key;
  LONG sts = key.Create(HKEY_LOCAL_MACHINE, kWinlogonUserListRegKey, KEY_WRITE);
  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  // The key is only set if we want to make a user invisible. The default
  // behavior is for the user to be visible so delete the key in this case.
  if (!visible) {
    sts = key.WriteValue(username.c_str(), visible);
  } else {
    sts = key.DeleteValue(username.c_str());
    if (sts == ERROR_FILE_NOT_FOUND)
      sts = ERROR_SUCCESS;
  }

  if (sts != ERROR_SUCCESS)
    return HRESULT_FROM_WIN32(sts);

  return S_OK;
}

HRESULT SetLogonUiUserTileEntry(const std::wstring& sid, CLSID cp_guid) {
  return SetMachineRegString(kLogonUiUserTileRegKey, sid,
                             base::win::WStringFromGUID(cp_guid));
}

HRESULT GetMachineGuid(std::wstring* machine_guid) {
  // The machine guid is a unique identifier assigned to a computer on every
  // install of Windows. This guid can be used to uniquely identify this device
  // to various management services. The same guid is used to identify the
  // device to Chrome Browser Cloud Management. It is fetched in this file:
  // chrome/browser/policy/browser_dm_token_storage_win.cc:InitClientId.
  DCHECK(machine_guid);
  wchar_t machine_guid_buffer[64];
  ULONG guid_length = std::size(machine_guid_buffer);
  HRESULT hr = GetMachineRegString(kMicrosoftCryptographyRegKey,
                                   kMicrosoftCryptographyMachineGuidRegKey,
                                   machine_guid_buffer, &guid_length);

  if (SUCCEEDED(hr))
    *machine_guid = machine_guid_buffer;

  return hr;
}

HRESULT SetMachineGuidForTesting(const std::wstring& machine_guid) {
  // Set a debug guid for the machine so that unit tests that override the
  // registry can run properly.
  return SetMachineRegString(kMicrosoftCryptographyRegKey,
                             kMicrosoftCryptographyMachineGuidRegKey,
                             machine_guid);
}

std::wstring GetUserDeviceResourceId(const std::wstring& sid) {
  wchar_t known_resource_id[512];
  ULONG known_resource_id_size = std::size(known_resource_id);
  HRESULT hr = GetUserProperty(sid, kRegUserDeviceResourceId, known_resource_id,
                               &known_resource_id_size);

  if (SUCCEEDED(hr) && known_resource_id_size > 0)
    return std::wstring(known_resource_id, known_resource_id_size - 1);

  return std::wstring();
}

HRESULT GetDmToken(std::string* dm_token) {
  DCHECK(dm_token);

  std::string binary_dm_token;
  HRESULT hr =
      GetMachineRegBinaryInternal(kEnrollmentRegKey, kDmTokenRegKey,
                                  &binary_dm_token, KEY_READ | KEY_WOW64_32KEY);
  if (SUCCEEDED(hr)) {
    *dm_token = base::Base64Encode(binary_dm_token);
  }
  return hr;
}

HRESULT SetDmTokenForTesting(const std::string& dm_token) {
  // Set a debug dm token for the machine so that unit tests that override the
  // registry can run properly.
  return SetMachineRegBinaryInternal(kEnrollmentRegKey, kDmTokenRegKey,
                                     dm_token, KEY_WRITE | KEY_WOW64_32KEY);
}

}  // namespace credential_provider