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
23 using component_updater::ComponentUpdateService;
24 using content::BrowserThread;
25
CRLSetFetcher()26 CRLSetFetcher::CRLSetFetcher() : cus_(NULL) {}
27
SetCRLSetFilePath(const base::FilePath & path)28 void CRLSetFetcher::SetCRLSetFilePath(const base::FilePath& path) {
29 crl_path_ = path.Append(chrome::kCRLSetFilename);
30 }
31
GetCRLSetFilePath() const32 base::FilePath CRLSetFetcher::GetCRLSetFilePath() const {
33 return crl_path_;
34 }
35
StartInitialLoad(ComponentUpdateService * cus,const base::FilePath & path)36 void 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
DeleteFromDisk(const base::FilePath & path)51 void 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
DoInitialLoadFromDisk()64 void 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
LoadFromDisk(base::FilePath path,scoped_refptr<net::CRLSet> * out_crl_set)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::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
SetCRLSetIfNewer(scoped_refptr<net::CRLSet> crl_set)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
RegisterComponent(uint32 sequence_of_loaded_crl)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
DoDeleteFromDisk()159 void CRLSetFetcher::DoDeleteFromDisk() {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
161
162 DeleteFile(GetCRLSetFilePath(), false /* not recursive */);
163 }
164
OnUpdateError(int error)165 void CRLSetFetcher::OnUpdateError(int error) {
166 LOG(WARNING) << "CRLSetFetcher got error " << error
167 << " from component installer";
168 }
169
Install(const base::DictionaryValue & manifest,const base::FilePath & unpack_path)170 bool 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
GetInstalledFile(const std::string & file,base::FilePath * installed_file)231 bool CRLSetFetcher::GetInstalledFile(
232 const std::string& file, base::FilePath* installed_file) {
233 return false;
234 }
235
~CRLSetFetcher()236 CRLSetFetcher::~CRLSetFetcher() {}
237