update_engine: Set min kernel version based on rollback policy.
- Sets max_kernel_rollforward to tpm_kernver for enterprise enrolled
devices if number of allowed rollback versions is > 0, or if
no policy is available.
- Sets max_kernel_rollforward to 0xfffffffe for consumer devices.
- This just holds the rollback window open for enterprise customers
while the server side piece of the feature is implemented.
- In future max_kernel_rollforward will be set based on the number
of allowed rollback versions to the lowest version that still
allows those rollbacks, and will progressively increase as the
versions age out of the rollback window.
BUG=chromium:814090
TEST=emerges
Change-Id: I7c192092183dd398f74d34b41bbc65dc2595d081
Reviewed-on: https://chromium-review.googlesource.com/940567
Commit-Ready: Zentaro Kavanagh <zentaro@chromium.org>
Tested-by: Zentaro Kavanagh <zentaro@chromium.org>
Reviewed-by: Zentaro Kavanagh <zentaro@chromium.org>
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index d08768e..424cb66 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -35,6 +35,7 @@
#include <brillo/key_value_store.h>
#include <expat.h>
#include <metrics/metrics_library.h>
+#include <policy/libpolicy.h>
#include "update_engine/common/action_pipe.h"
#include "update_engine/common/constants.h"
@@ -52,6 +53,7 @@
using base::Time;
using base::TimeDelta;
+using chromeos_update_manager::kRollforwardInfinity;
using std::map;
using std::string;
using std::vector;
@@ -619,12 +621,14 @@
std::unique_ptr<HttpFetcher> http_fetcher,
bool ping_only)
: system_state_(system_state),
+ params_(system_state->request_params()),
event_(event),
http_fetcher_(std::move(http_fetcher)),
+ policy_provider_(std::make_unique<policy::PolicyProvider>()),
ping_only_(ping_only),
ping_active_days_(0),
ping_roll_call_days_(0) {
- params_ = system_state->request_params();
+ policy_provider_->Reload();
}
OmahaRequestAction::~OmahaRequestAction() {}
@@ -1195,6 +1199,9 @@
payload_state->SetUsingP2PForSharing(false);
}
+ // Set the max kernel key version based on whether rollback is allowed.
+ SetMaxKernelKeyVersionForRollback();
+
// Update the payload state with the current response. The payload state
// will automatically reset all stale state if this response is different
// from what's stored already. We are updating the payload state as late
@@ -1799,4 +1806,62 @@
return is_allowed;
}
+bool OmahaRequestAction::IsRollbackEnabled() const {
+ if (policy_provider_->IsConsumerDevice()) {
+ LOG(INFO) << "Rollback is not enabled for consumer devices.";
+ return false;
+ }
+
+ if (!policy_provider_->device_policy_is_loaded()) {
+ LOG(INFO) << "No device policy is loaded. Assuming rollback enabled.";
+ return true;
+ }
+
+ int allowed_milestones;
+ if (!policy_provider_->GetDevicePolicy().GetRollbackAllowedMilestones(
+ &allowed_milestones)) {
+ LOG(INFO) << "RollbackAllowedMilestones policy can't be read. "
+ "Defaulting to rollback enabled.";
+ return true;
+ }
+
+ LOG(INFO) << "Rollback allows " << allowed_milestones << " milestones.";
+ return allowed_milestones > 0;
+}
+
+void OmahaRequestAction::SetMaxKernelKeyVersionForRollback() const {
+ bool max_rollforward_set = false;
+ if (IsRollbackEnabled()) {
+ // If rollback is enabled, set the max kernel key version to the current
+ // kernel key version. This has the effect of freezing kernel key roll
+ // forwards.
+ //
+ // TODO(zentaro): This behavior is temporary, and ensures that no kernel
+ // key roll forward happens until the server side components of rollback
+ // are implemented. Future changes will allow the Omaha server to return
+ // the kernel key version from max_rollback_versions in the past. At that
+ // point the max kernel key version will be set to that value, creating a
+ // sliding window of versions that can be rolled back to.
+ int min_kernel_version =
+ system_state_->hardware()->GetMinKernelKeyVersion();
+ LOG(INFO) << "Rollback is enabled. Setting max_kernel_rollforward to "
+ << min_kernel_version;
+ max_rollforward_set = system_state_->hardware()->SetMaxKernelKeyRollforward(
+ min_kernel_version);
+ } else {
+ // For devices that are not rollback enabled (ie. consumer devices), the
+ // max kernel key version is set to 0xfffffffe, which is logically
+ // infinity. This maintains the previous behavior that that kernel key
+ // versions roll forward each time they are incremented.
+ LOG(INFO) << "Rollback is disabled. Setting max_kernel_rollforward to "
+ << kRollforwardInfinity;
+ max_rollforward_set = system_state_->hardware()->SetMaxKernelKeyRollforward(
+ kRollforwardInfinity);
+ }
+
+ if (!max_rollforward_set) {
+ LOG(ERROR) << "Failed to set max_kernel_rollforward";
+ }
+}
+
} // namespace chromeos_update_engine
diff --git a/omaha_request_action.h b/omaha_request_action.h
index f1c4a02..7f526ec 100644
--- a/omaha_request_action.h
+++ b/omaha_request_action.h
@@ -39,6 +39,10 @@
// The Omaha Request action makes a request to Omaha and can output
// the response on the output ActionPipe.
+namespace policy {
+class PolicyProvider;
+}
+
namespace chromeos_update_engine {
// Encodes XML entities in a given string. Input must be ASCII-7 valid. If
@@ -172,6 +176,7 @@
bool IsEvent() const { return event_.get() != nullptr; }
private:
+ friend class OmahaRequestActionTest;
FRIEND_TEST(OmahaRequestActionTest, GetInstallDateWhenNoPrefsNorOOBE);
FRIEND_TEST(OmahaRequestActionTest,
GetInstallDateWhenOOBECompletedWithInvalidDate);
@@ -303,6 +308,14 @@
bool IsUpdateAllowedOverCurrentConnection(
ErrorCode* error, const OmahaResponse& response) const;
+ // Returns true if rollback is enabled. Always returns false for consumer
+ // devices.
+ bool IsRollbackEnabled() const;
+
+ // Sets the appropriate max kernel key version based on whether rollback is
+ // enabled.
+ void SetMaxKernelKeyVersionForRollback() const;
+
// Global system context.
SystemState* system_state_;
@@ -315,6 +328,9 @@
// pointer to the HttpFetcher that does the http work
std::unique_ptr<HttpFetcher> http_fetcher_;
+ // Used for fetching information about the device policy.
+ std::unique_ptr<policy::PolicyProvider> policy_provider_;
+
// If true, only include the <ping> element in the request.
bool ping_only_;
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 73da467..cb6c5bd 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include <base/bind.h>
@@ -35,6 +36,8 @@
#include <brillo/message_loops/message_loop.h>
#include <brillo/message_loops/message_loop_utils.h>
#include <gtest/gtest.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_libpolicy.h>
#include "update_engine/common/action_pipe.h"
#include "update_engine/common/constants.h"
@@ -49,9 +52,11 @@
#include "update_engine/mock_connection_manager.h"
#include "update_engine/mock_payload_state.h"
#include "update_engine/omaha_request_params.h"
+#include "update_engine/update_manager/rollback_prefs.h"
using base::Time;
using base::TimeDelta;
+using chromeos_update_manager::kRollforwardInfinity;
using std::string;
using std::vector;
using testing::AllOf;
@@ -61,6 +66,7 @@
using testing::Le;
using testing::NiceMock;
using testing::Return;
+using testing::ReturnRef;
using testing::ReturnPointee;
using testing::SaveArg;
using testing::SetArgPointee;
@@ -68,6 +74,10 @@
namespace {
+static_assert(kRollforwardInfinity == 0xfffffffe,
+ "Don't change the value of kRollforward infinity unless its "
+ "size has been changed in firmware.");
+
const char kTestAppId[] = "test-app-id";
const char kTestAppId2[] = "test-app2-id";
@@ -254,6 +264,9 @@
bool TestUpdateCheck(const string& http_response,
int fail_http_response_code,
bool ping_only,
+ bool is_consumer_device,
+ int rollback_allowed_milestones,
+ bool is_policy_loaded,
ErrorCode expected_code,
metrics::CheckResult expected_check_result,
metrics::CheckReaction expected_check_reaction,
@@ -261,6 +274,22 @@
OmahaResponse* out_response,
brillo::Blob* out_post_data);
+ // Overload of TestUpdateCheck that does not supply |is_consumer_device| or
+ // |rollback_allowed_milestones| which are only required for rollback tests.
+ bool TestUpdateCheck(const string& http_response,
+ int fail_http_response_code,
+ bool ping_only,
+ ErrorCode expected_code,
+ metrics::CheckResult expected_check_result,
+ metrics::CheckReaction expected_check_reaction,
+ metrics::DownloadErrorCode expected_download_error_code,
+ OmahaResponse* out_response,
+ brillo::Blob* out_post_data);
+
+ void TestRollbackCheck(bool is_consumer_device,
+ int rollback_allowed_milestones,
+ bool is_policy_loaded);
+
// Runs and checks a ping test. |ping_only| indicates whether it should send
// only a ping or also an updatecheck.
void PingTest(bool ping_only);
@@ -357,6 +386,9 @@
const string& http_response,
int fail_http_response_code,
bool ping_only,
+ bool is_consumer_device,
+ int rollback_allowed_milestones,
+ bool is_policy_loaded,
ErrorCode expected_code,
metrics::CheckResult expected_check_result,
metrics::CheckReaction expected_check_reaction,
@@ -379,6 +411,24 @@
nullptr,
base::WrapUnique(fetcher),
ping_only);
+ auto mock_policy_provider =
+ std::make_unique<NiceMock<policy::MockPolicyProvider>>();
+ EXPECT_CALL(*mock_policy_provider, IsConsumerDevice())
+ .WillRepeatedly(Return(is_consumer_device));
+
+ EXPECT_CALL(*mock_policy_provider, device_policy_is_loaded())
+ .WillRepeatedly(Return(is_policy_loaded));
+
+ const policy::MockDevicePolicy device_policy;
+ const bool get_allowed_milestone_succeeds = rollback_allowed_milestones >= 0;
+ EXPECT_CALL(device_policy, GetRollbackAllowedMilestones(_))
+ .WillRepeatedly(DoAll(SetArgPointee<0>(rollback_allowed_milestones),
+ Return(get_allowed_milestone_succeeds)));
+
+ EXPECT_CALL(*mock_policy_provider, GetDevicePolicy())
+ .WillRepeatedly(ReturnRef(device_policy));
+
+ action.policy_provider_ = std::move(mock_policy_provider);
OmahaRequestActionTestProcessorDelegate delegate;
delegate.expected_code_ = expected_code;
@@ -413,6 +463,50 @@
return collector_action.has_input_object_;
}
+bool OmahaRequestActionTest::TestUpdateCheck(
+ const string& http_response,
+ int fail_http_response_code,
+ bool ping_only,
+ ErrorCode expected_code,
+ metrics::CheckResult expected_check_result,
+ metrics::CheckReaction expected_check_reaction,
+ metrics::DownloadErrorCode expected_download_error_code,
+ OmahaResponse* out_response,
+ brillo::Blob* out_post_data) {
+ return TestUpdateCheck(http_response,
+ fail_http_response_code,
+ ping_only,
+ true, // is_consumer_device
+ 0, // rollback_allowed_milestones
+ false, // is_policy_loaded
+ expected_code,
+ expected_check_result,
+ expected_check_reaction,
+ expected_download_error_code,
+ out_response,
+ out_post_data);
+}
+
+void OmahaRequestActionTest::TestRollbackCheck(bool is_consumer_device,
+ int rollback_allowed_milestones,
+ bool is_policy_loaded) {
+ OmahaResponse response;
+ fake_update_response_.deadline = "20101020";
+ ASSERT_TRUE(TestUpdateCheck(fake_update_response_.GetUpdateResponse(),
+ -1,
+ false, // ping_only
+ is_consumer_device,
+ rollback_allowed_milestones,
+ is_policy_loaded,
+ ErrorCode::kSuccess,
+ metrics::CheckResult::kUpdateAvailable,
+ metrics::CheckReaction::kUpdating,
+ metrics::DownloadErrorCode::kUnset,
+ &response,
+ nullptr));
+ ASSERT_TRUE(response.update_exists);
+}
+
// Tests Event requests -- they should always succeed. |out_post_data|
// may be null; if non-null, the post-data received by the mock
// HttpFetcher is returned.
@@ -2593,4 +2687,108 @@
EXPECT_EQ(prefs_days, 28);
}
+// Verifies that a device with no device policy, and is not a consumer
+// device sets the max kernel key version to the current version.
+// ie. the same behavior as if rollback is enabled.
+TEST_F(OmahaRequestActionTest, NoPolicyEnterpriseDevicesSetMaxRollback) {
+ FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+ // Setup and verify some initial default values for the kernel TPM
+ // values that control verified boot and rollback.
+ const int min_kernel_version = 4;
+ fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+ fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+ TestRollbackCheck(false /* is_consumer_device */,
+ 3 /* rollback_allowed_milestones */,
+ false /* is_policy_loaded */);
+
+ // Verify max_kernel_rollforward was set to the current minimum
+ // kernel key version. This has the effect of freezing roll
+ // forwards indefinitely. This will hold the rollback window
+ // open until a future change will be able to move this forward
+ // relative the configured window.
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMaxKernelKeyRollforward());
+}
+
+// Verifies that a conmsumer device with no device policy sets the
+// max kernel key version to the current version. ie. the same
+// behavior as if rollback is enabled.
+TEST_F(OmahaRequestActionTest, NoPolicyConsumerDevicesSetMaxRollback) {
+ FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+ // Setup and verify some initial default values for the kernel TPM
+ // values that control verified boot and rollback.
+ const int min_kernel_version = 3;
+ fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+ fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+ TestRollbackCheck(true /* is_consumer_device */,
+ 3 /* rollback_allowed_milestones */,
+ false /* is_policy_loaded */);
+
+ // Verify that with rollback disabled that max_kernel_rollforward
+ // was set to logical infinity. This is the expected behavior for
+ // consumer devices and matches the existing behavior prior to the
+ // rollback features.
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+}
+
+// Verifies that a device with rollback enabled sets max_kernel_rollforward
+// in the TPM to prevent roll forward.
+TEST_F(OmahaRequestActionTest, RollbackEnabledDevicesSetMaxRollback) {
+ FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+ // Setup and verify some initial default values for the kernel TPM
+ // values that control verified boot and rollback.
+ const int allowed_milestones = 4;
+ const int min_kernel_version = 3;
+ fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+ fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+ TestRollbackCheck(false /* is_consumer_device */,
+ allowed_milestones,
+ true /* is_policy_loaded */);
+
+ // Verify that with rollback enabled that max_kernel_rollforward
+ // was set to the current minimum kernel key version. This has
+ // the effect of freezing roll forwards indefinitely. This will
+ // hold the rollback window open until a future change will
+ // be able to move this forward relative the configured window.
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMaxKernelKeyRollforward());
+}
+
+// Verifies that a device with rollback disabled sets max_kernel_rollforward
+// in the TPM to logical infinity, to allow roll forward.
+TEST_F(OmahaRequestActionTest, RollbackDisabledDevicesSetMaxRollback) {
+ FakeHardware* fake_hw = fake_system_state_.fake_hardware();
+
+ // Setup and verify some initial default values for the kernel TPM
+ // values that control verified boot and rollback.
+ const int allowed_milestones = 0;
+ const int min_kernel_version = 3;
+ fake_hw->SetMinKernelKeyVersion(min_kernel_version);
+ fake_hw->SetMaxKernelKeyRollforward(kRollforwardInfinity);
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+
+ TestRollbackCheck(false /* is_consumer_device */,
+ allowed_milestones,
+ true /* is_policy_loaded */);
+
+ // Verify that with rollback disabled that max_kernel_rollforward
+ // was set to logical infinity.
+ EXPECT_EQ(min_kernel_version, fake_hw->GetMinKernelKeyVersion());
+ EXPECT_EQ(kRollforwardInfinity, fake_hw->GetMaxKernelKeyRollforward());
+}
+
} // namespace chromeos_update_engine
diff --git a/update_manager/rollback_prefs.h b/update_manager/rollback_prefs.h
index 0305a46..1783eb0 100644
--- a/update_manager/rollback_prefs.h
+++ b/update_manager/rollback_prefs.h
@@ -19,6 +19,10 @@
namespace chromeos_update_manager {
+// Value used to represent that kernel key versions can always roll-forward.
+// This is the maximum value of a kernel key version.
+constexpr int kRollforwardInfinity = 0xfffffffe;
+
// Whether the device should roll back to the target version, and if yes, which
// type of rollback should it do. Matches chrome_device_policy.proto's
// AutoUpdateSettingsProto::RollbackToTargetVersion.