• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/file_util.h"
10 #include "base/numerics/safe_conversions.h"
11 #include "base/path_service.h"
12 #include "base/rand_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/component_updater/component_updater_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/common/chrome_constants.h"
18 #include "chrome/common/chrome_paths.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "net/cert/crl_set.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 
GetCRLSetFilePath(base::FilePath * path) const28 bool CRLSetFetcher::GetCRLSetFilePath(base::FilePath* path) const {
29   bool ok = PathService::Get(chrome::DIR_USER_DATA, path);
30   if (!ok) {
31     NOTREACHED();
32     return false;
33   }
34   *path = path->Append(chrome::kCRLSetFilename);
35   return true;
36 }
37 
StartInitialLoad(ComponentUpdateService * cus)38 void CRLSetFetcher::StartInitialLoad(ComponentUpdateService* cus) {
39   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
40 
41   cus_ = cus;
42 
43   if (!BrowserThread::PostTask(
44           BrowserThread::FILE, FROM_HERE,
45           base::Bind(&CRLSetFetcher::DoInitialLoadFromDisk, this))) {
46     NOTREACHED();
47   }
48 }
49 
DeleteFromDisk()50 void CRLSetFetcher::DeleteFromDisk() {
51   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 
53   if (!BrowserThread::PostTask(
54           BrowserThread::FILE, FROM_HERE,
55           base::Bind(&CRLSetFetcher::DoDeleteFromDisk, this))) {
56     NOTREACHED();
57   }
58 }
59 
DoInitialLoadFromDisk()60 void CRLSetFetcher::DoInitialLoadFromDisk() {
61   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
62 
63   base::FilePath crl_set_file_path;
64   if (!GetCRLSetFilePath(&crl_set_file_path))
65     return;
66 
67   LoadFromDisk(crl_set_file_path, &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::CRLSet::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   base::FilePath crl_set_file_path;
163   if (!GetCRLSetFilePath(&crl_set_file_path))
164     return;
165 
166   DeleteFile(crl_set_file_path, false /* not recursive */);
167 }
168 
OnUpdateError(int error)169 void CRLSetFetcher::OnUpdateError(int error) {
170   LOG(WARNING) << "CRLSetFetcher got error " << error
171                << " from component installer";
172 }
173 
Install(const base::DictionaryValue & manifest,const base::FilePath & unpack_path)174 bool CRLSetFetcher::Install(const base::DictionaryValue& manifest,
175                             const base::FilePath& unpack_path) {
176   base::FilePath crl_set_file_path =
177       unpack_path.Append(FILE_PATH_LITERAL("crl-set"));
178   base::FilePath save_to;
179   if (!GetCRLSetFilePath(&save_to))
180     return true;
181 
182   std::string crl_set_bytes;
183   if (!base::ReadFileToString(crl_set_file_path, &crl_set_bytes)) {
184     LOG(WARNING) << "Failed to find crl-set file inside CRX";
185     return false;
186   }
187 
188   bool is_delta;
189   if (!net::CRLSet::GetIsDeltaUpdate(crl_set_bytes, &is_delta)) {
190     LOG(WARNING) << "GetIsDeltaUpdate failed on CRL set from update CRX";
191     return false;
192   }
193 
194   if (!is_delta) {
195     if (!net::CRLSet::Parse(crl_set_bytes, &crl_set_)) {
196       LOG(WARNING) << "Failed to parse CRL set from update CRX";
197       return false;
198     }
199     int size = base::checked_cast<int>(crl_set_bytes.size());
200     if (base::WriteFile(save_to, crl_set_bytes.data(), size) != size) {
201       LOG(WARNING) << "Failed to save new CRL set to disk";
202       // We don't return false here because we can still use this CRL set. When
203       // we restart we might revert to an older version, then we'll
204       // advertise the older version to Omaha and everything will still work.
205     }
206   } else {
207     scoped_refptr<net::CRLSet> new_crl_set;
208     if (!crl_set_->ApplyDelta(crl_set_bytes, &new_crl_set)) {
209       LOG(WARNING) << "Failed to parse delta CRL set";
210       return false;
211     }
212     VLOG(1) << "Applied CRL set delta #" << crl_set_->sequence()
213             << "->#" << new_crl_set->sequence();
214     const std::string new_crl_set_bytes = new_crl_set->Serialize();
215     int size = base::checked_cast<int>(new_crl_set_bytes.size());
216     if (base::WriteFile(save_to, new_crl_set_bytes.data(), size) != size) {
217       LOG(WARNING) << "Failed to save new CRL set to disk";
218       // We don't return false here because we can still use this CRL set. When
219       // we restart we might revert to an older version, then we'll
220       // advertise the older version to Omaha and everything will still work.
221     }
222     crl_set_ = new_crl_set;
223   }
224 
225   if (!BrowserThread::PostTask(
226           BrowserThread::IO, FROM_HERE,
227           base::Bind(
228               &CRLSetFetcher::SetCRLSetIfNewer, this, crl_set_))) {
229     NOTREACHED();
230   }
231 
232   return true;
233 }
234 
GetInstalledFile(const std::string & file,base::FilePath * installed_file)235 bool CRLSetFetcher::GetInstalledFile(
236     const std::string& file, base::FilePath* installed_file) {
237   return false;
238 }
239 
~CRLSetFetcher()240 CRLSetFetcher::~CRLSetFetcher() {}
241