update_engine: Add rollback_allowed policy in Omaha requests

Send rollback_allowed="true" parameter to Omaha if rollback is
allowed through enterprise policy.

This parameter indicates to Omaha that the returned image might
be a rollback image, if the targetversionprefix parameter is set
and it is an earlier version than the one which is currently on
the device.

Also, this CL changes update_engine behavior to only send
targetversionprefix to Omaha if it's not empty.

BUG=chromium:814830
TEST='cros_run_unit_tests --board=cyan --packages update_engine'

Change-Id: Iaa5503e217631311fb9ba3a5237ccbfedf04afe9
Reviewed-on: https://chromium-review.googlesource.com/932123
Commit-Ready: Marton Hunyady <hunyadym@chromium.org>
Tested-by: Marton Hunyady <hunyadym@chromium.org>
Reviewed-by: Amin Hassani <ahassani@chromium.org>
diff --git a/mock_update_attempter.h b/mock_update_attempter.h
index d88b840..405fdd6 100644
--- a/mock_update_attempter.h
+++ b/mock_update_attempter.h
@@ -29,12 +29,14 @@
  public:
   using UpdateAttempter::UpdateAttempter;
 
-  MOCK_METHOD6(Update, void(const std::string& app_version,
-                            const std::string& omaha_url,
-                            const std::string& target_channel,
-                            const std::string& target_version_prefix,
-                            bool obey_proxies,
-                            bool interactive));
+  MOCK_METHOD7(Update,
+               void(const std::string& app_version,
+                    const std::string& omaha_url,
+                    const std::string& target_channel,
+                    const std::string& target_version_prefix,
+                    bool rollback_allowed,
+                    bool obey_proxies,
+                    bool interactive));
 
   MOCK_METHOD1(GetStatus, bool(update_engine::UpdateEngineStatus* out_status));
 
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 424cb66..6809979 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -127,10 +127,17 @@
     if (include_ping)
         app_body = GetPingXml(ping_active_days, ping_roll_call_days);
     if (!ping_only) {
-      app_body += base::StringPrintf(
-          "        <updatecheck targetversionprefix=\"%s\""
-          "></updatecheck>\n",
-          XmlEncodeWithDefault(params->target_version_prefix(), "").c_str());
+      app_body += "        <updatecheck";
+      if (!params->target_version_prefix().empty()) {
+        app_body += base::StringPrintf(
+            " targetversionprefix=\"%s\"",
+            XmlEncodeWithDefault(params->target_version_prefix(), "").c_str());
+        // Rollback requires target_version_prefix set.
+        if (params->rollback_allowed()) {
+          app_body += " rollback_allowed=\"true\"";
+        }
+      }
+      app_body += "></updatecheck>\n";
 
       // If this is the first update check after a reboot following a previous
       // update, generate an event containing the previous version number. If
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index cb6c5bd..fa531d2 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -240,6 +240,7 @@
     request_params_.set_interactive(false);
     request_params_.set_update_url("http://url");
     request_params_.set_target_version_prefix("");
+    request_params_.set_rollback_allowed(false);
     request_params_.set_is_powerwash_allowed(false);
 
     fake_system_state_.set_request_params(&request_params_);
@@ -1719,9 +1720,9 @@
                                &post_data));
   // convert post_data to string
   string post_str(post_data.begin(), post_data.end());
-  EXPECT_NE(post_str.find(
-      "        <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
-      "        <updatecheck targetversionprefix=\"\"></updatecheck>\n"),
+  EXPECT_NE(
+      post_str.find("        <ping active=\"1\" a=\"-1\" r=\"-1\"></ping>\n"
+                    "        <updatecheck></updatecheck>\n"),
       string::npos);
   EXPECT_NE(post_str.find("hardware_class=\"OEM MODEL 09235 7471\""),
             string::npos);
@@ -1836,13 +1837,75 @@
                                  &post_data));
     // convert post_data to string
     string post_str(post_data.begin(), post_data.end());
-    EXPECT_NE(post_str.find(base::StringPrintf("installsource=\"%s\"",
-                                               interactive_str)),
+    EXPECT_NE(post_str.find(
+                  base::StringPrintf("installsource=\"%s\"", interactive_str)),
               string::npos)
         << "i = " << i;
   }
 }
 
