1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/net/crl_set_fetcher.h" 6 7 #include "base/bind.h" 8 #include "base/debug/trace_event.h" 9 #include "base/files/file_util.h" 10 #include "base/numerics/safe_conversions.h" 11 #include "base/rand_util.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/time/time.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/common/chrome_constants.h" 16 #include "chrome/common/chrome_paths.h" 17 #include "components/component_updater/component_updater_service.h" 18 #include "content/public/browser/browser_thread.h" 19 #include "net/cert/crl_set.h" 20 #include "net/cert/crl_set_storage.h" 21 #include "net/ssl/ssl_config_service.h" 22 23 using component_updater::ComponentUpdateService; 24 using content::BrowserThread; 25 26 CRLSetFetcher::CRLSetFetcher() : cus_(NULL) {} 27 28 void CRLSetFetcher::SetCRLSetFilePath(const base::FilePath& path) { 29 crl_path_ = path.Append(chrome::kCRLSetFilename); 30 } 31 32 base::FilePath CRLSetFetcher::GetCRLSetFilePath() const { 33 return crl_path_; 34 } 35 36 void CRLSetFetcher::StartInitialLoad(ComponentUpdateService* cus, 37 const base::FilePath& path) { 38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 39 if (path.empty()) 40 return; 41 SetCRLSetFilePath(path); 42 cus_ = cus; 43 44 if (!BrowserThread::PostTask( 45 BrowserThread::FILE, FROM_HERE, 46 base::Bind(&CRLSetFetcher::DoInitialLoadFromDisk, this))) { 47 NOTREACHED(); 48 } 49 } 50 51 void CRLSetFetcher::DeleteFromDisk(const base::FilePath& path) { 52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 53 54 if (path.empty()) 55 return; 56 SetCRLSetFilePath(path); 57 if (!BrowserThread::PostTask( 58 BrowserThread::FILE, FROM_HERE, 59 base::Bind(&CRLSetFetcher::DoDeleteFromDisk, this))) { 60 NOTREACHED(); 61 } 62 } 63 64 void CRLSetFetcher::DoInitialLoadFromDisk() { 65 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 66 67 LoadFromDisk(GetCRLSetFilePath(), &crl_set_); 68 69 uint32 sequence_of_loaded_crl = 0; 70 if (crl_set_.get()) 71 sequence_of_loaded_crl = crl_set_->sequence(); 72 73 // Get updates, advertising the sequence number of the CRL set that we just 74 // loaded, if any. 75 if (!BrowserThread::PostTask( 76 BrowserThread::UI, FROM_HERE, 77 base::Bind( 78 &CRLSetFetcher::RegisterComponent, 79 this, 80 sequence_of_loaded_crl))) { 81 NOTREACHED(); 82 } 83 } 84 85 void CRLSetFetcher::LoadFromDisk(base::FilePath path, 86 scoped_refptr<net::CRLSet>* out_crl_set) { 87 TRACE_EVENT0("CRLSetFetcher", "LoadFromDisk"); 88 89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 90 91 std::string crl_set_bytes; 92 { 93 TRACE_EVENT0("CRLSetFetcher", "ReadFileToString"); 94 if (!base::ReadFileToString(path, &crl_set_bytes)) 95 return; 96 } 97 98 if (!net::CRLSetStorage::Parse(crl_set_bytes, out_crl_set)) { 99 LOG(WARNING) << "Failed to parse CRL set from " << path.MaybeAsASCII(); 100 return; 101 } 102 103 VLOG(1) << "Loaded " << crl_set_bytes.size() << " bytes of CRL set from disk"; 104 105 if (!BrowserThread::PostTask( 106 BrowserThread::IO, FROM_HERE, 107 base::Bind( 108 &CRLSetFetcher::SetCRLSetIfNewer, this, *out_crl_set))) { 109 NOTREACHED(); 110 } 111 } 112 113 void CRLSetFetcher::SetCRLSetIfNewer( 114 scoped_refptr<net::CRLSet> crl_set) { 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 116 117 scoped_refptr<net::CRLSet> old_crl_set(net::SSLConfigService::GetCRLSet()); 118 if (old_crl_set.get() && old_crl_set->sequence() > crl_set->sequence()) { 119 LOG(WARNING) << "Refusing to downgrade CRL set from #" 120 << old_crl_set->sequence() 121 << "to #" 122 << crl_set->sequence(); 123 } else { 124 net::SSLConfigService::SetCRLSet(crl_set); 125 VLOG(1) << "Installed CRL set #" << crl_set->sequence(); 126 } 127 } 128 129 // kPublicKeySHA256 is the SHA256 hash of the SubjectPublicKeyInfo of the key 130 // that's used to sign generated CRL sets. 131 static const uint8 kPublicKeySHA256[32] = { 132 0x75, 0xda, 0xf8, 0xcb, 0x77, 0x68, 0x40, 0x33, 133 0x65, 0x4c, 0x97, 0xe5, 0xc5, 0x1b, 0xcd, 0x81, 134 0x7b, 0x1e, 0xeb, 0x11, 0x2c, 0xe1, 0xa4, 0x33, 135 0x8c, 0xf5, 0x72, 0x5e, 0xed, 0xb8, 0x43, 0x97, 136 }; 137 138 void CRLSetFetcher::RegisterComponent(uint32 sequence_of_loaded_crl) { 139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 140 141 component_updater::CrxComponent component; 142 component.pk_hash.assign(kPublicKeySHA256, 143 kPublicKeySHA256 + sizeof(kPublicKeySHA256)); 144 component.installer = this; 145 component.name = "CRLSet"; 146 component.version = Version(base::UintToString(sequence_of_loaded_crl)); 147 component.allow_background_download = false; 148 if (!component.version.IsValid()) { 149 NOTREACHED(); 150 component.version = Version("0"); 151 } 152 153 if (cus_->RegisterComponent(component) != 154 ComponentUpdateService::kOk) { 155 NOTREACHED() << "RegisterComponent returned error"; 156 } 157 } 158 159 void CRLSetFetcher::DoDeleteFromDisk() { 160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 161 162 DeleteFile(GetCRLSetFilePath(), false /* not recursive */); 163 } 164 165 void CRLSetFetcher::OnUpdateError(int error) { 166 LOG(WARNING) << "CRLSetFetcher got error " << error 167 << " from component installer"; 168 } 169 170 bool CRLSetFetcher::Install(const base::DictionaryValue& manifest, 171 const base::FilePath& unpack_path) { 172 base::FilePath crl_set_file_path = 173 unpack_path.Append(FILE_PATH_LITERAL("crl-set")); 174 base::FilePath save_to = GetCRLSetFilePath(); 175 176 std::string crl_set_bytes; 177 if (!base::ReadFileToString(crl_set_file_path, &crl_set_bytes)) { 178 LOG(WARNING) << "Failed to find crl-set file inside CRX"; 179 return false; 180 } 181 182 bool is_delta; 183 if (!net::CRLSetStorage::GetIsDeltaUpdate(crl_set_bytes, &is_delta)) { 184 LOG(WARNING) << "GetIsDeltaUpdate failed on CRL set from update CRX"; 185 return false; 186 } 187 188 if (!is_delta) { 189 if (!net::CRLSetStorage::Parse(crl_set_bytes, &crl_set_)) { 190 LOG(WARNING) << "Failed to parse CRL set from update CRX"; 191 return false; 192 } 193 int size = base::checked_cast<int>(crl_set_bytes.size()); 194 if (base::WriteFile(save_to, crl_set_bytes.data(), size) != size) { 195 LOG(WARNING) << "Failed to save new CRL set to disk"; 196 // We don't return false here because we can still use this CRL set. When 197 // we restart we might revert to an older version, then we'll 198 // advertise the older version to Omaha and everything will still work. 199 } 200 } else { 201 scoped_refptr<net::CRLSet> new_crl_set; 202 if (!net::CRLSetStorage::ApplyDelta( 203 crl_set_.get(), crl_set_bytes, &new_crl_set)) { 204 LOG(WARNING) << "Failed to parse delta CRL set"; 205 return false; 206 } 207 VLOG(1) << "Applied CRL set delta #" << crl_set_->sequence() 208 << "->#" << new_crl_set->sequence(); 209 const std::string new_crl_set_bytes = 210 net::CRLSetStorage::Serialize(new_crl_set.get()); 211 int size = base::checked_cast<int>(new_crl_set_bytes.size()); 212 if (base::WriteFile(save_to, new_crl_set_bytes.data(), size) != size) { 213 LOG(WARNING) << "Failed to save new CRL set to disk"; 214 // We don't return false here because we can still use this CRL set. When 215 // we restart we might revert to an older version, then we'll 216 // advertise the older version to Omaha and everything will still work. 217 } 218 crl_set_ = new_crl_set; 219 } 220 221 if (!BrowserThread::PostTask( 222 BrowserThread::IO, FROM_HERE, 223 base::Bind( 224 &CRLSetFetcher::SetCRLSetIfNewer, this, crl_set_))) { 225 NOTREACHED(); 226 } 227 228 return true; 229 } 230 231 bool CRLSetFetcher::GetInstalledFile( 232 const std::string& file, base::FilePath* installed_file) { 233 return false; 234 } 235 236 CRLSetFetcher::~CRLSetFetcher() {} 237