Commit bd45631a authored by Robin Lewis's avatar Robin Lewis Committed by Commit Bot

Setup omaha update tracks using cloud policies.

Bug: 1119561
Change-Id: I0c4b5826cb4ed3e4bc18893f696f80089ad82f17
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2365755
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@{#802115}
parent d497cf18
......@@ -54,6 +54,8 @@ const wchar_t kRegUpdaterClientsAppPath[] =
const wchar_t kRegUninstallStringField[] = L"UninstallString";
const wchar_t kRegUninstallArgumentsField[] = L"UninstallArguments";
const wchar_t kRegUsageStatsName[] = L"usagestats";
const wchar_t kRegUpdateTracksName[] = L"ap";
const wchar_t kRegVersionName[] = L"pv";
// Chrome is being opened to show the credential provider logon page. This
// page is always shown in incognito mode.
......
......@@ -78,6 +78,8 @@ extern const wchar_t kRegUpdaterClientsAppPath[];
extern const wchar_t kRegUninstallStringField[];
extern const wchar_t kRegUninstallArgumentsField[];
extern const wchar_t kRegUsageStatsName[];
extern const wchar_t kRegUpdateTracksName[];
extern const wchar_t kRegVersionName[];
// These are command line switches passed to chrome to start it as a process
// used as a logon stub.
......
......@@ -11,6 +11,8 @@
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "base/win/registry.h"
#include "chrome/credential_provider/common/gcp_strings.h"
......@@ -23,6 +25,12 @@
#include "chrome/credential_provider/gaiacp/user_policies_manager.h"
#include "chrome/credential_provider/gaiacp/win_http_url_fetcher.h"
namespace {
// Character used to separate the update channel and version components in the
// update tracks value.
const wchar_t kChannelAndVersionSeparator[] = L"-";
} // namespace
namespace credential_provider {
// static
......@@ -82,4 +90,81 @@ void DevicePoliciesManager::GetDevicePolicies(DevicePolicies* device_policies) {
}
}
void DevicePoliciesManager::EnforceGcpwUpdatePolicy() {
// Apply the Omaha update policy.
DevicePolicies device_policies;
GetDevicePolicies(&device_policies);
base::win::RegKey key;
LONG status = key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_READ | KEY_SET_VALUE | KEY_WOW64_32KEY);
if (status != ERROR_SUCCESS) {
LOGFN(ERROR) << "Unable to open omaha key=" << kRegUpdaterClientStateAppPath
<< " status=" << status;
return;
}
base::string16 update_channel; // Empty value indicates the stable channel.
std::wstring ap_value;
status = key.ReadValue(kRegUpdateTracksName, &ap_value);
GcpwVersion current_pinned_version(base::UTF16ToUTF8(ap_value));
if (status == ERROR_SUCCESS && !current_pinned_version.IsValid()) {
std::vector<base::string16> ap_components =
base::SplitString(ap_value, kChannelAndVersionSeparator,
base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_NONEMPTY);
if (ap_components.size() > 0)
update_channel = ap_components[0];
}
if (device_policies.enable_gcpw_auto_update &&
!device_policies.gcpw_pinned_version.IsValid()) {
// Auto update enabled with no pinning so if installation was previously
// pinned to a version, remove the registry entry if device was on the
// stable channel or restore the previous channel.
if (update_channel.empty()) {
status = key.DeleteValue(kRegUpdateTracksName);
if (status != ERROR_SUCCESS) {
LOGFN(ERROR) << "Unable to delete " << kRegUpdateTracksName
<< " value status=" << status;
}
} else if (update_channel != ap_value) {
status = key.WriteValue(kRegUpdateTracksName, update_channel.c_str());
if (status != ERROR_SUCCESS) {
LOGFN(ERROR) << "Unable to reset " << kRegUpdateTracksName
<< " value to " << update_channel << ". status=" << status;
}
}
} else {
base::string16 gcpw_version;
if (device_policies.enable_gcpw_auto_update &&
device_policies.gcpw_pinned_version.IsValid()) {
// Auto update enabled with pinning so set it to the pinned track.
gcpw_version =
base::UTF8ToUTF16(device_policies.gcpw_pinned_version.ToString());
} else {
// Auto update is disabled so make sure we stay on the installed
// version.
GcpwVersion version = GcpwVersion::GetCurrentVersion();
if (!version.IsValid()) {
LOGFN(ERROR) << "Could not read currently installed version";
return;
}
gcpw_version = base::UTF8ToUTF16(version.ToString());
}
base::string16 ap_value = gcpw_version;
if (!update_channel.empty())
ap_value = update_channel + kChannelAndVersionSeparator + gcpw_version;
status = key.WriteValue(kRegUpdateTracksName, ap_value.c_str());
if (status != ERROR_SUCCESS) {
LOGFN(ERROR) << "Unable to write " << kRegUpdateTracksName
<< " value status=" << status;
}
}
}
} // namespace credential_provider
......@@ -22,6 +22,9 @@ class DevicePoliciesManager {
// policies of all the existing users on the device.
virtual void GetDevicePolicies(DevicePolicies* device_policies);
// Make sure GCPW update is set up correctly.
void EnforceGcpwUpdatePolicy();
protected:
// Returns the storage used for the instance pointer.
static DevicePoliciesManager** GetInstanceStorage();
......
......@@ -38,6 +38,7 @@
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/associated_user_validator.h"
#include "chrome/credential_provider/gaiacp/auth_utils.h"
#include "chrome/credential_provider/gaiacp/device_policies_manager.h"
#include "chrome/credential_provider/gaiacp/event_logs_upload_manager.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
......@@ -2082,6 +2083,11 @@ HRESULT CGaiaCredentialBase::PerformPostSigninActions(
LOGFN(ERROR) << "EnrollToGoogleMdmIfNeeded hr=" << putHR(hr);
}
// Ensure GCPW gets updated to the correct version.
if (DevicePoliciesManager::Get()->CloudPoliciesEnabled()) {
DevicePoliciesManager::Get()->EnforceGcpwUpdatePolicy();
}
// TODO(crbug.com/976744): Use the down scoped kKeyMdmAccessToken instead
// of login scoped token.
std::string access_token = GetDictStringUTF8(properties, kKeyAccessToken);
......
......@@ -3311,5 +3311,140 @@ INSTANTIATE_TEST_SUITE_P(All,
::testing::Bool(),
::testing::Bool()));
// Test that correct Omaha update tracks are set when auto update policies are
// defined.
// Parameters are:
// 1. bool : Whether cloud policies feature is enabled.
// 2. bool : Whether GCPW auto update policy is enabled.
// 3. string : GCPW pinned version policy set through the cloud policy.
// 4. string : Existing update channel (Ex. 'beta') specified in the Omaha
// update track registry entry for GCPW application. Empty value
// is stable channel.
// 5. string : Current value of GCPW pinned version.
class GcpGaiaCredentialBaseOmahaUpdatePolicyTest
: public GcpGaiaCredentialBaseTest,
public ::testing::WithParamInterface<std::tuple<bool,
bool,
const wchar_t*,
const wchar_t*,
const wchar_t*>> {};
TEST_P(GcpGaiaCredentialBaseOmahaUpdatePolicyTest, EnforceUpdatePolicy) {
bool cloud_policies_enabled = std::get<0>(GetParam());
bool enable_gcpw_auto_update = std::get<1>(GetParam());
base::string16 gcpw_pinned_version(std::get<2>(GetParam()));
base::string16 update_channel(std::get<3>(GetParam()));
base::string16 current_pinned_version(std::get<4>(GetParam()));
FakeDevicePoliciesManager fake_device_policies_manager(
cloud_policies_enabled);
DevicePolicies device_policies;
device_policies.enable_gcpw_auto_update = enable_gcpw_auto_update;
device_policies.gcpw_pinned_version =
GcpwVersion(base::UTF16ToUTF8(gcpw_pinned_version));
fake_device_policies_manager.SetDevicePolicies(device_policies);
const base::string16 current_gcpw_version(L"80.1.422.2");
// Add expected Omaha registry paths
base::win::RegKey clientsKey, clientsStateKey;
EXPECT_EQ(ERROR_SUCCESS,
clientsKey.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientsAppPath,
KEY_SET_VALUE | KEY_WOW64_32KEY));
EXPECT_EQ(ERROR_SUCCESS, clientsKey.WriteValue(kRegVersionName,
current_gcpw_version.c_str()));
EXPECT_EQ(
ERROR_SUCCESS,
clientsStateKey.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_SET_VALUE | KEY_WOW64_32KEY));
// Set existing update tracks including the currently pinned version.
base::string16 current_update_track = current_pinned_version;
if (!update_channel.empty())
current_update_track = update_channel + L"-" + current_pinned_version;
if (!current_update_track.empty()) {
EXPECT_EQ(ERROR_SUCCESS,
clientsStateKey.WriteValue(kRegUpdateTracksName,
current_update_track.c_str()));
}
// 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), base::string16(), &sid));
// Change token response to an valid one.
SetDefaultTokenHandleResponse(kDefaultValidTokenHandleResponse);
// 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));
ASSERT_EQ(S_OK, ReleaseProvider());
base::win::RegKey key;
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_READ | KEY_WOW64_32KEY));
std::wstring update_track_value;
LONG status = key.ReadValue(kRegUpdateTracksName, &update_track_value);
if (cloud_policies_enabled) {
if (device_policies.enable_gcpw_auto_update) {
if (device_policies.gcpw_pinned_version.IsValid()) {
// Check if pinned version is set.
ASSERT_EQ(ERROR_SUCCESS, status);
base::string16 expected_ap_value = gcpw_pinned_version;
if (!update_channel.empty())
expected_ap_value = update_channel + L"-" + gcpw_pinned_version;
ASSERT_EQ(expected_ap_value, update_track_value);
} else {
// Update track should be reset to the channel it was on before.
if (update_channel.empty()) {
ASSERT_NE(ERROR_SUCCESS, status);
} else {
ASSERT_EQ(ERROR_SUCCESS, status);
ASSERT_EQ(update_channel, update_track_value);
}
}
} else {
// Auto update is turned off.
ASSERT_EQ(ERROR_SUCCESS, status);
base::string16 expected_ap_value = current_gcpw_version;
if (!update_channel.empty())
expected_ap_value = update_channel + L"-" + current_gcpw_version;
ASSERT_EQ(expected_ap_value, update_track_value);
}
} else {
// There should be no change to existing update tracks.
if (current_update_track.empty()) {
ASSERT_NE(ERROR_SUCCESS, status);
} else {
ASSERT_EQ(ERROR_SUCCESS, status);
ASSERT_EQ(current_update_track, update_track_value);
}
}
}
INSTANTIATE_TEST_SUITE_P(
All,
GcpGaiaCredentialBaseOmahaUpdatePolicyTest,
::testing::Combine(::testing::Bool(),
::testing::Bool(),
::testing::Values(L"", L"81.1.33.42"),
::testing::Values(L"", L"beta"),
::testing::Values(L"", L"80.2.35.4")));
} // namespace testing
} // namespace credential_provider
......@@ -7,6 +7,10 @@
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/logging.h"
namespace credential_provider {
......@@ -21,8 +25,10 @@ GcpwVersion::GcpwVersion(const std::string& version_str) : GcpwVersion() {
base::SplitResult::SPLIT_WANT_NONEMPTY);
for (size_t i = 0; i < std::min(version_.size(), components.size()); ++i) {
unsigned value;
if (base::StringToUint(components[i], &value))
version_[i] = value;
if (!base::StringToUint(components[i], &value))
break;
version_[i] = value;
}
}
......@@ -69,4 +75,24 @@ bool GcpwVersion::operator<(const GcpwVersion& other) const {
return false;
}
bool GcpwVersion::IsValid() const {
return !(*this == GcpwVersion());
}
// static
GcpwVersion GcpwVersion::GetCurrentVersion() {
base::win::RegKey key;
LONG status = key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientsAppPath,
KEY_READ | KEY_WOW64_32KEY);
if (status == ERROR_SUCCESS) {
std::wstring version_wstr;
status = key.ReadValue(kRegVersionName, &version_wstr);
if (status == ERROR_SUCCESS)
return GcpwVersion(base::WideToUTF8(version_wstr));
}
LOGFN(ERROR) << "Unable to read version from omaha key="
<< kRegUpdaterClientsAppPath << " status=" << status;
return GcpwVersion();
}
} // namespace credential_provider
......@@ -13,7 +13,7 @@ namespace credential_provider {
// A structure to hold the version of GCPW.
class GcpwVersion {
public:
// Create a default with the current GCPW version.
// Create a default version which is not valid.
GcpwVersion();
// Construct using the given version string specified in
......@@ -23,6 +23,9 @@ class GcpwVersion {
// Copy constructor.
GcpwVersion(const GcpwVersion& other);
// Gets the installed version of the GCPW client on this device.
static GcpwVersion GetCurrentVersion();
// Returns a formatted string.
std::string ToString() const;
......@@ -46,6 +49,9 @@ class GcpwVersion {
// Returns true when this version is less than |other| version.
bool operator<(const GcpwVersion& other) const;
// Returns true if this is a valid version.
bool IsValid() const;
private:
std::array<unsigned, 4> version_;
};
......
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