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/file_util.h" 10 #include "base/numerics/safe_conversions.h" 11 #include "base/path_service.h" 12 #include "base/rand_util.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/time/time.h" 15 #include "chrome/browser/component_updater/component_updater_service.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/common/chrome_constants.h" 18 #include "chrome/common/chrome_paths.h" 19 #include "content/public/browser/browser_thread.h" 20 #include "net/cert/crl_set.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 bool CRLSetFetcher::GetCRLSetFilePath(base::FilePath* path) const { 29 bool ok = PathService::Get(chrome::DIR_USER_DATA, path); 30 if (!ok) { 31 NOTREACHED(); 32 return false; 33 } 34 *path = path->Append(chrome::kCRLSetFilename); 35 return true; 36 } 37 38 void CRLSetFetcher::StartInitialLoad(ComponentUpdateService* cus) { 39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 40 41 cus_ = cus; 42 43 if (!BrowserThread::PostTask( 44 BrowserThread::FILE, FROM_HERE, 45 base::Bind(&CRLSetFetcher::DoInitialLoadFromDisk, this))) { 46 NOTREACHED(); 47 } 48 } 49 50 void CRLSetFetcher::DeleteFromDisk() { 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 52 53 if (!BrowserThread::PostTask( 54 BrowserThread::FILE, FROM_HERE, 55 base::Bind(&CRLSetFetcher::DoDeleteFromDisk, this))) { 56 NOTREACHED(); 57 } 58 } 59 60 void CRLSetFetcher::DoInitialLoadFromDisk() { 61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 62 63 base::FilePath crl_set_file_path; 64 if (!GetCRLSetFilePath(&crl_set_file_path)) 65 return; 66 67 LoadFromDisk(crl_set_file_path, &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::CRLSet::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 base::FilePath crl_set_file_path; 163 if (!GetCRLSetFilePath(&crl_set_file_path)) 164 return; 165 166 DeleteFile(crl_set_file_path, false /* not recursive */); 167 } 168 169 void CRLSetFetcher::OnUpdateError(int error) { 170 LOG(WARNING) << "CRLSetFetcher got error " << error 171 << " from component installer"; 172 } 173 174 bool CRLSetFetcher::Install(const base::DictionaryValue& manifest, 175 const base::FilePath& unpack_path) { 176 base::FilePath crl_set_file_path = 177 unpack_path.Append(FILE_PATH_LITERAL("crl-set")); 178 base::FilePath save_to; 179 if (!GetCRLSetFilePath(&save_to)) 180 return true; 181 182 std::string crl_set_bytes; 183 if (!base::ReadFileToString(crl_set_file_path, &crl_set_bytes)) { 184 LOG(WARNING) << "Failed to find crl-set file inside CRX"; 185 return false; 186 } 187 188 bool is_delta; 189 if (!net::CRLSet::GetIsDeltaUpdate(crl_set_bytes, &is_delta)) { 190 LOG(WARNING) << "GetIsDeltaUpdate failed on CRL set from update CRX"; 191 return false; 192 } 193 194 if (!is_delta) { 195 if (!net::CRLSet::Parse(crl_set_bytes, &crl_set_)) { 196 LOG(WARNING) << "Failed to parse CRL set from update CRX"; 197 return false; 198 } 199 int size = base::checked_cast<int>(crl_set_bytes.size()); 200 if (base::WriteFile(save_to, crl_set_bytes.data(), size) != size) { 201 LOG(WARNING) << "Failed to save new CRL set to disk"; 202 // We don't return false here because we can still use this CRL set. When 203 // we restart we might revert to an older version, then we'll 204 // advertise the older version to Omaha and everything will still work. 205 } 206 } else { 207 scoped_refptr<net::CRLSet> new_crl_set; 208 if (!crl_set_->ApplyDelta(crl_set_bytes, &new_crl_set)) { 209 LOG(WARNING) << "Failed to parse delta CRL set"; 210 return false; 211 } 212 VLOG(1) << "Applied CRL set delta #" << crl_set_->sequence() 213 << "->#" << new_crl_set->sequence(); 214 const std::string new_crl_set_bytes = new_crl_set->Serialize(); 215 int size = base::checked_cast<int>(new_crl_set_bytes.size()); 216 if (base::WriteFile(save_to, new_crl_set_bytes.data(), size) != size) { 217 LOG(WARNING) << "Failed to save new CRL set to disk"; 218 // We don't return false here because we can still use this CRL set. When 219 // we restart we might revert to an older version, then we'll 220 // advertise the older version to Omaha and everything will still work. 221 } 222 crl_set_ = new_crl_set; 223 } 224 225 if (!BrowserThread::PostTask( 226 BrowserThread::IO, FROM_HERE, 227 base::Bind( 228 &CRLSetFetcher::SetCRLSetIfNewer, this, crl_set_))) { 229 NOTREACHED(); 230 } 231 232 return true; 233 } 234 235 bool CRLSetFetcher::GetInstalledFile( 236 const std::string& file, base::FilePath* installed_file) { 237 return false; 238 } 239 240 CRLSetFetcher::~CRLSetFetcher() {} 241