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
22 using content::BrowserThread;
23
CRLSetFetcher()24 CRLSetFetcher::CRLSetFetcher() : cus_(NULL) {}
25
GetCRLSetFilePath(base::FilePath * path) const26 bool 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
StartInitialLoad(ComponentUpdateService * cus)36 void 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
DoInitialLoadFromDisk()48 void 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
LoadFromDisk(base::FilePath path,scoped_refptr<net::CRLSet> * out_crl_set)73 void 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
SetCRLSetIfNewer(scoped_refptr<net::CRLSet> crl_set)96 void 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.
114 static 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
RegisterComponent(uint32 sequence_of_loaded_crl)121 void 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
OnUpdateError(int error)142 void CRLSetFetcher::OnUpdateError(int error) {
143 LOG(WARNING) << "CRLSetFetcher got error " << error
144 << " from component installer";
145 }
146
Install(const base::DictionaryValue & manifest,const base::FilePath & unpack_path)147 bool 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
GetInstalledFile(const std::string & file,base::FilePath * installed_file)208 bool CRLSetFetcher::GetInstalledFile(
209 const std::string& file, base::FilePath* installed_file) {
210 return false;
211 }
212
~CRLSetFetcher()213 CRLSetFetcher::~CRLSetFetcher() {}
214