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

Unified Diff: net/socket/ssl_client_socket_openssl.cc

Issue 3571011: implement certificate verification state machine (Closed)
Patch Set: wtc comments 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/socket/ssl_client_socket_openssl.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
« no previous file with comments | « net/socket/ssl_client_socket_openssl.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698