+TEST_F(OmahaRequestActionTest, FormatTargetVersionPrefixOutputTest) {
+  for (int i = 0; i < 2; i++) {
+    bool target_version_set = i == 1;
+    const char* target_version_prefix = target_version_set ? "10032." : "";
+    brillo::Blob post_data;
+    FakeSystemState fake_system_state;
+
+    request_params_.set_target_version_prefix(target_version_prefix);
+
+    ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                                 -1,
+                                 false,  // ping_only
+                                 ErrorCode::kOmahaRequestXMLParseError,
+                                 metrics::CheckResult::kParsingError,
+                                 metrics::CheckReaction::kUnset,
+                                 metrics::DownloadErrorCode::kUnset,
+                                 nullptr,
+                                 &post_data));
+    // convert post_data to string
+    string post_str(post_data.begin(), post_data.end());
+    if (target_version_set) {
+      EXPECT_NE(post_str.find("<updatecheck targetversionprefix=\"10032.\">"),
+                string::npos)
+          << "i = " << i;
+    } else {
+      EXPECT_EQ(post_str.find("targetversionprefix"), string::npos)
+          << "i = " << i;
+    }
+  }
+}
+
+TEST_F(OmahaRequestActionTest, FormatRollbackAllowedOutputTest) {
+  for (int i = 0; i < 4; i++) {
+    bool rollback_allowed = i / 2 == 0;
+    bool target_version_set = i % 2 == 0;
+    brillo::Blob post_data;
+    FakeSystemState fake_system_state;
+
+    request_params_.set_target_version_prefix(target_version_set ? "10032."
+                                                                 : "");
+    request_params_.set_rollback_allowed(rollback_allowed);
+
+    ASSERT_FALSE(TestUpdateCheck("invalid xml>",
+                                 -1,
+                                 false,  // ping_only
+                                 ErrorCode::kOmahaRequestXMLParseError,
+                                 metrics::CheckResult::kParsingError,
+                                 metrics::CheckReaction::kUnset,
+                                 metrics::DownloadErrorCode::kUnset,
+                                 nullptr,
+                                 &post_data));
+    // convert post_data to string
+    string post_str(post_data.begin(), post_data.end());
+    if (rollback_allowed && target_version_set) {
+      EXPECT_NE(post_str.find("rollback_allowed=\"true\""), string::npos)
+          << "i = " << i;
+    } else {
+      EXPECT_EQ(post_str.find("rollback_allowed"), string::npos) << "i = " << i;
+    }
+  }
+}
+
 TEST_F(OmahaRequestActionTest, OmahaEventTest) {
   OmahaEvent default_event;
   EXPECT_EQ(OmahaEvent::kTypeUnknown, default_event.type);
diff --git a/omaha_request_params.h b/omaha_request_params.h
index a57370d..62cbdab 100644
--- a/omaha_request_params.h
+++ b/omaha_request_params.h
@@ -49,6 +49,7 @@
         os_version_(kOsVersion),
         delta_okay_(true),
         interactive_(false),
+        rollback_allowed_(false),
         wall_clock_based_wait_enabled_(false),
         update_check_count_wait_enabled_(false),
         min_update_checks_needed_(kDefaultMinUpdateChecks),
@@ -123,6 +124,12 @@
     return target_version_prefix_;
   }
 
+  inline void set_rollback_allowed(bool rollback_allowed) {
+    rollback_allowed_ = rollback_allowed;
+  }
+
+  inline bool rollback_allowed() const { return rollback_allowed_; }
+
   inline void set_wall_clock_based_wait_enabled(bool enabled) {
     wall_clock_based_wait_enabled_ = enabled;
   }
@@ -301,6 +308,9 @@
   // to be pinned to. It's empty otherwise.
   std::string target_version_prefix_;
 
+  // Whether the client is accepting rollback images too.
+  bool rollback_allowed_;
+
   // True if scattering is enabled, in which case waiting_period_ specifies the
   // amount of absolute time that we've to wait for before sending a request to
   // Omaha.
