Chromium Code Reviews| 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 b57d6eee52ee035ddd237021ce08a715ada1444c..dd93b80a27c5d2cec8254aef0aa1871b5b39e0ff 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_; |
| @@ -325,7 +333,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( |
| @@ -462,12 +471,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); |
| @@ -488,6 +511,38 @@ 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): Need a way to acquire the private key for the client cert. |
| + NOTIMPLEMENTED(); |
|
bulach
2010/12/01 18:01:33
provided the second part is correct, it may be cle
joth
2010/12/02 17:12:01
Done.
|
| + EVP_PKEY* privkey = NULL; |
| + if (privkey) { |
| + // TODO(joth): (copied from NSS) We should wait for server certificate |
| + // verification before sending our credentials. See http://crbug.com/13934 |
| + *x509 = X509Certificate::DupOSCertHandle( |
| + ssl_config_.client_cert->os_cert_handle()); |
| + *pkey = privkey; |
| + return 1; |
| + } |
| + LOG(WARNING) << "Client cert found without private key"; |
| + } |
| + // Send no client certificate. |
| + return 0; |
| +} |
| + |
| int SSLClientSocketOpenSSL::DoVerifyCert(int result) { |
| DCHECK(server_cert_); |
| GotoState(STATE_VERIFY_CERT_COMPLETE); |