diff options
author | hawk@chromium.org <hawk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-27 17:49:41 +0000 |
---|---|---|
committer | hawk@chromium.org <hawk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-08-27 17:49:41 +0000 |
commit | 010e27ec98de24f68648b8c3ac68f3408f0578c0 (patch) | |
tree | 5d598c11cc366a4dd778f9d5aaed0ab62b082d05 /net/socket/ssl_client_socket_mac.cc | |
parent | 7cb43d53404c33f90398ba6217dc9645400e9c8e (diff) | |
download | chromium_src-010e27ec98de24f68648b8c3ac68f3408f0578c0.zip chromium_src-010e27ec98de24f68648b8c3ac68f3408f0578c0.tar.gz chromium_src-010e27ec98de24f68648b8c3ac68f3408f0578c0.tar.bz2 |
Enable SSLClientSocketTest unit tests on Mac OS X by implementing our own certificate validation code. This gives us proper hostname matching, multiple error codes (e.g., before a certificate could be marked as expired or untrusted, but not both), revocation checking, and EV certificate checking.
BUG=19286,10910,14733
TEST=https://www.paypal.com should work without warning. https://paypal.com should get a warning about a hostname mismatch. https://test-ssev.verisign.com:1443/test-SSEV-expired-verisign.html should give a warning about an expired certificate.
Review URL: http://codereview.chromium.org/174102
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24625 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/socket/ssl_client_socket_mac.cc')
-rw-r--r-- | net/socket/ssl_client_socket_mac.cc | 109 |
1 files changed, 64 insertions, 45 deletions
diff --git a/net/socket/ssl_client_socket_mac.cc b/net/socket/ssl_client_socket_mac.cc index 6684398..56b19d1 100644 --- a/net/socket/ssl_client_socket_mac.cc +++ b/net/socket/ssl_client_socket_mac.cc @@ -4,8 +4,10 @@ #include "net/socket/ssl_client_socket_mac.h" +#include "base/scoped_cftyperef.h" #include "base/singleton.h" #include "base/string_util.h" +#include "net/base/cert_verifier.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" #include "net/base/ssl_info.h" @@ -276,7 +278,6 @@ SSLClientSocketMac::SSLClientSocketMac(ClientSocket* transport_socket, user_callback_(NULL), next_state_(STATE_NONE), next_io_state_(STATE_NONE), - server_cert_status_(0), completed_handshake_(false), ssl_context_(NULL), pending_send_error_(OK), @@ -325,23 +326,11 @@ int SSLClientSocketMac::Connect(CompletionCallback* callback) { if (status) return NetErrorFromOSStatus(status); - if (ssl_config_.allowed_bad_certs.empty()) { - // We're going to use the default certificate verification that the system - // does, and accept its answer for the cert status. - status = SSLSetPeerDomainName(ssl_context_, hostname_.data(), - hostname_.length()); - if (status) - return NetErrorFromOSStatus(status); - - // TODO(wtc): for now, always check revocation. - server_cert_status_ = CERT_STATUS_REV_CHECKING_ENABLED; - } else { - // Disable certificate chain validation. We will only allow the certs in - // ssl_config_.allowed_bad_certs. - status = SSLSetEnableCertVerify(ssl_context_, false); - if (status) - return NetErrorFromOSStatus(status); - } + // Disable certificate verification within Secure Transport; we'll + // be handling that ourselves. + status = SSLSetEnableCertVerify(ssl_context_, false); + if (status) + return NetErrorFromOSStatus(status); next_state_ = STATE_HANDSHAKE; int rv = DoLoop(OK); @@ -430,7 +419,7 @@ void SSLClientSocketMac::GetSSLInfo(SSLInfo* ssl_info) { ssl_info->cert = server_cert_; // update status - ssl_info->cert_status = server_cert_status_; + ssl_info->cert_status = server_cert_verify_result_.cert_status; // security info SSLCipherSuite suite; @@ -483,6 +472,14 @@ int SSLClientSocketMac::DoLoop(int last_io_result) { // Do the SSL/TLS handshake. rv = DoHandshake(); break; + case STATE_VERIFY_CERT: + // Kick off server certificate validation. + rv = DoVerifyCert(); + break; + case STATE_VERIFY_CERT_COMPLETE: + // Check the results of the server certificate validation. + rv = DoVerifyCertComplete(rv); + break; case STATE_READ_COMPLETE: // A read off the network is complete; do the paperwork. rv = DoReadComplete(rv); @@ -506,38 +503,60 @@ int SSLClientSocketMac::DoLoop(int last_io_result) { int SSLClientSocketMac::DoHandshake() { OSStatus status = SSLHandshake(ssl_context_); - int net_error = NetErrorFromOSStatus(status); - if (status == errSSLWouldBlock) { + if (status == errSSLWouldBlock) next_state_ = STATE_HANDSHAKE; - } else if (status == noErr) { - completed_handshake_ = true; // We have a connection. + if (status == noErr) { server_cert_ = GetServerCert(ssl_context_); - DCHECK(server_cert_); - if (!ssl_config_.allowed_bad_certs.empty()) { - // Check server_cert_ because SecureTransport didn't verify it. - // TODO(wtc): If server_cert_ is not one of the allowed bad certificates, - // we should verify server_cert_ ourselves. Since we don't know how to - // do that yet, treat it as an invalid certificate. - net_error = ERR_CERT_INVALID; - server_cert_status_ |= CERT_STATUS_INVALID; - - for (size_t i = 0; i < ssl_config_.allowed_bad_certs.size(); ++i) { - if (server_cert_ == ssl_config_.allowed_bad_certs[i].cert) { - net_error = OK; - server_cert_status_ = ssl_config_.allowed_bad_certs[i].cert_status; - break; - } - } - } - } else if (IsCertificateError(net_error)) { - server_cert_ = GetServerCert(ssl_context_); - DCHECK(server_cert_); - server_cert_status_ |= MapNetErrorToCertStatus(net_error); + if (!server_cert_) + return ERR_UNEXPECTED; + next_state_ = STATE_VERIFY_CERT; + } + + return NetErrorFromOSStatus(status); +} + +int SSLClientSocketMac::DoVerifyCert() { + next_state_ = STATE_VERIFY_CERT_COMPLETE; + + if (!server_cert_) + return ERR_UNEXPECTED; + + // Add each of the intermediate certificates in the server's chain to the + // server's X509Certificate object. This makes them available to + // X509Certificate::Verify() for chain building. + CFArrayRef certs; + OSStatus status = SSLCopyPeerCertificates(ssl_context_, &certs); + if (status != noErr || !certs) + return ERR_UNEXPECTED; + scoped_cftyperef<CFArrayRef> scoped_certs(certs); + CFIndex certs_length = CFArrayGetCount(certs); + for (CFIndex i = 1; i < certs_length; ++i) { + SecCertificateRef cert_ref = reinterpret_cast<SecCertificateRef>( + const_cast<void*>(CFArrayGetValueAtIndex(certs, i))); + server_cert_->AddIntermediateCertificate(cert_ref); } - return net_error; + // TODO(hawk): set flags based on the SSLConfig, once SSLConfig is + // fully fleshed out on Mac OS X. + int flags = 0; + verifier_.reset(new CertVerifier); + return verifier_->Verify(server_cert_, hostname_, flags, + &server_cert_verify_result_, &io_callback_); +} + +int SSLClientSocketMac::DoVerifyCertComplete(int result) { + DCHECK(verifier_.get()); + verifier_.reset(); + + if (IsCertificateError(result) && ssl_config_.IsAllowedBadCert(server_cert_)) + result = OK; + + completed_handshake_ = true; + DCHECK(next_state_ == STATE_NONE); + + return result; } int SSLClientSocketMac::DoReadComplete(int result) { |