diff --git a/update_attempter.cc b/update_attempter.cc
index 10118ac..9d18932 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -241,6 +241,7 @@
                              const string& omaha_url,
                              const string& target_channel,
                              const string& target_version_prefix,
+                             bool rollback_allowed,
                              bool obey_proxies,
                              bool interactive) {
   // This is normally called frequently enough so it's appropriate to use as a
@@ -279,6 +280,7 @@
                              omaha_url,
                              target_channel,
                              target_version_prefix,
+                             rollback_allowed,
                              obey_proxies,
                              interactive)) {
     return;
@@ -354,6 +356,7 @@
                                             const string& omaha_url,
                                             const string& target_channel,
                                             const string& target_version_prefix,
+                                            bool rollback_allowed,
                                             bool obey_proxies,
                                             bool interactive) {
   http_response_code_ = 0;
@@ -369,6 +372,9 @@
   // Update the target version prefix.
   omaha_request_params_->set_target_version_prefix(target_version_prefix);
 
+  // Set whether rollback is allowed.
+  omaha_request_params_->set_rollback_allowed(rollback_allowed);
+
   CalculateScatteringParams(interactive);
 
   CalculateP2PParams(interactive);
@@ -414,6 +420,8 @@
 
   LOG(INFO) << "target_version_prefix = "
             << omaha_request_params_->target_version_prefix()
+            << ", rollback_allowed = "
+            << omaha_request_params_->rollback_allowed()
             << ", scatter_factor_in_seconds = "
             << utils::FormatSecs(scatter_factor_.InSeconds());
 
@@ -885,8 +893,13 @@
     LOG(INFO) << "Update attempt flags in use = 0x" << std::hex
               << current_update_attempt_flags_;
 
-    Update(forced_app_version_, forced_omaha_url_, params.target_channel,
-           params.target_version_prefix, false, params.is_interactive);
+    Update(forced_app_version_,
+           forced_omaha_url_,
+           params.target_channel,
+           params.target_version_prefix,
+           params.rollback_allowed,
+           /*obey_proxies=*/false,
+           params.is_interactive);
     // Always clear the forced app_version and omaha_url after an update attempt
     // so the next update uses the defaults.
     forced_app_version_.clear();
diff --git a/update_attempter.h b/update_attempter.h
index 24376e9..4b50072 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -83,6 +83,7 @@
                       const std::string& omaha_url,
                       const std::string& target_channel,
                       const std::string& target_version_prefix,
+                      bool rollback_allowed,
                       bool obey_proxies,
                       bool interactive);
 
@@ -264,6 +265,9 @@
   FRIEND_TEST(UpdateAttempterTest, MarkDeltaUpdateFailureTest);
   FRIEND_TEST(UpdateAttempterTest, PingOmahaTest);
   FRIEND_TEST(UpdateAttempterTest, ReportDailyMetrics);
+  FRIEND_TEST(UpdateAttempterTest, RollbackNotAllowed);
+  FRIEND_TEST(UpdateAttempterTest, RollbackAllowed);
+  FRIEND_TEST(UpdateAttempterTest, RollbackAllowedSetAndReset);
   FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionNoEventTest);
   FRIEND_TEST(UpdateAttempterTest, ScheduleErrorEventActionTest);
   FRIEND_TEST(UpdateAttempterTest, TargetVersionPrefixSetAndReset);
@@ -335,6 +339,7 @@
                              const std::string& omaha_url,
                              const std::string& target_channel,
                              const std::string& target_version_prefix,
+                             bool rollback_allowed,
                              bool obey_proxies,
                              bool interactive);
 
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 9dfc008..54a3f2b 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -478,7 +478,7 @@
     EXPECT_CALL(*processor_, StartProcessing());
   }
 
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   loop_.PostTask(FROM_HERE,
                  base::Bind(&UpdateAttempterTest::UpdateTestVerify,
                             base::Unretained(this)));
