Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(142)

Side by Side Diff: net/base/x509_certificate_mac.cc

Issue 1128008: Mac: Make client-cert picker only show certs the server will accept. (Closed)
Patch Set: Added a test case of parsing T61STRING. Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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>
8 #include <time.h> 9 #include <time.h>
9 10
10 #include "base/scoped_cftyperef.h" 11 #include "base/scoped_cftyperef.h"
11 #include "base/logging.h" 12 #include "base/logging.h"
12 #include "base/pickle.h" 13 #include "base/pickle.h"
13 #include "base/sys_string_conversions.h" 14 #include "base/sys_string_conversions.h"
14 #include "net/base/cert_status_flags.h" 15 #include "net/base/cert_status_flags.h"
15 #include "net/base/cert_verify_result.h" 16 #include "net/base/cert_verify_result.h"
16 #include "net/base/net_errors.h" 17 #include "net/base/net_errors.h"
17 18
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
73 74
74 void SetMacTestCertificate(X509Certificate* cert) { 75 void SetMacTestCertificate(X509Certificate* cert) {
75 Singleton<MacTrustedCertificates>::get()->SetTestCertificate(cert); 76 Singleton<MacTrustedCertificates>::get()->SetTestCertificate(cert);
76 } 77 }
77 78
78 namespace { 79 namespace {
79 80
80 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, 81 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
81 CFDictionaryRef*); 82 CFDictionaryRef*);
82 83
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
88 int NetErrorFromOSStatus(OSStatus status) { 84 int NetErrorFromOSStatus(OSStatus status) {
89 switch (status) { 85 switch (status) {
90 case noErr: 86 case noErr:
91 return OK; 87 return OK;
92 case errSecNotAvailable: 88 case errSecNotAvailable:
93 case errSecNoCertificateModule: 89 case errSecNoCertificateModule:
94 case errSecNoPolicyModule: 90 case errSecNoPolicyModule:
95 return ERR_NOT_IMPLEMENTED; 91 return ERR_NOT_IMPLEMENTED;
96 case errSecAuthFailed: 92 case errSecAuthFailed:
97 return ERR_ACCESS_DENIED; 93 return ERR_ACCESS_DENIED;
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
166 c != hostname.end() && is_dotted_ip; ++c) 162 c != hostname.end() && is_dotted_ip; ++c)
167 is_dotted_ip = (*c >= '0' && *c <= '9') || *c == '.'; 163 is_dotted_ip = (*c >= '0' && *c <= '9') || *c == '.';
168 if (is_dotted_ip) { 164 if (is_dotted_ip) {
169 for (std::vector<std::string>::const_iterator name = dns_names->begin(); 165 for (std::vector<std::string>::const_iterator name = dns_names->begin();
170 name != dns_names->end() && !override_hostname_mismatch; ++name) 166 name != dns_names->end() && !override_hostname_mismatch; ++name)
171 override_hostname_mismatch = (*name == hostname); 167 override_hostname_mismatch = (*name == hostname);
172 } 168 }
173 return override_hostname_mismatch; 169 return override_hostname_mismatch;
174 } 170 }
175 171
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
234 struct CSSMFields { 172 struct CSSMFields {
235 CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {} 173 CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {}
236 ~CSSMFields() { 174 ~CSSMFields() {
237 if (cl_handle) 175 if (cl_handle)
238 CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields); 176 CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields);
239 } 177 }
240 178
241 CSSM_CL_HANDLE cl_handle; 179 CSSM_CL_HANDLE cl_handle;
242 uint32 num_of_fields; 180 uint32 num_of_fields;
243 CSSM_FIELD_PTR fields; 181 CSSM_FIELD_PTR fields;
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 }; 317 };
380 err = SecPolicySetValue(*policy, &options_data); 318 err = SecPolicySetValue(*policy, &options_data);
381 if (err) { 319 if (err) {
382 CFRelease(*policy); 320 CFRelease(*policy);
383 return err; 321 return err;
384 } 322 }
385 } 323 }
386 return noErr; 324 return noErr;
387 } 325 }
388 326
327 // Gets the issuer chain of intermediate and root certs for a given cert.
328 // This function calls SecTrust but doesn't actually pay attention to the trust
329 // result: it shouldn't be used to determine trust, just to traverse the chain.
330 // Caller is responsible for releasing the value stored into *out_trust_chain.
331 OSStatus CopyCertChain(SecCertificateRef cert_handle,
wtc 2010/03/24 23:52:05 Nit: CopyCertChain also copies the entire cert cha
332 CFArrayRef* out_cert_chain) {
333 DCHECK(cert_handle && out_cert_chain);
334 // Create an SSL policy ref configured for client cert evaluation.
335 SecPolicyRef ssl_policy;
336 OSStatus result = X509Certificate::CreateSSLClientPolicy(&ssl_policy);
337 if (result)
338 return result;
339 scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
340
341 // Create a SecTrustRef.
342 scoped_cftyperef<CFArrayRef> input_certs(
343 CFArrayCreate(NULL, (const void**)&cert_handle, 1,
344 &kCFTypeArrayCallBacks));
345 SecTrustRef trust_ref = NULL;
346 result = SecTrustCreateWithCertificates(input_certs, ssl_policy, &trust_ref);
347 if (result)
348 return result;
349 scoped_cftyperef<SecTrustRef> trust(trust_ref);
350
351 // Evaluate trust, which creates the cert chain.
352 SecTrustResultType status;
353 CSSM_TP_APPLE_EVIDENCE_INFO* status_chain;
354 result = SecTrustEvaluate(trust, &status);
355 if (result)
356 return result;
357 return SecTrustGetResult(trust, &status, out_cert_chain, &status_chain);
358 }
359
389 } // namespace 360 } // namespace
390 361
391 void X509Certificate::Initialize() { 362 void X509Certificate::Initialize() {
392 const CSSM_X509_NAME* name; 363 const CSSM_X509_NAME* name;
393 OSStatus status = SecCertificateGetSubject(cert_handle_, &name); 364 OSStatus status = SecCertificateGetSubject(cert_handle_, &name);
394 if (!status) { 365 if (!status) {
395 ParsePrincipal(name, &subject_); 366 subject_.Parse(name);
396 } 367 }
397 status = SecCertificateGetIssuer(cert_handle_, &name); 368 status = SecCertificateGetIssuer(cert_handle_, &name);
398 if (!status) { 369 if (!status) {
399 ParsePrincipal(name, &issuer_); 370 issuer_.Parse(name);
400 } 371 }
401 372
402 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore, 373 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotBefore,
403 &valid_start_); 374 &valid_start_);
404 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotAfter, 375 GetCertDateForOID(cert_handle_, CSSMOID_X509V1ValidityNotAfter,
405 &valid_expiry_); 376 &valid_expiry_);
406 377
407 fingerprint_ = CalculateFingerprint(cert_handle_); 378 fingerprint_ = CalculateFingerprint(cert_handle_);
408 } 379 }
409 380
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after
735 } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_NetscapeCertType)) { 706 } else if (CSSMOIDEqual(&field.FieldOid, &CSSMOID_NetscapeCertType)) {
736 uint16_t flags = 707 uint16_t flags =
737 *reinterpret_cast<const uint16_t*>(ext->value.parsedValue); 708 *reinterpret_cast<const uint16_t*>(ext->value.parsedValue);
738 if (flags & CE_NCT_SSL_Client) 709 if (flags & CE_NCT_SSL_Client)
739 return true; 710 return true;
740 } 711 }
741 } 712 }
742 return false; 713 return false;
743 } 714 }
744 715
716 bool X509Certificate::IsIssuedBy(
717 const std::vector<CertPrincipal>& valid_issuers) {
718 // Get the cert's issuer chain.
719 CFArrayRef trust_chain = NULL;
wtc 2010/03/24 23:52:05 Nit: trust_chain => cert_chain scoped_trust_c
720 OSStatus result;
721 result = CopyCertChain(os_cert_handle(), &trust_chain);
722 if (result != noErr)
723 return false;
724 scoped_cftyperef<CFArrayRef> scoped_trust_chain(trust_chain);
725
726 // Check all the certs in the chain for a match.
727 int n = CFArrayGetCount(trust_chain);
728 for (int i = 0; i < n; ++i) {
729 SecCertificateRef intermediate_handle = reinterpret_cast<SecCertificateRef>(
wtc 2010/03/24 23:52:05 Nit: "intermediate" is misleading, because this ca
Jens Alfke 2010/03/26 17:15:04 It is, but we have to check the issuer of that cer
wtc 2010/03/26 21:03:22 In each iteration, you are checking intermediate->
730 const_cast<void*>(CFArrayGetValueAtIndex(trust_chain, i)));
731 CFRetain(intermediate_handle);
732 X509Certificate* intermediate = X509Certificate::CreateFromHandle(
wtc 2010/03/24 23:52:05 X509Certificate is reference counted, so always st
733 intermediate_handle,
734 X509Certificate::SOURCE_LONE_CERT_IMPORT,
735 X509Certificate::OSCertHandles());
736 for (unsigned j = 0; j < valid_issuers.size(); j++)
wtc 2010/03/24 23:52:05 Nit: use curly braces around the for loop's body,
737 if (intermediate->subject().Matches(valid_issuers[j])) {
738 return true;
739 }
740 }
741 return false;
742 }
743
745 // static 744 // static
746 OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) { 745 OSStatus X509Certificate::CreateSSLClientPolicy(SecPolicyRef* out_policy) {
747 CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = { 746 CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = {
748 CSSM_APPLE_TP_SSL_OPTS_VERSION, 747 CSSM_APPLE_TP_SSL_OPTS_VERSION,
749 0, 748 0,
750 NULL, 749 NULL,
751 CSSM_APPLE_TP_SSL_CLIENT 750 CSSM_APPLE_TP_SSL_CLIENT
752 }; 751 };
753 return CreatePolicy(&CSSMOID_APPLE_TP_SSL, 752 return CreatePolicy(&CSSMOID_APPLE_TP_SSL,
754 &tp_ssl_options, 753 &tp_ssl_options,
755 sizeof(tp_ssl_options), 754 sizeof(tp_ssl_options),
756 out_policy); 755 out_policy);
757 } 756 }
758 757
759 // static 758 // static
760 bool X509Certificate::GetSSLClientCertificates ( 759 bool X509Certificate::GetSSLClientCertificates (
761 const std::string& server_domain, 760 const std::string& server_domain,
761 const std::vector<Principal>& valid_issuers,
762 std::vector<scoped_refptr<X509Certificate> >* certs) { 762 std::vector<scoped_refptr<X509Certificate> >* certs) {
763 scoped_cftyperef<SecIdentityRef> preferred_identity; 763 scoped_cftyperef<SecIdentityRef> preferred_identity;
764 if (!server_domain.empty()) { 764 if (!server_domain.empty()) {
765 // See if there's an identity preference for this domain: 765 // See if there's an identity preference for this domain:
766 scoped_cftyperef<CFStringRef> domain_str( 766 scoped_cftyperef<CFStringRef> domain_str(
767 base::SysUTF8ToCFStringRef("https://" + server_domain)); 767 base::SysUTF8ToCFStringRef("https://" + server_domain));
768 SecIdentityRef identity = NULL; 768 SecIdentityRef identity = NULL;
769 if (SecIdentityCopyPreference(domain_str, 769 if (SecIdentityCopyPreference(domain_str,
770 0, 770 0,
771 NULL, 771 NULL,
wtc 2010/03/24 23:52:05 IMPORTANT: we can finally pass a real validIssuers
wtc 2010/03/25 01:00:35 I looked at the source code of SecIdentityCopyPref
772 &identity) == noErr) 772 &identity) == noErr)
773 preferred_identity.reset(identity); 773 preferred_identity.reset(identity);
774 } 774 }
775 775
776 // Now enumerate the identities in the available keychains.
776 SecIdentitySearchRef search = nil; 777 SecIdentitySearchRef search = nil;
777 OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search); 778 OSStatus err = SecIdentitySearchCreate(NULL, CSSM_KEYUSE_SIGN, &search);
778 scoped_cftyperef<SecIdentitySearchRef> scoped_search(search); 779 scoped_cftyperef<SecIdentitySearchRef> scoped_search(search);
779 while (!err) { 780 while (!err) {
780 SecIdentityRef identity = NULL; 781 SecIdentityRef identity = NULL;
781 err = SecIdentitySearchCopyNext(search, &identity); 782 err = SecIdentitySearchCopyNext(search, &identity);
782 if (err) 783 if (err)
783 break; 784 break;
784 scoped_cftyperef<SecIdentityRef> scoped_identity(identity); 785 scoped_cftyperef<SecIdentityRef> scoped_identity(identity);
785 786
(...skipping 12 matching lines...) Expand all
798 // Skip duplicates (a cert may be in multiple keychains). 799 // Skip duplicates (a cert may be in multiple keychains).
799 X509Certificate::Fingerprint fingerprint = cert->fingerprint(); 800 X509Certificate::Fingerprint fingerprint = cert->fingerprint();
800 unsigned i; 801 unsigned i;
801 for (i = 0; i < certs->size(); ++i) { 802 for (i = 0; i < certs->size(); ++i) {
802 if ((*certs)[i]->fingerprint().Equals(fingerprint)) 803 if ((*certs)[i]->fingerprint().Equals(fingerprint))
803 break; 804 break;
804 } 805 }
805 if (i < certs->size()) 806 if (i < certs->size())
806 continue; 807 continue;
807 808
809 bool is_preferred = preferred_identity &&
810 CFEqual(preferred_identity, identity);
811
812 // Make sure the issuer matches valid_issuers, if given.
813 // But an explicit cert preference overrides this.
814 if (!is_preferred &&
815 valid_issuers.size() > 0 &&
816 !cert->IsIssuedBy(valid_issuers))
817 continue;
818
808 // The cert passes, so add it to the vector. 819 // The cert passes, so add it to the vector.
809 // If it's the preferred identity, add it at the start (so it'll be 820 // If it's the preferred identity, add it at the start (so it'll be
810 // selected by default in the UI.) 821 // selected by default in the UI.)
811 if (preferred_identity && CFEqual(preferred_identity, identity)) 822 if (is_preferred)
812 certs->insert(certs->begin(), cert); 823 certs->insert(certs->begin(), cert);
813 else 824 else
814 certs->push_back(cert); 825 certs->push_back(cert);
815 } 826 }
816 827
817 if (err != errSecItemNotFound) { 828 if (err != errSecItemNotFound) {
818 LOG(ERROR) << "SecIdentitySearch error " << err; 829 LOG(ERROR) << "SecIdentitySearch error " << err;
819 return false; 830 return false;
820 } 831 }
821 return true; 832 return true;
822 } 833 }
823 834
824 CFArrayRef X509Certificate::CreateClientCertificateChain() const { 835 CFArrayRef X509Certificate::CreateClientCertificateChain() const {
825 // Initialize the result array with just the IdentityRef of the receiver: 836 // Initialize the result array with just the IdentityRef of the receiver:
826 OSStatus result; 837 OSStatus result;
827 SecIdentityRef identity; 838 SecIdentityRef identity;
828 result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity); 839 result = SecIdentityCreateWithCertificate(NULL, cert_handle_, &identity);
829 if (result) { 840 if (result) {
830 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result; 841 LOG(ERROR) << "SecIdentityCreateWithCertificate error " << result;
831 return NULL; 842 return NULL;
832 } 843 }
833 scoped_cftyperef<CFMutableArrayRef> chain( 844 scoped_cftyperef<CFMutableArrayRef> chain(
834 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks)); 845 CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks));
835 CFArrayAppendValue(chain, identity); 846 CFArrayAppendValue(chain, identity);
836 847
837 { 848 CFArrayRef trust_chain = NULL;
wtc 2010/03/24 23:52:05 Nit: trust_chain => cert_chain
838 // Create an SSL policy ref configured for client cert evaluation. 849 result = CopyCertChain(cert_handle_, &trust_chain);
839 SecPolicyRef ssl_policy; 850 if (result)
840 result = CreateSSLClientPolicy(&ssl_policy); 851 goto exit;
841 if (result)
842 goto exit;
843 scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
844 852
845 // Use a SecTrust object to find the intermediate certs in the trust chain. 853 // Append the intermediate certs from SecTrust to the result array:
846 scoped_cftyperef<CFArrayRef> input_certs( 854 if (trust_chain) {
847 CFArrayCreate(NULL, (const void**)&cert_handle_, 1, 855 int chain_count = CFArrayGetCount(trust_chain);
848 &kCFTypeArrayCallBacks)); 856 if (chain_count > 1) {
849 SecTrustRef trust_ref = NULL; 857 CFArrayAppendArray(chain,
850 result = SecTrustCreateWithCertificates(input_certs, 858 trust_chain,
851 ssl_policy, 859 CFRangeMake(1, chain_count - 1));
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);
876 } 860 }
861 CFRelease(trust_chain);
877 } 862 }
878 exit: 863 exit:
879 if (result) 864 if (result)
880 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; 865 LOG(ERROR) << "CreateIdentityCertificateChain error " << result;
881 return chain.release(); 866 return chain.release();
882 } 867 }
883 868
884 } // namespace net 869 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698