Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16fcc1ff authored by pjgowtham's avatar pjgowtham Committed by Nolen Johnson
Browse files

recovery: Extend A/B downgrades for older SPL

Some OEM devices lack the ability to sideload updates through stock
recovery, which can prevent users from reverting to older builds.
This change adds support for downgrading the security patch level
(SPL) on A/B devices, gated by a property and user confirmation.

Since SPL downgrades may trigger anti-rollback protections on certain
devices, recovery now warns the user before proceeding.

Change-Id: If8a3ab7347e51c90337c4044826226ea11d6a327
parent 8e2da939
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -65,7 +65,7 @@ bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::st
// pre-device and serial number (if presents). A/B OTA specific checks: pre-build version,
// pre-device and serial number (if presents). A/B OTA specific checks: pre-build version,
// fingerprint, timestamp.
// fingerprint, timestamp.
bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type,
bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type,
                          RecoveryUI* ui);
                          RecoveryUI* ui, bool spl_downgrade_approved = false);


// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the
// Ensures the path to the update package is mounted. Also set the |should_use_fuse| to true if the
// package stays on a removable media.
// package stays on a removable media.
+20 −9
Original line number Original line Diff line number Diff line
@@ -67,6 +67,7 @@ using namespace std::chrono_literals;
bool ask_to_ab_reboot(Device* device);
bool ask_to_ab_reboot(Device* device);
bool ask_to_continue_unverified(Device* device);
bool ask_to_continue_unverified(Device* device);
bool ask_to_continue_downgrade(Device* device);
bool ask_to_continue_downgrade(Device* device);
bool ask_to_continue_spl_downgrade(Device* device);


static constexpr int kRecoveryApiVersion = 3;
static constexpr int kRecoveryApiVersion = 3;
// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
// We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
@@ -155,7 +156,7 @@ static void ReadSourceTargetBuild(const std::map<std::string, std::string>& meta
// Downgrading is not allowed unless explicitly enabled in the package and only for
// Downgrading is not allowed unless explicitly enabled in the package and only for
// incremental packages.
// incremental packages.
static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata,
static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata,
                                    RecoveryUI* ui) {
                                    RecoveryUI* ui, bool spl_downgrade_approved) {
  // Incremental updates should match the current build.
  // Incremental updates should match the current build.
  auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
  auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
  auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
  auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
@@ -186,10 +187,10 @@ static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& me
      !android::base::ParseInt(pkg_post_timestamp_string, &pkg_post_timestamp) ||
      !android::base::ParseInt(pkg_post_timestamp_string, &pkg_post_timestamp) ||
      pkg_post_timestamp < build_timestamp) {
      pkg_post_timestamp < build_timestamp) {
    if (get_value(metadata, "ota-downgrade") != "yes") {
    if (get_value(metadata, "ota-downgrade") != "yes") {
      LOG(ERROR) << "Update package is older than the current build, expected a build "
      LOG(WARNING) << "Update package is older than the current build, expected a build "
                    "newer than timestamp "
                    "newer than timestamp "
                 << build_timestamp << " but package has timestamp " << pkg_post_timestamp
                 << build_timestamp << " but package has timestamp " << pkg_post_timestamp
                 << " and downgrade not allowed.";
                 << " this is considered a downgrade";
      undeclared_downgrade = true;
      undeclared_downgrade = true;
    } else if (pkg_pre_build_fingerprint.empty()) {
    } else if (pkg_pre_build_fingerprint.empty()) {
      LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
      LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
@@ -208,7 +209,7 @@ static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& me
    }
    }
  }
  }


  if (undeclared_downgrade &&
  if (!spl_downgrade_approved && undeclared_downgrade &&
      !(ui->IsTextVisible() && ask_to_continue_downgrade(ui->GetDevice()))) {
      !(ui->IsTextVisible() && ask_to_continue_downgrade(ui->GetDevice()))) {
    return false;
    return false;
  }
  }
@@ -217,7 +218,7 @@ static bool CheckAbSpecificMetadata(const std::map<std::string, std::string>& me
}
}


bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type,
bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type,
                          RecoveryUI* ui) {
                          RecoveryUI* ui, bool spl_downgrade_approved) {
  auto package_ota_type = get_value(metadata, "ota-type");
  auto package_ota_type = get_value(metadata, "ota-type");
  auto expected_ota_type = OtaTypeToString(ota_type);
  auto expected_ota_type = OtaTypeToString(ota_type);
  if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) {
  if (ota_type != OtaType::AB && ota_type != OtaType::BRICK) {
@@ -274,7 +275,7 @@ bool CheckPackageMetadata(const std::map<std::string, std::string>& metadata, Ot
  }
  }


  if (ota_type == OtaType::AB) {
  if (ota_type == OtaType::AB) {
    return CheckAbSpecificMetadata(metadata, ui);
    return CheckAbSpecificMetadata(metadata, ui, spl_downgrade_approved);
  }
  }


  return true;
  return true;
@@ -424,11 +425,21 @@ static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache,
  bool device_only_supports_ab = device_supports_ab && !ab_device_supports_nonab;
  bool device_only_supports_ab = device_supports_ab && !ab_device_supports_nonab;
  bool device_supports_virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
  bool device_supports_virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);


  bool spl_downgrade_approved = false;
  const auto allow_spl_downgrade =
      android::base::GetBoolProperty("persist.vendor.recovery_allow_spl_downgrade", false);
  const auto current_spl = android::base::GetProperty("ro.build.version.security_patch", "");
  const auto current_spl = android::base::GetProperty("ro.build.version.security_patch", "");
  if (ViolatesSPLDowngrade(zip, current_spl)) {
  if (ViolatesSPLDowngrade(zip, current_spl)) {
    if (!allow_spl_downgrade || !ui->IsTextVisible()) {
      LOG(ERROR) << "Denying OTA because it's SPL downgrade";
      LOG(ERROR) << "Denying OTA because it's SPL downgrade";
      return INSTALL_ERROR;
      return INSTALL_ERROR;
    }
    }
    if (!ask_to_continue_spl_downgrade(device)) {
      LOG(ERROR) << "User denied SPL downgrade";
      return INSTALL_ERROR;
    }
    spl_downgrade_approved = true;
  }


  const auto reboot_to_recovery = [] {
  const auto reboot_to_recovery = [] {
    if (std::string err; !clear_bootloader_message(&err)) {
    if (std::string err; !clear_bootloader_message(&err)) {
@@ -454,7 +465,7 @@ static InstallResult TryUpdateBinary(Package* package, bool* wipe_cache,
  // Package does not declare itself as an A/B package, but device only supports A/B;
  // Package does not declare itself as an A/B package, but device only supports A/B;
  //   still calls CheckPackageMetadata to get a meaningful error message.
  //   still calls CheckPackageMetadata to get a meaningful error message.
  if (package_is_ab || device_only_supports_ab) {
  if (package_is_ab || device_only_supports_ab) {
    if (!CheckPackageMetadata(metadata, OtaType::AB, ui)) {
    if (!CheckPackageMetadata(metadata, OtaType::AB, ui, spl_downgrade_approved)) {
      log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
      log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
      return INSTALL_ERROR;
      return INSTALL_ERROR;
    }
    }
+1 −1
Original line number Original line Diff line number Diff line
@@ -30,7 +30,7 @@ bool ViolatesSPLDowngrade(const build::tools::releasetools::OtaMetadata& metadat
  // example, 05 for May). Otherwise this comparison doesn't work. We don't expect SPL date formats
  // example, 05 for May). Otherwise this comparison doesn't work. We don't expect SPL date formats
  // to change, leave this as is.
  // to change, leave this as is.
  if (post_spl < current_spl) {
  if (post_spl < current_spl) {
    LOG(ERROR) << "Current SPL: " << current_spl << " Target SPL: " << post_spl
    LOG(WARNING) << "Current SPL: " << current_spl << " Target SPL: " << post_spl
               << " this is considered a downgrade";
               << " this is considered a downgrade";
    if (metadata.spl_downgrade() || metadata.downgrade()) {
    if (metadata.spl_downgrade() || metadata.downgrade()) {
      LOG(WARNING)
      LOG(WARNING)
+1 −1
Original line number Original line Diff line number Diff line
@@ -169,7 +169,7 @@ static bool CheckWipePackage(Package* wipe_package, RecoveryUI* ui) {
    return false;
    return false;
  }
  }


  return CheckPackageMetadata(metadata, OtaType::BRICK, ui);
  return CheckPackageMetadata(metadata, OtaType::BRICK, ui, false);
}
}


bool WipeAbDevice(Device* device, size_t wipe_package_size) {
bool WipeAbDevice(Device* device, size_t wipe_package_size) {
+13 −0
Original line number Original line Diff line number Diff line
@@ -196,6 +196,19 @@ bool ask_to_continue_downgrade(Device* device) {
  }
  }
}
}


bool ask_to_continue_spl_downgrade(Device* device) {
  if (get_build_type() == "user") {
    return false;
  } else {
    device->GetUI()->SetProgressType(RecoveryUI::EMPTY);
    return yes_no(device,
      "WARNING: Security patch level downgrade detected. "
      "This may require formatting data. "
      "Device may brick if hardware rollback protection is enabled. ",
      "Install anyway?");
  }
}

static bool ask_to_wipe_data(Device* device) {
static bool ask_to_wipe_data(Device* device) {
  std::vector<std::string> headers{ "Format user data?", "This includes internal storage.", "THIS CANNOT BE UNDONE!" };
  std::vector<std::string> headers{ "Format user data?", "This includes internal storage.", "THIS CANNOT BE UNDONE!" };
  std::vector<std::string> items{ " Cancel", " Format data" };
  std::vector<std::string> items{ " Cancel", " Format data" };