@@ -700,7 +700,7 @@
   fake_system_state_.set_p2p_manager(&mock_p2p_manager);
   mock_p2p_manager.fake().SetP2PEnabled(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading_);
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -722,7 +722,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(false);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping()).Times(0);
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -745,7 +745,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(false);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_FALSE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -767,7 +767,7 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   EXPECT_TRUE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -790,7 +790,13 @@
   mock_p2p_manager.fake().SetEnsureP2PRunningResult(true);
   mock_p2p_manager.fake().SetPerformHousekeepingResult(true);
   EXPECT_CALL(mock_p2p_manager, PerformHousekeeping());
-  attempter_.Update("", "", "", "", false, true /* interactive */);
+  attempter_.Update("",
+                    "",
+                    "",
+                    "",
+                    false,
+                    false,
+                    /*interactive=*/true);
   EXPECT_FALSE(actual_using_p2p_for_downloading());
   EXPECT_TRUE(actual_using_p2p_for_sharing());
   ScheduleQuitMainLoop();
@@ -821,7 +827,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   ScheduleQuitMainLoop();
@@ -860,7 +866,7 @@
   attempter_.policy_provider_.reset(
       new policy::PolicyProvider(std::move(device_policy)));
 
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   // Make sure the file still exists.
@@ -876,7 +882,7 @@
   // However, if the count is already 0, it's not decremented. Test that.
   initial_value = 0;
   EXPECT_TRUE(fake_prefs.SetInt64(kPrefsUpdateCheckCount, initial_value));
-  attempter_.Update("", "", "", "", false, false);
+  attempter_.Update("", "", "", "", false, false, false);
   EXPECT_TRUE(fake_prefs.Exists(kPrefsUpdateCheckCount));
   EXPECT_TRUE(fake_prefs.GetInt64(kPrefsUpdateCheckCount, &new_value));
   EXPECT_EQ(initial_value, new_value);
@@ -921,7 +927,13 @@
       new policy::PolicyProvider(std::move(device_policy)));
 
   // Trigger an interactive check so we can test that scattering is disabled.
-  attempter_.Update("", "", "", "", false, true);
+  attempter_.Update("",
+                    "",
+                    "",
+                    "",
+                    false,
+                    false,
+                    /*interactive=*/true);
   EXPECT_EQ(scatter_factor_in_seconds, attempter_.scatter_factor_.InSeconds());
 
   // Make sure scattering is disabled for manual (i.e. user initiated) update
@@ -1050,15 +1062,35 @@
 }
 
 TEST_F(UpdateAttempterTest, TargetVersionPrefixSetAndReset) {
-  attempter_.CalculateUpdateParams("", "", "", "1234", false, false);
+  attempter_.CalculateUpdateParams("", "", "", "1234", false, false, false);
   EXPECT_EQ("1234",
             fake_system_state_.request_params()->target_version_prefix());
 
-  attempter_.CalculateUpdateParams("", "", "", "", false, false);
+  attempter_.CalculateUpdateParams("", "", "", "", false, false, false);
   EXPECT_TRUE(
       fake_system_state_.request_params()->target_version_prefix().empty());
 }
 
+TEST_F(UpdateAttempterTest, RollbackAllowedSetAndReset) {
+  attempter_.CalculateUpdateParams("",
+                                   "",
+                                   "",
+                                   "1234",
+                                   /*rollback_allowed=*/true,
+                                   false,
+                                   false);
+  EXPECT_TRUE(fake_system_state_.request_params()->rollback_allowed());
+
+  attempter_.CalculateUpdateParams("",
+                                   "",
+                                   "",
+                                   "1234",
+                                   /*rollback_allowed=*/false,
+                                   false,
+                                   false);
+  EXPECT_FALSE(fake_system_state_.request_params()->rollback_allowed());
+}
+
 TEST_F(UpdateAttempterTest, UpdateDeferredByPolicyTest) {
   // Construct an OmahaResponseHandlerAction that has processed an InstallPlan,
   // but the update is being deferred by the Policy.
@@ -1124,6 +1156,20 @@
             attempter_.GetCurrentUpdateAttemptFlags());
 }
 
