| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/net/crl_set_fetcher.h" |
| |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "base/path_service.h" |
| #include "base/rand_util.h" |
| #include "base/safe_numerics.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/component_updater/component_updater_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "net/cert/crl_set.h" |
| #include "net/ssl/ssl_config_service.h" |
| |
| using content::BrowserThread; |
| |
| CRLSetFetcher::CRLSetFetcher() : cus_(NULL) {} |
| |
| bool CRLSetFetcher::GetCRLSetFilePath(base::FilePath* path) const { |
| bool ok = PathService::Get(chrome::DIR_USER_DATA, path); |
| if (!ok) { |
| NOTREACHED(); |
| return false; |
| } |
| *path = path->Append(chrome::kCRLSetFilename); |
| return true; |
| } |
| |
| void CRLSetFetcher::StartInitialLoad(ComponentUpdateService* cus) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| cus_ = cus; |
| |
| if (!BrowserThread::PostTask( |
| BrowserThread::FILE, FROM_HERE, |
| base::Bind(&CRLSetFetcher::DoInitialLoadFromDisk, this))) { |
| NOTREACHED(); |
| } |
| } |
| |
| void CRLSetFetcher::DoInitialLoadFromDisk() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| |
| base::FilePath crl_set_file_path; |
| if (!GetCRLSetFilePath(&crl_set_file_path)) |
| return; |
| |
| LoadFromDisk(crl_set_file_path, &crl_set_); |
| |
| uint32 sequence_of_loaded_crl = 0; |
| if (crl_set_.get()) |
| sequence_of_loaded_crl = crl_set_->sequence(); |
| |
| // Get updates, advertising the sequence number of the CRL set that we just |
| // loaded, if any. |
| if (!BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::Bind( |
| &CRLSetFetcher::RegisterComponent, |
| this, |
| sequence_of_loaded_crl))) { |
| NOTREACHED(); |
| } |
| } |
| |
| void CRLSetFetcher::LoadFromDisk(base::FilePath path, |
| scoped_refptr<net::CRLSet>* out_crl_set) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| |
| std::string crl_set_bytes; |
| if (!base::ReadFileToString(path, &crl_set_bytes)) |
| return; |
| |
| if (!net::CRLSet::Parse(crl_set_bytes, out_crl_set)) { |
| LOG(WARNING) << "Failed to parse CRL set from " << path.MaybeAsASCII(); |
| return; |
| } |
| |
| VLOG(1) << "Loaded " << crl_set_bytes.size() << " bytes of CRL set from disk"; |
| |
| if (!BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind( |
| &CRLSetFetcher::SetCRLSetIfNewer, this, *out_crl_set))) { |
| NOTREACHED(); |
| } |
| } |
| |
| void CRLSetFetcher::SetCRLSetIfNewer( |
| scoped_refptr<net::CRLSet> crl_set) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| scoped_refptr<net::CRLSet> old_crl_set(net::SSLConfigService::GetCRLSet()); |
| if (old_crl_set.get() && old_crl_set->sequence() > crl_set->sequence()) { |
| LOG(WARNING) << "Refusing to downgrade CRL set from #" |
| << old_crl_set->sequence() |
| << "to #" |
| << crl_set->sequence(); |
| } else { |
| net::SSLConfigService::SetCRLSet(crl_set); |
| VLOG(1) << "Installed CRL set #" << crl_set->sequence(); |
| } |
| } |
| |
| // kPublicKeySHA256 is the SHA256 hash of the SubjectPublicKeyInfo of the key |
| // that's used to sign generated CRL sets. |
| static const uint8 kPublicKeySHA256[32] = { |
| 0x75, 0xda, 0xf8, 0xcb, 0x77, 0x68, 0x40, 0x33, |
| 0x65, 0x4c, 0x97, 0xe5, 0xc5, 0x1b, 0xcd, 0x81, |
| 0x7b, 0x1e, 0xeb, 0x11, 0x2c, 0xe1, 0xa4, 0x33, |
| 0x8c, 0xf5, 0x72, 0x5e, 0xed, 0xb8, 0x43, 0x97, |
| }; |
| |
| void CRLSetFetcher::RegisterComponent(uint32 sequence_of_loaded_crl) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| CrxComponent component; |
| component.pk_hash.assign(kPublicKeySHA256, |
| kPublicKeySHA256 + sizeof(kPublicKeySHA256)); |
| component.installer = this; |
| component.name = "CRLSet"; |
| component.version = Version(base::UintToString(sequence_of_loaded_crl)); |
| if (!component.version.IsValid()) { |
| NOTREACHED(); |
| component.version = Version("0"); |
| } |
| |
| if (cus_->RegisterComponent(component) != |
| ComponentUpdateService::kOk) { |
| NOTREACHED() << "RegisterComponent returned error"; |
| } |
| } |
| |
| void CRLSetFetcher::OnUpdateError(int error) { |
| LOG(WARNING) << "CRLSetFetcher got error " << error |
| << " from component installer"; |
| } |
| |
| bool CRLSetFetcher::Install(const base::DictionaryValue& manifest, |
| const base::FilePath& unpack_path) { |
| base::FilePath crl_set_file_path = |
| unpack_path.Append(FILE_PATH_LITERAL("crl-set")); |
| base::FilePath save_to; |
| if (!GetCRLSetFilePath(&save_to)) |
| return true; |
| |
| std::string crl_set_bytes; |
| if (!base::ReadFileToString(crl_set_file_path, &crl_set_bytes)) { |
| LOG(WARNING) << "Failed to find crl-set file inside CRX"; |
| return false; |
| } |
| |
| bool is_delta; |
| if (!net::CRLSet::GetIsDeltaUpdate(crl_set_bytes, &is_delta)) { |
| LOG(WARNING) << "GetIsDeltaUpdate failed on CRL set from update CRX"; |
| return false; |
| } |
| |
| if (!is_delta) { |
| if (!net::CRLSet::Parse(crl_set_bytes, &crl_set_)) { |
| LOG(WARNING) << "Failed to parse CRL set from update CRX"; |
| return false; |
| } |
| int size = base::checked_numeric_cast<int>(crl_set_bytes.size()); |
| if (file_util::WriteFile(save_to, crl_set_bytes.data(), size) != size) { |
| LOG(WARNING) << "Failed to save new CRL set to disk"; |
| // We don't return false here because we can still use this CRL set. When |
| // we restart we might revert to an older version, then we'll |
| // advertise the older version to Omaha and everything will still work. |
| } |
| } else { |
| scoped_refptr<net::CRLSet> new_crl_set; |
| if (!crl_set_->ApplyDelta(crl_set_bytes, &new_crl_set)) { |
| LOG(WARNING) << "Failed to parse delta CRL set"; |
| return false; |
| } |
| VLOG(1) << "Applied CRL set delta #" << crl_set_->sequence() |
| << "->#" << new_crl_set->sequence(); |
| const std::string new_crl_set_bytes = new_crl_set->Serialize(); |
| int size = base::checked_numeric_cast<int>(new_crl_set_bytes.size()); |
| if (file_util::WriteFile(save_to, new_crl_set_bytes.data(), size) != size) { |
| LOG(WARNING) << "Failed to save new CRL set to disk"; |
| // We don't return false here because we can still use this CRL set. When |
| // we restart we might revert to an older version, then we'll |
| // advertise the older version to Omaha and everything will still work. |
| } |
| crl_set_ = new_crl_set; |
| } |
| |
| if (!BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind( |
| &CRLSetFetcher::SetCRLSetIfNewer, this, crl_set_))) { |
| NOTREACHED(); |
| } |
| |
| return true; |
| } |
| |
| bool CRLSetFetcher::GetInstalledFile( |
| const std::string& file, base::FilePath* installed_file) { |
| return false; |
| } |
| |
| CRLSetFetcher::~CRLSetFetcher() {} |