Implement the update engine portion for new enterprise policies.
Enterprises need the ability to stop the auto updates and pin clients
to a given target version. This CL adds support for these features in
the update_engine.
BUG=27307: Implement StopAutoUpdate based on enterprise policy
TEST=Added new unit tests, manually tested all cases on ZGB.
CQ-DEPEND=I523c3f67e0cb07fd24744dc0a30382ff2fe2128a
Change-Id: Id576401afc6d2c93f0e9ece7c6c0ddcf4b1bc00d
Reviewed-on: https://gerrit.chromium.org/gerrit/17867
Commit-Ready: Jay Srinivasan <jaysri@chromium.org>
Reviewed-by: Jay Srinivasan <jaysri@chromium.org>
Tested-by: Jay Srinivasan <jaysri@chromium.org>
diff --git a/action_processor.h b/action_processor.h
index 57b1d37..c45c3c8 100644
--- a/action_processor.h
+++ b/action_processor.h
@@ -43,6 +43,7 @@
kActionCodeSignedDeltaPayloadExpectedError = 17,
kActionCodeDownloadPayloadPubKeyVerificationError = 18,
kActionCodePostinstallBootedFromFirmwareB = 19,
+ kActionCodeOmahaUpdateIgnoredPerPolicy = 20,
kActionCodeOmahaRequestEmptyResponseError = 200,
kActionCodeOmahaRequestXMLParseError = 201,
kActionCodeOmahaRequestNoUpdateCheckNode = 202,
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index bbcc00b..c97a9a7 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -100,7 +100,14 @@
if (event == NULL) {
body = GetPingBody(ping_active_days, ping_roll_call_days);
if (!ping_only) {
- body += " <o:updatecheck></o:updatecheck>\n";
+ body += StringPrintf(
+ " <o:updatecheck"
+ " updatedisabled=\"%s\""
+ " targetversionprefix=\"%s\""
+ "></o:updatecheck>\n",
+ params.update_disabled ? "true" : "false",
+ XmlEncode(params.target_version_prefix).c_str());
+
// If this is the first update check after a reboot following a previous
// update, generate an event containing the previous version number. If
// the previous version preference file doesn't exist the event is still
@@ -233,6 +240,7 @@
ping_active_days_,
ping_roll_call_days_,
prefs_));
+
http_fetcher_->SetPostData(request_post.data(), request_post.size(),
kHttpContentTypeTextXml);
LOG(INFO) << "Posting an Omaha request to " << params_.update_url;
@@ -451,6 +459,12 @@
return;
}
+ if (params_.update_disabled) {
+ LOG(ERROR) << "Ignoring Omaha updates as updates are disabled by policy.";
+ completer.set_code(kActionCodeOmahaUpdateIgnoredPerPolicy);
+ return;
+ }
+
// In best-effort fashion, fetch the rest of the expected attributes
// from the updatecheck node, then return the object
output_object.update_exists = true;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 776e5e7..e92fb03 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -46,7 +46,9 @@
"unittest",
"OEM MODEL 09235 7471",
false, // delta okay
- "http://url");
+ "http://url",
+ false, // update_disabled
+ ""); // target_version_prefix
string GetNoUpdateResponse(const string& app_id) {
return string(
@@ -65,11 +67,13 @@
const string& needsadmin,
const string& size,
const string& deadline) {
- return string("<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
- "xmlns=\"http://www.google.com/update2/response\" "
- "protocol=\"2.0\"><app "
- "appid=\"") + app_id + "\" status=\"ok\"><ping "
+ return string(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?><gupdate "
+ "xmlns=\"http://www.google.com/update2/response\" "
+ "protocol=\"2.0\"><app "
+ "appid=\"") + app_id + "\" status=\"ok\"><ping "
"status=\"ok\"/><updatecheck DisplayVersion=\"" + display_version + "\" "
+ "ChromeOSVersion=\"" + display_version + "\" "
"MoreInfo=\"" + more_info_url + "\" Prompt=\"" + prompt + "\" "
"IsDelta=\"true\" "
"codebase=\"" + codebase + "\" hash=\"not-applicable\" "
@@ -264,6 +268,48 @@
EXPECT_EQ("20101020", response.deadline);
}
+TEST(OmahaRequestActionTest, ValidUpdateBlockedByPolicyTest) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.update_disabled = true;
+ ASSERT_FALSE(
+ TestUpdateCheck(NULL, // prefs
+ params,
+ GetUpdateResponse(OmahaRequestParams::kAppId,
+ "1.2.3.4", // version
+ "http://more/info",
+ "true", // prompt
+ "http://code/base", // dl url
+ "HASH1234=", // checksum
+ "false", // needs admin
+ "123", // size
+ "20101020"), // deadline
+ -1,
+ false, // ping_only
+ kActionCodeOmahaUpdateIgnoredPerPolicy,
+ &response,
+ NULL));
+ EXPECT_FALSE(response.update_exists);
+}
+
+
+TEST(OmahaRequestActionTest, NoUpdatesSentWhenBlockedByPolicyTest) {
+ OmahaResponse response;
+ OmahaRequestParams params = kDefaultTestParams;
+ params.update_disabled = true;
+ ASSERT_TRUE(
+ TestUpdateCheck(NULL, // prefs
+ params,
+ GetNoUpdateResponse(OmahaRequestParams::kAppId),
+ -1,
+ false, // ping_only
+ kActionCodeSuccess,
+ &response,
+ NULL));
+ EXPECT_FALSE(response.update_exists);
+}
+
+
TEST(OmahaRequestActionTest, NoOutputPipeTest) {
const string http_response(GetNoUpdateResponse(OmahaRequestParams::kAppId));
@@ -379,6 +425,7 @@
+ "\" status=\"ok\"><ping "
"status=\"ok\"/><updatecheck "
"DisplayVersion=\"1.2.3.4\" "
+ "ChromeOSVersion=\"1.2.3.4\" "
"Prompt=\"false\" "
"IsDelta=\"true\" "
"codebase=\"http://code/base\" hash=\"foo\" "
@@ -461,7 +508,9 @@
"unittest_track<",
"<OEM MODEL>",
false, // delta okay
- "http://url");
+ "http://url",
+ false, // update_disabled
+ ""); // target_version_prefix
OmahaResponse response;
ASSERT_FALSE(
TestUpdateCheck(NULL, // prefs
@@ -550,23 +599,28 @@
// convert post_data to string
string post_str(&post_data[0], post_data.size());
EXPECT_NE(post_str.find(
- " <o:ping active=\"1\" a=\"-1\" r=\"-1\"></o:ping>\n"
- " <o:updatecheck></o:updatecheck>\n"),
- string::npos);
+ " <o:ping active=\"1\" a=\"-1\" r=\"-1\"></o:ping>\n"
+ " <o:updatecheck"
+ " updatedisabled=\"false\""
+ " targetversionprefix=\"\""
+ "></o:updatecheck>\n"),
+ string::npos);
EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
string::npos);
EXPECT_EQ(post_str.find("o:event"), string::npos);
}
-TEST(OmahaRequestActionTest, FormatUpdateCheckPrevVersionOutputTest) {
+
+TEST(OmahaRequestActionTest, FormatUpdateDisabledTest) {
vector<char> post_data;
NiceMock<PrefsMock> prefs;
EXPECT_CALL(prefs, GetString(kPrefsPreviousVersion, _))
- .WillOnce(DoAll(SetArgumentPointee<1>(string("1.2>3.4")), Return(true)));
- EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, ""))
- .WillOnce(Return(true));
+ .WillOnce(DoAll(SetArgumentPointee<1>(string("")), Return(true)));
+ EXPECT_CALL(prefs, SetString(kPrefsPreviousVersion, _)).Times(0);
+ OmahaRequestParams params = kDefaultTestParams;
+ params.update_disabled = true;
ASSERT_FALSE(TestUpdateCheck(&prefs,
- kDefaultTestParams,
+ params,
"invalid xml>",
-1,
false, // ping_only
@@ -576,17 +630,15 @@
// convert post_data to string
string post_str(&post_data[0], post_data.size());
EXPECT_NE(post_str.find(
- " <o:ping active=\"1\" a=\"-1\" r=\"-1\"></o:ping>\n"
- " <o:updatecheck></o:updatecheck>\n"),
- string::npos);
+ " <o:ping active=\"1\" a=\"-1\" r=\"-1\"></o:ping>\n"
+ " <o:updatecheck"
+ " updatedisabled=\"true\""
+ " targetversionprefix=\"\""
+ "></o:updatecheck>\n"),
+ string::npos);
EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
string::npos);
- string prev_version_event = StringPrintf(
- " <o:event eventtype=\"%d\" eventresult=\"%d\" "
- "previousversion=\"1.2>3.4\"></o:event>\n",
- OmahaEvent::kTypeUpdateComplete,
- OmahaEvent::kResultSuccessReboot);
- EXPECT_NE(post_str.find(prev_version_event), string::npos);
+ EXPECT_EQ(post_str.find("o:event"), string::npos);
}
TEST(OmahaRequestActionTest, FormatSuccessEventOutputTest) {
@@ -665,7 +717,9 @@
"unittest_track",
"OEM MODEL REV 1234",
delta_okay,
- "http://url");
+ "http://url",
+ false, // update_disabled
+ ""); // target_version_prefix
ASSERT_FALSE(TestUpdateCheck(NULL, // prefs
params,
"invalid xml>",
diff --git a/omaha_request_params.h b/omaha_request_params.h
index da091c0..482a8b7 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -18,8 +18,14 @@
// This struct encapsulates the data Omaha gets for the request.
// These strings in this struct should not be XML escaped.
struct OmahaRequestParams {
+
OmahaRequestParams()
- : os_platform(kOsPlatform), os_version(kOsVersion), app_id(kAppId) {}
+ : os_platform(kOsPlatform),
+ os_version(kOsVersion),
+ app_id(kAppId),
+ delta_okay(true),
+ update_disabled(false) {}
+
OmahaRequestParams(const std::string& in_os_platform,
const std::string& in_os_version,
const std::string& in_os_sp,
@@ -29,8 +35,10 @@
const std::string& in_app_lang,
const std::string& in_app_track,
const std::string& in_hardware_class,
- const bool in_delta_okay,
- const std::string& in_update_url)
+ bool in_delta_okay,
+ const std::string& in_update_url,
+ bool in_update_disabled,
+ const std::string& in_target_version_prefix)
: os_platform(in_os_platform),
os_version(in_os_version),
os_sp(in_os_sp),
@@ -41,7 +49,9 @@
app_track(in_app_track),
hardware_class(in_hardware_class),
delta_okay(in_delta_okay),
- update_url(in_update_url) {}
+ update_url(in_update_url),
+ update_disabled(in_update_disabled),
+ target_version_prefix(in_target_version_prefix) {}
std::string os_platform;
std::string os_version;
@@ -58,6 +68,9 @@
static const char kUpdateTrackKey[];
+ bool update_disabled;
+ std::string target_version_prefix;
+
// Suggested defaults
static const char* const kAppId;
static const char* const kOsPlatform;
diff --git a/update_attempter.cc b/update_attempter.cc
index 31bc47b..c23135b 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -168,8 +168,14 @@
// If the release_track is specified by policy, that takes precedence.
string release_track;
- if (policy_provider_->device_policy_is_loaded())
- policy_provider_->GetDevicePolicy().GetReleaseChannel(&release_track);
+ if (policy_provider_->device_policy_is_loaded()) {
+ const policy::DevicePolicy& device_policy =
+ policy_provider_->GetDevicePolicy();
+ device_policy.GetReleaseChannel(&release_track);
+ device_policy.GetUpdateDisabled(&omaha_request_params_.update_disabled);
+ device_policy.GetTargetVersionPrefix(
+ &omaha_request_params_.target_version_prefix);
+ }
// Determine whether an alternative test address should be used.
string omaha_url_to_use = omaha_url;
@@ -178,12 +184,18 @@
LOG(INFO) << "using alternative server address: " << omaha_url_to_use;
}
- if (!omaha_request_params_.Init(app_version, omaha_url_to_use,
+ if (!omaha_request_params_.Init(app_version,
+ omaha_url_to_use,
release_track)) {
LOG(ERROR) << "Unable to initialize Omaha request device params.";
return;
}
+ LOG(INFO) << "update_disabled = "
+ << (omaha_request_params_.update_disabled ? "true" : "false")
+ << ", target_version_prefix = "
+ << omaha_request_params_.target_version_prefix;
+
obeying_proxies_ = true;
if (obey_proxies || proxy_manual_checks_ == 0) {
LOG(INFO) << "forced to obey proxies";
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 463d5a6..6402bb6 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -75,6 +75,13 @@
void ReadTrackFromPolicyTestStart();
static gboolean StaticReadTrackFromPolicyTestStart(gpointer data);
+ void ReadUpdateDisabledFromPolicyTestStart();
+ static gboolean StaticReadUpdateDisabledFromPolicyTestStart(gpointer data);
+
+ void ReadTargetVersionPrefixFromPolicyTestStart();
+ static gboolean StaticReadTargetVersionPrefixFromPolicyTestStart(
+ gpointer data);
+
MockDbusGlib dbus_;
UpdateAttempterUnderTest attempter_;
ActionProcessorMock* processor_;
@@ -272,6 +279,20 @@
return FALSE;
}
+gboolean UpdateAttempterTest::StaticReadUpdateDisabledFromPolicyTestStart(
+ gpointer data) {
+ UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+ ua_test->ReadUpdateDisabledFromPolicyTestStart();
+ return FALSE;
+}
+
+gboolean UpdateAttempterTest::StaticReadTargetVersionPrefixFromPolicyTestStart(
+ gpointer data) {
+ UpdateAttempterTest* ua_test = reinterpret_cast<UpdateAttempterTest*>(data);
+ ua_test->ReadTargetVersionPrefixFromPolicyTestStart();
+ return FALSE;
+}
+
namespace {
const string kActionTypes[] = {
OmahaRequestAction::StaticType(),
@@ -404,4 +425,64 @@
g_idle_add(&StaticQuitMainLoop, this);
}
+TEST_F(UpdateAttempterTest, ReadUpdateDisabledFromPolicy) {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ g_idle_add(&StaticReadUpdateDisabledFromPolicyTestStart, this);
+ g_main_loop_run(loop_);
+ g_main_loop_unref(loop_);
+ loop_ = NULL;
+}
+
+void UpdateAttempterTest::ReadUpdateDisabledFromPolicyTestStart() {
+ // Tests that the update_disbled flag is properly fetched
+ // from the device policy.
+
+ policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+ attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+
+ EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*device_policy, GetUpdateDisabled(_))
+ .WillRepeatedly(DoAll(
+ SetArgumentPointee<0>(true),
+ Return(true)));
+
+ attempter_.Update("", "", false, false, false);
+ EXPECT_TRUE(attempter_.omaha_request_params_.update_disabled);
+
+ g_idle_add(&StaticQuitMainLoop, this);
+}
+
+TEST_F(UpdateAttempterTest, ReadTargetVersionPrefixFromPolicy) {
+ loop_ = g_main_loop_new(g_main_context_default(), FALSE);
+ g_idle_add(&StaticReadTargetVersionPrefixFromPolicyTestStart, this);
+ g_main_loop_run(loop_);
+ g_main_loop_unref(loop_);
+ loop_ = NULL;
+}
+
+void UpdateAttempterTest::ReadTargetVersionPrefixFromPolicyTestStart() {
+ // Tests that the target_version_prefix value is properly fetched
+ // from the device policy.
+
+ const std::string target_version_prefix = "1412.";
+
+ policy::MockDevicePolicy* device_policy = new policy::MockDevicePolicy();
+ attempter_.policy_provider_.reset(new policy::PolicyProvider(device_policy));
+
+ EXPECT_CALL(*device_policy, LoadPolicy()).WillRepeatedly(Return(true));
+
+ EXPECT_CALL(*device_policy, GetTargetVersionPrefix(_))
+ .WillRepeatedly(DoAll(
+ SetArgumentPointee<0>(target_version_prefix),
+ Return(true)));
+
+ attempter_.Update("", "", false, false, false);
+ EXPECT_EQ(target_version_prefix.c_str(),
+ attempter_.omaha_request_params_.target_version_prefix);
+
+ g_idle_add(&StaticQuitMainLoop, this);
+}
+
+
} // namespace chromeos_update_engine