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

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

Issue 3112013: Move chain building/verification out of X509Certificate (Closed)
Patch Set: Rebase to trunk - Without OpenSSL fixes Created 10 years, 2 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 <Security/Security.h>
9 #include <time.h> 9 #include <time.h>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "base/pickle.h" 12 #include "base/pickle.h"
13 #include "base/scoped_cftyperef.h" 13 #include "base/scoped_cftyperef.h"
14 #include "base/sys_string_conversions.h" 14 #include "base/sys_string_conversions.h"
15 #include "net/base/cert_status_flags.h"
16 #include "net/base/cert_verify_result.h"
17 #include "net/base/net_errors.h"
18 15
19 using base::Time; 16 using base::Time;
20 17
21 namespace net { 18 namespace net {
22 19
23 class MacTrustedCertificates {
24 public:
25 // Sets the trusted root certificate used by tests. Call with |cert| set
26 // to NULL to clear the test certificate.
27 void SetTestCertificate(X509Certificate* cert) {
28 AutoLock lock(lock_);
29 test_certificate_ = cert;
30 }
31
32 // Returns an array containing the trusted certificates for use with
33 // SecTrustSetAnchorCertificates(). Returns NULL if the system-supplied
34 // list of trust anchors is acceptable (that is, there is not test
35 // certificate available). Ownership follows the Create Rule (caller
36 // is responsible for calling CFRelease on the non-NULL result).
37 CFArrayRef CopyTrustedCertificateArray() {
38 AutoLock lock(lock_);
39
40 if (!test_certificate_)
41 return NULL;
42
43 // Failure to copy the anchor certificates or add the test certificate
44 // is non-fatal; SecTrustEvaluate() will use the system anchors instead.
45 CFArrayRef anchor_array;
46 OSStatus status = SecTrustCopyAnchorCertificates(&anchor_array);
47 if (status)
48 return NULL;
49 scoped_cftyperef<CFArrayRef> scoped_anchor_array(anchor_array);
50 CFMutableArrayRef merged_array = CFArrayCreateMutableCopy(
51 kCFAllocatorDefault, 0, anchor_array);
52 if (!merged_array)
53 return NULL;
54 CFArrayAppendValue(merged_array, test_certificate_->os_cert_handle());
55
56 return merged_array;
57 }
58 private:
59 friend struct DefaultSingletonTraits<MacTrustedCertificates>;
60
61 // Obtain an instance of MacTrustedCertificates via the singleton
62 // interface.
63 MacTrustedCertificates() : test_certificate_(NULL) { }
64
65 // An X509Certificate object that may be appended to the list of
66 // system trusted anchors.
67 scoped_refptr<X509Certificate> test_certificate_;
68
69 // The trusted cache may be accessed from multiple threads.
70 mutable Lock lock_;
71
72 DISALLOW_COPY_AND_ASSIGN(MacTrustedCertificates);
73 };
74
75 void SetMacTestCertificate(X509Certificate* cert) {
76 Singleton<MacTrustedCertificates>::get()->SetTestCertificate(cert);
77 }
78
79 namespace { 20 namespace {
80
81 typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef,
82 CFDictionaryRef*);
83
84 int NetErrorFromOSStatus(OSStatus status) {
85 switch (status) {
86 case noErr:
87 return OK;
88 case errSecNotAvailable:
89 case errSecNoCertificateModule:
90 case errSecNoPolicyModule:
91 return ERR_NOT_IMPLEMENTED;
92 case errSecAuthFailed:
93 return ERR_ACCESS_DENIED;
94 default:
95 LOG(ERROR) << "Unknown error " << status << " mapped to net::ERR_FAILED";
96 return ERR_FAILED;
97 }
98 }
99
100 int CertStatusFromOSStatus(OSStatus status) {
101 switch (status) {
102 case noErr:
103 return 0;
104
105 case CSSMERR_TP_INVALID_ANCHOR_CERT:
106 case CSSMERR_TP_NOT_TRUSTED:
107 case CSSMERR_TP_INVALID_CERT_AUTHORITY:
108 return CERT_STATUS_AUTHORITY_INVALID;
109
110 case CSSMERR_TP_CERT_EXPIRED:
111 case CSSMERR_TP_CERT_NOT_VALID_YET:
112 // "Expired" and "not yet valid" collapse into a single status.
113 return CERT_STATUS_DATE_INVALID;
114
115 case CSSMERR_TP_CERT_REVOKED:
116 case CSSMERR_TP_CERT_SUSPENDED:
117 return CERT_STATUS_REVOKED;
118
119 case CSSMERR_APPLETP_HOSTNAME_MISMATCH:
120 return CERT_STATUS_COMMON_NAME_INVALID;
121
122 case CSSMERR_APPLETP_CRL_NOT_FOUND:
123 case CSSMERR_APPLETP_INCOMPLETE_REVOCATION_CHECK:
124 case CSSMERR_APPLETP_OCSP_UNAVAILABLE:
125 return CERT_STATUS_NO_REVOCATION_MECHANISM;
126
127 case CSSMERR_APPLETP_CRL_NOT_TRUSTED:
128 case CSSMERR_APPLETP_CRL_SERVER_DOWN:
129 case CSSMERR_APPLETP_CRL_NOT_VALID_YET:
130 case CSSMERR_APPLETP_NETWORK_FAILURE:
131 case CSSMERR_APPLETP_OCSP_BAD_RESPONSE:
132 case CSSMERR_APPLETP_OCSP_NO_SIGNER:
133 case CSSMERR_APPLETP_OCSP_RESP_UNAUTHORIZED:
134 case CSSMERR_APPLETP_OCSP_RESP_SIG_REQUIRED:
135 case CSSMERR_APPLETP_OCSP_RESP_MALFORMED_REQ:
136 case CSSMERR_APPLETP_OCSP_RESP_INTERNAL_ERR:
137 case CSSMERR_APPLETP_OCSP_RESP_TRY_LATER:
138 // We asked for a revocation check, but didn't get it.
139 return CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
140
141 default:
142 // Failure was due to something Chromium doesn't define a
143 // specific status for (such as basic constraints violation, or
144 // unknown critical extension)
145 return CERT_STATUS_INVALID;
146 }
147 }
148
149 bool OverrideHostnameMismatch(const std::string& hostname,
150 std::vector<std::string>* dns_names) {
151 // SecTrustEvaluate() does not check dotted IP addresses. If
152 // hostname is provided as, say, 127.0.0.1, then the error
153 // CSSMERR_APPLETP_HOSTNAME_MISMATCH will always be returned,
154 // even if the certificate contains 127.0.0.1 as one of its names.
155 // We, however, want to allow that behavior. SecTrustEvaluate()
156 // only checks for digits and dots when considering whether a
157 // hostname is an IP address, so IPv6 and hex addresses go through
158 // its normal comparison.
159 bool is_dotted_ip = true;
160 bool override_hostname_mismatch = false;
161 for (std::string::const_iterator c = hostname.begin();
162 c != hostname.end() && is_dotted_ip; ++c)
163 is_dotted_ip = (*c >= '0' && *c <= '9') || *c == '.';
164 if (is_dotted_ip) {
165 for (std::vector<std::string>::const_iterator name = dns_names->begin();
166 name != dns_names->end() && !override_hostname_mismatch; ++name)
167 override_hostname_mismatch = (*name == hostname);
168 }
169 return override_hostname_mismatch;
170 }
171
172 struct CSSMFields { 21 struct CSSMFields {
173 CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {} 22 CSSMFields() : cl_handle(NULL), num_of_fields(0), fields(NULL) {}
174 ~CSSMFields() { 23 ~CSSMFields() {
175 if (cl_handle) 24 if (cl_handle)
176 CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields); 25 CSSM_CL_FreeFields(cl_handle, num_of_fields, &fields);
177 } 26 }
178 27
179 CSSM_CL_HANDLE cl_handle; 28 CSSM_CL_HANDLE cl_handle;
180 uint32 num_of_fields; 29 uint32 num_of_fields;
181 CSSM_FIELD_PTR fields; 30 CSSM_FIELD_PTR fields;
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
288 exploded.minute = time.tm_min; 137 exploded.minute = time.tm_min;
289 exploded.second = time.tm_sec; 138 exploded.second = time.tm_sec;
290 exploded.millisecond = 0; 139 exploded.millisecond = 0;
291 140
292 *result = Time::FromUTCExploded(exploded); 141 *result = Time::FromUTCExploded(exploded);
293 break; 142 break;
294 } 143 }
295 } 144 }
296 } 145 }
297 146
147 // Serializes the |cert_handle| into |pickle|, such that it may be unserialized
148 // by ReadCertHandleFromPickle(). Returns FALSE on failure.
149 bool WriteCertHandleToPickle(X509Certificate::OSCertHandle cert_handle,
150 Pickle* pickle) {
151 CSSM_DATA cert_data;
152 OSStatus status = SecCertificateGetData(cert_handle, &cert_data);
153 if (status) {
154 return false;
155 }
156 return pickle->WriteData(reinterpret_cast<char*>(cert_data.Data),
157 cert_data.Length);
158 }
159
160 // Reads a previously serialized OSCertHandle from the specified |pickle|. If
161 // an error is encountered, returns NULL.
162 SecCertificateRef ReadCertHandleFromPickle(const Pickle& pickle,
163 void** pickle_iter) {
164 const char* data;
165 int length;
166 if (!pickle.ReadData(pickle_iter, &data, &length))
167 return NULL;
168
169 return X509Certificate::CreateOSCertHandleFromBytes(data, length);
170 }
171
298 // Creates a SecPolicyRef for the given OID, with optional value. 172 // Creates a SecPolicyRef for the given OID, with optional value.
299 OSStatus CreatePolicy(const CSSM_OID* policy_OID, 173 OSStatus CreatePolicy(const CSSM_OID* policy_OID,
300 void* option_data, 174 void* option_data,
301 size_t option_length, 175 size_t option_length,
302 SecPolicyRef* policy) { 176 SecPolicyRef* policy) {
303 SecPolicySearchRef search; 177 SecPolicySearchRef search;
304 OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_OID, NULL, 178 OSStatus err = SecPolicySearchCreate(CSSM_CERT_X_509v3, policy_OID, NULL,
305 &search); 179 &search);
306 if (err) 180 if (err)
307 return err; 181 return err;
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after
479 return NULL; 353 return NULL;
480 354
481 CFArrayAppendValue(cert_list, cert_handle_); 355 CFArrayAppendValue(cert_list, cert_handle_);
482 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { 356 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
483 CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]); 357 CFArrayAppendValue(cert_list, intermediate_ca_certs_[i]);
484 } 358 }
485 359
486 return cert_list; 360 return cert_list;
487 } 361 }
488 362
489 int X509Certificate::Verify(const std::string& hostname, int flags,
490 CertVerifyResult* verify_result) const {
491 verify_result->Reset();
492
493 // Create an SSL SecPolicyRef, and configure it to perform hostname
494 // validation. The hostname check does 99% of what we want, with the
495 // exception of dotted IPv4 addreses, which we handle ourselves below.
496 CSSM_APPLE_TP_SSL_OPTIONS tp_ssl_options = {
497 CSSM_APPLE_TP_SSL_OPTS_VERSION,
498 hostname.size(),
499 hostname.data(),
500 0
501 };
502 SecPolicyRef ssl_policy;
503 OSStatus status = CreatePolicy(&CSSMOID_APPLE_TP_SSL,
504 &tp_ssl_options,
505 sizeof(tp_ssl_options),
506 &ssl_policy);
507 if (status)
508 return NetErrorFromOSStatus(status);
509 scoped_cftyperef<SecPolicyRef> scoped_ssl_policy(ssl_policy);
510
511 // Create and configure a SecTrustRef, which takes our certificate(s)
512 // and our SSL SecPolicyRef. SecTrustCreateWithCertificates() takes an
513 // array of certificates, the first of which is the certificate we're
514 // verifying, and the subsequent (optional) certificates are used for
515 // chain building.
516 scoped_cftyperef<CFArrayRef> cert_list(CreateOSCertListHandle());
517
518 // From here on, only one thread can be active at a time. We have had a number
519 // of sporadic crashes in the SecTrustEvaluate call below, way down inside
520 // Apple's cert code, which we suspect are caused by a thread-safety issue.
521 // So as a speculative fix allow only one thread to use SecTrust on this cert.
522 AutoLock lock(verification_lock_);
523
524 SecTrustRef trust_ref = NULL;
525 status = SecTrustCreateWithCertificates(cert_list, ssl_policy, &trust_ref);
526 if (status)
527 return NetErrorFromOSStatus(status);
528 scoped_cftyperef<SecTrustRef> scoped_trust_ref(trust_ref);
529
530 // Set the trusted anchor certificates for the SecTrustRef by merging the
531 // system trust anchors and the test root certificate.
532 CFArrayRef anchor_array =
533 Singleton<MacTrustedCertificates>::get()->CopyTrustedCertificateArray();
534 scoped_cftyperef<CFArrayRef> scoped_anchor_array(anchor_array);
535 if (anchor_array) {
536 status = SecTrustSetAnchorCertificates(trust_ref, anchor_array);
537 if (status)
538 return NetErrorFromOSStatus(status);
539 }
540
541 if (flags & VERIFY_REV_CHECKING_ENABLED) {
542 // When called with VERIFY_REV_CHECKING_ENABLED, we ask SecTrustEvaluate()
543 // to apply OCSP and CRL checking, but we're still subject to the global
544 // settings, which are configured in the Keychain Access application (in
545 // the Certificates tab of the Preferences dialog). If the user has
546 // revocation disabled (which is the default), then we will get
547 // kSecTrustResultRecoverableTrustFailure back from SecTrustEvaluate()
548 // with one of a number of sub error codes indicating that revocation
549 // checking did not occur. In that case, we'll set our own result to include
550 // CERT_STATUS_UNABLE_TO_CHECK_REVOCATION.
551 //
552 // NOTE: This does not apply to EV certificates, which always get
553 // revocation checks regardless of the global settings.
554 verify_result->cert_status |= CERT_STATUS_REV_CHECKING_ENABLED;
555 CSSM_APPLE_TP_ACTION_DATA tp_action_data = { CSSM_APPLE_TP_ACTION_VERSION };
556 tp_action_data.ActionFlags = CSSM_TP_ACTION_REQUIRE_REV_PER_CERT;
557 CFDataRef action_data_ref =
558 CFDataCreate(NULL, reinterpret_cast<UInt8*>(&tp_action_data),
559 sizeof(tp_action_data));
560 if (!action_data_ref)
561 return ERR_OUT_OF_MEMORY;
562 scoped_cftyperef<CFDataRef> scoped_action_data_ref(action_data_ref);
563 status = SecTrustSetParameters(trust_ref, CSSM_TP_ACTION_DEFAULT,
564 action_data_ref);
565 if (status)
566 return NetErrorFromOSStatus(status);
567 }
568
569 // Verify the certificate. A non-zero result from SecTrustGetResult()
570 // indicates that some fatal error occurred and the chain couldn't be
571 // processed, not that the chain contains no errors. We need to examine the
572 // output of SecTrustGetResult() to determine that.
573 SecTrustResultType trust_result;
574 status = SecTrustEvaluate(trust_ref, &trust_result);
575 if (status)
576 return NetErrorFromOSStatus(status);
577 CFArrayRef completed_chain = NULL;
578 CSSM_TP_APPLE_EVIDENCE_INFO* chain_info;
579 status = SecTrustGetResult(trust_ref, &trust_result, &completed_chain,
580 &chain_info);
581 if (status)
582 return NetErrorFromOSStatus(status);
583 scoped_cftyperef<CFArrayRef> scoped_completed_chain(completed_chain);
584
585 // Evaluate the results
586 OSStatus cssm_result;
587 bool got_certificate_error = false;
588 switch (trust_result) {
589 case kSecTrustResultUnspecified:
590 case kSecTrustResultProceed:
591 // Certificate chain is valid and trusted ("unspecified" indicates that
592 // the user has not explicitly set a trust setting)
593 break;
594
595 case kSecTrustResultDeny:
596 case kSecTrustResultConfirm:
597 // Certificate chain is explicitly untrusted. For kSecTrustResultConfirm,
598 // we're following what Secure Transport does and treating it as
599 // "deny".
600 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
601 break;
602
603 case kSecTrustResultRecoverableTrustFailure:
604 // Certificate chain has a failure that can be overridden by the user.
605 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
606 if (status)
607 return NetErrorFromOSStatus(status);
608 switch (cssm_result) {
609 case CSSMERR_TP_NOT_TRUSTED:
610 case CSSMERR_TP_INVALID_ANCHOR_CERT:
611 verify_result->cert_status |= CERT_STATUS_AUTHORITY_INVALID;
612 break;
613 case CSSMERR_TP_CERT_EXPIRED:
614 case CSSMERR_TP_CERT_NOT_VALID_YET:
615 verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
616 break;
617 case CSSMERR_TP_CERT_REVOKED:
618 case CSSMERR_TP_CERT_SUSPENDED:
619 verify_result->cert_status |= CERT_STATUS_REVOKED;
620 break;
621 default:
622 // Look for specific per-certificate errors below.
623 break;
624 }
625 // Walk the chain of error codes in the CSSM_TP_APPLE_EVIDENCE_INFO
626 // structure which can catch multiple errors from each certificate.
627 for (CFIndex index = 0, chain_count = CFArrayGetCount(completed_chain);
628 index < chain_count; ++index) {
629 if (chain_info[index].StatusBits & CSSM_CERT_STATUS_EXPIRED ||
630 chain_info[index].StatusBits & CSSM_CERT_STATUS_NOT_VALID_YET)
631 verify_result->cert_status |= CERT_STATUS_DATE_INVALID;
632 for (uint32 status_code_index = 0;
633 status_code_index < chain_info[index].NumStatusCodes;
634 ++status_code_index) {
635 got_certificate_error = true;
636 int cert_status = CertStatusFromOSStatus(
637 chain_info[index].StatusCodes[status_code_index]);
638 if (cert_status == CERT_STATUS_COMMON_NAME_INVALID) {
639 std::vector<std::string> names;
640 GetDNSNames(&names);
641 if (OverrideHostnameMismatch(hostname, &names)) {
642 cert_status = 0;
643 }
644 }
645 verify_result->cert_status |= cert_status;
646 }
647 }
648 // Be paranoid and ensure that we recorded at least one certificate
649 // status on receiving kSecTrustResultRecoverableTrustFailure. The
650 // call to SecTrustGetCssmResultCode() should pick up when the chain
651 // is not trusted and the loop through CSSM_TP_APPLE_EVIDENCE_INFO
652 // should pick up everything else, but let's be safe.
653 if (!verify_result->cert_status && !got_certificate_error) {
654 verify_result->cert_status |= CERT_STATUS_INVALID;
655 NOTREACHED();
656 }
657 break;
658
659 default:
660 status = SecTrustGetCssmResultCode(trust_ref, &cssm_result);
661 if (status)
662 return NetErrorFromOSStatus(status);
663 verify_result->cert_status |= CertStatusFromOSStatus(cssm_result);
664 if (!verify_result->cert_status) {
665 verify_result->cert_status |= CERT_STATUS_INVALID;
666 }
667 break;
668 }
669
670 // TODO(wtc): Suppress CERT_STATUS_NO_REVOCATION_MECHANISM for now to be
671 // compatible with Windows, which in turn implements this behavior to be
672 // compatible with WinHTTP, which doesn't report this error (bug 3004).
673 verify_result->cert_status &= ~CERT_STATUS_NO_REVOCATION_MECHANISM;
674
675 if (IsCertStatusError(verify_result->cert_status))
676 return MapCertStatusToNetError(verify_result->cert_status);
677
678 if (flags & VERIFY_EV_CERT) {
679 // Determine the certificate's EV status using SecTrustCopyExtendedResult(),
680 // which we need to look up because the function wasn't added until
681 // Mac OS X 10.5.7.
682 CFBundleRef bundle =
683 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security"));
684 if (bundle) {
685 SecTrustCopyExtendedResultFuncPtr copy_extended_result =
686 reinterpret_cast<SecTrustCopyExtendedResultFuncPtr>(
687 CFBundleGetFunctionPointerForName(bundle,
688 CFSTR("SecTrustCopyExtendedResult")));
689 if (copy_extended_result) {
690 CFDictionaryRef ev_dict = NULL;
691 status = copy_extended_result(trust_ref, &ev_dict);
692 if (!status && ev_dict) {
693 // The returned dictionary contains the EV organization name from the
694 // server certificate, which we don't need at this point (and we
695 // have other ways to access, anyway). All we care is that
696 // SecTrustCopyExtendedResult() returned noErr and a non-NULL
697 // dictionary.
698 CFRelease(ev_dict);
699 verify_result->cert_status |= CERT_STATUS_IS_EV;
700 }
701 }
702 }
703 }
704
705 return OK;
706 }
707
708 bool X509Certificate::VerifyEV() const {
709 // We don't call this private method, but we do need to implement it because
710 // it's defined in x509_certificate.h. We perform EV checking in the
711 // Verify() above.
712 NOTREACHED();
713 return false;
714 }
715
716 // static 363 // static
717 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, 364 bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a,
718 X509Certificate::OSCertHandle b) { 365 X509Certificate::OSCertHandle b) {
719 DCHECK(a && b); 366 DCHECK(a && b);
720 if (a == b) 367 if (a == b)
721 return true; 368 return true;
722 if (CFEqual(a, b)) 369 if (CFEqual(a, b))
723 return true; 370 return true;
724 CSSM_DATA a_data, b_data; 371 CSSM_DATA a_data, b_data;
725 return SecCertificateGetData(a, &a_data) == noErr && 372 return SecCertificateGetData(a, &a_data) == noErr &&
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after
1015 } 662 }
1016 CFRelease(cert_chain); 663 CFRelease(cert_chain);
1017 } 664 }
1018 exit: 665 exit:
1019 if (result) 666 if (result)
1020 LOG(ERROR) << "CreateIdentityCertificateChain error " << result; 667 LOG(ERROR) << "CreateIdentityCertificateChain error " << result;
1021 return chain.release(); 668 return chain.release();
1022 } 669 }
1023 670
1024 } // namespace net 671 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698