Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(120)

Side by Side Diff: chromeos/network/cert_loader.cc

Issue 20130002: Call crypto::InitializeTPMToken on the IO thread (Take 2) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chromeos/network/cert_loader.h ('k') | chromeos/network/network_connection_handler.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chromeos/network/cert_loader.h"
6
7 #include <algorithm>
8
9 #include "base/chromeos/chromeos_version.h"
10 #include "base/observer_list.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/task_runner_util.h"
13 #include "base/threading/worker_pool.h"
14 #include "chromeos/dbus/cryptohome_client.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "crypto/encryptor.h"
17 #include "crypto/nss_util.h"
18 #include "crypto/sha2.h"
19 #include "crypto/symmetric_key.h"
20 #include "net/cert/nss_cert_database.h"
21
22 namespace chromeos {
23
24 namespace {
25
26 const int64 kInitialRequestDelayMs = 100;
27 const int64 kMaxRequestDelayMs = 300000; // 5 minutes
28
29 // Calculates the delay before running next attempt to initiatialize the TPM
30 // token, if |last_delay| was the last or initial delay.
31 base::TimeDelta GetNextRequestDelayMs(base::TimeDelta last_delay) {
32 // This implements an exponential backoff, as we don't know in which order of
33 // magnitude the TPM token changes it's state.
34 base::TimeDelta next_delay = last_delay * 2;
35
36 // Cap the delay to prevent an overflow. This threshold is arbitrarily chosen.
37 const base::TimeDelta max_delay =
38 base::TimeDelta::FromMilliseconds(kMaxRequestDelayMs);
39 if (next_delay > max_delay)
40 next_delay = max_delay;
41 return next_delay;
42 }
43
44 void LoadNSSCertificates(net::CertificateList* cert_list) {
45 if (base::chromeos::IsRunningOnChromeOS())
46 net::NSSCertDatabase::GetInstance()->ListCerts(cert_list);
47 }
48
49 } // namespace
50
51 CertLoader::CertLoader()
52 : certificates_requested_(false),
53 certificates_loaded_(false),
54 certificates_update_required_(false),
55 certificates_update_running_(false),
56 tpm_token_state_(TPM_STATE_UNKNOWN),
57 tpm_request_delay_(
58 base::TimeDelta::FromMilliseconds(kInitialRequestDelayMs)),
59 initialize_token_factory_(this),
60 update_certificates_factory_(this) {
61 net::CertDatabase::GetInstance()->AddObserver(this);
62 if (LoginState::IsInitialized())
63 LoginState::Get()->AddObserver(this);
64 RequestCertificates();
65 }
66
67 CertLoader::~CertLoader() {
68 net::CertDatabase::GetInstance()->RemoveObserver(this);
69 if (LoginState::IsInitialized())
70 LoginState::Get()->RemoveObserver(this);
71 }
72
73 void CertLoader::AddObserver(CertLoader::Observer* observer) {
74 observers_.AddObserver(observer);
75 }
76
77 void CertLoader::RemoveObserver(CertLoader::Observer* observer) {
78 observers_.RemoveObserver(observer);
79 }
80
81 bool CertLoader::CertificatesLoading() const {
82 return certificates_requested_ && !certificates_loaded_;
83 }
84
85 bool CertLoader::IsHardwareBacked() const {
86 return !tpm_token_name_.empty();
87 }
88
89 void CertLoader::RequestCertificates() {
90 CHECK(thread_checker_.CalledOnValidThread());
91 const bool logged_in = LoginState::IsInitialized() ?
92 LoginState::Get()->IsUserLoggedIn() : false;
93 VLOG(1) << "RequestCertificates: " << logged_in;
94 if (certificates_requested_ || !logged_in)
95 return;
96
97 certificates_requested_ = true;
98
99 // Ensure we've opened the user's key/certificate database.
100 crypto::OpenPersistentNSSDB();
101 if (base::chromeos::IsRunningOnChromeOS())
102 crypto::EnableTPMTokenForNSS();
103
104 // This is the entry point to the TPM token initialization process, which we
105 // should do at most once.
106 DCHECK(!initialize_token_factory_.HasWeakPtrs());
107 InitializeTokenAndLoadCertificates();
108 }
109
110 void CertLoader::InitializeTokenAndLoadCertificates() {
111 CHECK(thread_checker_.CalledOnValidThread());
112 VLOG(1) << "InitializeTokenAndLoadCertificates";
113
114 switch (tpm_token_state_) {
115 case TPM_STATE_UNKNOWN: {
116 DBusThreadManager::Get()->GetCryptohomeClient()->TpmIsEnabled(
117 base::Bind(&CertLoader::OnTpmIsEnabled,
118 initialize_token_factory_.GetWeakPtr()));
119 return;
120 }
121 case TPM_DISABLED: {
122 // TPM is disabled, so proceed with empty tpm token name.
123 StartLoadCertificates();
124 return;
125 }
126 case TPM_ENABLED: {
127 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11IsTpmTokenReady(
128 base::Bind(&CertLoader::OnPkcs11IsTpmTokenReady,
129 initialize_token_factory_.GetWeakPtr()));
130 return;
131 }
132 case TPM_TOKEN_READY: {
133 // Retrieve token_name_ and user_pin_ here since they will never change
134 // and CryptohomeClient calls are not thread safe.
135 DBusThreadManager::Get()->GetCryptohomeClient()->Pkcs11GetTpmTokenInfo(
136 base::Bind(&CertLoader::OnPkcs11GetTpmTokenInfo,
137 initialize_token_factory_.GetWeakPtr()));
138 return;
139 }
140 case TPM_TOKEN_INFO_RECEIVED: {
141 InitializeNSSForTPMToken();
142 return;
143 }
144 case TPM_TOKEN_NSS_INITIALIZED: {
145 StartLoadCertificates();
146 return;
147 }
148 }
149 }
150
151 void CertLoader::RetryTokenInitializationLater() {
152 LOG(WARNING) << "Re-Requesting Certificates later.";
153 base::MessageLoop::current()->PostDelayedTask(
154 FROM_HERE,
155 base::Bind(&CertLoader::InitializeTokenAndLoadCertificates,
156 initialize_token_factory_.GetWeakPtr()),
157 tpm_request_delay_);
158 tpm_request_delay_ = GetNextRequestDelayMs(tpm_request_delay_);
159 }
160
161 // For background see this discussion on dev-tech-crypto.lists.mozilla.org:
162 // http://web.archiveorange.com/archive/v/6JJW7E40sypfZGtbkzxX
163 //
164 // NOTE: This function relies on the convention that the same PKCS#11 ID
165 // is shared between a certificate and its associated private and public
166 // keys. I tried to implement this with PK11_GetLowLevelKeyIDForCert(),
167 // but that always returns NULL on Chrome OS for me.
168 std::string CertLoader::GetPkcs11IdForCert(
169 const net::X509Certificate& cert) const {
170 if (!IsHardwareBacked())
171 return std::string();
172
173 CERTCertificateStr* cert_handle = cert.os_cert_handle();
174 SECKEYPrivateKey *priv_key =
175 PK11_FindKeyByAnyCert(cert_handle, NULL /* wincx */);
176 if (!priv_key)
177 return std::string();
178
179 // Get the CKA_ID attribute for a key.
180 SECItem* sec_item = PK11_GetLowLevelKeyIDForPrivateKey(priv_key);
181 std::string pkcs11_id;
182 if (sec_item) {
183 pkcs11_id = base::HexEncode(sec_item->data, sec_item->len);
184 SECITEM_FreeItem(sec_item, PR_TRUE);
185 }
186 SECKEY_DestroyPrivateKey(priv_key);
187
188 return pkcs11_id;
189 }
190
191 void CertLoader::OnTpmIsEnabled(DBusMethodCallStatus call_status,
192 bool tpm_is_enabled) {
193 VLOG(1) << "OnTpmIsEnabled: " << tpm_is_enabled;
194
195 if (call_status == DBUS_METHOD_CALL_SUCCESS && tpm_is_enabled)
196 tpm_token_state_ = TPM_ENABLED;
197 else
198 tpm_token_state_ = TPM_DISABLED;
199
200 InitializeTokenAndLoadCertificates();
201 }
202
203 void CertLoader::OnPkcs11IsTpmTokenReady(DBusMethodCallStatus call_status,
204 bool is_tpm_token_ready) {
205 VLOG(1) << "OnPkcs11IsTpmTokenReady: " << is_tpm_token_ready;
206
207 if (call_status == DBUS_METHOD_CALL_FAILURE || !is_tpm_token_ready) {
208 RetryTokenInitializationLater();
209 return;
210 }
211
212 tpm_token_state_ = TPM_TOKEN_READY;
213 InitializeTokenAndLoadCertificates();
214 }
215
216 void CertLoader::OnPkcs11GetTpmTokenInfo(DBusMethodCallStatus call_status,
217 const std::string& token_name,
218 const std::string& user_pin) {
219 VLOG(1) << "OnPkcs11GetTpmTokenInfo: " << token_name;
220
221 if (call_status == DBUS_METHOD_CALL_FAILURE) {
222 RetryTokenInitializationLater();
223 return;
224 }
225
226 tpm_token_name_ = token_name;
227 // TODO(stevenjb): The network code expects a slot ID, not a label. See
228 // crbug.com/201101. For now, use a hard coded, well known slot instead.
229 const char kHardcodedTpmSlot[] = "0";
230 tpm_token_slot_ = kHardcodedTpmSlot;
231 tpm_user_pin_ = user_pin;
232 tpm_token_state_ = TPM_TOKEN_INFO_RECEIVED;
233
234 InitializeTokenAndLoadCertificates();
235 }
236
237 void CertLoader::InitializeNSSForTPMToken() {
238 VLOG(1) << "InitializeNSSForTPMToken";
239
240 if (base::chromeos::IsRunningOnChromeOS() &&
241 !crypto::InitializeTPMToken(tpm_token_name_, tpm_user_pin_)) {
242 RetryTokenInitializationLater();
243 return;
244 }
245
246 tpm_token_state_ = TPM_TOKEN_NSS_INITIALIZED;
247 InitializeTokenAndLoadCertificates();
248 }
249
250 void CertLoader::StartLoadCertificates() {
251 VLOG(1) << "StartLoadCertificates";
252
253 if (certificates_update_running_) {
254 certificates_update_required_ = true;
255 return;
256 }
257
258 net::CertificateList* cert_list = new net::CertificateList;
259 certificates_update_running_ = true;
260 certificates_update_required_ = false;
261 base::WorkerPool::GetTaskRunner(true /* task_is_slow */)->
262 PostTaskAndReply(
263 FROM_HERE,
264 base::Bind(LoadNSSCertificates, cert_list),
265 base::Bind(&CertLoader::UpdateCertificates,
266 update_certificates_factory_.GetWeakPtr(),
267 base::Owned(cert_list)));
268 }
269
270 void CertLoader::UpdateCertificates(net::CertificateList* cert_list) {
271 CHECK(thread_checker_.CalledOnValidThread());
272 DCHECK(certificates_update_running_);
273 VLOG(1) << "UpdateCertificates: " << cert_list->size();
274
275 // Ignore any existing certificates.
276 cert_list_.swap(*cert_list);
277
278 NotifyCertificatesLoaded(!certificates_loaded_);
279 certificates_loaded_ = true;
280
281 certificates_update_running_ = false;
282 if (certificates_update_required_)
283 StartLoadCertificates();
284 }
285
286 void CertLoader::NotifyCertificatesLoaded(bool initial_load) {
287 FOR_EACH_OBSERVER(Observer, observers_,
288 OnCertificatesLoaded(cert_list_, initial_load));
289 }
290
291 void CertLoader::OnCertTrustChanged(const net::X509Certificate* cert) {
292 }
293
294 void CertLoader::OnCertAdded(const net::X509Certificate* cert) {
295 VLOG(1) << "OnCertAdded";
296 StartLoadCertificates();
297 }
298
299 void CertLoader::OnCertRemoved(const net::X509Certificate* cert) {
300 VLOG(1) << "OnCertRemoved";
301 StartLoadCertificates();
302 }
303
304 void CertLoader::LoggedInStateChanged(LoginState::LoggedInState state) {
305 VLOG(1) << "LoggedInStateChanged: " << state;
306 RequestCertificates();
307 }
308
309 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/network/cert_loader.h ('k') | chromeos/network/network_connection_handler.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698