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