Index: net/ssl/client_cert_store_mac.cc |
diff --git a/net/ssl/client_cert_store_mac.cc b/net/ssl/client_cert_store_mac.cc |
index 4b44ab37d1219ba18c5caed09d084797940e06ed..93debe9621e5a61e1764dba626341d0f95f88539 100644 |
--- a/net/ssl/client_cert_store_mac.cc |
+++ b/net/ssl/client_cert_store_mac.cc |
@@ -121,6 +121,57 @@ bool IsIssuedByInKeychain(const std::vector<std::string>& valid_issuers, |
return true; |
} |
+// Returns true if |purpose| is listed as allowed in |usage|. This |
+// function also considers the "Any" purpose. If the attribute is |
+// present and empty, we return false. |
+bool ExtendedKeyUsageAllows(const CE_ExtendedKeyUsage* usage, |
+ const CSSM_OID* purpose) { |
+ for (unsigned p = 0; p < usage->numPurposes; ++p) { |
+ if (CSSMOIDEqual(&usage->purposes[p], purpose)) |
+ return true; |
+ if (CSSMOIDEqual(&usage->purposes[p], &CSSMOID_ExtendedKeyUsageAny)) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+// Does |cert|'s usage allow SSL client authentication? |
+bool SupportsSSLClientAuth(SecCertificateRef cert) { |
+ x509_util::CSSMCachedCertificate cached_cert; |
+ OSStatus status = cached_cert.Init(cert); |
+ if (status) |
+ return false; |
+ |
+ // RFC5280 says to take the intersection of the two extensions. |
+ // |
+ // Our underlying crypto libraries don't expose |
+ // ClientCertificateType, so for now we will not support fixed |
+ // Diffie-Hellman mechanisms. For rsa_sign, we need the |
+ // digitalSignature bit. |
+ // |
+ // In particular, if a key has the nonRepudiation bit and not the |
+ // digitalSignature one, we will not offer it to the user. |
+ x509_util::CSSMFieldValue key_usage; |
+ status = cached_cert.GetField(&CSSMOID_KeyUsage, &key_usage); |
+ if (status == CSSM_OK && key_usage.field()) { |
+ const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>(); |
+ const CE_KeyUsage* key_usage_value = |
+ reinterpret_cast<const CE_KeyUsage*>(ext->value.parsedValue); |
+ if (!((*key_usage_value) & CE_KU_DigitalSignature)) |
+ return false; |
+ } |
+ |
+ status = cached_cert.GetField(&CSSMOID_ExtendedKeyUsage, &key_usage); |
+ if (status == CSSM_OK && key_usage.field()) { |
+ const CSSM_X509_EXTENSION* ext = key_usage.GetAs<CSSM_X509_EXTENSION>(); |
+ const CE_ExtendedKeyUsage* ext_key_usage = |
+ reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue); |
+ if (!ExtendedKeyUsageAllows(ext_key_usage, &CSSMOID_ClientAuth)) |
+ return false; |
+ } |
+ return true; |
+} |
+ |
// Examines the certificates in |preferred_cert| and |regular_certs| to find |
// all certificates that match the client certificate request in |request|, |
// storing the matching certificates in |selected_certs|. |
@@ -142,7 +193,7 @@ void GetClientCertsImpl(const scoped_refptr<X509Certificate>& preferred_cert, |
selected_certs->clear(); |
for (size_t i = 0; i < preliminary_list.size(); ++i) { |
scoped_refptr<X509Certificate>& cert = preliminary_list[i]; |
- if (cert->HasExpired() || !cert->SupportsSSLClientAuth()) |
+ if (cert->HasExpired() || !SupportsSSLClientAuth(cert->os_cert_handle())) |
continue; |
// Skip duplicates (a cert may be in multiple keychains). |