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
23using component_updater::ComponentUpdateService;
24using content::BrowserThread;
25
26CRLSetFetcher::CRLSetFetcher() : cus_(NULL) {}
27
28void CRLSetFetcher::SetCRLSetFilePath(const base::FilePath& path) {
29  crl_path_ = path.Append(chrome::kCRLSetFilename);
30}
31
32base::FilePath CRLSetFetcher::GetCRLSetFilePath() const {
33  return crl_path_;
34}
35
36void 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
51void 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
64void 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
85void 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
113void 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.
131static 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
138void 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
159void CRLSetFetcher::DoDeleteFromDisk() {
160  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
161
162  DeleteFile(GetCRLSetFilePath(), false /* not recursive */);
163}
164
165void CRLSetFetcher::OnUpdateError(int error) {
166  LOG(WARNING) << "CRLSetFetcher got error " << error
167               << " from component installer";
168}
169
170bool 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
231bool CRLSetFetcher::GetInstalledFile(
232    const std::string& file, base::FilePath* installed_file) {
233  return false;
234}
235
236CRLSetFetcher::~CRLSetFetcher() {}
237