1// 2// Copyright (C) 2011 The Android Open Source Project 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15// 16 17#include "update_engine/omaha_response_handler_action.h" 18 19#include <string> 20 21#include <base/logging.h> 22#include <base/strings/string_number_conversions.h> 23#include <base/strings/string_util.h> 24#include <policy/device_policy.h> 25 26#include "update_engine/common/constants.h" 27#include "update_engine/common/hardware_interface.h" 28#include "update_engine/common/prefs_interface.h" 29#include "update_engine/common/utils.h" 30#include "update_engine/connection_manager_interface.h" 31#include "update_engine/omaha_request_params.h" 32#include "update_engine/payload_consumer/delta_performer.h" 33#include "update_engine/payload_state_interface.h" 34 35using std::string; 36 37namespace chromeos_update_engine { 38 39OmahaResponseHandlerAction::OmahaResponseHandlerAction( 40 SystemState* system_state) 41 : OmahaResponseHandlerAction(system_state, 42 constants::kOmahaResponseDeadlineFile) {} 43 44OmahaResponseHandlerAction::OmahaResponseHandlerAction( 45 SystemState* system_state, const string& deadline_file) 46 : system_state_(system_state), 47 got_no_update_response_(false), 48 key_path_(constants::kUpdatePayloadPublicKeyPath), 49 deadline_file_(deadline_file) {} 50 51void OmahaResponseHandlerAction::PerformAction() { 52 CHECK(HasInputObject()); 53 ScopedActionCompleter completer(processor_, this); 54 const OmahaResponse& response = GetInputObject(); 55 if (!response.update_exists) { 56 got_no_update_response_ = true; 57 LOG(INFO) << "There are no updates. Aborting."; 58 return; 59 } 60 61 // All decisions as to which URL should be used have already been done. So, 62 // make the current URL as the download URL. 63 string current_url = system_state_->payload_state()->GetCurrentUrl(); 64 if (current_url.empty()) { 65 // This shouldn't happen as we should always supply the HTTPS backup URL. 66 // Handling this anyway, just in case. 67 LOG(ERROR) << "There are no suitable URLs in the response to use."; 68 completer.set_code(ErrorCode::kOmahaResponseInvalid); 69 return; 70 } 71 72 install_plan_.download_url = current_url; 73 install_plan_.version = response.version; 74 75 OmahaRequestParams* const params = system_state_->request_params(); 76 PayloadStateInterface* const payload_state = system_state_->payload_state(); 77 78 // If we're using p2p to download and there is a local peer, use it. 79 if (payload_state->GetUsingP2PForDownloading() && 80 !payload_state->GetP2PUrl().empty()) { 81 LOG(INFO) << "Replacing URL " << install_plan_.download_url 82 << " with local URL " << payload_state->GetP2PUrl() 83 << " since p2p is enabled."; 84 install_plan_.download_url = payload_state->GetP2PUrl(); 85 payload_state->SetUsingP2PForDownloading(true); 86 } 87 88 // Fill up the other properties based on the response. 89 string update_check_response_hash; 90 for (const auto& package : response.packages) { 91 brillo::Blob raw_hash; 92 if (!base::HexStringToBytes(package.hash, &raw_hash)) { 93 LOG(ERROR) << "Failed to convert payload hash from hex string to bytes: " 94 << package.hash; 95 completer.set_code(ErrorCode::kOmahaResponseInvalid); 96 return; 97 } 98 install_plan_.payloads.push_back( 99 {.size = package.size, 100 .metadata_size = package.metadata_size, 101 .metadata_signature = package.metadata_signature, 102 .hash = raw_hash, 103 .type = package.is_delta ? InstallPayloadType::kDelta 104 : InstallPayloadType::kFull}); 105 update_check_response_hash += package.hash + ":"; 106 } 107 install_plan_.public_key_rsa = response.public_key_rsa; 108 install_plan_.hash_checks_mandatory = AreHashChecksMandatory(response); 109 install_plan_.is_resume = DeltaPerformer::CanResumeUpdate( 110 system_state_->prefs(), update_check_response_hash); 111 if (install_plan_.is_resume) { 112 payload_state->UpdateResumed(); 113 } else { 114 payload_state->UpdateRestarted(); 115 LOG_IF(WARNING, 116 !DeltaPerformer::ResetUpdateProgress(system_state_->prefs(), false)) 117 << "Unable to reset the update progress."; 118 LOG_IF(WARNING, 119 !system_state_->prefs()->SetString(kPrefsUpdateCheckResponseHash, 120 update_check_response_hash)) 121 << "Unable to save the update check response hash."; 122 } 123 124 install_plan_.source_slot = system_state_->boot_control()->GetCurrentSlot(); 125 install_plan_.target_slot = install_plan_.source_slot == 0 ? 1 : 0; 126 127 // The Omaha response doesn't include the channel name for this image, so we 128 // use the download_channel we used during the request to tag the target slot. 129 // This will be used in the next boot to know the channel the image was 130 // downloaded from. 131 string current_channel_key = 132 kPrefsChannelOnSlotPrefix + std::to_string(install_plan_.target_slot); 133 system_state_->prefs()->SetString(current_channel_key, 134 params->download_channel()); 135 136 if (params->to_more_stable_channel() && params->is_powerwash_allowed()) 137 install_plan_.powerwash_required = true; 138 139 TEST_AND_RETURN(HasOutputPipe()); 140 if (HasOutputPipe()) 141 SetOutputObject(install_plan_); 142 LOG(INFO) << "Using this install plan:"; 143 install_plan_.Dump(); 144 145 // Send the deadline data (if any) to Chrome through a file. This is a pretty 146 // hacky solution but should be OK for now. 147 // 148 // TODO(petkov): Re-architect this to avoid communication through a 149 // file. Ideally, we would include this information in D-Bus's GetStatus 150 // method and UpdateStatus signal. A potential issue is that update_engine may 151 // be unresponsive during an update download. 152 if (!deadline_file_.empty()) { 153 utils::WriteFile(deadline_file_.c_str(), 154 response.deadline.data(), 155 response.deadline.size()); 156 chmod(deadline_file_.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 157 } 158 159 completer.set_code(ErrorCode::kSuccess); 160} 161 162bool OmahaResponseHandlerAction::AreHashChecksMandatory( 163 const OmahaResponse& response) { 164 // We sometimes need to waive the hash checks in order to download from 165 // sources that don't provide hashes, such as dev server. 166 // At this point UpdateAttempter::IsAnyUpdateSourceAllowed() has already been 167 // checked, so an unofficial update URL won't get this far unless it's OK to 168 // use without a hash. Additionally, we want to always waive hash checks on 169 // unofficial builds (i.e. dev/test images). 170 // The end result is this: 171 // * Base image: 172 // - Official URLs require a hash. 173 // - Unofficial URLs only get this far if the IsAnyUpdateSourceAllowed() 174 // devmode/debugd checks pass, in which case the hash is waived. 175 // * Dev/test image: 176 // - Any URL is allowed through with no hash checking. 177 if (!system_state_->request_params()->IsUpdateUrlOfficial() || 178 !system_state_->hardware()->IsOfficialBuild()) { 179 // Still do a hash check if a public key is included. 180 if (!response.public_key_rsa.empty()) { 181 // The autoupdate_CatchBadSignatures test checks for this string 182 // in log-files. Keep in sync. 183 LOG(INFO) << "Mandating payload hash checks since Omaha Response " 184 << "for unofficial build includes public RSA key."; 185 return true; 186 } else { 187 LOG(INFO) << "Waiving payload hash checks for unofficial update URL."; 188 return false; 189 } 190 } 191 192 // If we're using p2p, |install_plan_.download_url| may contain a 193 // HTTP URL even if |response.payload_urls| contain only HTTPS URLs. 194 if (!base::StartsWith(install_plan_.download_url, "https://", 195 base::CompareCase::INSENSITIVE_ASCII)) { 196 LOG(INFO) << "Mandating hash checks since download_url is not HTTPS."; 197 return true; 198 } 199 200 // TODO(jaysri): VALIDATION: For official builds, we currently waive hash 201 // checks for HTTPS until we have rolled out at least once and are confident 202 // nothing breaks. chromium-os:37082 tracks turning this on for HTTPS 203 // eventually. 204 205 // Even if there's a single non-HTTPS URL, make the hash checks as 206 // mandatory because we could be downloading the payload from any URL later 207 // on. It's really hard to do book-keeping based on each byte being 208 // downloaded to see whether we only used HTTPS throughout. 209 for (const auto& package : response.packages) { 210 for (const string& payload_url : package.payload_urls) { 211 if (!base::StartsWith( 212 payload_url, "https://", base::CompareCase::INSENSITIVE_ASCII)) { 213 LOG(INFO) << "Mandating payload hash checks since Omaha response " 214 << "contains non-HTTPS URL(s)"; 215 return true; 216 } 217 } 218 } 219 220 LOG(INFO) << "Waiving payload hash checks since Omaha response " 221 << "only has HTTPS URL(s)"; 222 return false; 223} 224 225} // namespace chromeos_update_engine 226