OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 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 "net/ssl/client_cert_store_win.h" | 5 #include "net/ssl/client_cert_store_win.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #define SECURITY_WIN32 // Needs to be defined before including security.h | 10 #define SECURITY_WIN32 // Needs to be defined before including security.h |
11 #include <windows.h> | 11 #include <windows.h> |
12 #include <security.h> | 12 #include <security.h> |
13 | 13 |
14 #include "base/bind.h" | |
15 #include "base/bind_helpers.h" | |
14 #include "base/callback.h" | 16 #include "base/callback.h" |
15 #include "base/logging.h" | 17 #include "base/logging.h" |
18 #include "base/memory/ptr_util.h" | |
19 #include "base/task_runner_util.h" | |
16 #include "crypto/wincrypt_shim.h" | 20 #include "crypto/wincrypt_shim.h" |
17 #include "net/cert/x509_util.h" | 21 #include "net/cert/x509_util.h" |
22 #include "net/ssl/ssl_platform_key_util.h" | |
23 #include "net/ssl/ssl_platform_key_win.h" | |
24 #include "net/ssl/ssl_private_key.h" | |
18 | 25 |
19 namespace net { | 26 namespace net { |
20 | 27 |
21 namespace { | 28 namespace { |
22 | 29 |
30 class ClientCertIdentityWin : public ClientCertIdentity { | |
31 public: | |
32 // Takes ownership of |cert_context|. | |
33 ClientCertIdentityWin(scoped_refptr<net::X509Certificate> cert, | |
34 PCCERT_CONTEXT cert_context) | |
35 : ClientCertIdentity(std::move(cert)), cert_context_(cert_context) {} | |
36 ~ClientCertIdentityWin() override { | |
37 CertFreeCertificateContext(cert_context_); | |
38 } | |
39 | |
40 void AcquirePrivateKey( | |
41 const base::Callback<void(scoped_refptr<SSLPrivateKey>)>& | |
42 private_key_callback) override; | |
43 | |
44 private: | |
45 PCCERT_CONTEXT cert_context_; | |
46 }; | |
47 | |
48 void ClientCertIdentityWin::AcquirePrivateKey( | |
49 const base::Callback<void(scoped_refptr<SSLPrivateKey>)>& | |
50 private_key_callback) { | |
51 if (base::PostTaskAndReplyWithResult( | |
52 GetSSLPlatformKeyTaskRunner().get(), FROM_HERE, | |
53 base::Bind(&FetchClientCertPrivateKey, | |
54 base::Unretained(certificate()), cert_context_), | |
55 private_key_callback)) { | |
56 return; | |
57 } | |
58 // If the task could not be posted, behave as if there was no key. | |
59 private_key_callback.Run(nullptr); | |
60 } | |
61 | |
23 // Callback required by Windows API function CertFindChainInStore(). In addition | 62 // Callback required by Windows API function CertFindChainInStore(). In addition |
24 // to filtering by extended/enhanced key usage, we do not show expired | 63 // to filtering by extended/enhanced key usage, we do not show expired |
25 // certificates and require digital signature usage in the key usage extension. | 64 // certificates and require digital signature usage in the key usage extension. |
26 // | 65 // |
27 // This matches our behavior on Mac OS X and that of NSS. It also matches the | 66 // This matches our behavior on Mac OS X and that of NSS. It also matches the |
28 // default behavior of IE8. See http://support.microsoft.com/kb/890326 and | 67 // default behavior of IE8. See http://support.microsoft.com/kb/890326 and |
29 // http://blogs.msdn.com/b/askie/archive/2009/06/09/my-expired-client-certifica | 68 // http://blogs.msdn.com/b/askie/archive/2009/06/09/my-expired-client-certifica |
30 // tes-no-longer-display-when-connecting-to-my-web-server-using-ie8.aspx | 69 // tes-no-longer-display-when-connecting-to-my-web-server-using-ie8.aspx |
31 static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context, | 70 static BOOL WINAPI ClientCertFindCallback(PCCERT_CONTEXT cert_context, |
32 void* find_arg) { | 71 void* find_arg) { |
(...skipping 25 matching lines...) Expand all Loading... | |
58 if (!CertGetCertificateContextProperty( | 97 if (!CertGetCertificateContextProperty( |
59 cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) { | 98 cert_context, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size)) { |
60 return FALSE; | 99 return FALSE; |
61 } | 100 } |
62 | 101 |
63 return TRUE; | 102 return TRUE; |
64 } | 103 } |
65 | 104 |
66 void GetClientCertsImpl(HCERTSTORE cert_store, | 105 void GetClientCertsImpl(HCERTSTORE cert_store, |
67 const SSLCertRequestInfo& request, | 106 const SSLCertRequestInfo& request, |
68 CertificateList* selected_certs) { | 107 ClientCertIdentityList* selected_identities) { |
69 selected_certs->clear(); | 108 selected_identities->clear(); |
70 | 109 |
71 const size_t auth_count = request.cert_authorities.size(); | 110 const size_t auth_count = request.cert_authorities.size(); |
72 std::vector<CERT_NAME_BLOB> issuers(auth_count); | 111 std::vector<CERT_NAME_BLOB> issuers(auth_count); |
73 for (size_t i = 0; i < auth_count; ++i) { | 112 for (size_t i = 0; i < auth_count; ++i) { |
74 issuers[i].cbData = static_cast<DWORD>(request.cert_authorities[i].size()); | 113 issuers[i].cbData = static_cast<DWORD>(request.cert_authorities[i].size()); |
75 issuers[i].pbData = reinterpret_cast<BYTE*>( | 114 issuers[i].pbData = reinterpret_cast<BYTE*>( |
76 const_cast<char*>(request.cert_authorities[i].data())); | 115 const_cast<char*>(request.cert_authorities[i].data())); |
77 } | 116 } |
78 | 117 |
79 // Enumerate the client certificates. | 118 // Enumerate the client certificates. |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
139 if (!intermediates.empty() && | 178 if (!intermediates.empty() && |
140 X509Certificate::IsSelfSigned(intermediates.back())) { | 179 X509Certificate::IsSelfSigned(intermediates.back())) { |
141 CertFreeCertificateContext(intermediates.back()); | 180 CertFreeCertificateContext(intermediates.back()); |
142 intermediates.pop_back(); | 181 intermediates.pop_back(); |
143 } | 182 } |
144 | 183 |
145 // TODO(svaldez): cert currently wraps cert_context2 which may be backed | 184 // TODO(svaldez): cert currently wraps cert_context2 which may be backed |
146 // by a smartcard with threading difficulties. Instead, create a fresh | 185 // by a smartcard with threading difficulties. Instead, create a fresh |
147 // X509Certificate with CreateFromBytes and route cert_context2 into the | 186 // X509Certificate with CreateFromBytes and route cert_context2 into the |
148 // SSLPrivateKey. Probably changing CertificateList to be a | 187 // SSLPrivateKey. Probably changing CertificateList to be a |
149 // pair<X509Certificate, SSLPrivateKeyCallback>. | 188 // pair<X509Certificate, SSLPrivateKeyCallback>. |
davidben
2017/06/01 23:41:25
I think |cert| should probably be created from byt
mattm
2017/06/02 04:04:20
I was planning to leave this as is in this CL sinc
Ryan Sleevi
2017/06/08 18:13:47
|cert_context2| is not - line 150 handles that.
T
| |
150 scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( | 189 scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( |
151 cert_context2, intermediates); | 190 cert_context2, intermediates); |
152 if (cert) | 191 if (cert) { |
153 selected_certs->push_back(std::move(cert)); | 192 selected_identities->push_back(base::MakeUnique<ClientCertIdentityWin>( |
154 CertFreeCertificateContext(cert_context2); | 193 std::move(cert), |
194 cert_context2)); // Takes ownership of |cert_context2| | |
195 } | |
155 for (size_t i = 0; i < intermediates.size(); ++i) | 196 for (size_t i = 0; i < intermediates.size(); ++i) |
156 CertFreeCertificateContext(intermediates[i]); | 197 CertFreeCertificateContext(intermediates[i]); |
157 } | 198 } |
158 | 199 |
159 std::sort(selected_certs->begin(), selected_certs->end(), | 200 std::sort(selected_identities->begin(), selected_identities->end(), |
160 x509_util::ClientCertSorter()); | 201 ClientCertIdentitySorter()); |
161 } | 202 } |
162 | 203 |
163 } // namespace | 204 } // namespace |
164 | 205 |
165 ClientCertStoreWin::ClientCertStoreWin() {} | 206 ClientCertStoreWin::ClientCertStoreWin() {} |
166 | 207 |
167 ClientCertStoreWin::ClientCertStoreWin(HCERTSTORE cert_store) { | 208 ClientCertStoreWin::ClientCertStoreWin(HCERTSTORE cert_store) { |
168 DCHECK(cert_store); | 209 DCHECK(cert_store); |
169 cert_store_.reset(cert_store); | 210 cert_store_.reset(cert_store); |
170 } | 211 } |
171 | 212 |
172 ClientCertStoreWin::~ClientCertStoreWin() {} | 213 ClientCertStoreWin::~ClientCertStoreWin() {} |
173 | 214 |
174 void ClientCertStoreWin::GetClientCerts( | 215 void ClientCertStoreWin::GetClientCerts( |
175 const SSLCertRequestInfo& request, | 216 const SSLCertRequestInfo& request, |
176 const ClientCertListCallback& callback) { | 217 const ClientCertListCallback& callback) { |
davidben
2017/06/01 23:41:25
This is probably a Ryan question, but in the same
davidben
2017/06/01 23:46:14
Likewise, should we try to keep thread affinity th
mattm
2017/06/02 04:04:20
Sounds reasonable but I'll wait and see if Ryan ch
davidben
2017/06/07 23:06:16
Fair enough. I guess the main reason we might want
Ryan Sleevi
2017/06/08 18:13:47
Right, so the risk I tried to capture in previous
davidben
2017/06/08 21:40:48
This CL currently moves FetchClientCertPrivateKey
mattm
2017/06/08 21:47:55
So for context:
Before this CL, net::FetchClientC
mattm
2017/06/12 21:28:30
FYI, I have a followup CL (after this CL and after
| |
177 CertificateList selected_certs; | 218 ClientCertIdentityList selected_identities; |
178 if (cert_store_) { | 219 if (cert_store_) { |
179 // Use the existing client cert store. Note: Under some situations, | 220 // Use the existing client cert store. Note: Under some situations, |
180 // it's possible for this to return certificates that aren't usable | 221 // it's possible for this to return certificates that aren't usable |
181 // (see below). | 222 // (see below). |
182 GetClientCertsImpl(cert_store_, request, &selected_certs); | 223 GetClientCertsImpl(cert_store_, request, &selected_identities); |
183 callback.Run(std::move(selected_certs)); | 224 callback.Run(std::move(selected_identities)); |
184 return; | 225 return; |
185 } | 226 } |
186 | 227 |
187 // Always open a new instance of the "MY" store, to ensure that there | 228 // Always open a new instance of the "MY" store, to ensure that there |
188 // are no previously cached certificates being reused after they're | 229 // are no previously cached certificates being reused after they're |
189 // no longer available (some smartcard providers fail to update the "MY" | 230 // no longer available (some smartcard providers fail to update the "MY" |
190 // store handles and instead interpose CertOpenSystemStore). | 231 // store handles and instead interpose CertOpenSystemStore). |
191 ScopedHCERTSTORE my_cert_store(CertOpenSystemStore(NULL, L"MY")); | 232 ScopedHCERTSTORE my_cert_store(CertOpenSystemStore(NULL, L"MY")); |
192 if (!my_cert_store) { | 233 if (!my_cert_store) { |
193 PLOG(ERROR) << "Could not open the \"MY\" system certificate store: "; | 234 PLOG(ERROR) << "Could not open the \"MY\" system certificate store: "; |
194 callback.Run(CertificateList()); | 235 callback.Run(ClientCertIdentityList()); |
195 return; | 236 return; |
196 } | 237 } |
197 | 238 |
198 GetClientCertsImpl(my_cert_store, request, &selected_certs); | 239 GetClientCertsImpl(my_cert_store, request, &selected_identities); |
199 callback.Run(std::move(selected_certs)); | 240 callback.Run(std::move(selected_identities)); |
200 } | 241 } |
201 | 242 |
202 bool ClientCertStoreWin::SelectClientCertsForTesting( | 243 bool ClientCertStoreWin::SelectClientCertsForTesting( |
203 const CertificateList& input_certs, | 244 const CertificateList& input_certs, |
204 const SSLCertRequestInfo& request, | 245 const SSLCertRequestInfo& request, |
205 CertificateList* selected_certs) { | 246 ClientCertIdentityList* selected_identities) { |
206 ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, | 247 ScopedHCERTSTORE test_store(CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, 0, |
207 NULL)); | 248 NULL)); |
208 if (!test_store) | 249 if (!test_store) |
209 return false; | 250 return false; |
210 | 251 |
211 // Add available certificates to the test store. | 252 // Add available certificates to the test store. |
212 for (size_t i = 0; i < input_certs.size(); ++i) { | 253 for (size_t i = 0; i < input_certs.size(); ++i) { |
213 // Add the certificate to the test store. | 254 // Add the certificate to the test store. |
214 PCCERT_CONTEXT cert = NULL; | 255 PCCERT_CONTEXT cert = NULL; |
215 if (!CertAddCertificateContextToStore(test_store, | 256 if (!CertAddCertificateContextToStore(test_store, |
216 input_certs[i]->os_cert_handle(), | 257 input_certs[i]->os_cert_handle(), |
217 CERT_STORE_ADD_NEW, &cert)) { | 258 CERT_STORE_ADD_NEW, &cert)) { |
218 return false; | 259 return false; |
219 } | 260 } |
220 // Add dummy private key data to the certificate - otherwise the certificate | 261 // Add dummy private key data to the certificate - otherwise the certificate |
221 // would be discarded by the filtering routines. | 262 // would be discarded by the filtering routines. |
222 CRYPT_KEY_PROV_INFO private_key_data; | 263 CRYPT_KEY_PROV_INFO private_key_data; |
223 memset(&private_key_data, 0, sizeof(private_key_data)); | 264 memset(&private_key_data, 0, sizeof(private_key_data)); |
224 if (!CertSetCertificateContextProperty(cert, | 265 if (!CertSetCertificateContextProperty(cert, |
225 CERT_KEY_PROV_INFO_PROP_ID, | 266 CERT_KEY_PROV_INFO_PROP_ID, |
226 0, &private_key_data)) { | 267 0, &private_key_data)) { |
227 return false; | 268 return false; |
228 } | 269 } |
229 // Decrement the reference count of the certificate (since we requested a | 270 // Decrement the reference count of the certificate (since we requested a |
230 // copy). | 271 // copy). |
231 if (!CertFreeCertificateContext(cert)) | 272 if (!CertFreeCertificateContext(cert)) |
232 return false; | 273 return false; |
233 } | 274 } |
234 | 275 |
235 GetClientCertsImpl(test_store.get(), request, selected_certs); | 276 GetClientCertsImpl(test_store.get(), request, selected_identities); |
236 return true; | 277 return true; |
237 } | 278 } |
238 | 279 |
239 } // namespace net | 280 } // namespace net |
OLD | NEW |