// 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/debug/trace_event.h"
#include "base/file_util.h"
#include "base/numerics/safe_conversions.h"
#include "base/path_service.h"
#include "base/rand_util.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 component_updater::ComponentUpdateService;
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::DeleteFromDisk() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
base::Bind(&CRLSetFetcher::DoDeleteFromDisk, 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) {
TRACE_EVENT0("CRLSetFetcher", "LoadFromDisk");
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
std::string crl_set_bytes;
{
TRACE_EVENT0("CRLSetFetcher", "ReadFileToString");
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));
component_updater::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));
component.allow_background_download = false;
if (!component.version.IsValid()) {
NOTREACHED();
component.version = Version("0");
}
if (cus_->RegisterComponent(component) !=
ComponentUpdateService::kOk) {
NOTREACHED() << "RegisterComponent returned error";
}
}
void CRLSetFetcher::DoDeleteFromDisk() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::FilePath crl_set_file_path;
if (!GetCRLSetFilePath(&crl_set_file_path))
return;
DeleteFile(crl_set_file_path, false /* not recursive */);
}
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_cast<int>(crl_set_bytes.size());
if (base::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_cast<int>(new_crl_set_bytes.size());
if (base::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() {}