Chromium Code Reviews| 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 <time.h> | 8 #include <time.h> |
| 9 | 9 |
| 10 #include "base/scoped_cftyperef.h" | 10 #include "base/scoped_cftyperef.h" |
| 11 #include "base/logging.h" | 11 #include "base/logging.h" |
| 12 #include "base/sys_string_conversions.h" | |
|
wtc
2010/02/24 01:44:51
Nit: List "base/sys_string_conversions.h" after
"b
| |
| 12 #include "base/pickle.h" | 13 #include "base/pickle.h" |
| 13 #include "net/base/cert_status_flags.h" | 14 #include "net/base/cert_status_flags.h" |
| 14 #include "net/base/cert_verify_result.h" | 15 #include "net/base/cert_verify_result.h" |
| 15 #include "net/base/net_errors.h" | 16 #include "net/base/net_errors.h" |
| 16 | 17 |
| 17 using base::Time; | 18 using base::Time; |
| 18 | 19 |
| 19 namespace net { | 20 namespace net { |
| 20 | 21 |
| 21 class MacTrustedCertificates { | 22 class MacTrustedCertificates { |
| (...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 349 exploded.minute = time.tm_min; | 350 exploded.minute = time.tm_min; |
| 350 exploded.second = time.tm_sec; | 351 exploded.second = time.tm_sec; |
| 351 exploded.millisecond = 0; | 352 exploded.millisecond = 0; |
| 352 | 353 |
| 353 *result = Time::FromUTCExploded(exploded); | 354 *result = Time::FromUTCExploded(exploded); |
| 354 break; | 355 break; |
| 355 } | 356 } |
| 356 } | 357 } |
| 357 } | 358 } |
| 358 | 359 |
| 359 // Returns true if this cert supports a given extended key usage. | |
| 360 bool CertSupportsUsage(SecCertificateRef cert, const CSSM_OID& usage_oid) { | |
| 361 CSSMFields fields; | |
| 362 if (GetCertFields(cert, &fields) != noErr) | |
| 363 return false; | |
| 364 for (unsigned f = 0; f < fields.num_of_fields; ++f) { | |
| 365 const CSSM_FIELD &field = fields.fields[f]; | |
| 366 if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_ExtendedKeyUsage)) { | |
| 367 const CSSM_X509_EXTENSION* ext = | |
| 368 reinterpret_cast<const CSSM_X509_EXTENSION*>(field.FieldValue.Data); | |
| 369 const CE_ExtendedKeyUsage* usage = | |
| 370 reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue); | |
| 371 for (unsigned p = 0; p < usage->numPurposes; ++p) { | |
| 372 if (CSSMOIDEqual(&usage->purposes[p], &usage_oid)) | |
| 373 return true; | |
| 374 } | |
| 375 } | |
| 376 } | |
| 377 return false; | |
| 378 } | |
| 379 | |
| 380 // Creates a SecPolicyRef for the given OID, with optional value. | 360 // Creates a SecPolicyRef for the given OID, with optional value. |
| 381 OSStatus CreatePolicy(const CSSM_OID* policy_OID, | 361 OSStatus CreatePolicy(const CSSM_OID* policy_OID, |
| 382 void* option_data, | 362 void* option_data, |
| 383 size_t option_length, | 363 size_t option_length, |
| 384 SecPolicyRef* policy) { | 364 SecPolicyRef* policy) { |
| 385 SecPolicySearchRef search; | 365 SecPolicySearchRef search; |
| 386 OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_OID, NULL, | 366 OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_OID, NULL, |
| 387 &search); | 367 &search); |
| 388 if (err) | 368 if (err) |
| 389 return err; | 369 return err; |
| (...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 725 return sha1; | 705 return sha1; |
| 726 | 706 |
| 727 DCHECK(NULL != cert_data.Data); | 707 DCHECK(NULL != cert_data.Data); |
| 728 DCHECK(0 != cert_data.Length); | 708 DCHECK(0 != cert_data.Length); |
| 729 | 709 |
| 730 CC_SHA1(cert_data.Data, cert_data.Length, sha1.data); | 710 CC_SHA1(cert_data.Data, cert_data.Length, sha1.data); |
| 731 | 711 |
| 732 return sha1; | 712 return sha1; |
| 733 } | 713 } |
| 734 | 714 |
| 715 bool X509Certificate::SupportsSSLClientAuth() const { | |
| 716 CSSMFields fields; | |
| 717 if (GetCertFields(cert_handle_, &fields) != noErr) | |
| 718 return false; | |
| 719 for (unsigned f = 0; f < fields.num_of_fields; ++f) { | |
| 720 const CSSM_FIELD &field = fields.fields[f]; | |
|
wtc
2010/02/24 01:44:51
Nit: put & next to the type, CSSM_FIELD.
| |
| 721 const CSSM_X509_EXTENSION* ext = | |
| 722 reinterpret_cast<const CSSM_X509_EXTENSION*>(field.FieldValue.Data); | |
| 723 if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_ExtendedKeyUsage)) { | |
| 724 const CE_ExtendedKeyUsage* usage = | |
| 725 reinterpret_cast<const CE_ExtendedKeyUsage*>(ext->value.parsedValue); | |
| 726 for (unsigned p = 0; p < usage->numPurposes; ++p) { | |
| 727 if (CSSMOIDEqual(&usage->purposes[p], &CSSMOID_ClientAuth)) | |
| 728 return true; | |
| 729 } | |
| 730 } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_NetscapeCertType)) { | |
| 731 uint16_t flags = | |
| 732 *reinterpret_cast<const uint16_t*>(ext->value.parsedValue); | |
| 733 if (flags & CE_NCT_SSL_Client) | |
| 734 return true; | |
| 735 } | |
| 736 } | |
| 737 return false; | |
| 738 } | |
| 739 | |
| 735 // static | 740 // static |
| 736 OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { | 741 OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { |
| 737 CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = { | 742 CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = { |
| 738 CSSM_APPLE_TP_SSL_OPTS_VERSION, | 743 CSSM_APPLE_TP_SSL_OPTS_VERSION, |
| 739 0, | 744 0, |
| 740 NULL, | 745 NULL, |
| 741 CSSM_APPLE_TP_SSL_CLIENT | 746 CSSM_APPLE_TP_SSL_CLIENT |
| 742 }; | 747 }; |
| 743 return CreatePolicy(&CSSMOID_APPLE_TP_SSL, | 748 return CreatePolicy(&CSSMOID_APPLE_TP_SSL, |
| 744 &tp_ssl_options, | 749 &tp_ssl_options, |
| 745 sizeof(tp_ssl_options), | 750 sizeof(tp_ssl_options), |
| 746 out_policy); | 751 out_policy); |
| 747 } | 752 } |
| 748 | 753 |
| 749 // static | 754 // static |
| 750 bool X509Certificate::GetSSLClientCertificates ( | 755 bool X509Certificate::GetSSLClientCertificates ( |
| 756 std::string domain, | |
| 751 std::vector<scoped_refptr<X509Certificate> >* certs) { | 757 std::vector<scoped_refptr<X509Certificate> >* certs) { |
| 758 scoped_cftyperef<SecIdentityRef> preferred_identity; | |
| 759 if (domain != "") { | |
|
wtc
2010/02/24 01:44:51
Is it better to say
!domain.empty()
?
| |
| 760 // See if there's an identity preference for this domain: | |
| 761 scoped_cftyperef<CFStringRef> domain_str( | |
| 762 base::SysUTF8ToCFStringRef("https://" + domain)); | |
| 763 SecIdentityRef identity = NULL; | |
| 764 if (SecIdentityCopyPreference(domain_str, | |
|
wtc
2010/02/24 01:44:51
The first argument should also contain the port, i
wtc
2010/02/24 20:13:51
I see the difficulty of getting the port. I forgo
| |
| 765 0, | |
| 766 NULL, | |
| 767 &identity) == noErr) | |
| 768 preferred_identity.reset(identity); | |
| 769 } | |
| 770 | |
| 752 SecIdentitySearchRef search = nil; | 771 SecIdentitySearchRef search = nil; |
| 753 OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); | 772 OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); |
| 754 fprintf(stderr,"====Created SecIdentitySearch %p\n",search);//TEMP | 773 fprintf(stderr,"====Created SecIdentitySearch %p\n",search);//TEMP |
| 755 scoped_cftyperef<SecIdentitySearchRef> scoped_search(search); | 774 scoped_cftyperef<SecIdentitySearchRef> scoped_search(search); |
| 756 while (!err) { | 775 while (!err) { |
| 757 SecIdentityRef identity = NULL; | 776 SecIdentityRef identity = NULL; |
| 758 err = SecIdentitySearchCopyNext(search, &identity); | 777 err = SecIdentitySearchCopyNext(search, &identity); |
| 759 if (err) | 778 if (err) |
| 760 break; | 779 break; |
| 761 scoped_cftyperef<SecIdentityRef> scoped_identity(identity); | 780 scoped_cftyperef<SecIdentityRef> scoped_identity(identity); |
| 762 | 781 |
| 763 SecCertificateRef cert_handle; | 782 SecCertificateRef cert_handle; |
| 764 err = SecIdentityCopyCertificate(identity, &cert_handle); | 783 err = SecIdentityCopyCertificate(identity, &cert_handle); |
| 765 if (err != noErr) | 784 if (err != noErr) |
| 766 continue; | 785 continue; |
| 767 | 786 |
| 768 scoped_refptr<X509Certificate> cert( | 787 scoped_refptr<X509Certificate> cert( |
| 769 CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT)); | 788 CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT)); |
| 770 // cert_handle is adoped by cert, so I don't need to release it myself. | 789 // cert_handle is adoped by cert, so I don't need to release it myself. |
| 771 if (cert->HasExpired() || | 790 if (cert->HasExpired() || !cert->SupportsSSLClientAuth()) |
| 772 !CertSupportsUsage(cert_handle, CSSMOID_ClientAuth)) | |
| 773 continue; | 791 continue; |
| 774 | 792 |
| 775 // Skip duplicates (a cert may be in multiple keychains). | 793 // Skip duplicates (a cert may be in multiple keychains). |
| 776 X509Certificate::Fingerprint fingerprint = cert->fingerprint(); | 794 X509Certificate::Fingerprint fingerprint = cert->fingerprint(); |
| 777 unsigned i; | 795 unsigned i; |
| 778 for (i = 0; i < certs->size(); ++i) { | 796 for (i = 0; i < certs->size(); ++i) { |
| 779 if ((*certs)[i]->fingerprint().Equals(fingerprint)) | 797 if ((*certs)[i]->fingerprint().Equals(fingerprint)) |
| 780 break; | 798 break; |
| 781 } | 799 } |
| 782 if (i < certs->size()) | 800 if (i < certs->size()) |
| 783 continue; | 801 continue; |
| 784 | 802 |
| 785 // The cert passes, so add it to the vector. | 803 // The cert passes, so add it to the vector. |
| 786 certs->push_back(cert); | 804 // If it's the preferred identity, add it at the start (so it'll be |
| 805 // selected by default in the UI.) | |
|
wtc
2010/02/24 01:44:51
Nit: the period (.) should be after the closing pa
| |
| 806 if (preferred_identity && CFEqual(preferred_identity, identity)) | |
| 807 certs->insert(certs->begin(), cert); | |
| 808 else | |
| 809 certs->push_back(cert); | |
| 787 } | 810 } |
| 788 | 811 |
| 789 if (err != errSecItemNotFound) { | 812 if (err != errSecItemNotFound) { |
| 790 LOG(ERROR) << "SecIdentitySearch error " << err; | 813 LOG(ERROR) << "SecIdentitySearch error " << err; |
| 791 return false; | 814 return false; |
| 792 } | 815 } |
| 793 return true; | 816 return true; |
| 794 } | 817 } |
| 795 | 818 |
| 796 CFArrayRef X509Certificate::CreateClientCertificateChain() { | 819 CFArrayRef X509Certificate::CreateClientCertificateChain() const { |
| 797 // Initialize the result array with just the IdentityRef of the receiver: | 820 // Initialize the result array with just the IdentityRef of the receiver: |
| 798 OSStatus result; | 821 OSStatus result; |
| 799 SecIdentityRef identity; | 822 SecIdentityRef identity; |
| 800 result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity); | 823 result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity); |
| 801 if (result) { | 824 if (result) { |
| 802 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; | 825 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; |
| 803 return NULL; | 826 return NULL; |
| 804 } | 827 } |
| 805 scoped_cftyperef<CFMutableArrayRef> chain( | 828 scoped_cftyperef<CFMutableArrayRef> chain( |
| 806 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); | 829 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 847 CFRelease(trust_chain); | 870 CFRelease(trust_chain); |
| 848 } | 871 } |
| 849 } | 872 } |
| 850 exit: | 873 exit: |
| 851 if (result) | 874 if (result) |
| 852 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; | 875 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; |
| 853 return chain.release(); | 876 return chain.release(); |
| 854 } | 877 } |
| 855 | 878 |
| 856 } // namespace net | 879 } // namespace net |
| OLD | NEW |