+TEST_F(UpdateAttempterTest, RollbackNotAllowed) {
+  UpdateCheckParams params = {.updates_enabled = true,
+                              .rollback_allowed = false};
+  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
+  EXPECT_FALSE(fake_system_state_.request_params()->rollback_allowed());
+}
+
+TEST_F(UpdateAttempterTest, RollbackAllowed) {
+  UpdateCheckParams params = {.updates_enabled = true,
+                              .rollback_allowed = true};
+  attempter_.OnUpdateScheduled(EvalStatus::kSucceeded, params);
+  EXPECT_TRUE(fake_system_state_.request_params()->rollback_allowed());
+}
+
 TEST_F(UpdateAttempterTest, InteractiveUpdateUsesPassedRestrictions) {
   attempter_.SetUpdateAttemptFlags(UpdateAttemptFlags::kFlagRestrictDownload);
 
@@ -1163,7 +1209,7 @@
               SetRollbackHappened(false))
       .Times(expected_reset ? 1 : 0);
   attempter_.policy_provider_ = std::move(mock_policy_provider);
-  attempter_.Update("", "", "", "", false, /*interactive=*/true);
+  attempter_.Update("", "", "", "", false, false, false);
   ScheduleQuitMainLoop();
 }
 
diff --git a/update_manager/android_things_policy.cc b/update_manager/android_things_policy.cc
index 92ab95f..259ca2b 100644
--- a/update_manager/android_things_policy.cc
+++ b/update_manager/android_things_policy.cc
@@ -53,7 +53,7 @@
   result->updates_enabled = true;
   result->target_channel.clear();
   result->target_version_prefix.clear();
-  result->rollback_to_target_version = RollbackToTargetVersion::kUnspecified;
+  result->rollback_allowed = false;
   result->rollback_allowed_milestones = -1;
   result->is_interactive = false;
 
diff --git a/update_manager/chromeos_policy.cc b/update_manager/chromeos_policy.cc
index 585ea19..c389d8b 100644
--- a/update_manager/chromeos_policy.cc
+++ b/update_manager/chromeos_policy.cc
@@ -199,7 +199,7 @@
   result->updates_enabled = true;
   result->target_channel.clear();
   result->target_version_prefix.clear();
-  result->rollback_to_target_version = RollbackToTargetVersion::kUnspecified;
+  result->rollback_allowed = false;
   result->rollback_allowed_milestones = -1;
   result->is_interactive = false;
 
diff --git a/update_manager/chromeos_policy_unittest.cc b/update_manager/chromeos_policy_unittest.cc
index 9538c4e..c7380e8 100644
--- a/update_manager/chromeos_policy_unittest.cc
+++ b/update_manager/chromeos_policy_unittest.cc
@@ -189,9 +189,6 @@
   // Override specific device policy attributes.
   fake_state_.device_policy_provider()->var_target_version_prefix()->
       reset(new string("1.2"));
-  fake_state_.device_policy_provider()->var_rollback_to_target_version()->reset(
-      new RollbackToTargetVersion(
-          RollbackToTargetVersion::kRollbackWithFullPowerwash));
   fake_state_.device_policy_provider()
       ->var_rollback_allowed_milestones()
       ->reset(new int(5));
@@ -205,13 +202,70 @@
                      &Policy::UpdateCheckAllowed, &result);
   EXPECT_TRUE(result.updates_enabled);
   EXPECT_EQ("1.2", result.target_version_prefix);
