Chromium Code Reviews| Index: net/ssl/client_cert_store_impl_mac.cc |
| diff --git a/net/ssl/client_cert_store_impl_mac.cc b/net/ssl/client_cert_store_impl_mac.cc |
| index ed10a7f38bd20037be0ea88e0a397dd4b54b1bf9..53c3a6e51d9d413f87ce76bf14fc828f393e695b 100644 |
| --- a/net/ssl/client_cert_store_impl_mac.cc |
| +++ b/net/ssl/client_cert_store_impl_mac.cc |
| @@ -14,6 +14,7 @@ |
| #include <string> |
| #include "base/logging.h" |
| +#include "base/mac/foundation_util.h" |
| #include "base/mac/mac_logging.h" |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/strings/sys_string_conversions.h" |
| @@ -28,9 +29,96 @@ namespace net { |
| namespace { |
| +// Gets the issuer for a given cert, starting with the cert itself and |
| +// including the intermediate and finally root certificates (if any). |
| +// This function calls SecTrust but doesn't actually pay attention to the trust |
| +// result: it shouldn't be used to determine trust, just to traverse the chain. |
| +// Caller is responsible for releasing the value stored into *out_cert_chain. |
| +OSStatus CopyCertChain(SecCertificateRef cert_handle, |
| + CFArrayRef* out_cert_chain) { |
| + DCHECK(cert_handle); |
| + DCHECK(out_cert_chain); |
| + |
| + // Create an SSL policy ref configured for client cert evaluation. |
| + SecPolicyRef ssl_policy; |
| + OSStatus result = x509_util::CreateSSLClientPolicy(&ssl_policy); |
| + if (result) |
| + return result; |
| + ScopedCFTypeRef<SecPolicyRef> scoped_ssl_policy(ssl_policy); |
| + |
| + // Create a SecTrustRef. |
| + ScopedCFTypeRef<CFArrayRef> input_certs(CFArrayCreate( |
| + NULL, const_cast<const void**>(reinterpret_cast<void**>(&cert_handle)), |
| + 1, &kCFTypeArrayCallBacks)); |
| + SecTrustRef trust_ref = NULL; |
| + { |
| + base::AutoLock lock(crypto::GetMacSecurityServicesLock()); |
| + result = SecTrustCreateWithCertificates(input_certs, ssl_policy, |
| + &trust_ref); |
| + } |
| + if (result) |
| + return result; |
| + ScopedCFTypeRef<SecTrustRef> trust(trust_ref); |
| + |
| + // Evaluate trust, which creates the cert chain. |
| + SecTrustResultType status; |
| + CSSM_TP_APPLE_EVIDENCE_INFO* status_chain; |
| + { |
| + base::AutoLock lock(crypto::GetMacSecurityServicesLock()); |
| + result = SecTrustEvaluate(trust, &status); |
| + } |
| + if (result) |
| + return result; |
| + { |
| + base::AutoLock lock(crypto::GetMacSecurityServicesLock()); |
| + result = SecTrustGetResult(trust, &status, out_cert_chain, &status_chain); |
| + } |
| + return result; |
| +} |
| + |
| +// Returns true if |*cert| is issued by an authority in |valid_issuers| |
| +// according to Keychain Services, rather than using |cert|'s intermediate |
| +// certificates. If it is, |*cert| is updated to point to the completed |
| +// certificate |
| +bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers, |
| + scoped_refptr<X509Certificate>* cert) { |
| + DCHECK(cert); |
| + DCHECK(*cert); |
| + |
| + X509Certificate::OSCertHandle cert_handle = (*cert)->os_cert_handle(); |
| + CFArrayRef cert_chain = NULL; |
| + OSStatus result = CopyCertChain(cert_handle, &cert_chain); |
| + if (result) { |
| + OSSTATUS_LOG(ERROR, result) << "CopyCertChain error"; |
| + return false; |
| + } |
| + |
| + if (!cert_chain) |
| + return false; |
| + |
| + X509Certificate::OSCertHandles intermediates; |
| + for (CFIndex i = 1, chain_count = CFArrayGetCount(cert_chain); |
| + i < chain_count; ++i) { |
| + SecCertificateRef cert = base::mac::CFCastStrict<SecCertificateRef>( |
| + CFArrayGetValueAtIndex(cert_chain, i)); |
| + intermediates.push_back(cert); |
| + } |
| + |
| + scoped_refptr<X509Certificate> new_cert(X509Certificate::CreateFromHandle( |
| + cert_handle, intermediates)); |
| + CFRelease(cert_chain); // Also frees |intermediates|. |
| + |
| + if (!new_cert->IsIssuedByEncoded(valid_issuers)) |
|
Ryan Sleevi
2013/04/16 02:32:50
The downside to this approach is that |new_cert->o
|
| + return false; |
| + |
| + cert->swap(new_cert); |
| + return true; |
| +} |
| + |
| bool GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert, |
| const CertificateList& regular_certs, |
| const SSLCertRequestInfo& request, |
| + bool query_keychain, |
|
wtc
2013/04/16 18:50:32
query_keychain probably should be documented.
It'
Ryan Sleevi
2013/04/16 19:00:26
Good point. Will add comments to these functions.
|
| CertificateList* selected_certs) { |
| CertificateList preliminary_list; |
| if (preferred_cert) |
| @@ -55,11 +143,12 @@ bool GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert, |
| continue; |
| // Check if the certificate issuer is allowed by the server. |
| - if (!request.cert_authorities.empty() && |
| - !cert->IsIssuedByEncoded(request.cert_authorities)) { |
| - continue; |
| + if (request.cert_authorities.empty() || |
| + cert->IsIssuedByEncoded(request.cert_authorities) || |
| + (query_keychain && |
| + IsIssuedByInKeychain(request.cert_authorities, &cert))) { |
| + selected_certs->push_back(cert); |
| } |
| - selected_certs->push_back(cert); |
| } |
| // Preferred cert should appear first in the ui, so exclude it from the |
| @@ -147,14 +236,14 @@ bool ClientCertStoreImpl::GetClientCerts(const SSLCertRequestInfo& request, |
| return false; |
| } |
| - return GetClientCertsImpl(preferred_cert, regular_certs, request, |
| + return GetClientCertsImpl(preferred_cert, regular_certs, request, true, |
| selected_certs); |
| } |
| bool ClientCertStoreImpl::SelectClientCerts(const CertificateList& input_certs, |
| const SSLCertRequestInfo& request, |
| CertificateList* selected_certs) { |
| - return GetClientCertsImpl(NULL, input_certs, request, |
| + return GetClientCertsImpl(NULL, input_certs, request, false, |
| selected_certs); |
| } |
| @@ -164,7 +253,7 @@ bool ClientCertStoreImpl::SelectClientCertsGivenPreferred( |
| const CertificateList& regular_certs, |
| const SSLCertRequestInfo& request, |
| CertificateList* selected_certs) { |
| - return GetClientCertsImpl(preferred_cert, regular_certs, request, |
| + return GetClientCertsImpl(preferred_cert, regular_certs, request, false, |
| selected_certs); |
| } |
| #endif |