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 da400110e27159921873fc6009a09864c4380068..7fe771235dd7a7e3cd2a270fab7c639462ef5df0 100644 |
--- a/net/socket/ssl_client_socket_openssl.cc |
+++ b/net/socket/ssl_client_socket_openssl.cc |
@@ -16,6 +16,7 @@ |
#include "base/singleton.h" |
#include "net/base/cert_verifier.h" |
#include "net/base/net_errors.h" |
+#include "net/base/ssl_cert_request_info.h" |
#include "net/base/ssl_connection_status_flags.h" |
#include "net/base/ssl_info.h" |
@@ -173,6 +174,7 @@ class SSLContext { |
SSL_CTX_sess_set_remove_cb(ssl_ctx_.get(), RemoveSessionCallbackStatic); |
SSL_CTX_set_timeout(ssl_ctx_.get(), kSessionCacheTimeoutSeconds); |
SSL_CTX_sess_set_cache_size(ssl_ctx_.get(), kSessionCacheMaxEntires); |
+ SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback); |
} |
static int NewSessionCallbackStatic(SSL* ssl, SSL_SESSION* session) { |
@@ -194,6 +196,12 @@ class SSLContext { |
session_cache_.OnSessionRemoved(session); |
} |
+ static int ClientCertCallback(SSL* ssl, X509** x509, EVP_PKEY** pkey) { |
+ SSLClientSocketOpenSSL* socket = Get()->GetClientSocketFromSSL(ssl); |
+ CHECK(socket); |
+ return socket->ClientCertRequestCallback(ssl, x509, pkey); |
+ } |
+ |
// This is the index used with SSL_get_ex_data to retrieve the owner |
// SSLClientSocketOpenSSL object from an SSL instance. |
int ssl_socket_data_index_; |
@@ -356,7 +364,8 @@ void SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { |
void SSLClientSocketOpenSSL::GetSSLCertRequestInfo( |
SSLCertRequestInfo* cert_request_info) { |
- NOTREACHED(); |
+ cert_request_info->host_and_port = host_and_port_.ToString(); |
+ cert_request_info->client_certs = client_certs_; |
} |
SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto( |
@@ -493,12 +502,26 @@ int SSLClientSocketOpenSSL::DoHandshake() { |
int net_error = net::OK; |
int rv = SSL_do_handshake(ssl_); |
- if (rv == 1) { |
+ if (client_auth_cert_needed_) { |
+ net_error = ERR_SSL_CLIENT_AUTH_CERT_NEEDED; |
+ // If the handshake already succeeded (because the server requests but |
+ // doesn't require a client cert), we need to invalidate the SSL session |
+ // so that we won't try to resume the non-client-authenticated session in |
+ // the next handshake. This will cause the server to ask for a client |
+ // cert again. |
+ if (rv == 1) { |
+ // Remove from session cache but don't clear this connection. |
+ SSL_SESSION* session = SSL_get_session(ssl_); |
+ if (session) { |
+ int rv = SSL_CTX_remove_session(SSL_get_SSL_CTX(ssl_), session); |
+ LOG_IF(WARNING, !rv) << "Couldn't invalidate SSL session: " << session; |
+ } |
+ } |
+ } else if (rv == 1) { |
if (trying_cached_session_ && logging::DEBUG_MODE) { |
DVLOG(2) << "Result of session reuse for " << host_and_port_.ToString() |
<< " is: " << (SSL_session_reused(ssl_) ? "Success" : "Fail"); |
} |
- |
// SSL handshake is completed. Let's verify the certificate. |
const bool got_cert = !!UpdateServerCert(); |
DCHECK(got_cert); |
@@ -519,6 +542,31 @@ int SSLClientSocketOpenSSL::DoHandshake() { |
return net_error; |
} |
+int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, |
+ X509** x509, |
+ EVP_PKEY** pkey) { |
+ DVLOG(3) << "OpenSSL ClientCertRequestCallback called"; |
+ DCHECK(ssl == ssl_); |
+ DCHECK(*x509 == NULL); |
+ DCHECK(*pkey == NULL); |
+ |
+ if (!ssl_config_.send_client_cert) { |
+ client_auth_cert_needed_ = true; |
+ return -1; // Suspends handshake. |
+ } |
+ |
+ // Second pass: a client certificate should have been selected. |
+ if (ssl_config_.client_cert) { |
+ // TODO(joth): We need a way to lookup the private key this |
+ // certificate. See http://crbug.com/64951 and example code in |
+ // http://codereview.chromium.org/5195001/diff/6001/net/socket/ssl_client_socket_openssl.cc |
+ NOTIMPLEMENTED(); |
+ } |
+ |
+ // Send no client certificate. |
+ return 0; |
+} |
+ |
int SSLClientSocketOpenSSL::DoVerifyCert(int result) { |
DCHECK(server_cert_); |
GotoState(STATE_VERIFY_CERT_COMPLETE); |