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

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

Issue 2898573002: Refactor client cert private key handling. (Closed)
Patch Set: rebase on https://codereview.chromium.org/2899083006/ 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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 |*cert| 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 |cert|'s intermediate
88 // certificates. If it is, |*cert| is updated to point to the completed 90 // certificates. If it is, |*cert| is updated to point to the completed
89 // certificate 91 // certificate
90 bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers, 92 bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers,
91 scoped_refptr<X509Certificate>* cert) { 93 ClientCertIdentity* cert) {
92 DCHECK(cert); 94 DCHECK(cert);
93 DCHECK(cert->get());
94 95
95 base::ScopedCFTypeRef<SecCertificateRef> os_cert( 96 base::ScopedCFTypeRef<SecCertificateRef> os_cert(
96 x509_util::CreateSecCertificateFromX509Certificate(cert->get())); 97 x509_util::CreateSecCertificateFromX509Certificate(cert->certificate()));
97 if (!os_cert) 98 if (!os_cert)
98 return false; 99 return false;
99 CFArrayRef cert_chain = NULL; 100 CFArrayRef cert_chain = NULL;
100 OSStatus result = CopyCertChain(os_cert.get(), &cert_chain); 101 OSStatus result = CopyCertChain(os_cert.get(), &cert_chain);
101 if (result) { 102 if (result) {
102 OSSTATUS_LOG(ERROR, result) << "CopyCertChain error"; 103 OSSTATUS_LOG(ERROR, result) << "CopyCertChain error";
103 return false; 104 return false;
104 } 105 }
105 106
106 if (!cert_chain) 107 if (!cert_chain)
107 return false; 108 return false;
108 109
109 std::vector<SecCertificateRef> intermediates; 110 std::vector<SecCertificateRef> intermediates;
110 for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain); 111 for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain);
111 i < chain_count; ++i) { 112 i < chain_count; ++i) {
112 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>( 113 SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(
113 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i))); 114 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i)));
114 intermediates.push_back(cert); 115 intermediates.push_back(cert);
115 } 116 }
116 117
117 scoped_refptr<X509Certificate> new_cert( 118 scoped_refptr<X509Certificate> new_cert(
118 x509_util::CreateX509CertificateFromSecCertificate(os_cert.get(), 119 x509_util::CreateX509CertificateFromSecCertificate(os_cert.get(),
119 intermediates)); 120 intermediates));
120 CFRelease(cert_chain); // Also frees |intermediates|. 121 CFRelease(cert_chain); // Also frees |intermediates|.
121 122
122 if (!new_cert || !new_cert->IsIssuedByEncoded(valid_issuers)) 123 if (!new_cert || !new_cert->IsIssuedByEncoded(valid_issuers))
123 return false; 124 return false;
124 125
125 cert->swap(new_cert); 126 // TODO(mattm): could do this in a more efficient way (not creating
127 // new_cert), if there was a version of IsIssuedByEncoded that only took
128 // OSCertHandles.
129 cert->SetIntermediates(new_cert->GetIntermediateCertificates());
126 return true; 130 return true;
127 } 131 }
128 132
129 // Returns true if |purpose| is listed as allowed in |usage|. This 133 // Returns true if |purpose| is listed as allowed in |usage|. This
130 // function also considers the "Any" purpose. If the attribute is 134 // function also considers the "Any" purpose. If the attribute is
131 // present and empty, we return false. 135 // present and empty, we return false.
132 bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage, 136 bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage,
133 const CSSM_OID* purpose) { 137 const CSSM_OID* purpose) {
134 for (unsigned p = 0; p < usage->numPurposes; ++p) { 138 for (unsigned p = 0; p < usage->numPurposes; ++p) {
135 if (CSSMOIDEqual(&usage->purposes[p], purpose)) 139 if (CSSMOIDEqual(&usage->purposes[p], purpose))
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
177 return true; 181 return true;
178 } 182 }
179 183
180 // Examines the certificates in |preferred_cert| and |regular_certs| to find 184 // Examines the certificates in |preferred_cert| and |regular_certs| to find
181 // all certificates that match the client certificate request in |request|, 185 // all certificates that match the client certificate request in |request|,
182 // storing the matching certificates in |selected_certs|. 186 // storing the matching certificates in |selected_certs|.
183 // If |query_keychain| is true, Keychain Services will be queried to construct 187 // 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 188 // full certificate chains. If it is false, only the the certificates and their
185 // intermediates (available via X509Certificate::GetIntermediateCertificates()) 189 // intermediates (available via X509Certificate::GetIntermediateCertificates())
186 // will be considered. 190 // will be considered.
187 void GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert, 191 void GetClientCertsImpl(std::unique_ptr<ClientCertIdentity> preferred_cert,
188 const CertificateList& regular_certs, 192 ClientCertIdentityList regular_certs,
189 const SSLCertRequestInfo& request, 193 const SSLCertRequestInfo& request,
190 bool query_keychain, 194 bool query_keychain,
191 CertificateList* selected_certs) { 195 ClientCertIdentityList* selected_certs) {
192 CertificateList preliminary_list; 196 scoped_refptr<X509Certificate> preferred_cert_orig;
193 if (preferred_cert.get()) 197 ClientCertIdentityList preliminary_list(std::move(regular_certs));
194 preliminary_list.push_back(preferred_cert); 198 if (preferred_cert) {
195 preliminary_list.insert(preliminary_list.end(), regular_certs.begin(), 199 preferred_cert_orig = preferred_cert->certificate();
196 regular_certs.end()); 200 preliminary_list.insert(preliminary_list.begin(),
201 std::move(preferred_cert));
202 }
197 203
198 selected_certs->clear(); 204 selected_certs->clear();
199 for (size_t i = 0; i < preliminary_list.size(); ++i) { 205 for (size_t i = 0; i < preliminary_list.size(); ++i) {
200 scoped_refptr<X509Certificate>& cert = preliminary_list[i]; 206 std::unique_ptr<ClientCertIdentity>& cert = preliminary_list[i];
201 if (cert->HasExpired()) 207 if (cert->certificate()->HasExpired())
202 continue; 208 continue;
203 209
204 // Skip duplicates (a cert may be in multiple keychains). 210 // Skip duplicates (a cert may be in multiple keychains).
205 auto cert_iter = std::find_if( 211 auto cert_iter = std::find_if(
206 selected_certs->begin(), selected_certs->end(), 212 selected_certs->begin(), selected_certs->end(),
207 [&cert](const scoped_refptr<X509Certificate>& other_cert) { 213 [&cert](
208 return X509Certificate::IsSameOSCert(cert->os_cert_handle(), 214 const std::unique_ptr<ClientCertIdentity>& other_cert_identity) {
209 other_cert->os_cert_handle()); 215 return X509Certificate::IsSameOSCert(
216 cert->certificate()->os_cert_handle(),
217 other_cert_identity->certificate()->os_cert_handle());
210 }); 218 });
211 if (cert_iter != selected_certs->end()) 219 if (cert_iter != selected_certs->end())
212 continue; 220 continue;
213 221
214 // Check if the certificate issuer is allowed by the server. 222 // Check if the certificate issuer is allowed by the server.
215 if (request.cert_authorities.empty() || 223 if (request.cert_authorities.empty() ||
216 cert->IsIssuedByEncoded(request.cert_authorities) || 224 cert->certificate()->IsIssuedByEncoded(request.cert_authorities) ||
217 (query_keychain && 225 (query_keychain &&
218 IsIssuedByInKeychain(request.cert_authorities, &cert))) { 226 IsIssuedByInKeychain(request.cert_authorities, cert.get()))) {
219 selected_certs->push_back(cert); 227 selected_certs->push_back(std::move(cert));
220 } 228 }
221 } 229 }
222 230
223 // Preferred cert should appear first in the ui, so exclude it from the 231 // Preferred cert should appear first in the ui, so exclude it from the
224 // sorting. 232 // sorting. Compare the os_cert_handle since the X509Certificate object may
225 CertificateList::iterator sort_begin = selected_certs->begin(); 233 // have changed if intermediates were added.
226 CertificateList::iterator sort_end = selected_certs->end(); 234 ClientCertIdentityList::iterator sort_begin = selected_certs->begin();
227 if (preferred_cert.get() && sort_begin != sort_end && 235 ClientCertIdentityList::iterator sort_end = selected_certs->end();
228 sort_begin->get() == preferred_cert.get()) { 236 if (preferred_cert_orig && sort_begin != sort_end &&
237 X509Certificate::IsSameOSCert(
238 sort_begin->get()->certificate()->os_cert_handle(),
239 preferred_cert_orig->os_cert_handle())) {
229 ++sort_begin; 240 ++sort_begin;
230 } 241 }
231 sort(sort_begin, sort_end, x509_util::ClientCertSorter()); 242 sort(sort_begin, sort_end, ClientCertIdentitySorter());
232 } 243 }
233 244
234 } // namespace 245 } // namespace
235 246
236 ClientCertStoreMac::ClientCertStoreMac() {} 247 ClientCertStoreMac::ClientCertStoreMac() {}
237 248
238 ClientCertStoreMac::~ClientCertStoreMac() {} 249 ClientCertStoreMac::~ClientCertStoreMac() {}
239 250
240 void ClientCertStoreMac::GetClientCerts( 251 void ClientCertStoreMac::GetClientCerts(
241 const SSLCertRequestInfo& request, 252 const SSLCertRequestInfo& request,
242 const ClientCertListCallback& callback) { 253 const ClientCertListCallback& callback) {
243 std::string server_domain = request.host_and_port.host(); 254 std::string server_domain = request.host_and_port.host();
244 255
245 ScopedCFTypeRef<SecIdentityRef> preferred_identity; 256 ScopedCFTypeRef<SecIdentityRef> preferred_identity;
246 if (!server_domain.empty()) { 257 if (!server_domain.empty()) {
247 // See if there's an identity preference for this domain: 258 // See if there's an identity preference for this domain:
248 ScopedCFTypeRef<CFStringRef> domain_str( 259 ScopedCFTypeRef<CFStringRef> domain_str(
249 base::SysUTF8ToCFStringRef("https://" + server_domain)); 260 base::SysUTF8ToCFStringRef("https://" + server_domain));
250 SecIdentityRef identity = NULL; 261 SecIdentityRef sec_identity = NULL;
251 // While SecIdentityCopyPreferences appears to take a list of CA issuers 262 // While SecIdentityCopyPreferences appears to take a list of CA issuers
252 // to restrict the identity search to, within Security.framework the 263 // to restrict the identity search to, within Security.framework the
253 // argument is ignored and filtering unimplemented. See 264 // argument is ignored and filtering unimplemented. See
254 // SecIdentity.cpp in libsecurity_keychain, specifically 265 // SecIdentity.cpp in libsecurity_keychain, specifically
255 // _SecIdentityCopyPreferenceMatchingName(). 266 // _SecIdentityCopyPreferenceMatchingName().
256 { 267 {
257 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 268 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
258 if (SecIdentityCopyPreference(domain_str, 0, NULL, &identity) == noErr) 269 if (SecIdentityCopyPreference(domain_str, 0, NULL, &sec_identity) ==
259 preferred_identity.reset(identity); 270 noErr)
271 preferred_identity.reset(sec_identity);
260 } 272 }
261 } 273 }
262 274
263 // Now enumerate the identities in the available keychains. 275 // Now enumerate the identities in the available keychains.
264 scoped_refptr<X509Certificate> preferred_cert = NULL; 276 // scoped_refptr<X509Certificate> preferred_cert = NULL;
265 CertificateList regular_certs; 277 std::unique_ptr<ClientCertIdentity> preferred_cert;
278 ClientCertIdentityList regular_certs;
266 279
267 SecIdentitySearchRef search = NULL; 280 SecIdentitySearchRef search = NULL;
268 OSStatus err; 281 OSStatus err;
269 { 282 {
270 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 283 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
271 err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); 284 err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
272 } 285 }
273 if (err) { 286 if (err) {
274 callback.Run(CertificateList()); 287 callback.Run(ClientCertIdentityList());
275 return; 288 return;
276 } 289 }
277 ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search); 290 ScopedCFTypeRef<SecIdentitySearchRef> scoped_search(search);
278 while (!err) { 291 while (!err) {
279 SecIdentityRef identity = NULL; 292 ScopedCFTypeRef<SecIdentityRef> sec_identity;
280 { 293 {
281 base::AutoLock lock(crypto::GetMacSecurityServicesLock()); 294 base::AutoLock lock(crypto::GetMacSecurityServicesLock());
282 err = SecIdentitySearchCopyNext(search, &identity); 295 err = SecIdentitySearchCopyNext(search, sec_identity.InitializeInto());
283 } 296 }
284 if (err) 297 if (err)
285 break; 298 break;
286 ScopedCFTypeRef<SecIdentityRef> scoped_identity(identity);
287 299
288 SecCertificateRef cert_handle; 300 SecCertificateRef cert_handle;
289 err = SecIdentityCopyCertificate(identity, &cert_handle); 301 err = SecIdentityCopyCertificate(sec_identity.get(), &cert_handle);
290 if (err != noErr) 302 if (err != noErr)
291 continue; 303 continue;
292 ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle); 304 ScopedCFTypeRef<SecCertificateRef> scoped_cert_handle(cert_handle);
293 305
294 if (!SupportsSSLClientAuth(cert_handle)) 306 if (!SupportsSSLClientAuth(cert_handle))
295 continue; 307 continue;
296 308
297 scoped_refptr<X509Certificate> cert( 309 scoped_refptr<X509Certificate> cert(
298 x509_util::CreateX509CertificateFromSecCertificate( 310 x509_util::CreateX509CertificateFromSecCertificate(
299 cert_handle, std::vector<SecCertificateRef>())); 311 cert_handle, std::vector<SecCertificateRef>()));
300 if (!cert) 312 if (!cert)
301 continue; 313 continue;
302 314
303 if (preferred_identity && CFEqual(preferred_identity, identity)) { 315 if (preferred_identity && CFEqual(preferred_identity, sec_identity.get())) {
304 // Only one certificate should match. 316 // Only one certificate should match.
305 DCHECK(!preferred_cert.get()); 317 DCHECK(!preferred_cert.get());
306 preferred_cert = cert; 318 preferred_cert = base::MakeUnique<ClientCertIdentityMac>(
319 std::move(cert), std::move(sec_identity));
307 } else { 320 } else {
308 regular_certs.push_back(cert); 321 regular_certs.push_back(base::MakeUnique<ClientCertIdentityMac>(
322 std::move(cert), std::move(sec_identity)));
309 } 323 }
310 } 324 }
311 325
312 if (err != errSecItemNotFound) { 326 if (err != errSecItemNotFound) {
313 OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error"; 327 OSSTATUS_LOG(ERROR, err) << "SecIdentitySearch error";
314 callback.Run(CertificateList()); 328 callback.Run(ClientCertIdentityList());
315 return; 329 return;
316 } 330 }
317 331
318 CertificateList selected_certs; 332 ClientCertIdentityList selected_certs;
319 GetClientCertsImpl(preferred_cert, regular_certs, request, true, 333 GetClientCertsImpl(std::move(preferred_cert), std::move(regular_certs),
320 &selected_certs); 334 request, true, &selected_certs);
321 callback.Run(std::move(selected_certs)); 335 callback.Run(std::move(selected_certs));
322 } 336 }
323 337
324 bool ClientCertStoreMac::SelectClientCertsForTesting( 338 bool ClientCertStoreMac::SelectClientCertsForTesting(
325 const CertificateList& input_certs, 339 ClientCertIdentityList input_certs,
326 const SSLCertRequestInfo& request, 340 const SSLCertRequestInfo& request,
327 CertificateList* selected_certs) { 341 ClientCertIdentityList* selected_certs) {
328 GetClientCertsImpl(NULL, input_certs, request, false, selected_certs); 342 GetClientCertsImpl(NULL, std::move(input_certs), request, false,
343 selected_certs);
329 return true; 344 return true;
330 } 345 }
331 346
332 bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting( 347 bool ClientCertStoreMac::SelectClientCertsGivenPreferredForTesting(
333 const scoped_refptr<X509Certificate>& preferred_cert, 348 std::unique_ptr<ClientCertIdentity> preferred_cert,
334 const CertificateList& regular_certs, 349 ClientCertIdentityList regular_certs,
335 const SSLCertRequestInfo& request, 350 const SSLCertRequestInfo& request,
336 CertificateList* selected_certs) { 351 ClientCertIdentityList* selected_certs) {
337 GetClientCertsImpl( 352 GetClientCertsImpl(std::move(preferred_cert), std::move(regular_certs),
338 preferred_cert, regular_certs, request, false, selected_certs); 353 request, false, selected_certs);
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