OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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/base/x509_certificate.h" | 5 #include "net/base/x509_certificate.h" |
6 | 6 |
7 #include <CommonCrypto/CommonDigest.h> | 7 #include <CommonCrypto/CommonDigest.h> |
8 #include <Security/Security.h> | |
9 #include <time.h> | 8 #include <time.h> |
10 | 9 |
11 #include "base/scoped_cftyperef.h" | 10 #include "base/scoped_cftyperef.h" |
12 #include "base/logging.h" | 11 #include "base/logging.h" |
13 #include "base/pickle.h" | 12 #include "base/pickle.h" |
14 #include "base/sys_string_conversions.h" | 13 #include "base/sys_string_conversions.h" |
15 #include "net/base/cert_status_flags.h" | 14 #include "net/base/cert_status_flags.h" |
16 #include "net/base/cert_verify_result.h" | 15 #include "net/base/cert_verify_result.h" |
17 #include "net/base/net_errors.h" | 16 #include "net/base/net_errors.h" |
18 | 17 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
74 | 73 |
75 void SetMacTestCertificate(X509Certificate* cert) { | 74 void SetMacTestCertificate(X509Certificate* cert) { |
76 Singleton<MacTrustedCertificates>::get()->SetTestCertificate(cert); | 75 Singleton<MacTrustedCertificates>::get()->SetTestCertificate(cert); |
77 } | 76 } |
78 | 77 |
79 namespace { | 78 namespace { |
80 | 79 |
81 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, | 80 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, |
82 CFDictionaryRef*); | 81 CFDictionaryRef*); |
83 | 82 |
| 83 inline bool CSSMOIDEqual(const CSSM_OID* oid1, const CSSM_OID* oid2) { |
| 84 return oid1->Length == oid2->Length && |
| 85 (memcmp(oid1->Data, oid2->Data, oid1->Length) == 0); |
| 86 } |
| 87 |
84 int NetErrorFromOSStatus(OSStatus status) { | 88 int NetErrorFromOSStatus(OSStatus status) { |
85 switch (status) { | 89 switch (status) { |
86 case noErr: | 90 case noErr: |
87 return OK; | 91 return OK; |
88 case errSecNotAvailable: | 92 case errSecNotAvailable: |
89 case errSecNoCertificateModule: | 93 case errSecNoCertificateModule: |
90 case errSecNoPolicyModule: | 94 case errSecNoPolicyModule: |
91 return ERR_NOT_IMPLEMENTED; | 95 return ERR_NOT_IMPLEMENTED; |
92 case errSecAuthFailed: | 96 case errSecAuthFailed: |
93 return ERR_ACCESS_DENIED; | 97 return ERR_ACCESS_DENIED; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
162 c != hostname.end() && is_dotted_ip; ++c) | 166 c != hostname.end() && is_dotted_ip; ++c) |
163 is_dotted_ip = (*c >= '0' && *c <= '9') || *c == '.'; | 167 is_dotted_ip = (*c >= '0' && *c <= '9') || *c == '.'; |
164 if (is_dotted_ip) { | 168 if (is_dotted_ip) { |
165 for (std::vector<std::string>::const_iterator name = dns_names->begin(); | 169 for (std::vector<std::string>::const_iterator name = dns_names->begin(); |
166 name != dns_names->end() && !override_hostname_mismatch; ++name) | 170 name != dns_names->end() && !override_hostname_mismatch; ++name) |
167 override_hostname_mismatch = (*name == hostname); | 171 override_hostname_mismatch = (*name == hostname); |
168 } | 172 } |
169 return override_hostname_mismatch; | 173 return override_hostname_mismatch; |
170 } | 174 } |
171 | 175 |
| 176 void ParsePrincipal(const CSSM_X509_NAME* name, |
| 177 X509Certificate::Principal* principal) { |
| 178 std::vector<std::string> common_names, locality_names, state_names, |
| 179 country_names; |
| 180 |
| 181 // TODO(jcampan): add business_category and serial_number. |
| 182 const CSSM_OID* kOIDs[] = { &CSSMOID_CommonName, |
| 183 &CSSMOID_LocalityName, |
| 184 &CSSMOID_StateProvinceName, |
| 185 &CSSMOID_CountryName, |
| 186 &CSSMOID_StreetAddress, |
| 187 &CSSMOID_OrganizationName, |
| 188 &CSSMOID_OrganizationalUnitName, |
| 189 &CSSMOID_DNQualifier }; // This should be "DC" |
| 190 // but is undoubtedly |
| 191 // wrong. TODO(avi): |
| 192 // Find the right OID. |
| 193 |
| 194 std::vector<std::string>* values[] = { |
| 195 &common_names, &locality_names, |
| 196 &state_names, &country_names, |
| 197 &(principal->street_addresses), |
| 198 &(principal->organization_names), |
| 199 &(principal->organization_unit_names), |
| 200 &(principal->domain_components) }; |
| 201 DCHECK(arraysize(kOIDs) == arraysize(values)); |
| 202 |
| 203 for (size_t rdn = 0; rdn < name->numberOfRDNs; ++rdn) { |
| 204 CSSM_X509_RDN rdn_struct = name->RelativeDistinguishedName[rdn]; |
| 205 for (size_t pair = 0; pair < rdn_struct.numberOfPairs; ++pair) { |
| 206 CSSM_X509_TYPE_VALUE_PAIR pair_struct = |
| 207 rdn_struct.AttributeTypeAndValue[pair]; |
| 208 for (size_t oid = 0; oid < arraysize(kOIDs); ++oid) { |
| 209 if (CSSMOIDEqual(&pair_struct.type, kOIDs[oid])) { |
| 210 std::string value = |
| 211 std::string(reinterpret_cast<std::string::value_type*> |
| 212 (pair_struct.value.Data), |
| 213 pair_struct.value.Length); |
| 214 values[oid]->push_back(value); |
| 215 break; |
| 216 } |
| 217 } |
| 218 } |
| 219 } |
| 220 |
| 221 // We don't expect to have more than one CN, L, S, and C. |
| 222 std::vector<std::string>* single_value_lists[4] = { |
| 223 &common_names, &locality_names, &state_names, &country_names }; |
| 224 std::string* single_values[4] = { |
| 225 &principal->common_name, &principal->locality_name, |
| 226 &principal->state_or_province_name, &principal->country_name }; |
| 227 for (size_t i = 0; i < arraysize(single_value_lists); ++i) { |
| 228 DCHECK(single_value_lists[i]->size() <= 1); |
| 229 if (single_value_lists[i]->size() > 0) |
| 230 *(single_values[i]) = (*(single_value_lists[i]))[0]; |
| 231 } |
| 232 } |
| 233 |
172 struct CSSMFields { | 234 struct CSSMFields { |
173 CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {} | 235 CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {} |
174 ~CSSMFields() { | 236 ~CSSMFields() { |
175 if (cl_handle) | 237 if (cl_handle) |
176 CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields); | 238 CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields); |
177 } | 239 } |
178 | 240 |
179 CSSM_CL_HANDLE cl_handle; | 241 CSSM_CL_HANDLE cl_handle; |
180 uint32 num_of_fields; | 242 uint32 num_of_fields; |
181 CSSM_FIELD_PTR fields; | 243 CSSM_FIELD_PTR fields; |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 }; | 379 }; |
318 err = SecPolicySetValue(*policy, &options_data); | 380 err = SecPolicySetValue(*policy, &options_data); |
319 if (err) { | 381 if (err) { |
320 CFRelease(*policy); | 382 CFRelease(*policy); |
321 return err; | 383 return err; |
322 } | 384 } |
323 } | 385 } |
324 return noErr; | 386 return noErr; |
325 } | 387 } |
326 | 388 |
327 // Gets the issuer for a given cert, starting with the cert itself and | |
328 // including the intermediate and finally root certificates (if any). | |
329 // This function calls SecTrust but doesn't actually pay attention to the trust | |
330 // result: it shouldn't be used to determine trust, just to traverse the chain. | |
331 // Caller is responsible for releasing the value stored into *out_cert_chain. | |
332 OSStatus CopyCertChain(SecCertificateRef cert_handle, | |
333 CFArrayRef* out_cert_chain) { | |
334 DCHECK(cert_handle && out_cert_chain); | |
335 // Create an SSL policy ref configured for client cert evaluation. | |
336 SecPolicyRef ssl_policy; | |
337 OSStatus result = X509Certificate::CreateSSLClientPolicy(&ssl_policy); | |
338 if (result) | |
339 return result; | |
340 scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy); | |
341 | |
342 // Create a SecTrustRef. | |
343 scoped_cftyperef<CFArrayRef> input_certs( | |
344 CFArrayCreate(NULL, (const void**)&cert_handle, 1, | |
345 &kCFTypeArrayCallBacks)); | |
346 SecTrustRef trust_ref = NULL; | |
347 result = SecTrustCreateWithCertificates(input_certs, ssl_policy, &trust_ref); | |
348 if (result) | |
349 return result; | |
350 scoped_cftyperef<SecTrustRef> trust(trust_ref); | |
351 | |
352 // Evaluate trust, which creates the cert chain. | |
353 SecTrustResultType status; | |
354 CSSM_TP_APPLE_EVIDENCE_INFO* status_chain; | |
355 result = SecTrustEvaluate(trust, &status); | |
356 if (result) | |
357 return result; | |
358 return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain); | |
359 } | |
360 | |
361 } // namespace | 389 } // namespace |
362 | 390 |
363 void X509Certificate::Initialize() { | 391 void X509Certificate::Initialize() { |
364 const CSSM_X509_NAME* name; | 392 const CSSM_X509_NAME* name; |
365 OSStatus status = SecCertificateGetSubject(cert_handle_, &name); | 393 OSStatus status = SecCertificateGetSubject(cert_handle_, &name); |
366 if (!status) { | 394 if (!status) { |
367 subject_.Parse(name); | 395 ParsePrincipal(name, &subject_); |
368 } | 396 } |
369 status = SecCertificateGetIssuer(cert_handle_, &name); | 397 status = SecCertificateGetIssuer(cert_handle_, &name); |
370 if (!status) { | 398 if (!status) { |
371 issuer_.Parse(name); | 399 ParsePrincipal(name, &issuer_); |
372 } | 400 } |
373 | 401 |
374 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore, | 402 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore, |
375 &valid_start_); | 403 &valid_start_); |
376 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotAfter, | 404 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotAfter, |
377 &valid_expiry_); | 405 &valid_expiry_); |
378 | 406 |
379 fingerprint_ = CalculateFingerprint(cert_handle_); | 407 fingerprint_ = CalculateFingerprint(cert_handle_); |
380 } | 408 } |
381 | 409 |
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
707 } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_NetscapeCertType)) { | 735 } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_NetscapeCertType)) { |
708 uint16_t flags = | 736 uint16_t flags = |
709 *reinterpret_cast<const uint16_t*>(ext->value.parsedValue); | 737 *reinterpret_cast<const uint16_t*>(ext->value.parsedValue); |
710 if (flags & CE_NCT_SSL_Client) | 738 if (flags & CE_NCT_SSL_Client) |
711 return true; | 739 return true; |
712 } | 740 } |
713 } | 741 } |
714 return false; | 742 return false; |
715 } | 743 } |
716 | 744 |
717 bool X509Certificate::IsIssuedBy( | |
718 const std::vector<CertPrincipal>& valid_issuers) { | |
719 // Get the cert's issuer chain. | |
720 CFArrayRef cert_chain = NULL; | |
721 OSStatus result; | |
722 result = CopyCertChain(os_cert_handle(), &cert_chain); | |
723 if (result != noErr) | |
724 return false; | |
725 scoped_cftyperef<CFArrayRef> scoped_cert_chain(cert_chain); | |
726 | |
727 // Check all the certs in the chain for a match. | |
728 int n = CFArrayGetCount(cert_chain); | |
729 for (int i = 0; i < n; ++i) { | |
730 SecCertificateRef cert_handle = reinterpret_cast<SecCertificateRef>( | |
731 const_cast<void*>(CFArrayGetValueAtIndex(cert_chain, i))); | |
732 CFRetain(cert_handle); | |
733 scoped_refptr<X509Certificate> cert = X509Certificate::CreateFromHandle( | |
734 cert_handle, | |
735 X509Certificate::SOURCE_LONE_CERT_IMPORT, | |
736 X509Certificate::OSCertHandles()); | |
737 for (unsigned j = 0; j < valid_issuers.size(); j++) { | |
738 if (cert->subject().Matches(valid_issuers[j])) | |
739 return true; | |
740 } | |
741 } | |
742 return false; | |
743 } | |
744 | |
745 // static | 745 // static |
746 OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { | 746 OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { |
747 CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = { | 747 CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = { |
748 CSSM_APPLE_TP_SSL_OPTS_VERSION, | 748 CSSM_APPLE_TP_SSL_OPTS_VERSION, |
749 0, | 749 0, |
750 NULL, | 750 NULL, |
751 CSSM_APPLE_TP_SSL_CLIENT | 751 CSSM_APPLE_TP_SSL_CLIENT |
752 }; | 752 }; |
753 return CreatePolicy(&CSSMOID_APPLE_TP_SSL, | 753 return CreatePolicy(&CSSMOID_APPLE_TP_SSL, |
754 &tp_ssl_options, | 754 &tp_ssl_options, |
755 sizeof(tp_ssl_options), | 755 sizeof(tp_ssl_options), |
756 out_policy); | 756 out_policy); |
757 } | 757 } |
758 | 758 |
759 // static | 759 // static |
760 bool X509Certificate::GetSSLClientCertificates ( | 760 bool X509Certificate::GetSSLClientCertificates ( |
761 const std::string& server_domain, | 761 const std::string& server_domain, |
762 const std::vector<Principal>& valid_issuers, | |
763 std::vector<scoped_refptr<X509Certificate> >* certs) { | 762 std::vector<scoped_refptr<X509Certificate> >* certs) { |
764 scoped_cftyperef<SecIdentityRef> preferred_identity; | 763 scoped_cftyperef<SecIdentityRef> preferred_identity; |
765 if (!server_domain.empty()) { | 764 if (!server_domain.empty()) { |
766 // See if there's an identity preference for this domain: | 765 // See if there's an identity preference for this domain: |
767 scoped_cftyperef<CFStringRef> domain_str( | 766 scoped_cftyperef<CFStringRef> domain_str( |
768 base::SysUTF8ToCFStringRef("https://" + server_domain)); | 767 base::SysUTF8ToCFStringRef("https://" + server_domain)); |
769 SecIdentityRef identity = NULL; | 768 SecIdentityRef identity = NULL; |
770 if (SecIdentityCopyPreference(domain_str, | 769 if (SecIdentityCopyPreference(domain_str, |
771 0, | 770 0, |
772 NULL, // validIssuers argument is ignored :( | 771 NULL, |
773 &identity) == noErr) | 772 &identity) == noErr) |
774 preferred_identity.reset(identity); | 773 preferred_identity.reset(identity); |
775 } | 774 } |
776 | 775 |
777 // Now enumerate the identities in the available keychains. | |
778 SecIdentitySearchRef search = nil; | 776 SecIdentitySearchRef search = nil; |
779 OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); | 777 OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); |
780 scoped_cftyperef<SecIdentitySearchRef> scoped_search(search); | 778 scoped_cftyperef<SecIdentitySearchRef> scoped_search(search); |
781 while (!err) { | 779 while (!err) { |
782 SecIdentityRef identity = NULL; | 780 SecIdentityRef identity = NULL; |
783 err = SecIdentitySearchCopyNext(search, &identity); | 781 err = SecIdentitySearchCopyNext(search, &identity); |
784 if (err) | 782 if (err) |
785 break; | 783 break; |
786 scoped_cftyperef<SecIdentityRef> scoped_identity(identity); | 784 scoped_cftyperef<SecIdentityRef> scoped_identity(identity); |
787 | 785 |
(...skipping 12 matching lines...) Expand all Loading... |
800 // Skip duplicates (a cert may be in multiple keychains). | 798 // Skip duplicates (a cert may be in multiple keychains). |
801 X509Certificate::Fingerprint fingerprint = cert->fingerprint(); | 799 X509Certificate::Fingerprint fingerprint = cert->fingerprint(); |
802 unsigned i; | 800 unsigned i; |
803 for (i = 0; i < certs->size(); ++i) { | 801 for (i = 0; i < certs->size(); ++i) { |
804 if ((*certs)[i]->fingerprint().Equals(fingerprint)) | 802 if ((*certs)[i]->fingerprint().Equals(fingerprint)) |
805 break; | 803 break; |
806 } | 804 } |
807 if (i < certs->size()) | 805 if (i < certs->size()) |
808 continue; | 806 continue; |
809 | 807 |
810 bool is_preferred = preferred_identity && | |
811 CFEqual(preferred_identity, identity); | |
812 | |
813 // Make sure the issuer matches valid_issuers, if given. | |
814 // But an explicit cert preference overrides this. | |
815 if (!is_preferred && | |
816 valid_issuers.size() > 0 && | |
817 !cert->IsIssuedBy(valid_issuers)) | |
818 continue; | |
819 | |
820 // The cert passes, so add it to the vector. | 808 // The cert passes, so add it to the vector. |
821 // If it's the preferred identity, add it at the start (so it'll be | 809 // If it's the preferred identity, add it at the start (so it'll be |
822 // selected by default in the UI.) | 810 // selected by default in the UI.) |
823 if (is_preferred) | 811 if (preferred_identity && CFEqual(preferred_identity, identity)) |
824 certs->insert(certs->begin(), cert); | 812 certs->insert(certs->begin(), cert); |
825 else | 813 else |
826 certs->push_back(cert); | 814 certs->push_back(cert); |
827 } | 815 } |
828 | 816 |
829 if (err != errSecItemNotFound) { | 817 if (err != errSecItemNotFound) { |
830 LOG(ERROR) << "SecIdentitySearch error " << err; | 818 LOG(ERROR) << "SecIdentitySearch error " << err; |
831 return false; | 819 return false; |
832 } | 820 } |
833 return true; | 821 return true; |
834 } | 822 } |
835 | 823 |
836 CFArrayRef X509Certificate::CreateClientCertificateChain() const { | 824 CFArrayRef X509Certificate::CreateClientCertificateChain() const { |
837 // Initialize the result array with just the IdentityRef of the receiver: | 825 // Initialize the result array with just the IdentityRef of the receiver: |
838 OSStatus result; | 826 OSStatus result; |
839 SecIdentityRef identity; | 827 SecIdentityRef identity; |
840 result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity); | 828 result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity); |
841 if (result) { | 829 if (result) { |
842 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; | 830 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; |
843 return NULL; | 831 return NULL; |
844 } | 832 } |
845 scoped_cftyperef<CFMutableArrayRef> chain( | 833 scoped_cftyperef<CFMutableArrayRef> chain( |
846 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); | 834 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); |
847 CFArrayAppendValue(chain, identity); | 835 CFArrayAppendValue(chain, identity); |
848 | 836 |
849 CFArrayRef cert_chain = NULL; | 837 { |
850 result = CopyCertChain(cert_handle_, &cert_chain); | 838 // Create an SSL policy ref configured for client cert evaluation. |
851 if (result) | 839 SecPolicyRef ssl_policy; |
852 goto exit; | 840 result = CreateSSLClientPolicy(&ssl_policy); |
| 841 if (result) |
| 842 goto exit; |
| 843 scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy); |
853 | 844 |
854 // Append the intermediate certs from SecTrust to the result array: | 845 // Use a SecTrust object to find the intermediate certs in the trust chain. |
855 if (cert_chain) { | 846 scoped_cftyperef<CFArrayRef> input_certs( |
856 int chain_count = CFArrayGetCount(cert_chain); | 847 CFArrayCreate(NULL, (const void**)&cert_handle_, 1, |
857 if (chain_count > 1) { | 848 &kCFTypeArrayCallBacks)); |
858 CFArrayAppendArray(chain, | 849 SecTrustRef trust_ref = NULL; |
859 cert_chain, | 850 result = SecTrustCreateWithCertificates(input_certs, |
860 CFRangeMake(1, chain_count - 1)); | 851 ssl_policy, |
| 852 &trust_ref); |
| 853 if (result) |
| 854 goto exit; |
| 855 scoped_cftyperef<SecTrustRef> trust(trust_ref); |
| 856 |
| 857 SecTrustResultType status; |
| 858 CFArrayRef trust_chain = NULL; |
| 859 CSSM_TP_APPLE_EVIDENCE_INFO* status_chain; |
| 860 result = SecTrustEvaluate(trust, &status); |
| 861 if (result) |
| 862 goto exit; |
| 863 result = SecTrustGetResult(trust, &status, &trust_chain, &status_chain); |
| 864 if (result) |
| 865 goto exit; |
| 866 |
| 867 // Append the intermediate certs from SecTrust to the result array: |
| 868 if (trust_chain) { |
| 869 int chain_count = CFArrayGetCount(trust_chain); |
| 870 if (chain_count > 1) { |
| 871 CFArrayAppendArray(chain, |
| 872 trust_chain, |
| 873 CFRangeMake(1, chain_count - 1)); |
| 874 } |
| 875 CFRelease(trust_chain); |
861 } | 876 } |
862 CFRelease(cert_chain); | |
863 } | 877 } |
864 exit: | 878 exit: |
865 if (result) | 879 if (result) |
866 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; | 880 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; |
867 return chain.release(); | 881 return chain.release(); |
868 } | 882 } |
869 | 883 |
870 } // namespace net | 884 } // namespace net |
OLD | NEW |