blob: 9c09939f6db8f4927515e472e872daaef6b848df [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/credential_provider/gaiacp/mdm_utils.h"
#include <windows.h>
#define _NTDEF_ // Prevent redefition errors, must come after <winternl.h>
#include <MDMRegistration.h> // For RegisterDeviceWithManagement()
#include <atlconv.h>
#include "base/base64.h"
#include "base/files/file_path.h"
#include "base/json/json_writer.h"
#include "base/scoped_native_library.h"
#include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/win_util.h"
#include "base/win/wmi.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
namespace credential_provider {
constexpr wchar_t kRegMdmUrl[] = L"mdm";
constexpr wchar_t kRegMdmSupportsMultiUser[] = L"mdm_mu";
// Overridden in tests to force the MDM enrollment to either succeed or fail.
enum class EnrollmentStatus {
kForceSuccess,
kForceFailure,
kDontForce,
};
EnrollmentStatus g_enrollment_status = EnrollmentStatus::kDontForce;
// Overridden in tests to force the MDM enrollment check to either return true
// or false.
enum class EnrolledStatus {
kForceTrue,
kForceFalse,
kDontForce,
};
EnrolledStatus g_enrolled_status = EnrolledStatus::kDontForce;
namespace {
template <typename T>
T GetMdmFunctionPointer(const base::ScopedNativeLibrary& library,
const char* function_name) {
if (!library.is_valid())
return nullptr;
return reinterpret_cast<T>(library.GetFunctionPointer(function_name));
}
#define GET_MDM_FUNCTION_POINTER(library, name) \
GetMdmFunctionPointer<decltype(&::name)>(library, #name)
base::string16 GetMdmUrl() {
wchar_t mdm_url[256];
ULONG length = base::size(mdm_url);
HRESULT hr = GetGlobalFlag(kRegMdmUrl, mdm_url, &length);
return hr == S_OK ? mdm_url : base::string16();
}
bool IsEnrolledWithGoogleMdm(const base::string16& mdm_url) {
switch (g_enrolled_status) {
case EnrolledStatus::kForceTrue:
return true;
case EnrolledStatus::kForceFalse:
return false;
case EnrolledStatus::kDontForce:
break;
}
base::ScopedNativeLibrary library(
base::FilePath(FILE_PATH_LITERAL("MDMRegistration.dll")));
auto get_device_registration_info_function =
GET_MDM_FUNCTION_POINTER(library, GetDeviceRegistrationInfo);
if (!get_device_registration_info_function) {
LOGFN(ERROR) << "GET_MDM_FUNCTION_POINTER(GetDeviceRegistrationInfo)";
return false;
}
MANAGEMENT_REGISTRATION_INFO* info;
HRESULT hr = get_device_registration_info_function(
DeviceRegistrationBasicInfo, reinterpret_cast<void**>(&info));
bool is_enrolled = SUCCEEDED(hr) && info->fDeviceRegisteredWithManagement &&
GURL(mdm_url) == GURL(info->pszMDMServiceUri);
if (SUCCEEDED(hr))
::HeapFree(::GetProcessHeap(), 0, info);
return is_enrolled;
}
HRESULT RegisterWithGoogleDeviceManagement(const base::string16& mdm_url,
const base::string16& email,
const std::string& data) {
switch (g_enrollment_status) {
case EnrollmentStatus::kForceSuccess:
return S_OK;
case EnrollmentStatus::kForceFailure:
return E_FAIL;
case EnrollmentStatus::kDontForce:
break;
}
// TODO(crbug.com/935577): Check if machine is already enrolled because
// attempting to enroll when already enrolled causes a crash.
if (IsEnrolledWithGoogleMdm(mdm_url)) {
LOGFN(INFO) << "Already enrolled to Google MDM";
return S_OK;
}
base::ScopedNativeLibrary library(
base::FilePath(FILE_PATH_LITERAL("MDMRegistration.dll")));
auto register_device_with_management_function =
GET_MDM_FUNCTION_POINTER(library, RegisterDeviceWithManagement);
if (!register_device_with_management_function) {
LOGFN(ERROR) << "GET_MDM_FUNCTION_POINTER(RegisterDeviceWithManagement)";
return false;
}
std::string data_encoded;
base::Base64Encode(data, &data_encoded);
// This register call is blocking. It won't return until the machine is
// properly registered with the MDM server.
return register_device_with_management_function(
email.c_str(), mdm_url.c_str(), base::UTF8ToWide(data_encoded).c_str());
}
} // namespace
bool NeedsToEnrollWithMdm() {
base::string16 mdm_url = GetMdmUrl();
return !mdm_url.empty() && !IsEnrolledWithGoogleMdm(mdm_url);
}
bool MdmEnrollmentEnabled() {
base::string16 mdm_url = GetMdmUrl();
return !mdm_url.empty();
}
HRESULT EnrollToGoogleMdmIfNeeded(const base::DictionaryValue& properties) {
USES_CONVERSION;
LOGFN(INFO);
base::string16 email = GetDictString(&properties, kKeyEmail);
base::string16 token = GetDictString(&properties, kKeyMdmIdToken);
if (email.empty()) {
LOGFN(ERROR) << "Email is empty";
return E_INVALIDARG;
}
if (token.empty()) {
LOGFN(ERROR) << "MDM id token is empty";
return E_INVALIDARG;
}
// Only enroll with MDM if configured.
base::string16 mdm_url = GetMdmUrl();
if (mdm_url.empty())
return S_OK;
LOGFN(INFO) << "MDM_URL=" << mdm_url
<< " token=" << base::string16(token.c_str(), 10);
// Build the json data needed by the server.
base::string16 serial_number =
base::win::WmiComputerSystemInfo::Get().serial_number();
base::DictionaryValue registration_data;
registration_data.SetString("serial_number", serial_number);
registration_data.SetString("id_token", token);
std::string registration_data_str;
if (!base::JSONWriter::Write(registration_data, &registration_data_str)) {
LOGFN(ERROR) << "JSONWriter::Write(registration_data)";
return E_FAIL;
}
HRESULT hr =
RegisterWithGoogleDeviceManagement(mdm_url, email, registration_data_str);
if (FAILED(hr))
LOGFN(ERROR) << "RegisterWithGoogleDeviceManagement hr=" << putHR(hr);
return hr;
}
// GoogleMdmEnrollmentStatusForTesting ////////////////////////////////////////
GoogleMdmEnrollmentStatusForTesting::GoogleMdmEnrollmentStatusForTesting(
bool success) {
g_enrollment_status = success ? EnrollmentStatus::kForceSuccess
: EnrollmentStatus::kForceFailure;
}
GoogleMdmEnrollmentStatusForTesting::~GoogleMdmEnrollmentStatusForTesting() {
g_enrollment_status = EnrollmentStatus::kDontForce;
}
// GoogleMdmEnrolledStatusForTesting //////////////////////////////////////////
GoogleMdmEnrolledStatusForTesting::GoogleMdmEnrolledStatusForTesting(
bool success) {
g_enrolled_status =
success ? EnrolledStatus::kForceTrue : EnrolledStatus::kForceFalse;
}
GoogleMdmEnrolledStatusForTesting::~GoogleMdmEnrolledStatusForTesting() {
g_enrolled_status = EnrolledStatus::kDontForce;
}
} // namespace credential_provider