Index: net/socket/ssl_client_socket_openssl.cc |
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc |
index 6dc3c51e62f7d2f71c65d519088550c8c5aae8e0..858fe14b47cb1d7309e3962245784f178f511c7e 100644 |
--- a/net/socket/ssl_client_socket_openssl.cc |
+++ b/net/socket/ssl_client_socket_openssl.cc |
@@ -10,6 +10,7 @@ |
#include <openssl/ssl.h> |
#include <openssl/err.h> |
+#include "net/base/cert_verifier.h" |
#include "net/base/net_errors.h" |
#include "net/base/ssl_connection_status_flags.h" |
#include "net/base/ssl_info.h" |
@@ -46,6 +47,10 @@ int MapOpenSSLError(int err) { |
case SSL_ERROR_WANT_READ: |
case SSL_ERROR_WANT_WRITE: |
return ERR_IO_PENDING; |
+ case SSL_ERROR_SYSCALL: |
+ DVLOG(1) << "OpenSSL SYSVCALL error, errno " << errno; |
wtc
2010/10/04 23:13:53
Typo: SYSVCALL => SYSCALL
Should this really be m
|
+ MaybeLogSSLError(); |
+ return ERR_SSL_PROTOCOL_ERROR; |
default: |
// TODO(joth): Implement full mapping. |
LOG(WARNING) << "Unknown OpenSSL error " << err; |
@@ -86,6 +91,8 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( |
user_read_callback_(NULL), |
user_write_callback_(NULL), |
client_auth_cert_needed_(false), |
+ ALLOW_THIS_IN_INITIALIZER_LIST(handshake_io_callback_( |
+ this, &SSLClientSocketOpenSSL::OnHandshakeIOComplete)), |
ssl_(NULL), |
transport_bio_(NULL), |
transport_(transport_socket), |
@@ -165,36 +172,30 @@ int SSLClientSocketOpenSSL::SSLVerifyCallback(int preverify_ok, |
SSL* ssl, |
X509_STORE_CTX* x509_ctx) { |
DCHECK_EQ(ssl_, ssl); |
- if (!preverify_ok) { |
- int depth = X509_STORE_CTX_get_error_depth(x509_ctx); |
- DVLOG(2) << "SSLVerifyCallback " << preverify_ok << " depth " << depth; |
- MaybeLogSSLError(); |
- } |
+ int depth = X509_STORE_CTX_get_error_depth(x509_ctx); |
+ DVLOG(preverify_ok ? 3 : 1) << "SSLVerifyCallback " << preverify_ok |
+ << " depth " << depth; |
+ MaybeLogSSLError(); |
return preverify_ok; |
} |
// SSLClientSocket methods |
void SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { |
+ ssl_info->Reset(); |
+ if (!server_cert_) |
+ return; |
+ |
// Chrome DCHECKs that https pages provide a valid cert. For now (whilst in |
// early dev) we just create a spoof cert. |
// TODO(joth): implement X509Certificate for OpenSSL and remove this hack. |
LOG(WARNING) << "Filling in certificate with bogus content"; |
- ssl_info->Reset(); |
- ssl_info->cert = X509Certificate::CreateFromBytes( |
- reinterpret_cast<const char*>(google_der), sizeof(google_der)); |
- DCHECK(ssl_info->cert); |
- ssl_info->cert_status = 0; |
+ ssl_info->cert = server_cert_; |
+ ssl_info->cert_status = server_cert_verify_result_.cert_status; |
ssl_info->security_bits = 56; |
ssl_info->connection_status = |
((TLS1_CK_RSA_EXPORT1024_WITH_DES_CBC_SHA) & |
SSL_CONNECTION_CIPHERSUITE_MASK) << SSL_CONNECTION_CIPHERSUITE_SHIFT; |
- |
- // Silence compiler about unused vars. |
- (void)google_der; |
- (void)webkit_der; |
- (void)thawte_der; |
- (void)paypal_null_der; |
} |
void SSLClientSocketOpenSSL::GetSSLCertRequestInfo( |
@@ -259,6 +260,20 @@ int SSLClientSocketOpenSSL::Connect(CompletionCallback* callback) { |
} |
void SSLClientSocketOpenSSL::Disconnect() { |
+ if (ssl_) { |
+ SSL_free(ssl_); |
+ ssl_ = NULL; |
+ } |
+ if (transport_bio_) { |
+ BIO_free_all(transport_bio_); |
+ transport_bio_ = NULL; |
+ } |
+ |
+ // Shut down anything that may call us back (through buffer_send_callback_, |
+ // buffer_recv_callback, or handshake_io_callback_). |
+ verifier_.reset(); |
+ transport_->socket()->Disconnect(); |
+ |
// Null all callbacks, delete all buffers. |
transport_send_busy_ = false; |
send_buffer_ = NULL; |
@@ -276,17 +291,8 @@ void SSLClientSocketOpenSSL::Disconnect() { |
client_certs_.clear(); |
client_auth_cert_needed_ = false; |
- if (ssl_) { |
- SSL_free(ssl_); |
- ssl_ = NULL; |
- } |
- if (transport_bio_) { |
- BIO_free(transport_bio_); |
- transport_bio_ = NULL; |
- } |
- |
+ server_cert_verify_result_.Reset(); |
completed_handshake_ = false; |
- transport_->socket()->Disconnect(); |
} |
int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) { |
@@ -307,7 +313,6 @@ int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) { |
case STATE_HANDSHAKE: |
rv = DoHandshake(); |
break; |
-#if 0 // TODO(joth): Implement cert verification. |
case STATE_VERIFY_CERT: |
DCHECK(rv == OK); |
rv = DoVerifyCert(rv); |
@@ -315,7 +320,6 @@ int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) { |
case STATE_VERIFY_CERT_COMPLETE: |
rv = DoVerifyCertComplete(rv); |
break; |
-#endif |
default: |
rv = ERR_UNEXPECTED; |
NOTREACHED() << "unexpected state" << state; |
@@ -339,21 +343,113 @@ int SSLClientSocketOpenSSL::DoHandshake() { |
if (rv == 1) { |
// SSL handshake is completed. Let's verify the certificate. |
- // For now we are done, certificates not implemented yet. |
- // TODO(joth): Implement certificates |
- GotoState(STATE_NONE); |
- completed_handshake_ = true; |
+ if (UpdateServerCert() == NULL) { |
+ net_error = ERR_SSL_PROTOCOL_ERROR; |
+ } else { |
+ GotoState(STATE_VERIFY_CERT); |
+ |
+ // TODO(joth): Remove this check when X509Certificate::Verify is |
+ // implemented for OpenSSL |
+ long verify_result = SSL_get_verify_result(ssl_); |
+ LOG_IF(WARNING, verify_result != X509_V_OK) |
+ << "Built in verify failed: " << verify_result; |
+ } |
} else { |
int ssl_error = SSL_get_error(ssl_, rv); |
+ net_error = MapOpenSSLError(ssl_error); |
// If not done, stay in this state |
- GotoState(STATE_HANDSHAKE); |
- net_error = MapOpenSSLError(ssl_error); |
+ if (net_error == ERR_IO_PENDING) { |
+ GotoState(STATE_HANDSHAKE); |
+ } else { |
+ LOG(ERROR) << "handshake failed; returned " << rv |
+ << ", SSL error code " << ssl_error |
+ << ", net_error " << net_error; |
+ MaybeLogSSLError(); |
+ } |
} |
- |
return net_error; |
} |
+int SSLClientSocketOpenSSL::DoVerifyCert(int result) { |
+ DCHECK(server_cert_); |
+ GotoState(STATE_VERIFY_CERT_COMPLETE); |
+ int flags = 0; |
+ |
+ if (ssl_config_.rev_checking_enabled) |
+ flags |= X509Certificate::VERIFY_REV_CHECKING_ENABLED; |
+ if (ssl_config_.verify_ev_cert) |
+ flags |= X509Certificate::VERIFY_EV_CERT; |
+ verifier_.reset(new CertVerifier); |
+ return verifier_->Verify(server_cert_, hostname_, flags, |
+ &server_cert_verify_result_, |
+ &handshake_io_callback_); |
+} |
+ |
+int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) { |
+ verifier_.reset(); |
+ |
+ if (result == OK) { |
+ // TODO(joth): Work out if we need to remember the intermediate CA certs |
+ // when the server sends them to us, and do so here. |
+ } |
+ |
+ // If we have been explicitly told to accept this certificate, override the |
+ // result of verifier_.Verify. |
+ // Eventually, we should cache the cert verification results so that we don't |
+ // need to call verifier_.Verify repeatedly. But for now we need to do this. |
+ // Alternatively, we could use the cert's status that we stored along with |
+ // the cert in the allowed_bad_certs vector. |
+ if (IsCertificateError(result) && |
+ ssl_config_.IsAllowedBadCert(server_cert_)) { |
+ LOG(INFO) << "accepting bad SSL certificate, as user told us to"; |
+ result = OK; |
+ } |
+ |
+ completed_handshake_ = true; |
+ // The NSS version has a comment that we may not need this call because it is |
+ // now harmless to have a session with a bad cert. |
+ // This may or may not apply here, but let's invalidate it anyway. |
+ InvalidateSessionIfBadCertificate(); |
+ // Exit DoHandshakeLoop and return the result to the caller to Connect. |
+ DCHECK_EQ(STATE_NONE, next_handshake_state_); |
+ return result; |
+} |
+ |
+void SSLClientSocketOpenSSL::InvalidateSessionIfBadCertificate() { |
+ if (UpdateServerCert() != NULL && |
+ ssl_config_.IsAllowedBadCert(server_cert_)) { |
+ // Remove from session cache but don't clear this connection. |
+ // TODO(joth): This should be a no-op until we enable session caching, |
+ // see SSL_CTX_set_session_cache_mode(SSL_SESS_CACHE_CLIENT). |
+ SSL_SESSION* session = SSL_get_session(ssl_); |
+ LOG_IF(ERROR, session) << "Connection has a session?? " << session; |
+ int rv = SSL_CTX_remove_session(g_ctx, session); |
+ LOG_IF(ERROR, rv) << "Session was cached?? " << rv; |
+ } |
+} |
+ |
+X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() { |
+ if (server_cert_) |
+ return server_cert_; |
+ |
+ X509* cert = SSL_get_peer_certificate(ssl_); |
+ if (cert == NULL) { |
+ LOG(WARNING) << "SSL_get_peer_certificate returned NULL"; |
+ return NULL; |
+ } |
+ |
+ // TODO(joth): Get |server_cert_| from |cert|. |
+ server_cert_ = X509Certificate::CreateFromBytes( |
+ reinterpret_cast<const char*>(google_der), sizeof(google_der)); |
+ X509_free(cert); |
+ DCHECK(server_cert_); |
+ // Silence compiler about unused vars. |
+ (void)google_der; (void)webkit_der; (void)thawte_der; (void)paypal_null_der; |
+ |
+ return server_cert_; |
+} |
+ |
bool SSLClientSocketOpenSSL::DoTransportIO() { |
bool network_moved = false; |
int nsent = BufferSend(); |