Commit 47f18717 authored by Robin Lewis's avatar Robin Lewis Committed by Commit Bot

Fetch user policies from GCPW service at login.

Bug: 1102868
Change-Id: I14ee702024f04f4e1a5c61586b37d505164270a1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2284372
Commit-Queue: Robin Lewis <wrlewis@google.com>
Reviewed-by: default avatarYusuf Sengul <yusufsn@google.com>
Reviewed-by: default avatarRakesh Soma <rakeshsoma@google.com>
Cr-Commit-Position: refs/heads/master@{#789777}
parent ed7d227f
...@@ -111,6 +111,10 @@ source_set("gaiacp_lib") { ...@@ -111,6 +111,10 @@ source_set("gaiacp_lib") {
"scoped_user_profile.cc", "scoped_user_profile.cc",
"scoped_user_profile.h", "scoped_user_profile.h",
"stdafx.h", "stdafx.h",
"user_policies.cc",
"user_policies.h",
"user_policies_manager.cc",
"user_policies_manager.h",
"win_http_url_fetcher.cc", "win_http_url_fetcher.cc",
"win_http_url_fetcher.h", "win_http_url_fetcher.h",
] ]
......
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
#include "chrome/credential_provider/gaiacp/reg_utils.h" #include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h" #include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h"
#include "chrome/credential_provider/gaiacp/scoped_user_profile.h" #include "chrome/credential_provider/gaiacp/scoped_user_profile.h"
#include "chrome/credential_provider/gaiacp/user_policies_manager.h"
#include "chrome/credential_provider/gaiacp/win_http_url_fetcher.h" #include "chrome/credential_provider/gaiacp/win_http_url_fetcher.h"
#include "chrome/installer/launcher_support/chrome_launcher_support.h" #include "chrome/installer/launcher_support/chrome_launcher_support.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
...@@ -2460,6 +2461,20 @@ HRESULT CGaiaCredentialBase::OnUserAuthenticated(BSTR authentication_info, ...@@ -2460,6 +2461,20 @@ HRESULT CGaiaCredentialBase::OnUserAuthenticated(BSTR authentication_info,
base::Value(base::NumberToString(current_time))); base::Value(base::NumberToString(current_time)));
} }
base::string16 sid = OLE2CW(user_sid_);
if (UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid) >
kMaxTimeDeltaSinceLastUserPolicyRefresh) {
// TODO(crbug.com/976744) Use downscoped token here.
base::string16 access_token = GetDictString(*properties, kKeyAccessToken);
HRESULT hr = UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
sid, base::UTF16ToUTF8(access_token));
SecurelyClearString(access_token);
if (FAILED(hr)) {
LOGFN(ERROR) << "Failed fetching user policies for user " << sid
<< " Error: " << putHR(hr);
}
}
base::string16 local_password = base::string16 local_password =
GetDictString(*authentication_results_, kKeyPassword); GetDictString(*authentication_results_, kKeyPassword);
password_ = ::SysAllocString(local_password.c_str()); password_ = ::SysAllocString(local_password.c_str());
......
...@@ -9,12 +9,15 @@ ...@@ -9,12 +9,15 @@
#include <algorithm> #include <algorithm>
#include <vector> #include <vector>
#include "base/base_paths_win.h"
#include "base/files/scoped_temp_dir.h" #include "base/files/scoped_temp_dir.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_path_override.h"
#include "base/time/time_override.h" #include "base/time/time_override.h"
#include "chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h" #include "chrome/browser/ui/startup/credential_provider_signin_dialog_win_test_data.h"
...@@ -25,6 +28,7 @@ ...@@ -25,6 +28,7 @@
#include "chrome/credential_provider/gaiacp/mdm_utils.h" #include "chrome/credential_provider/gaiacp/mdm_utils.h"
#include "chrome/credential_provider/gaiacp/password_recovery_manager.h" #include "chrome/credential_provider/gaiacp/password_recovery_manager.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h" #include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "chrome/credential_provider/gaiacp/user_policies_manager.h"
#include "chrome/credential_provider/test/gls_runner_test_base.h" #include "chrome/credential_provider/test/gls_runner_test_base.h"
#include "chrome/credential_provider/test/test_credential.h" #include "chrome/credential_provider/test/test_credential.h"
#include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/gaia_urls.h"
...@@ -3208,5 +3212,103 @@ INSTANTIATE_TEST_SUITE_P(All, ...@@ -3208,5 +3212,103 @@ INSTANTIATE_TEST_SUITE_P(All,
GcpGaiaCredentialBaseChromeAvailabilityTest, GcpGaiaCredentialBaseChromeAvailabilityTest,
::testing::Values(true, false)); ::testing::Values(true, false));
// Test fetching of user cloud policies from the GEM service with different
// failure scenarios.
// Parameters are:
// 1. bool true: HTTP call to fetch policies succeeds.
// false: Fails the upload call due to invalid response from the GEM
// http server.
// 2. bool true: Policies were fetched recently and don't need refreshing.
// false: Policies were never fetched or are very old.
class GcpGaiaCredentialBaseFetchCloudPoliciesTest
: public GcpGaiaCredentialBaseTest,
public ::testing::WithParamInterface<std::tuple<bool, bool>> {};
TEST_P(GcpGaiaCredentialBaseFetchCloudPoliciesTest, FetchAndStore) {
bool fail_fetch_policies = std::get<0>(GetParam());
bool policy_refreshed_recently = std::get<1>(GetParam());
GoogleMdmEnrolledStatusForTesting force_success(true);
// Create a fake user associated to a gaia id.
CComBSTR sid_str;
ASSERT_EQ(S_OK,
fake_os_user_manager()->CreateTestOSUser(
kDefaultUsername, L"password", L"Full Name", L"comment",
base::UTF8ToUTF16(kDefaultGaiaId), base::string16(), &sid_str));
base::string16 sid = OLE2W(sid_str);
base::string16 fetch_time_millis = L"0";
if (policy_refreshed_recently) {
fetch_time_millis = base::NumberToString16(
base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds());
}
ASSERT_EQ(S_OK, SetUserProperty(sid, L"last_policy_refresh_time",
fetch_time_millis));
// Change token response to an valid one.
SetDefaultTokenHandleResponse(kDefaultValidTokenHandleResponse);
std::string expected_response;
if (fail_fetch_policies) {
expected_response = "Invalid json response";
} else {
UserPolicies policies;
base::Value policies_value = policies.ToValue();
base::JSONWriter::Write(policies_value, &expected_response);
}
fake_http_url_fetcher_factory()->SetFakeResponse(
UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid),
FakeWinHttpUrlFetcher::Headers(), expected_response);
// Create provider and start logon.
Microsoft::WRL::ComPtr<ICredentialProviderCredential> cred;
ASSERT_EQ(S_OK, InitializeProviderAndGetCredential(0, &cred));
ASSERT_EQ(S_OK, StartLogonProcessAndWait());
// Finish logon successfully.
ASSERT_EQ(S_OK, FinishLogonProcess(true, true, 0));
base::TimeDelta time_since_last_fetch =
UserPoliciesManager::Get()->GetTimeDeltaSinceLastPolicyFetch(sid);
// Expected number of HTTP calls when not fetching user policies since upload
// device details is always called.
const size_t base_num_http_requests = 1;
const size_t requests_created =
fake_http_url_fetcher_factory()->requests_created();
if (policy_refreshed_recently) {
// No new requests for fetching policies.
ASSERT_EQ(base_num_http_requests, requests_created);
} else {
// Verify the fetch status matches expected value.
HRESULT hr = UserPoliciesManager::Get()->GetLastFetchStatusForTesting();
if (!fail_fetch_policies) {
ASSERT_TRUE(SUCCEEDED(hr));
// One additional request for fetching policies.
ASSERT_EQ(1 + base_num_http_requests, requests_created);
ASSERT_TRUE(time_since_last_fetch <
kMaxTimeDeltaSinceLastUserPolicyRefresh);
} else {
ASSERT_TRUE(FAILED(hr));
// Two additional requests since we retry on failure.
ASSERT_EQ(2 + base_num_http_requests, requests_created);
ASSERT_TRUE(time_since_last_fetch >
kMaxTimeDeltaSinceLastUserPolicyRefresh);
}
}
ASSERT_EQ(S_OK, ReleaseProvider());
}
INSTANTIATE_TEST_SUITE_P(All,
GcpGaiaCredentialBaseFetchCloudPoliciesTest,
::testing::Combine(::testing::Bool(),
::testing::Bool()));
} // namespace testing } // namespace testing
} // namespace credential_provider } // namespace credential_provider
...@@ -58,6 +58,8 @@ constexpr wchar_t kUserPasswordLsaStoreKeyPrefix[] = ...@@ -58,6 +58,8 @@ constexpr wchar_t kUserPasswordLsaStoreKeyPrefix[] =
#endif #endif
const char kErrorKeyInRequestResult[] = "error"; const char kErrorKeyInRequestResult[] = "error";
constexpr int kMaxNumConsecutiveUploadDeviceFailures = 3; constexpr int kMaxNumConsecutiveUploadDeviceFailures = 3;
const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh =
base::TimeDelta::FromDays(1);
// Overridden in tests to force the MDM enrollment to either succeed or fail. // Overridden in tests to force the MDM enrollment to either succeed or fail.
enum class EnrollmentStatus { enum class EnrollmentStatus {
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <string> #include <string>
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/time/time.h"
#include "base/values.h" #include "base/values.h"
#include "base/win/windows_types.h" #include "base/win/windows_types.h"
#include "url/gurl.h" #include "url/gurl.h"
...@@ -69,6 +70,10 @@ extern const int kMaxNumConsecutiveUploadDeviceFailures; ...@@ -69,6 +70,10 @@ extern const int kMaxNumConsecutiveUploadDeviceFailures;
// it is empty, developer mode isn't enabled. // it is empty, developer mode isn't enabled.
extern const wchar_t kRegDeveloperMode[]; extern const wchar_t kRegDeveloperMode[];
// Maximum allowed time delta after which user policies should be refreshed
// again.
extern const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh;
// Class used in tests to force either a successful on unsuccessful enrollment // Class used in tests to force either a successful on unsuccessful enrollment
// to google MDM. // to google MDM.
class GoogleMdmEnrollmentStatusForTesting { class GoogleMdmEnrollmentStatusForTesting {
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h" #include "base/win/registry.h"
#include "base/win/win_util.h" #include "base/win/win_util.h"
#include "build/branding_buildflags.h" #include "build/branding_buildflags.h"
...@@ -355,6 +356,18 @@ HRESULT GetIdFromSid(const wchar_t* sid, base::string16* id) { ...@@ -355,6 +356,18 @@ HRESULT GetIdFromSid(const wchar_t* sid, base::string16* id) {
return HRESULT_FROM_WIN32(ERROR_NONE_MAPPED); return HRESULT_FROM_WIN32(ERROR_NONE_MAPPED);
} }
std::string GetUserEmailFromSid(const base::string16& sid) {
wchar_t email_id[512];
ULONG email_id_size = base::size(email_id);
HRESULT hr = GetUserProperty(sid, kUserEmail, email_id, &email_id_size);
base::string16 email_id_str;
if (SUCCEEDED(hr) && email_id_size > 0)
email_id_str = base::string16(email_id, email_id_size - 1);
return base::UTF16ToUTF8(email_id_str);
}
HRESULT SetUserWinlogonUserListEntry(const base::string16& username, HRESULT SetUserWinlogonUserListEntry(const base::string16& username,
DWORD visible) { DWORD visible) {
// Sets the value of the key that will hide the user from all credential // Sets the value of the key that will hide the user from all credential
......
...@@ -138,6 +138,9 @@ HRESULT GetSidFromKey(const wchar_t* key, ...@@ -138,6 +138,9 @@ HRESULT GetSidFromKey(const wchar_t* key,
// HRESULT_FROM_WIN32(ERROR_NONE_MAPPED). // HRESULT_FROM_WIN32(ERROR_NONE_MAPPED).
HRESULT GetIdFromSid(const wchar_t* sid, base::string16* id); HRESULT GetIdFromSid(const wchar_t* sid, base::string16* id);
// Get the email ID associated with the user with |sid|.
std::string GetUserEmailFromSid(const base::string16& sid);
// Gets a specific account picture registry key in HKEY_LOCAL_MACHINE // Gets a specific account picture registry key in HKEY_LOCAL_MACHINE
HRESULT GetAccountPictureRegString(const base::string16& user_sid, HRESULT GetAccountPictureRegString(const base::string16& user_sid,
int image_size, int image_size,
......
// Copyright 2020 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/user_policies.h"
#include <limits>
#include "base/strings/utf_string_conversions.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/gcpw_strings.h"
#include "chrome/credential_provider/gaiacp/mdm_utils.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
namespace credential_provider {
namespace {
// Parameter names that are used in the JSON payload of the response.
const char kGcpwPolicyDmEnrollmentParameterName[] = "enable_dm_enrollment";
const char kGcpwPolicyAutoUpdateParameterName[] = "enable_gcpw_auto_update";
const char kGcpwPolicyPinnerVersionParameterName[] = "gcpw_pinned_version";
const char kGcpwPolicMultiUserLoginParameterName[] = "enable_multi_user_login";
const char kGcpwPolicyValidityPeriodParameterName[] = "validity_period_days";
// Default value of each user policy.
constexpr bool kUserPolicyDefaultDeviceEnrollment = true;
constexpr bool kUserPolicyDefaultGcpwAutoUpdate = true;
constexpr bool kUserPolicyDefaultMultiUserLogin = true;
constexpr int kUserPolicyDefaultValidityPeriodDays =
std::numeric_limits<int>::max();
} // namespace
UserPolicies::UserPolicies()
: enable_dm_enrollment(kUserPolicyDefaultDeviceEnrollment),
enable_gcpw_auto_update(kUserPolicyDefaultGcpwAutoUpdate),
enable_multi_user_login(kUserPolicyDefaultMultiUserLogin),
validity_period_days(kUserPolicyDefaultValidityPeriodDays) {
// Override with the policies set in the registry.
DWORD reg_enable_dm_enrollment;
HRESULT hr = GetGlobalFlag(kRegEnableDmEnrollment, &reg_enable_dm_enrollment);
if (SUCCEEDED(hr)) {
enable_dm_enrollment = reg_enable_dm_enrollment;
}
DWORD reg_supports_multi_user;
hr = GetGlobalFlag(kRegMdmSupportsMultiUser, &reg_supports_multi_user);
if (SUCCEEDED(hr)) {
enable_multi_user_login = reg_supports_multi_user == 1;
}
DWORD reg_validity_period_days;
hr = GetGlobalFlag(base::UTF8ToUTF16(kKeyValidityPeriodInDays),
&reg_validity_period_days);
if (SUCCEEDED(hr)) {
validity_period_days = reg_validity_period_days;
}
}
// static
UserPolicies UserPolicies::FromValue(const base::Value& dict) {
DCHECK(dict.is_dict());
UserPolicies user_policies;
base::Optional<bool> dm_enrollment =
dict.FindBoolKey(kGcpwPolicyDmEnrollmentParameterName);
if (dm_enrollment) {
user_policies.enable_dm_enrollment = *dm_enrollment;
}
base::Optional<bool> gcpw_auto_update =
dict.FindBoolKey(kGcpwPolicyAutoUpdateParameterName);
if (gcpw_auto_update) {
user_policies.enable_gcpw_auto_update = *gcpw_auto_update;
}
const std::string* pin_version =
dict.FindStringKey(kGcpwPolicyPinnerVersionParameterName);
if (pin_version) {
user_policies.gcpw_pinned_version = *pin_version;
}
base::Optional<bool> multi_user_login =
dict.FindBoolKey(kGcpwPolicMultiUserLoginParameterName);
if (multi_user_login) {
user_policies.enable_multi_user_login = *multi_user_login;
}
base::Optional<int> validity_period_days =
dict.FindIntKey(kGcpwPolicyValidityPeriodParameterName);
if (validity_period_days) {
user_policies.validity_period_days = *validity_period_days;
}
return user_policies;
}
base::Value UserPolicies::ToValue() const {
base::Value dict(base::Value::Type::DICTIONARY);
dict.SetBoolKey(kGcpwPolicyDmEnrollmentParameterName, enable_dm_enrollment);
dict.SetBoolKey(kGcpwPolicyAutoUpdateParameterName, enable_gcpw_auto_update);
dict.SetStringKey(kGcpwPolicyPinnerVersionParameterName, gcpw_pinned_version);
dict.SetBoolKey(kGcpwPolicMultiUserLoginParameterName,
enable_multi_user_login);
dict.SetIntKey(kGcpwPolicyValidityPeriodParameterName, validity_period_days);
return dict;
}
bool UserPolicies::operator==(const UserPolicies& other) const {
return (enable_dm_enrollment == other.enable_dm_enrollment) &&
(enable_gcpw_auto_update == other.enable_gcpw_auto_update) &&
(gcpw_pinned_version == other.gcpw_pinned_version) &&
(enable_multi_user_login == other.enable_multi_user_login) &&
(validity_period_days == other.validity_period_days);
}
} // namespace credential_provider
// Copyright 2020 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.
#ifndef CHROME_CREDENTIAL_PROVIDER_GAIACP_USER_POLICIES_H_
#define CHROME_CREDENTIAL_PROVIDER_GAIACP_USER_POLICIES_H_
#include "base/values.h"
namespace credential_provider {
// Structure to hold the policies for each user.
struct UserPolicies {
// Controls whether MDM enrollment is enabled/disabled.
bool enable_dm_enrollment;
// Controls whether GCPW should be automatically updated by Omaha/Google
bool enable_gcpw_auto_update;
// The GCPW version to pin the device to.
std::string gcpw_pinned_version;
// If set to disabled only 1 GCPW user can be created on the device.
bool enable_multi_user_login;
// Number of days after which online login is enforced.
uint32_t validity_period_days;
// Creates a default policy for a user on this device honoring any existing
// registry settings.
UserPolicies();
// Creates the user policies by reading the values found in the |dict|
// dictionary.
static UserPolicies FromValue(const base::Value& dict);
base::Value ToValue() const;
bool operator==(const UserPolicies& other) const;
};
} // namespace credential_provider
#endif // CHROME_CREDENTIAL_PROVIDER_GAIACP_USER_POLICIES_H_
// Copyright 2020 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/user_policies_manager.h"
#include <limits>
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/gcpw_strings.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/mdm_utils.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "chrome/credential_provider/gaiacp/win_http_url_fetcher.h"
namespace credential_provider {
namespace {
// HTTP endpoint on the GCPW service to fetch user policies.
const char kUserEmailUrlPlaceholder[] = "{email}";
const char kGcpwServiceFetchUserPoliciesPath[] = "/v1/users/{email}/policies";
// Default timeout when trying to make requests to the GCPW service.
const base::TimeDelta kDefaultFetchPoliciesRequestTimeout =
base::TimeDelta::FromMilliseconds(5000);
// Path elements for the path where the policies are stored on disk.
constexpr base::FilePath::CharType kGcpwPoliciesDirectory[] = L"Policies";
constexpr base::FilePath::CharType kGcpwUserPolicyFileName[] =
L"PolicyFetchResponse";
// Registry key where the the last time the policy is refreshed for the user is
// stored.
const wchar_t kLastUserPolicyRefreshTimeRegKey[] = L"last_policy_refresh_time";
// Maximum number of retries if a HTTP call to the backend fails.
constexpr unsigned int kMaxNumHttpRetries = 1;
// Get the path to the directory where the policies will be stored for the user
// with |sid|.
base::FilePath GetUserPolicyDirectoryFilePath(const base::string16& sid) {
base::FilePath path = GetInstallDirectory();
path = path.Append(kGcpwPoliciesDirectory).Append(sid);
return path;
}
std::unique_ptr<base::File> GetOpenedPolicyFileForUser(
const base::string16& sid,
uint32_t open_flags) {
base::FilePath policy_dir = GetUserPolicyDirectoryFilePath(sid);
if (!base::DirectoryExists(policy_dir)) {
base::File::Error error;
if (!CreateDirectoryAndGetError(policy_dir, &error)) {
LOGFN(ERROR) << "Policy data directory could not be created for " << sid
<< " Error: " << error;
return nullptr;
}
}
base::FilePath policy_file_path = policy_dir.Append(kGcpwUserPolicyFileName);
std::unique_ptr<base::File> policy_file(
new base::File(policy_file_path, open_flags));
if (!policy_file->IsValid()) {
LOGFN(ERROR) << "Error opening policy file for user " << sid
<< " with flags " << open_flags
<< " Error: " << policy_file->error_details();
return nullptr;
}
base::File::Error lock_error = policy_file->Lock();
if (lock_error != base::File::FILE_OK) {
LOGFN(ERROR) << "Failed to obtain exclusive lock on policy file! Error: "
<< lock_error;
return nullptr;
}
return policy_file;
}
} // namespace
// static
UserPoliciesManager* UserPoliciesManager::Get() {
return *GetInstanceStorage();
}
// static
UserPoliciesManager** UserPoliciesManager::GetInstanceStorage() {
static UserPoliciesManager instance;
static UserPoliciesManager* instance_storage = &instance;
return &instance_storage;
}
UserPoliciesManager::UserPoliciesManager() : fetch_status_(S_OK) {}
UserPoliciesManager::~UserPoliciesManager() = default;
GURL UserPoliciesManager::GetGcpwServiceUserPoliciesUrl(
const base::string16& sid) {
GURL gcpw_service_url = GetGcpwServiceUrl();
std::string fetchUserPoliciesPath(kGcpwServiceFetchUserPoliciesPath);
std::string placeholder(kUserEmailUrlPlaceholder);
fetchUserPoliciesPath.replace(fetchUserPoliciesPath.find(placeholder),
placeholder.size(), GetUserEmailFromSid(sid));
return gcpw_service_url.Resolve(fetchUserPoliciesPath);
}
HRESULT UserPoliciesManager::FetchAndStoreCloudUserPolicies(
const base::string16& sid,
const std::string& access_token) {
fetch_status_ = E_FAIL;
base::Optional<base::Value> request_result;
// Make the fetch policies HTTP request.
HRESULT hr = WinHttpUrlFetcher::BuildRequestAndFetchResultFromHttpService(
UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid),
access_token, {}, {}, kDefaultFetchPoliciesRequestTimeout,
kMaxNumHttpRetries, &request_result);
if (FAILED(hr)) {
LOGFN(ERROR) << "BuildRequestAndFetchResultFromHttpService hr="
<< putHR(hr);
return (fetch_status_ = hr);
}
std::string policy_data;
if (request_result && request_result->is_dict()) {
if (!base::JSONWriter::Write(*request_result, &policy_data)) {
LOGFN(ERROR) << "base::JSONWriter::Write failed";
return (fetch_status_ = E_FAIL);
}
} else {
LOGFN(ERROR) << "Failed to parse policy response!";
return (fetch_status_ = E_FAIL);
}
uint32_t open_flags = base::File::FLAG_CREATE_ALWAYS |
base::File::FLAG_WRITE |
base::File::FLAG_EXCLUSIVE_WRITE;
std::unique_ptr<base::File> policy_file =
GetOpenedPolicyFileForUser(sid, open_flags);
if (!policy_file) {
return (fetch_status_ = E_FAIL);
}
int num_bytes_written =
policy_file->Write(0, policy_data.c_str(), policy_data.size());
policy_file.reset();
if (size_t(num_bytes_written) != policy_data.size()) {
LOGFN(ERROR) << "Failed writing policy data to file! Only "
<< num_bytes_written << " bytes written out of "
<< policy_data.size();
return (fetch_status_ = E_FAIL);
}
base::Time fetch_time = base::Time::Now();
base::string16 fetch_time_millis = base::NumberToString16(
fetch_time.ToDeltaSinceWindowsEpoch().InMilliseconds());
// Store the fetch time so we know whether a refresh is needed.
SetUserProperty(sid, kLastUserPolicyRefreshTimeRegKey, fetch_time_millis);
return (fetch_status_ = S_OK);
}
base::TimeDelta UserPoliciesManager::GetTimeDeltaSinceLastPolicyFetch(
const base::string16& sid) const {
wchar_t last_fetch_millis[512];
ULONG last_fetch_size = base::size(last_fetch_millis);
HRESULT hr = GetUserProperty(sid, kLastUserPolicyRefreshTimeRegKey,
last_fetch_millis, &last_fetch_size);
if (FAILED(hr)) {
// The policy was never fetched before.
return base::TimeDelta::Max();
}
int64_t last_fetch_millis_int64;
base::StringToInt64(last_fetch_millis, &last_fetch_millis_int64);
int64_t time_delta_from_last_fetch_ms =
base::Time::Now().ToDeltaSinceWindowsEpoch().InMilliseconds() -
last_fetch_millis_int64;
return base::TimeDelta::FromMilliseconds(time_delta_from_last_fetch_ms);
}
bool UserPoliciesManager::GetUserPolicies(const base::string16& sid,
UserPolicies* user_policies) {
DCHECK(user_policies);
uint32_t open_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
std::unique_ptr<base::File> policy_file =
GetOpenedPolicyFileForUser(sid, open_flags);
if (!policy_file) {
return false;
}
std::vector<char> buffer(policy_file->GetLength());
policy_file->Read(0, buffer.data(), buffer.size());
policy_file.reset();
base::Optional<base::Value> policy_data =
base::JSONReader::Read(base::StringPiece(buffer.data(), buffer.size()),
base::JSON_ALLOW_TRAILING_COMMAS);
if (!policy_data || !policy_data->is_dict()) {
LOGFN(ERROR) << "Failed to read policy data from file!";
return false;
}
// Override policies with those we just read.
*user_policies = UserPolicies::FromValue(*policy_data);
return true;
}
HRESULT UserPoliciesManager::GetLastFetchStatusForTesting() const {
return fetch_status_;
}
} // namespace credential_provider
// Copyright 2020 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.
#ifndef CHROME_CREDENTIAL_PROVIDER_GAIACP_USER_POLICIES_MANAGER_H_
#define CHROME_CREDENTIAL_PROVIDER_GAIACP_USER_POLICIES_MANAGER_H_
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "base/win/windows_types.h"
#include "chrome/credential_provider/gaiacp/user_policies.h"
#include "url/gurl.h"
namespace credential_provider {
// Manager used to fetch user policies from GCPW backends.
class UserPoliciesManager {
public:
// Get the user policies manager instance.
static UserPoliciesManager* Get();
// Fetch the policies for the user from GCPW backend with |sid| using
// |access_token| for authentication and authorization and saves it in file
// storage replacing any previously fetched versions.
HRESULT FetchAndStoreCloudUserPolicies(const base::string16& sid,
const std::string& access_token);
// Return the elapsed time delta since the last time the policies were
// successfully fetched for the user with |sid|.
base::TimeDelta GetTimeDeltaSinceLastPolicyFetch(
const base::string16& sid) const;
// Get the URL of GCPW service for HTTP request for fetching user policies.
GURL GetGcpwServiceUserPoliciesUrl(const base::string16& sid);
// Retrieves the policies for the user with |sid| from local storage. Returns
// the default user policy if policy not fetched or on any error.
bool GetUserPolicies(const base::string16& sid, UserPolicies* user_policies);
// For testing only return the status of the last policy fetch.
HRESULT GetLastFetchStatusForTesting() const;
protected:
// Returns the storage used for the instance pointer.
static UserPoliciesManager** GetInstanceStorage();
UserPoliciesManager();
virtual ~UserPoliciesManager();
HRESULT fetch_status_;
};
} // namespace credential_provider
#endif // CHROME_CREDENTIAL_PROVIDER_GAIACP_USER_POLICIES_MANAGER_H_
// Copyright 2020 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 <windows.h>
#include "base/base_paths_win.h"
#include "base/files/scoped_temp_dir.h"
#include "base/json/json_writer.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_path_override.h"
#include "chrome/credential_provider/gaiacp/gcpw_strings.h"
#include "chrome/credential_provider/gaiacp/mdm_utils.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "chrome/credential_provider/gaiacp/user_policies_manager.h"
#include "chrome/credential_provider/test/gls_runner_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace credential_provider {
namespace testing {
class GcpUserPoliciesBaseTest : public GlsRunnerTestBase {};
TEST_F(GcpUserPoliciesBaseTest, NonExistentUser) {
ASSERT_TRUE(FAILED(UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
L"not-valid-sid", "not-valid-token")));
UserPolicies policies;
ASSERT_FALSE(
UserPoliciesManager::Get()->GetUserPolicies(L"not-valid", &policies));
}
class GcpUserPoliciesFetchAndReadTest
: public GcpUserPoliciesBaseTest,
public ::testing::WithParamInterface<
std::tuple<bool, bool, const char*, bool, int>> {
protected:
void SetUp() override;
void SetRegistryValues(bool dm_enrollment,
bool multi_user,
DWORD validity_days);
UserPolicies policies_;
base::string16 sid_;
};
void GcpUserPoliciesFetchAndReadTest::SetUp() {
GcpUserPoliciesBaseTest::SetUp();
policies_.enable_dm_enrollment = std::get<0>(GetParam());
policies_.enable_gcpw_auto_update = std::get<1>(GetParam());
policies_.gcpw_pinned_version = std::get<2>(GetParam());
policies_.enable_multi_user_login = std::get<3>(GetParam());
policies_.validity_period_days = std::get<4>(GetParam());
// Create a fake user associated to a gaia id.
CComBSTR sid;
ASSERT_EQ(S_OK,
fake_os_user_manager()->CreateTestOSUser(
kDefaultUsername, L"password", L"Full Name", L"comment",
base::UTF8ToUTF16(kDefaultGaiaId), L"user@company.com", &sid));
sid_ = OLE2W(sid);
}
void GcpUserPoliciesFetchAndReadTest::SetRegistryValues(bool dm_enrollment,
bool multi_user,
DWORD validity_days) {
ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegEnableDmEnrollment,
dm_enrollment ? 1 : 0));
ASSERT_EQ(S_OK, SetGlobalFlagForTesting(kRegMdmSupportsMultiUser,
multi_user ? 1 : 0));
ASSERT_EQ(S_OK,
SetGlobalFlagForTesting(base::UTF8ToUTF16(kKeyValidityPeriodInDays),
validity_days));
}
TEST_P(GcpUserPoliciesFetchAndReadTest, ValueConversion) {
base::Value policies_value = policies_.ToValue();
UserPolicies policies_from_value = UserPolicies::FromValue(policies_value);
ASSERT_EQ(policies_, policies_from_value);
}
TEST_P(GcpUserPoliciesFetchAndReadTest, CloudPoliciesWin) {
// Set conflicting policy values in registry.
SetRegistryValues(!policies_.enable_dm_enrollment,
!policies_.enable_multi_user_login,
policies_.validity_period_days + 100);
base::Value policies_value = policies_.ToValue();
std::string expected_response;
base::JSONWriter::Write(policies_value, &expected_response);
// Set valid cloud policies for all settings.
fake_http_url_fetcher_factory()->SetFakeResponse(
UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid_),
FakeWinHttpUrlFetcher::Headers(), expected_response);
ASSERT_TRUE(
SUCCEEDED(UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
sid_, "access_token")));
UserPolicies policies_fetched;
ASSERT_TRUE(
UserPoliciesManager::Get()->GetUserPolicies(sid_, &policies_fetched));
ASSERT_EQ(policies_, policies_fetched);
}
TEST_P(GcpUserPoliciesFetchAndReadTest, RegistryValuesWin) {
// Set expected values in registry.
SetRegistryValues(policies_.enable_dm_enrollment,
policies_.enable_multi_user_login,
policies_.validity_period_days);
// Only set values for cloud policies for those not already set in registry.
base::Value policies_value(base::Value::Type::DICTIONARY);
policies_value.SetBoolKey("enable_gcpw_auto_update",
policies_.enable_gcpw_auto_update);
policies_value.SetStringKey("gcpw_pinned_version",
policies_.gcpw_pinned_version);
std::string expected_response;
base::JSONWriter::Write(policies_value, &expected_response);
fake_http_url_fetcher_factory()->SetFakeResponse(
UserPoliciesManager::Get()->GetGcpwServiceUserPoliciesUrl(sid_),
FakeWinHttpUrlFetcher::Headers(), expected_response);
ASSERT_TRUE(
SUCCEEDED(UserPoliciesManager::Get()->FetchAndStoreCloudUserPolicies(
sid_, "access_token")));
UserPolicies policies_fetched;
// Also check if the defaults conform to the registry values.
ASSERT_EQ(policies_.enable_dm_enrollment,
policies_fetched.enable_dm_enrollment);
ASSERT_EQ(policies_.enable_multi_user_login,
policies_fetched.enable_multi_user_login);
ASSERT_EQ(policies_.validity_period_days,
policies_fetched.validity_period_days);
ASSERT_TRUE(
UserPoliciesManager::Get()->GetUserPolicies(sid_, &policies_fetched));
ASSERT_EQ(policies_, policies_fetched);
}
INSTANTIATE_TEST_SUITE_P(All,
GcpUserPoliciesFetchAndReadTest,
::testing::Combine(::testing::Bool(),
::testing::Bool(),
::testing::Values("", "110.2.33.2"),
::testing::Bool(),
::testing::Values(0, 30)));
} // namespace testing
} // namespace credential_provider
...@@ -14,6 +14,7 @@ test("gcp_unittests") { ...@@ -14,6 +14,7 @@ test("gcp_unittests") {
"../gaiacp/gaia_credential_unittests.cc", "../gaiacp/gaia_credential_unittests.cc",
"../gaiacp/gcp_utils_unittests.cc", "../gaiacp/gcp_utils_unittests.cc",
"../gaiacp/reauth_credential_unittests.cc", "../gaiacp/reauth_credential_unittests.cc",
"../gaiacp/user_policies_manager_unittests.cc",
"../gaiacp/win_http_url_fetcher_unittests.cc", "../gaiacp/win_http_url_fetcher_unittests.cc",
"com_fakes.cc", "com_fakes.cc",
"com_fakes.h", "com_fakes.h",
......
...@@ -148,6 +148,12 @@ void GlsRunnerTestBase::SetUp() { ...@@ -148,6 +148,12 @@ void GlsRunnerTestBase::SetUp() {
// Make sure not to read random GCPW settings from the machine that is running // Make sure not to read random GCPW settings from the machine that is running
// the tests. // the tests.
InitializeRegistryOverrideForTesting(&registry_override_); InitializeRegistryOverrideForTesting(&registry_override_);
// Override location of "Program Files" system folder so we don't modify local
// machine settings.
ASSERT_TRUE(scoped_temp_program_files_dir_.CreateUniqueTempDir());
program_files_override_.reset(new base::ScopedPathOverride(
base::DIR_PROGRAM_FILES, scoped_temp_program_files_dir_.GetPath()));
} }
void GlsRunnerTestBase::TearDown() { void GlsRunnerTestBase::TearDown() {
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include <wrl/client.h> #include <wrl/client.h>
#include "base/base_paths_win.h"
#include "base/test/scoped_path_override.h"
#include "base/test/test_reg_util_win.h" #include "base/test/test_reg_util_win.h"
#include "chrome/credential_provider/common/gcp_strings.h" #include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h" #include "chrome/credential_provider/gaiacp/gaia_credential_provider.h"
...@@ -236,6 +238,9 @@ class GlsRunnerTestBase : public ::testing::Test { ...@@ -236,6 +238,9 @@ class GlsRunnerTestBase : public ::testing::Test {
// Default response returned by |fake_http_url_fetcher_factory_| when checking // Default response returned by |fake_http_url_fetcher_factory_| when checking
// for token handle validity. // for token handle validity.
std::string default_token_handle_response_; std::string default_token_handle_response_;
base::ScopedTempDir scoped_temp_program_files_dir_;
std::unique_ptr<base::ScopedPathOverride> program_files_override_;
}; };
} // namespace testing } // namespace testing
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment