OLD | NEW |
---|---|
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chromeos/cert_loader.h" | 5 #include "chromeos/cert_loader.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <utility> | 8 #include <utility> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
11 #include "base/location.h" | 11 #include "base/location.h" |
12 #include "base/memory/ptr_util.h" | 12 #include "base/memory/ptr_util.h" |
13 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
14 #include "base/task_scheduler/post_task.h" | 14 #include "base/task_scheduler/post_task.h" |
15 #include "crypto/nss_util.h" | 15 #include "crypto/nss_util.h" |
16 #include "crypto/scoped_nss_types.h" | 16 #include "crypto/scoped_nss_types.h" |
17 #include "net/cert/cert_database.h" | |
17 #include "net/cert/nss_cert_database.h" | 18 #include "net/cert/nss_cert_database.h" |
18 #include "net/cert/nss_cert_database_chromeos.h" | 19 #include "net/cert/nss_cert_database_chromeos.h" |
19 #include "net/cert/x509_certificate.h" | 20 #include "net/cert/x509_certificate.h" |
20 | 21 |
21 namespace chromeos { | 22 namespace chromeos { |
22 | 23 |
24 // Caches certificates from a NSSCertDatabase. Handles reloading of certificates | |
25 // on update notifications and provides status flags (loading / loaded). | |
26 class CertLoader::CertCache : public net::CertDatabase::Observer { | |
27 public: | |
28 explicit CertCache(base::RepeatingClosure certificates_updated_callback) | |
29 : certificates_updated_callback_(certificates_updated_callback), | |
30 cert_list_(base::MakeUnique<net::CertificateList>()), | |
31 weak_factory_(this) {} | |
32 | |
33 ~CertCache() override { | |
34 net::CertDatabase::GetInstance()->RemoveObserver(this); | |
35 } | |
36 | |
37 void StartWithNSSDB(net::NSSCertDatabase* database) { | |
38 CHECK(!database_); | |
39 database_ = database; | |
40 | |
41 // Start observing cert database for changes. | |
42 // Observing net::CertDatabase is preferred over observing |database_| | |
43 // directly, as |database_| observers receive only events generated directly | |
44 // by |database_|, so they may miss a few relevant ones. | |
45 // TODO(tbarzic): Once singleton NSSCertDatabase is removed, investigate if | |
46 // it would be OK to observe |database_| directly; or change NSSCertDatabase | |
47 // to send notification on all relevant changes. | |
48 net::CertDatabase::GetInstance()->AddObserver(this); | |
49 | |
50 LoadCertificates(); | |
51 } | |
52 | |
53 net::NSSCertDatabase* GetNSSDB() { return database_; } | |
stevenjb
2017/05/09 17:04:02
nit: database()
(and maybe rename the member/acces
pmarko
2017/05/10 11:48:01
Done.
| |
54 | |
55 // Trigger a certificate load. If a certificate loading task is already in | |
56 // progress, will start a reload once the current task is finished. | |
57 void LoadCertificates() { | |
58 CHECK(thread_checker_.CalledOnValidThread()); | |
59 VLOG(1) << "LoadCertificates: " << certificates_update_running_; | |
60 | |
61 if (certificates_update_running_) { | |
62 certificates_update_required_ = true; | |
63 return; | |
64 } | |
65 | |
66 certificates_update_running_ = true; | |
67 certificates_update_required_ = false; | |
68 | |
69 if (database_) { | |
70 has_system_certificates_ = static_cast<bool>(database_->GetSystemSlot()); | |
71 database_->ListCerts(base::Bind(&CertCache::UpdateCertificates, | |
72 weak_factory_.GetWeakPtr())); | |
73 } | |
74 } | |
75 | |
76 // Called if a certificate load task is finished. | |
77 void UpdateCertificates(std::unique_ptr<net::CertificateList> cert_list) { | |
78 CHECK(thread_checker_.CalledOnValidThread()); | |
79 DCHECK(certificates_update_running_); | |
80 VLOG(1) << "UpdateCertificates: " << cert_list->size(); | |
81 | |
82 // Ignore any existing certificates. | |
83 cert_list_ = std::move(cert_list); | |
84 | |
85 certificates_loaded_ = true; | |
86 certificates_updated_callback_.Run(); | |
87 | |
88 certificates_update_running_ = false; | |
89 if (certificates_update_required_) | |
90 LoadCertificates(); | |
91 } | |
92 | |
93 // net::CertDatabase::Observer | |
94 void OnCertDBChanged() override { | |
95 VLOG(1) << "OnCertDBChanged"; | |
96 LoadCertificates(); | |
97 } | |
98 | |
99 const net::CertificateList& cert_list() const { return *cert_list_; } | |
100 | |
101 bool CertificatesLoading() const { | |
102 return database_ && !certificates_loaded_; | |
103 } | |
104 | |
105 bool certificates_loaded() const { return certificates_loaded_; } | |
106 bool has_system_certificates() const { return has_system_certificates_; } | |
107 | |
108 private: | |
109 // To be called when certificates have been updated. | |
110 base::RepeatingClosure certificates_updated_callback_; | |
111 | |
112 bool has_system_certificates_ = false; | |
113 | |
114 // Flags describing current CertLoader state. | |
115 bool certificates_loaded_ = false; | |
116 bool certificates_update_required_ = false; | |
117 bool certificates_update_running_ = false; | |
118 | |
119 // The user-specific NSS certificate database from which the certificates | |
120 // should be loaded. | |
121 net::NSSCertDatabase* database_ = nullptr; | |
122 | |
123 // Cached Certificates loaded from the database. | |
124 std::unique_ptr<net::CertificateList> cert_list_; | |
125 | |
126 base::ThreadChecker thread_checker_; | |
127 | |
128 base::WeakPtrFactory<CertCache> weak_factory_; | |
129 }; | |
130 | |
23 namespace { | 131 namespace { |
24 | 132 |
25 // Checks if |certificate| is on the given |slot|. | 133 // Checks if |certificate| is on the given |slot|. |
26 bool IsCertificateOnSlot(const net::X509Certificate* certificate, | 134 bool IsCertificateOnSlot(const net::X509Certificate* certificate, |
27 PK11SlotInfo* slot) { | 135 PK11SlotInfo* slot) { |
28 crypto::ScopedPK11SlotList slots_for_cert( | 136 crypto::ScopedPK11SlotList slots_for_cert( |
29 PK11_GetAllSlotsForCert(certificate->os_cert_handle(), nullptr)); | 137 PK11_GetAllSlotsForCert(certificate->os_cert_handle(), nullptr)); |
30 if (!slots_for_cert) | 138 if (!slots_for_cert) |
31 return false; | 139 return false; |
32 | 140 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
90 CHECK(g_cert_loader) << "CertLoader::Get() called before Initialize()"; | 198 CHECK(g_cert_loader) << "CertLoader::Get() called before Initialize()"; |
91 return g_cert_loader; | 199 return g_cert_loader; |
92 } | 200 } |
93 | 201 |
94 // static | 202 // static |
95 bool CertLoader::IsInitialized() { | 203 bool CertLoader::IsInitialized() { |
96 return g_cert_loader; | 204 return g_cert_loader; |
97 } | 205 } |
98 | 206 |
99 CertLoader::CertLoader() | 207 CertLoader::CertLoader() |
100 : certificates_loaded_(false), | 208 : pending_initial_load_(true), |
101 certificates_update_required_(false), | 209 system_cert_cache_(base::MakeUnique<CertCache>( |
102 certificates_update_running_(false), | 210 base::BindRepeating(&CertLoader::CacheUpdated, |
103 database_(nullptr), | 211 base::Unretained(this)))), |
212 user_cert_cache_(base::MakeUnique<CertCache>( | |
213 base::BindRepeating(&CertLoader::CacheUpdated, | |
214 base::Unretained(this)))), | |
104 all_certs_(base::MakeUnique<net::CertificateList>()), | 215 all_certs_(base::MakeUnique<net::CertificateList>()), |
216 system_certs_(base::MakeUnique<net::CertificateList>()), | |
105 weak_factory_(this) {} | 217 weak_factory_(this) {} |
106 | 218 |
107 CertLoader::~CertLoader() { | 219 CertLoader::~CertLoader() { |
108 net::CertDatabase::GetInstance()->RemoveObserver(this); | |
109 } | 220 } |
110 | 221 |
111 void CertLoader::StartWithNSSDB(net::NSSCertDatabase* database) { | 222 void CertLoader::StartWithSystemNSSDB( |
112 CHECK(!database_); | 223 net::NSSCertDatabase* system_slot_database) { |
113 database_ = database; | 224 system_cert_cache_->StartWithNSSDB(system_slot_database); |
225 } | |
114 | 226 |
115 // Start observing cert database for changes. | 227 void CertLoader::StartWithUserNSSDB(net::NSSCertDatabase* user_database) { |
116 // Observing net::CertDatabase is preferred over observing |database_| | 228 user_cert_cache_->StartWithNSSDB(user_database); |
117 // directly, as |database_| observers receive only events generated directly | |
118 // by |database_|, so they may miss a few relevant ones. | |
119 // TODO(tbarzic): Once singleton NSSCertDatabase is removed, investigate if | |
120 // it would be OK to observe |database_| directly; or change NSSCertDatabase | |
121 // to send notification on all relevant changes. | |
122 net::CertDatabase::GetInstance()->AddObserver(this); | |
123 | |
124 LoadCertificates(); | |
125 } | 229 } |
126 | 230 |
127 void CertLoader::AddObserver(CertLoader::Observer* observer) { | 231 void CertLoader::AddObserver(CertLoader::Observer* observer) { |
128 observers_.AddObserver(observer); | 232 observers_.AddObserver(observer); |
129 } | 233 } |
130 | 234 |
131 void CertLoader::RemoveObserver(CertLoader::Observer* observer) { | 235 void CertLoader::RemoveObserver(CertLoader::Observer* observer) { |
132 observers_.RemoveObserver(observer); | 236 observers_.RemoveObserver(observer); |
133 } | 237 } |
134 | 238 |
135 // static | 239 // static |
136 bool CertLoader::IsCertificateHardwareBacked(const net::X509Certificate* cert) { | 240 bool CertLoader::IsCertificateHardwareBacked(const net::X509Certificate* cert) { |
137 if (g_force_hardware_backed_for_test) | 241 if (g_force_hardware_backed_for_test) |
138 return true; | 242 return true; |
139 PK11SlotInfo* slot = cert->os_cert_handle()->slot; | 243 PK11SlotInfo* slot = cert->os_cert_handle()->slot; |
140 return slot && PK11_IsHW(slot); | 244 return slot && PK11_IsHW(slot); |
141 } | 245 } |
142 | 246 |
143 bool CertLoader::CertificatesLoading() const { | 247 bool CertLoader::CertificatesLoading() const { |
144 return database_ && !certificates_loaded_; | 248 return system_cert_cache_->CertificatesLoading() || |
249 user_cert_cache_->CertificatesLoading(); | |
250 } | |
251 | |
252 bool CertLoader::certificates_loaded() const { | |
253 return system_cert_cache_->certificates_loaded() || | |
254 user_cert_cache_->certificates_loaded(); | |
stevenjb
2017/05/09 17:04:02
Is this correct?
I'm a bit fuzzy on whether both
pmarko
2017/05/10 11:48:01
CertLoader can be used with one or both - I've imp
| |
145 } | 255 } |
146 | 256 |
147 // static | 257 // static |
148 void CertLoader::ForceHardwareBackedForTesting() { | 258 void CertLoader::ForceHardwareBackedForTesting() { |
149 g_force_hardware_backed_for_test = true; | 259 g_force_hardware_backed_for_test = true; |
150 } | 260 } |
151 | 261 |
152 // static | 262 // static |
153 // | 263 // |
154 // For background see this discussion on dev-tech-crypto.lists.mozilla.org: | 264 // For background see this discussion on dev-tech-crypto.lists.mozilla.org: |
(...skipping 21 matching lines...) Expand all Loading... | |
176 std::string pkcs11_id; | 286 std::string pkcs11_id; |
177 if (sec_item) { | 287 if (sec_item) { |
178 pkcs11_id = base::HexEncode(sec_item->data, sec_item->len); | 288 pkcs11_id = base::HexEncode(sec_item->data, sec_item->len); |
179 SECITEM_FreeItem(sec_item, PR_TRUE); | 289 SECITEM_FreeItem(sec_item, PR_TRUE); |
180 } | 290 } |
181 SECKEY_DestroyPrivateKey(priv_key); | 291 SECKEY_DestroyPrivateKey(priv_key); |
182 | 292 |
183 return pkcs11_id; | 293 return pkcs11_id; |
184 } | 294 } |
185 | 295 |
186 void CertLoader::LoadCertificates() { | 296 void CertLoader::CacheUpdated() { |
187 DCHECK(thread_checker_.CalledOnValidThread()); | 297 DCHECK(thread_checker_.CalledOnValidThread()); |
188 VLOG(1) << "LoadCertificates: " << certificates_update_running_; | 298 VLOG(1) << "CacheUpdated"; |
189 | 299 |
190 if (certificates_update_running_) { | 300 if (user_cert_cache_->has_system_certificates()) { |
191 certificates_update_required_ = true; | 301 // The user's cert cache also contains system certificates. |
192 return; | 302 // Filter those. |
303 crypto::ScopedPK11Slot system_slot = | |
304 user_cert_cache_->GetNSSDB()->GetSystemSlot(); | |
305 DCHECK(system_slot); | |
306 std::unique_ptr<net::CertificateList> all_certs = | |
307 base::MakeUnique<net::CertificateList>(user_cert_cache_->cert_list()); | |
308 base::PostTaskWithTraitsAndReplyWithResult( | |
309 FROM_HERE, | |
310 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, | |
311 base::BindOnce(&FilterSystemTokenCertificates, | |
312 base::Unretained(all_certs.get()), | |
313 std::move(system_slot)), | |
314 base::BindOnce(&CertLoader::UpdateCertificates, | |
315 weak_factory_.GetWeakPtr(), std::move(all_certs))); | |
316 } else { | |
317 // The user's cert cache does not contain system certificates. | |
318 std::unique_ptr<net::CertificateList> system_certs = | |
319 base::MakeUnique<net::CertificateList>(system_cert_cache_->cert_list()); | |
320 std::unique_ptr<net::CertificateList> all_certs = | |
321 base::MakeUnique<net::CertificateList>(user_cert_cache_->cert_list()); | |
322 all_certs->insert(all_certs->end(), system_certs->begin(), | |
323 system_certs->end()); | |
324 UpdateCertificates(std::move(all_certs), std::move(system_certs)); | |
193 } | 325 } |
194 | |
195 certificates_update_running_ = true; | |
196 certificates_update_required_ = false; | |
197 | |
198 database_->ListCerts( | |
199 base::Bind(&CertLoader::CertificatesLoaded, weak_factory_.GetWeakPtr())); | |
200 } | |
201 | |
202 void CertLoader::CertificatesLoaded( | |
203 std::unique_ptr<net::CertificateList> all_certs) { | |
204 DCHECK(thread_checker_.CalledOnValidThread()); | |
205 VLOG(1) << "CertificatesLoaded: " << all_certs->size(); | |
206 | |
207 crypto::ScopedPK11Slot system_slot = database_->GetSystemSlot(); | |
208 base::PostTaskWithTraitsAndReplyWithResult( | |
209 FROM_HERE, | |
210 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, | |
211 base::BindOnce(&FilterSystemTokenCertificates, | |
212 base::Unretained(all_certs.get()), std::move(system_slot)), | |
213 base::BindOnce(&CertLoader::UpdateCertificates, | |
214 weak_factory_.GetWeakPtr(), std::move(all_certs))); | |
215 } | 326 } |
216 | 327 |
217 void CertLoader::UpdateCertificates( | 328 void CertLoader::UpdateCertificates( |
218 std::unique_ptr<net::CertificateList> all_certs, | 329 std::unique_ptr<net::CertificateList> all_certs, |
219 std::unique_ptr<net::CertificateList> system_certs) { | 330 std::unique_ptr<net::CertificateList> system_certs) { |
220 DCHECK(thread_checker_.CalledOnValidThread()); | 331 DCHECK(thread_checker_.CalledOnValidThread()); |
221 DCHECK(certificates_update_running_); | 332 bool initial_load = pending_initial_load_; |
333 pending_initial_load_ = false; | |
334 | |
222 VLOG(1) << "UpdateCertificates: " << all_certs->size() << " (" | 335 VLOG(1) << "UpdateCertificates: " << all_certs->size() << " (" |
223 << system_certs->size() << " on system slot)"; | 336 << system_certs->size() << " on system slot)" |
337 << ", initial_load=" << initial_load; | |
224 | 338 |
225 // Ignore any existing certificates. | 339 // Ignore any existing certificates. |
226 all_certs_ = std::move(all_certs); | 340 all_certs_ = std::move(all_certs); |
227 system_certs_ = std::move(system_certs); | 341 system_certs_ = std::move(system_certs); |
228 | 342 |
229 bool initial_load = !certificates_loaded_; | |
230 certificates_loaded_ = true; | |
231 NotifyCertificatesLoaded(initial_load); | 343 NotifyCertificatesLoaded(initial_load); |
232 | |
233 certificates_update_running_ = false; | |
234 if (certificates_update_required_) | |
235 LoadCertificates(); | |
236 } | 344 } |
237 | 345 |
238 void CertLoader::NotifyCertificatesLoaded(bool initial_load) { | 346 void CertLoader::NotifyCertificatesLoaded(bool initial_load) { |
239 for (auto& observer : observers_) | 347 for (auto& observer : observers_) |
240 observer.OnCertificatesLoaded(*all_certs_, initial_load); | 348 observer.OnCertificatesLoaded(*all_certs_, initial_load); |
241 } | 349 } |
242 | 350 |
243 void CertLoader::OnCertDBChanged() { | |
244 VLOG(1) << "OnCertDBChanged"; | |
245 LoadCertificates(); | |
246 } | |
247 | |
248 } // namespace chromeos | 351 } // namespace chromeos |
OLD | NEW |