| // 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, ®istration_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 |