| 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 |