-  EXPECT_EQ(RollbackToTargetVersion::kRollbackWithFullPowerwash,
-            result.rollback_to_target_version);
   EXPECT_EQ(5, result.rollback_allowed_milestones);
   EXPECT_EQ("foo-channel", result.target_channel);
   EXPECT_FALSE(result.is_interactive);
 }
 
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackAllowed) {
+  // Update check is allowed, response includes attributes for use in the
+  // request.
+  SetUpdateCheckAllowed(true);
+
+  // Override RollbackToTargetVersion device policy attribute.
+  fake_state_.device_policy_provider()->var_rollback_to_target_version()->reset(
+      new RollbackToTargetVersion(
+          RollbackToTargetVersion::kRollbackWithFullPowerwash));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_TRUE(result.rollback_allowed);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackDisabled) {
+  // Update check is allowed, response includes attributes for use in the
+  // request.
+  SetUpdateCheckAllowed(true);
+
+  // Override RollbackToTargetVersion device policy attribute.
+  fake_state_.device_policy_provider()->var_rollback_to_target_version()->reset(
+      new RollbackToTargetVersion(RollbackToTargetVersion::kDisabled));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_FALSE(result.rollback_allowed);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackUnspecified) {
+  // Update check is allowed, response includes attributes for use in the
+  // request.
+  SetUpdateCheckAllowed(true);
+
+  // Override RollbackToTargetVersion device policy attribute.
+  fake_state_.device_policy_provider()->var_rollback_to_target_version()->reset(
+      new RollbackToTargetVersion(RollbackToTargetVersion::kUnspecified));
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_FALSE(result.rollback_allowed);
+}
+
+TEST_F(UmChromeOSPolicyTest, UpdateCheckAllowedRollbackNotSet) {
+  // Update check is allowed, response includes attributes for use in the
+  // request.
+  SetUpdateCheckAllowed(true);
+
+  // Don't set RollbackToTargetVersion device policy attribute.
+
+  UpdateCheckParams result;
+  ExpectPolicyStatus(
+      EvalStatus::kSucceeded, &Policy::UpdateCheckAllowed, &result);
+  EXPECT_FALSE(result.rollback_allowed);
+}
+
 TEST_F(UmChromeOSPolicyTest,
        UpdateCheckAllowedUpdatesDisabledForUnofficialBuilds) {
   // UpdateCheckAllowed should return kAskMeAgainLater if this is an unofficial
diff --git a/update_manager/default_policy.cc b/update_manager/default_policy.cc
index 0cd58fb..17d0e3e 100644
--- a/update_manager/default_policy.cc
+++ b/update_manager/default_policy.cc
@@ -40,7 +40,7 @@
   result->updates_enabled = true;
   result->target_channel.clear();
   result->target_version_prefix.clear();
-  result->rollback_to_target_version = RollbackToTargetVersion::kUnspecified;
+  result->rollback_allowed = false;
   result->rollback_allowed_milestones = -1;  // No version rolls should happen.
   result->is_interactive = false;
 
diff --git a/update_manager/enterprise_device_policy_impl.cc b/update_manager/enterprise_device_policy_impl.cc
index 2cb1d9c..94ae5e7 100644
--- a/update_manager/enterprise_device_policy_impl.cc
+++ b/update_manager/enterprise_device_policy_impl.cc
@@ -81,8 +81,21 @@
 
       const RollbackToTargetVersion* rollback_to_target_version_p =
           ec->GetValue(dp_provider->var_rollback_to_target_version());
-      if (rollback_to_target_version_p)
-        result->rollback_to_target_version = *rollback_to_target_version_p;
+      if (rollback_to_target_version_p) {
+        switch (*rollback_to_target_version_p) {
+          case RollbackToTargetVersion::kUnspecified:
+          case RollbackToTargetVersion::kDisabled:
+            result->rollback_allowed = false;
+            break;
+          case RollbackToTargetVersion::kRollbackWithFullPowerwash:
+            result->rollback_allowed = true;
+            break;
+          case RollbackToTargetVersion::kMaxValue:
+            NOTREACHED();
+            // Don't add a default case to let the compiler warn about newly
+            // added enum values which should be added here.
+        }
+      }
     }
 
     // Determine allowed milestones for rollback
diff --git a/update_manager/policy.h b/update_manager/policy.h
index c651846..7089c30 100644
--- a/update_manager/policy.h
+++ b/update_manager/policy.h
@@ -48,9 +48,8 @@
   //
   // A target version prefix, if imposed by policy; otherwise, an empty string.
   std::string target_version_prefix;
-  // Specifies what should happen if target_version_prefix specifies an earlier
-  // version than the current version of the OS.
-  RollbackToTargetVersion rollback_to_target_version;
+  // Specifies whether rollback images are allowed by device policy.
+  bool rollback_allowed;
   // Specifies the number of Chrome milestones rollback should be allowed,
   // starting from the stable version at any time. Value is -1 if unspecified
   // (e.g. no device policy is available yet), in this case no version