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

Side by Side Diff: net/ssl/client_cert_store_mac.cc

Issue 2898573002: Refactor client cert private key handling. (Closed)
Patch Set: removed no longer needed forward declaration Created 3 years, 6 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
« no previous file with comments | « net/ssl/client_cert_store_mac.h ('k') | net/ssl/client_cert_store_mac_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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_mac.h" 5 #include "net/ssl/client_cert_store_mac.h"
6 6
7 #include <CommonCrypto/CommonDigest.h> 7 #include <CommonCrypto/CommonDigest.h>
8 #include <CoreFoundation/CFArray.h> 8 #include <CoreFoundation/CFArray.h>
9 #include <CoreServices/CoreServices.h> 9 #include <CoreServices/CoreServices.h>
10 #include <Security/SecBase.h> 10 #include <Security/SecBase.h>
11 #include <Security/Security.h> 11 #include <Security/Security.h>
12 12
13 #include <algorithm> 13 #include <algorithm>
14 #include <string> 14 #include <string>
15 15
16 #include "base/callback.h" 16 #include "base/callback.h"
17 #include "base/logging.h" 17 #include "base/logging.h"
18 #include "base/mac/mac_logging.h" 18 #include "base/mac/mac_logging.h"
19 #include "base/mac/scoped_cftyperef.h" 19 #include "base/mac/scoped_cftyperef.h"
20 #include "base/memory/ptr_util.h"
20 #include "base/strings/sys_string_conversions.h" 21 #include "base/strings/sys_string_conversions.h"
21 #include "base/synchronization/lock.h" 22 #include "base/synchronization/lock.h"
22 #include "crypto/mac_security_services_lock.h" 23 #include "crypto/mac_security_services_lock.h"
23 #include "net/base/host_port_pair.h" 24 #include "net/base/host_port_pair.h"
24 #include "net/cert/x509_util.h" 25 #include "net/cert/x509_util.h"
25 #include "net/cert/x509_util_ios_and_mac.h" 26 #include "net/cert/x509_util_ios_and_mac.h"
26 #include "net/cert/x509_util_mac.h" 27 #include "net/cert/x509_util_mac.h"
28 #include "net/ssl/client_cert_identity_mac.h"
27 29
28 using base::ScopedCFTypeRef; 30 using base::ScopedCFTypeRef;
29 31
30 namespace net { 32 namespace net {
31 33
32 // CSSM functions are deprecated as of OSX 10.7, but have no replacement. 34 // CSSM functions are deprecated as of OSX 10.7, but have no replacement.
33 // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1 35 // https://bugs.chromium.org/p/chromium/issues/detail?id=590914#c1
34 #pragma clang diagnostic push 36 #pragma clang diagnostic push
35 #pragma clang diagnostic ignored "-Wdeprecated-declarations" 37 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
36 38
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
76 } 78 }
77 if (result) 79 if (result)
78 return result; 80 return result;
79 { 81 {
80 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 82 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
81 result = SecTrustGetResult(trust, &status, out_cert_chain, &status_chain); 83 result = SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
82 } 84 }
83 return result; 85 return result;
84 } 86 }
85 87
86 // Returns true if |*cert| is issued by an authority in |valid_issuers| 88 // Returns true if |*identity| is issued by an authority in |valid_issuers|
87 // according to Keychain Services, rather than using |cert|'s intermediate 89 // according to Keychain Services, rather than using |identity|'s intermediate
88 // certificates. If it is, |*cert| is updated to point to the completed 90 // certificates. If it is, |*identity| is updated to include the intermediates.
89 // certificate
90 bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers, 91 bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
91 scoped_refptr<X509Certificate>* cert) { 92 ClientCertIdentity* identity) {
92 DCHECK(cert); 93 DCHECK(identity);
93 DCHECK(cert->get()); 94 DCHECK(identity->sec_identity_ref());
94 95
95 base::ScopedCFTypeRef<SecCertificateRef> os_cert( 96 ScopedCFTypeRef<SecCertificateRef> os_cert;
96 x509_util::CreateSecCertificateFromX509Certificate(cert->get())); 97 int err = SecIdentityCopyCertificate(identity->sec_identity_ref(),
97 if (!os_cert) 98 os_cert.InitializeInto());
99 if (err != noErr)
98 return false; 100 return false;
99 CFArrayRef cert_chain = NULL; 101 CFArrayRef cert_chain = NULL;
100 OSStatus result = CopyCertChain(os_cert.get(), &cert_chain); 102 OSStatus result = CopyCertChain(os_cert.get(), &cert_chain);
101 if (result) { 103 if (result) {
102 OSSTATUS_LOG(ERROR, result) << "CopyCertChain error"; 104 OSSTATUS_LOG(ERROR, result) << "CopyCertChain error";
103 return false; 105 return false;
104 } 106 }
105 107
106 if (!cert_chain) 108 if (!cert_chain)
107 return false; 109 return false;
108 110
109 std::vector<SecCertificateRef> intermediates; 111 std::vector<SecCertificateRef> intermediates;
110 for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain); 112 for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
111 i < chain_count; ++i) { 113 i < chain_count; ++i) {
112 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( 114 SecCertificateRef sec_cert = reinterpret_cast<SecCertificateRef>(
113 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i))); 115 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
114 intermediates.push_back(cert); 116 intermediates.push_back(sec_cert);
115 } 117 }
116 118
117 scoped_refptr<X509Certificate> new_cert( 119 scoped_refptr<X509Certificate> new_cert(
118 x509_util::CreateX509CertificateFromSecCertificate(os_cert.get(), 120 x509_util::CreateX509CertificateFromSecCertificate(os_cert.get(),
119 intermediates)); 121 intermediates));
120 CFRelease(cert_chain); // Also frees |intermediates|. 122 CFRelease(cert_chain); // Also frees |intermediates|.
121 123
122 if (!new_cert || !new_cert->IsIssuedByEncoded(valid_issuers)) 124 if (!new_cert || !new_cert->IsIssuedByEncoded(valid_issuers))
123 return false; 125 return false;
124 126
125 cert->swap(new_cert); 127 identity->SetIntermediates(new_cert->GetIntermediateCertificates());
126 return true; 128 return true;
127 } 129 }
128 130
129 // Returns true if |purpose| is listed as allowed in |usage|. This 131 // Returns true if |purpose| is listed as allowed in |usage|. This
130 // function also considers the "Any" purpose. If the attribute is 132 // function also considers the "Any" purpose. If the attribute is
131 // present and empty, we return false. 133 // present and empty, we return false.
132 bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage, 134 bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage,
133 const CSSM_OID* purpose) { 135 const CSSM_OID* purpose) {
134 for (unsigned p = 0; p < usage->numPurposes; ++p) { 136 for (unsigned p = 0; p < usage->numPurposes; ++p) {
135 if (CSSMOIDEqual(&usage->purposes[p], purpose)) 137 if (CSSMOIDEqual(&usage->purposes[p], purpose))
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
170 if (status == CSSM_OK && key_usage.field()) { 172 if (status == CSSM_OK && key_usage.field()) {
171 const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>(); 173 const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>();
172 const CE_ExtendedKeyUsage* ext_key_usage = 174 const CE_ExtendedKeyUsage* ext_key_usage =
173 reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue); 175 reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue);
174 if (!ExtendedKeyUsageAllows(ext_key_usage, &CSSMOID_ClientAuth)) 176 if (!ExtendedKeyUsageAllows(ext_key_usage, &CSSMOID_ClientAuth))
175 return false; 177 return false;
176 } 178 }
177 return true; 179 return true;
178 } 180 }
179 181
180 // Examines the certificates in |preferred_cert| and |regular_certs| to find 182 // Examines the certificates in |preferred_identity| and |regular_identities| to
181 // all certificates that match the client certificate request in |request|, 183 // find all certificates that match the client certificate request in |request|,
182 // storing the matching certificates in |selected_certs|. 184 // storing the matching certificates in |selected_identities|.
183 // If |query_keychain| is true, Keychain Services will be queried to construct 185 // If |query_keychain| is true, Keychain Services will be queried to construct
184 // full certificate chains. If it is false, only the the certificates and their 186 // full certificate chains. If it is false, only the the certificates and their
185 // intermediates (available via X509Certificate::GetIntermediateCertificates()) 187 // intermediates (available via X509Certificate::GetIntermediateCertificates())
186 // will be considered. 188 // will be considered.
187 void GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert, 189 void GetClientCertsImpl(std::unique_ptr<ClientCertIdentity> preferred_identity,
188 const CertificateList& regular_certs, 190 ClientCertIdentityList regular_identities,
189 const SSLCertRequestInfo& request, 191 const SSLCertRequestInfo& request,
190 bool query_keychain, 192 bool query_keychain,
191 CertificateList* selected_certs) { 193 ClientCertIdentityList* selected_identities) {
192 CertificateList preliminary_list; 194 scoped_refptr<X509Certificate> preferred_cert_orig;
193 if (preferred_cert.get()) 195 ClientCertIdentityList preliminary_list(std::move(regular_identities));
194 preliminary_list.push_back(preferred_cert); 196 if (preferred_identity) {
195 preliminary_list.insert(preliminary_list.end(), regular_certs.begin(), 197 preferred_cert_orig = preferred_identity->certificate();
196 regular_certs.end()); 198 preliminary_list.insert(preliminary_list.begin(),
199 std::move(preferred_identity));
200 }
197 201
198 selected_certs->clear(); 202 selected_identities->clear();
199 for (size_t i = 0; i < preliminary_list.size(); ++i) { 203 for (size_t i = 0; i < preliminary_list.size(); ++i) {
200 scoped_refptr<X509Certificate>& cert = preliminary_list[i]; 204 std::unique_ptr<ClientCertIdentity>& cert = preliminary_list[i];
201 if (cert->HasExpired()) 205 if (cert->certificate()->HasExpired())
202 continue; 206 continue;
203 207
204 // Skip duplicates (a cert may be in multiple keychains). 208 // Skip duplicates (a cert may be in multiple keychains).
205 auto cert_iter = std::find_if( 209 auto cert_iter = std::find_if(
206 selected_certs->begin(), selected_certs->end(), 210 selected_identities->begin(), selected_identities->end(),
207 [&cert](const scoped_refptr<X509Certificate>& other_cert) { 211 [&cert](
208 return X509Certificate::IsSameOSCert(cert->os_cert_handle(), 212 const std::unique_ptr<ClientCertIdentity>& other_cert_identity) {
209 other_cert->os_cert_handle()); 213 return X509Certificate::IsSameOSCert(
214 cert->certificate()->os_cert_handle(),
215 other_cert_identity->certificate()->os_cert_handle());
210 }); 216 });
211 if (cert_iter != selected_certs->end()) 217 if (cert_iter != selected_identities->end())
212 continue; 218 continue;
213 219
214 // Check if the certificate issuer is allowed by the server. 220 // Check if the certificate issuer is allowed by the server.
215 if (request.cert_authorities.empty() || 221 if (request.cert_authorities.empty() ||
216 cert->IsIssuedByEncoded(request.cert_authorities) || 222 cert->certificate()->IsIssuedByEncoded(request.cert_authorities) ||
217 (query_keychain && 223 (query_keychain &&
218 IsIssuedByInKeychain(request.cert_authorities, &cert))) { 224 IsIssuedByInKeychain(request.cert_authorities, cert.get()))) {
219 selected_certs->push_back(cert); 225 selected_identities->push_back(std::move(cert));
220 } 226 }
221 } 227 }
222 228
223 // Preferred cert should appear first in the ui, so exclude it from the 229 // Preferred cert should appear first in the ui, so exclude it from the
224 // sorting. 230 // sorting. Compare the os_cert_handle since the X509Certificate object may
225 CertificateList::iterator sort_begin = selected_certs->begin(); 231 // have changed if intermediates were added.
226 CertificateList::iterator sort_end = selected_certs->end(); 232 ClientCertIdentityList::iterator sort_begin = selected_identities->begin();
227 if (preferred_cert.get() && sort_begin != sort_end && 233 ClientCertIdentityList::iterator sort_end = selected_identities->end();
228 sort_begin->get() == preferred_cert.get()) { 234 if (preferred_cert_orig && sort_begin != sort_end &&
235 X509Certificate::IsSameOSCert(
236 sort_begin->get()->certificate()->os_cert_handle(),
237 preferred_cert_orig->os_cert_handle())) {
229 ++sort_begin; 238 ++sort_begin;
230 } 239 }
231 sort(sort_begin, sort_end, x509_util::ClientCertSorter()); 240 sort(sort_begin, sort_end, ClientCertIdentitySorter());
232 } 241 }
233 242
234 } // namespace 243 } // namespace
235 244
236 ClientCertStoreMac::ClientCertStoreMac() {} 245 ClientCertStoreMac::ClientCertStoreMac() {}
237 246
238 ClientCertStoreMac::~ClientCertStoreMac() {} 247 ClientCertStoreMac::~ClientCertStoreMac() {}
239 248
240 void ClientCertStoreMac::GetClientCerts( 249 void ClientCertStoreMac::GetClientCerts(
241 const SSLCertRequestInfo& request, 250 const SSLCertRequestInfo& request,
242 const ClientCertListCallback& callback) { 251 const ClientCertListCallback& callback) {
243 std::string server_domain = request.host_and_port.host(); 252 std::string server_domain = request.host_and_port.host();
244 253
245 ScopedCFTypeRef<SecIdentityRef> preferred_identity; 254 ScopedCFTypeRef<SecIdentityRef> preferred_sec_identity;
246 if (!server_domain.empty()) { 255 if (!server_domain.empty()) {
247 // See if there's an identity preference for this domain: 256 // See if there's an identity preference for this domain:
248 ScopedCFTypeRef<CFStringRef> domain_str( 257 ScopedCFTypeRef<CFStringRef> domain_str(
249 base::SysUTF8ToCFStringRef("https://" + server_domain)); 258 base::SysUTF8ToCFStringRef("https://" + server_domain));
250 SecIdentityRef identity = NULL; 259 SecIdentityRef sec_identity = NULL;
251 // While SecIdentityCopyPreferences appears to take a list of CA issuers 260 // While SecIdentityCopyPreferences appears to take a list of CA issuers
252 // to restrict the identity search to, within Security.framework the 261 // to restrict the identity search to, within Security.framework the
253 // argument is ignored and filtering unimplemented. See 262 // argument is ignored and filtering unimplemented. See
254 // SecIdentity.cpp in libsecurity_keychain, specifically 263 // SecIdentity.cpp in libsecurity_keychain, specifically
255 // _SecIdentityCopyPreferenceMatchingName(). 264 // _SecIdentityCopyPreferenceMatchingName().
256 { 265 {
257 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 266 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
258 if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr) 267 if (SecIdentityCopyPreference(domain_str, 0, NULL, &sec_identity) ==
259 preferred_identity.reset(identity); 268 noErr)
269 preferred_sec_identity.reset(sec_identity);
260 } 270 }
261 } 271 }
262 272
263 // Now enumerate the identities in the available keychains. 273 // Now enumerate the identities in the available keychains.
264 scoped_refptr<X509Certificate> preferred_cert = NULL; 274 std::unique_ptr<ClientCertIdentity> preferred_identity;
265 CertificateList regular_certs; 275 ClientCertIdentityList regular_identities;
266 276
267 SecIdentitySearchRef search = NULL; 277 SecIdentitySearchRef search = NULL;
268 OSStatus err; 278 OSStatus err;
269 { 279 {
270 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 280 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
271 err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); 281 err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
272 } 282 }
273 if (err) { 283 if (err) {
274 callback.Run(CertificateList()); 284 callback.Run(ClientCertIdentityList());
275 return; 285 return;
276 } 286 }
277 ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search); 287 ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
278 while (!err) { 288 while (!err) {
279 SecIdentityRef identity = NULL; 289 ScopedCFTypeRef<SecIdentityRef> sec_identity;
280 { 290 {
281 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 291 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
282 err = SecIdentitySearchCopyNext(search, &identity); 292 err = SecIdentitySearchCopyNext(search, sec_identity.InitializeInto());
283 } 293 }
284 if (err) 294 if (err)
285 break; 295 break;
286 ScopedCFTypeRef<SecIdentityRef> scoped_identity(identity);
287 296
288 SecCertificateRef cert_handle; 297 ScopedCFTypeRef<SecCertificateRef> cert_handle;
289 err = SecIdentityCopyCertificate(identity, &cert_handle); 298 err = SecIdentityCopyCertificate(sec_identity.get(),
299 cert_handle.InitializeInto());
290 if (err != noErr) 300 if (err != noErr)
291 continue; 301 continue;
292 ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);
293 302
294 if (!SupportsSSLClientAuth(cert_handle)) 303 if (!SupportsSSLClientAuth(cert_handle.get()))
295 continue; 304 continue;
296 305
297 scoped_refptr<X509Certificate> cert( 306 scoped_refptr<X509Certificate> cert(
298 x509_util::CreateX509CertificateFromSecCertificate( 307 x509_util::CreateX509CertificateFromSecCertificate(
299 cert_handle, std::vector<SecCertificateRef>())); 308 cert_handle.get(), std::vector<SecCertificateRef>()));
300 if (!cert) 309 if (!cert)
301 continue; 310 continue;
302 311
303 if (preferred_identity && CFEqual(preferred_identity, identity)) { 312 if (preferred_sec_identity &&
313 CFEqual(preferred_sec_identity, sec_identity.get())) {
304 // Only one certificate should match. 314 // Only one certificate should match.
305 DCHECK(!preferred_cert.get()); 315 DCHECK(!preferred_identity.get());
306 preferred_cert = cert; 316 preferred_identity = base::MakeUnique<ClientCertIdentityMac>(
317 std::move(cert), std::move(sec_identity));
307 } else { 318 } else {
308 regular_certs.push_back(cert); 319 regular_identities.push_back(base::MakeUnique<ClientCertIdentityMac>(
320 std::move(cert), std::move(sec_identity)));
309 } 321 }
310 } 322 }
311 323
312 if (err != errSecItemNotFound) { 324 if (err != errSecItemNotFound) {
313 OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error"; 325 OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error";
314 callback.Run(CertificateList()); 326 callback.Run(ClientCertIdentityList());
315 return; 327 return;
316 } 328 }
317 329
318 CertificateList selected_certs; 330 ClientCertIdentityList selected_identities;
319 GetClientCertsImpl(preferred_cert, regular_certs, request, true, 331 GetClientCertsImpl(std::move(preferred_identity),
320 &selected_certs); 332 std::move(regular_identities), request, true,
321 callback.Run(std::move(selected_certs)); 333 &selected_identities);
334 callback.Run(std::move(selected_identities));
322 } 335 }
323 336
324 bool ClientCertStoreMac::SelectClientCertsForTesting( 337 bool ClientCertStoreMac::SelectClientCertsForTesting(
325 const CertificateList& input_certs, 338 ClientCertIdentityList input_identities,
326 const SSLCertRequestInfo& request, 339 const SSLCertRequestInfo& request,
327 CertificateList* selected_certs) { 340 ClientCertIdentityList* selected_identities) {
328 GetClientCertsImpl(NULL, input_certs, request, false, selected_certs); 341 GetClientCertsImpl(NULL, std::move(input_identities), request, false,
342 selected_identities);
329 return true; 343 return true;
330 } 344 }
331 345
332 bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting( 346 bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting(
333 const scoped_refptr<X509Certificate>& preferred_cert, 347 std::unique_ptr<ClientCertIdentity> preferred_identity,
334 const CertificateList& regular_certs, 348 ClientCertIdentityList regular_identities,
335 const SSLCertRequestInfo& request, 349 const SSLCertRequestInfo& request,
336 CertificateList* selected_certs) { 350 ClientCertIdentityList* selected_identities) {
337 GetClientCertsImpl( 351 GetClientCertsImpl(std::move(preferred_identity),
338 preferred_cert, regular_certs, request, false, selected_certs); 352 std::move(regular_identities), request, false,
353 selected_identities);
339 return true; 354 return true;
340 } 355 }
341 356
342 #pragma clang diagnostic pop // "-Wdeprecated-declarations" 357 #pragma clang diagnostic pop // "-Wdeprecated-declarations"
343 358
344 } // namespace net 359 } // namespace net
OLDNEW
« no previous file with comments | « net/ssl/client_cert_store_mac.h ('k') | net/ssl/client_cert_store_mac_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698