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