summaryrefslogtreecommitdiffstats
path: root/chromium/net/socket/ssl_client_socket_openssl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/socket/ssl_client_socket_openssl.cc')
-rw-r--r--chromium/net/socket/ssl_client_socket_openssl.cc435
1 files changed, 226 insertions, 209 deletions
diff --git a/chromium/net/socket/ssl_client_socket_openssl.cc b/chromium/net/socket/ssl_client_socket_openssl.cc
index 49bdc8eb68a..4ff8d438e96 100644
--- a/chromium/net/socket/ssl_client_socket_openssl.cc
+++ b/chromium/net/socket/ssl_client_socket_openssl.cc
@@ -13,7 +13,6 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
-#include "base/debug/alias.h"
#include "base/memory/singleton.h"
#include "base/metrics/histogram.h"
#include "base/synchronization/lock.h"
@@ -23,6 +22,7 @@
#include "net/cert/cert_verifier.h"
#include "net/cert/single_request_cert_verifier.h"
#include "net/cert/x509_certificate_net_log_param.h"
+#include "net/socket/openssl_ssl_util.h"
#include "net/socket/ssl_error_params.h"
#include "net/socket/ssl_session_cache_openssl.h"
#include "net/ssl/openssl_client_key_store.h"
@@ -87,149 +87,6 @@ int GetNetSSLVersion(SSL* ssl) {
}
}
-int MapOpenSSLErrorSSL() {
- // Walk down the error stack to find the SSLerr generated reason.
- unsigned long error_code;
- do {
- error_code = ERR_get_error();
- if (error_code == 0)
- return ERR_SSL_PROTOCOL_ERROR;
- } while (ERR_GET_LIB(error_code) != ERR_LIB_SSL);
-
- DVLOG(1) << "OpenSSL SSL error, reason: " << ERR_GET_REASON(error_code)
- << ", name: " << ERR_error_string(error_code, NULL);
- switch (ERR_GET_REASON(error_code)) {
- case SSL_R_READ_TIMEOUT_EXPIRED:
- return ERR_TIMED_OUT;
- case SSL_R_BAD_RESPONSE_ARGUMENT:
- return ERR_INVALID_ARGUMENT;
- case SSL_R_UNKNOWN_CERTIFICATE_TYPE:
- case SSL_R_UNKNOWN_CIPHER_TYPE:
- case SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE:
- case SSL_R_UNKNOWN_PKEY_TYPE:
- case SSL_R_UNKNOWN_REMOTE_ERROR_TYPE:
- case SSL_R_UNKNOWN_SSL_VERSION:
- return ERR_NOT_IMPLEMENTED;
- case SSL_R_UNSUPPORTED_SSL_VERSION:
- case SSL_R_NO_CIPHER_MATCH:
- case SSL_R_NO_SHARED_CIPHER:
- case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:
- case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:
- case SSL_R_UNSUPPORTED_PROTOCOL:
- return ERR_SSL_VERSION_OR_CIPHER_MISMATCH;
- case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:
- case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:
- case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:
- case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:
- case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:
- case SSL_R_TLSV1_ALERT_ACCESS_DENIED:
- case SSL_R_TLSV1_ALERT_UNKNOWN_CA:
- return ERR_BAD_SSL_CLIENT_AUTH_CERT;
- case SSL_R_BAD_DECOMPRESSION:
- case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:
- return ERR_SSL_DECOMPRESSION_FAILURE_ALERT;
- case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:
- return ERR_SSL_BAD_RECORD_MAC_ALERT;
- case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:
- return ERR_SSL_DECRYPT_ERROR_ALERT;
- case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED:
- return ERR_SSL_UNSAFE_NEGOTIATION;
- case SSL_R_WRONG_NUMBER_OF_KEY_BITS:
- return ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY;
- // SSL_R_UNKNOWN_PROTOCOL is reported if premature application data is
- // received (see http://crbug.com/42538), and also if all the protocol
- // versions supported by the server were disabled in this socket instance.
- // Mapped to ERR_SSL_PROTOCOL_ERROR for compatibility with other SSL sockets
- // in the former scenario.
- case SSL_R_UNKNOWN_PROTOCOL:
- case SSL_R_SSL_HANDSHAKE_FAILURE:
- case SSL_R_DECRYPTION_FAILED:
- case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC:
- case SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG:
- case SSL_R_DIGEST_CHECK_FAILED:
- case SSL_R_DUPLICATE_COMPRESSION_ID:
- case SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER:
- case SSL_R_ENCRYPTED_LENGTH_TOO_LONG:
- case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST:
- case SSL_R_EXCESSIVE_MESSAGE_SIZE:
- case SSL_R_EXTRA_DATA_IN_MESSAGE:
- case SSL_R_GOT_A_FIN_BEFORE_A_CCS:
- case SSL_R_ILLEGAL_PADDING:
- case SSL_R_INVALID_CHALLENGE_LENGTH:
- case SSL_R_INVALID_COMMAND:
- case SSL_R_INVALID_PURPOSE:
- case SSL_R_INVALID_STATUS_RESPONSE:
- case SSL_R_INVALID_TICKET_KEYS_LENGTH:
- case SSL_R_KEY_ARG_TOO_LONG:
- case SSL_R_READ_WRONG_PACKET_TYPE:
- // SSL_do_handshake reports this error when the server responds to a
- // ClientHello with a fatal close_notify alert.
- case SSL_AD_REASON_OFFSET + SSL_AD_CLOSE_NOTIFY:
- case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:
- // TODO(joth): SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE may be returned from the
- // server after receiving ClientHello if there's no common supported cipher.
- // Ideally we'd map that specific case to ERR_SSL_VERSION_OR_CIPHER_MISMATCH
- // to match the NSS implementation. See also http://goo.gl/oMtZW
- case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:
- case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:
- case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:
- case SSL_R_TLSV1_ALERT_DECODE_ERROR:
- case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:
- case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:
- case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:
- case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION:
- case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:
- case SSL_R_TLSV1_ALERT_USER_CANCELLED:
- return ERR_SSL_PROTOCOL_ERROR;
- default:
- LOG(WARNING) << "Unmapped error reason: " << ERR_GET_REASON(error_code);
- return ERR_FAILED;
- }
-}
-
-// Converts an OpenSSL error code into a net error code, walking the OpenSSL
-// error stack if needed. Note that |tracer| is not currently used in the
-// implementation, but is passed in anyway as this ensures the caller will clear
-// any residual codes left on the error stack.
-int MapOpenSSLError(int err, const crypto::OpenSSLErrStackTracer& tracer) {
- switch (err) {
- case SSL_ERROR_WANT_READ:
- case SSL_ERROR_WANT_WRITE:
- return ERR_IO_PENDING;
- case SSL_ERROR_SYSCALL:
- LOG(ERROR) << "OpenSSL SYSCALL error, earliest error code in "
- "error queue: " << ERR_peek_error() << ", errno: "
- << errno;
- return ERR_SSL_PROTOCOL_ERROR;
- case SSL_ERROR_SSL:
- return MapOpenSSLErrorSSL();
- default:
- // TODO(joth): Implement full mapping.
- LOG(WARNING) << "Unknown OpenSSL error " << err;
- return ERR_SSL_PROTOCOL_ERROR;
- }
-}
-
-// We do certificate verification after handshake, so we disable the default
-// by registering a no-op verify function.
-int NoOpVerifyCallback(X509_STORE_CTX*, void *) {
- DVLOG(3) << "skipping cert verify";
- return 1;
-}
-
-// Utility to construct the appropriate set & clear masks for use the OpenSSL
-// options and mode configuration functions. (SSL_set_options etc)
-struct SslSetClearMask {
- SslSetClearMask() : set_mask(0), clear_mask(0) {}
- void ConfigureFlag(long flag, bool state) {
- (state ? set_mask : clear_mask) |= flag;
- // Make sure we haven't got any intersection in the set & clear options.
- DCHECK_EQ(0, set_mask & clear_mask) << flag << ":" << state;
- }
- long set_mask;
- long clear_mask;
-};
-
// Compute a unique key string for the SSL session cache. |socket| is an
// input socket object. Return a string.
std::string GetSocketSessionCacheKey(const SSLClientSocketOpenSSL& socket) {
@@ -268,16 +125,15 @@ class SSLClientSocketOpenSSL::SSLContext {
DCHECK_NE(ssl_socket_data_index_, -1);
ssl_ctx_.reset(SSL_CTX_new(SSLv23_client_method()));
session_cache_.Reset(ssl_ctx_.get(), kDefaultSessionCacheConfig);
- SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), NoOpVerifyCallback, NULL);
+ SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), CertVerifyCallback, NULL);
SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback);
SSL_CTX_set_channel_id_cb(ssl_ctx_.get(), ChannelIDCallback);
-#if defined(OPENSSL_NPN_NEGOTIATED)
+ SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER, NULL);
// TODO(kristianm): Only select this if ssl_config_.next_proto is not empty.
// It would be better if the callback were not a global setting,
// but that is an OpenSSL issue.
SSL_CTX_set_next_proto_select_cb(ssl_ctx_.get(), SelectNextProtoCallback,
NULL);
-#endif
}
static std::string GetSessionCacheKey(const SSL* ssl) {
@@ -300,6 +156,15 @@ class SSLClientSocketOpenSSL::SSLContext {
socket->ChannelIDRequestCallback(ssl, pkey);
}
+ static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) {
+ SSL* ssl = reinterpret_cast<SSL*>(X509_STORE_CTX_get_ex_data(
+ store_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()));
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ CHECK(socket);
+
+ return socket->CertVerifyCallback(store_ctx);
+ }
+
static int SelectNextProtoCallback(SSL* ssl,
unsigned char** out, unsigned char* outlen,
const unsigned char* in,
@@ -317,6 +182,137 @@ class SSLClientSocketOpenSSL::SSLContext {
SSLSessionCacheOpenSSL session_cache_;
};
+// PeerCertificateChain is a helper object which extracts the certificate
+// chain, as given by the server, from an OpenSSL socket and performs the needed
+// resource management. The first element of the chain is the leaf certificate
+// and the other elements are in the order given by the server.
+class SSLClientSocketOpenSSL::PeerCertificateChain {
+ public:
+ explicit PeerCertificateChain(STACK_OF(X509)* chain) { Reset(chain); }
+ PeerCertificateChain(const PeerCertificateChain& other) { *this = other; }
+ ~PeerCertificateChain() {}
+ PeerCertificateChain& operator=(const PeerCertificateChain& other);
+
+ // Resets the PeerCertificateChain to the set of certificates in|chain|,
+ // which may be NULL, indicating to empty the store certificates.
+ // Note: If an error occurs, such as being unable to parse the certificates,
+ // this will behave as if Reset(NULL) was called.
+ void Reset(STACK_OF(X509)* chain);
+
+ // Note that when USE_OPENSSL is defined, OSCertHandle is X509*
+ const scoped_refptr<X509Certificate>& AsOSChain() const { return os_chain_; }
+
+ size_t size() const {
+ if (!openssl_chain_.get())
+ return 0;
+ return sk_X509_num(openssl_chain_.get());
+ }
+
+ X509* operator[](size_t index) const {
+ DCHECK_LT(index, size());
+ return sk_X509_value(openssl_chain_.get(), index);
+ }
+
+ bool IsValid() { return os_chain_.get() && openssl_chain_.get(); }
+
+ private:
+ static void FreeX509Stack(STACK_OF(X509)* cert_chain) {
+ sk_X509_pop_free(cert_chain, X509_free);
+ }
+
+ friend class crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack>;
+
+ crypto::ScopedOpenSSL<STACK_OF(X509), FreeX509Stack> openssl_chain_;
+
+ scoped_refptr<X509Certificate> os_chain_;
+};
+
+SSLClientSocketOpenSSL::PeerCertificateChain&
+SSLClientSocketOpenSSL::PeerCertificateChain::operator=(
+ const PeerCertificateChain& other) {
+ if (this == &other)
+ return *this;
+
+ // os_chain_ is reference counted by scoped_refptr;
+ os_chain_ = other.os_chain_;
+
+ // Must increase the reference count manually for sk_X509_dup
+ openssl_chain_.reset(sk_X509_dup(other.openssl_chain_.get()));
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) {
+ X509* x = sk_X509_value(openssl_chain_.get(), i);
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+ }
+ return *this;
+}
+
+#if defined(USE_OPENSSL_CERTS)
+// When OSCertHandle is typedef'ed to X509, this implementation does a short cut
+// to avoid converting back and forth between der and X509 struct.
+void SSLClientSocketOpenSSL::PeerCertificateChain::Reset(
+ STACK_OF(X509)* chain) {
+ openssl_chain_.reset(NULL);
+ os_chain_ = NULL;
+
+ if (!chain)
+ return;
+
+ X509Certificate::OSCertHandles intermediates;
+ for (int i = 1; i < sk_X509_num(chain); ++i)
+ intermediates.push_back(sk_X509_value(chain, i));
+
+ os_chain_ =
+ X509Certificate::CreateFromHandle(sk_X509_value(chain, 0), intermediates);
+
+ // sk_X509_dup does not increase reference count on the certs in the stack.
+ openssl_chain_.reset(sk_X509_dup(chain));
+
+ std::vector<base::StringPiece> der_chain;
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) {
+ X509* x = sk_X509_value(openssl_chain_.get(), i);
+ // Increase the reference count for the certs in openssl_chain_.
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+ }
+}
+#else // !defined(USE_OPENSSL_CERTS)
+void SSLClientSocketOpenSSL::PeerCertificateChain::Reset(
+ STACK_OF(X509)* chain) {
+ openssl_chain_.reset(NULL);
+ os_chain_ = NULL;
+
+ if (!chain)
+ return;
+
+ // sk_X509_dup does not increase reference count on the certs in the stack.
+ openssl_chain_.reset(sk_X509_dup(chain));
+
+ std::vector<base::StringPiece> der_chain;
+ for (int i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) {
+ X509* x = sk_X509_value(openssl_chain_.get(), i);
+
+ // Increase the reference count for the certs in openssl_chain_.
+ CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509);
+
+ unsigned char* cert_data = NULL;
+ int cert_data_length = i2d_X509(x, &cert_data);
+ if (cert_data_length && cert_data)
+ der_chain.push_back(base::StringPiece(reinterpret_cast<char*>(cert_data),
+ cert_data_length));
+ }
+
+ os_chain_ = X509Certificate::CreateFromDERCertChain(der_chain);
+
+ for (size_t i = 0; i < der_chain.size(); ++i) {
+ OPENSSL_free(const_cast<char*>(der_chain[i].data()));
+ }
+
+ if (der_chain.size() !=
+ static_cast<size_t>(sk_X509_num(openssl_chain_.get()))) {
+ openssl_chain_.reset(NULL);
+ os_chain_ = NULL;
+ }
+}
+#endif // defined(USE_OPENSSL_CERTS)
+
// static
SSLSessionCacheOpenSSL::Config
SSLClientSocketOpenSSL::SSLContext::kDefaultSessionCacheConfig = {
@@ -331,6 +327,9 @@ void SSLClientSocket::ClearSessionCache() {
SSLClientSocketOpenSSL::SSLContext* context =
SSLClientSocketOpenSSL::SSLContext::GetInstance();
context->session_cache()->Flush();
+#if defined(USE_OPENSSL_CERTS)
+ OpenSSLClientKeyStore::GetInstance()->Flush();
+#endif
}
SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
@@ -344,7 +343,9 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
weak_factory_(this),
pending_read_error_(kNoPendingReadResult),
transport_write_error_(OK),
+ server_cert_chain_(new PeerCertificateChain(NULL)),
completed_handshake_(false),
+ was_ever_used_(false),
client_auth_cert_needed_(false),
cert_verifier_(context.cert_verifier),
server_bound_cert_service_(context.server_bound_cert_service),
@@ -359,8 +360,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
npn_status_(kNextProtoUnsupported),
channel_id_request_return_value_(ERR_UNEXPECTED),
channel_id_xtn_negotiated_(false),
- net_log_(transport_->socket()->NetLog()) {
-}
+ net_log_(transport_->socket()->NetLog()) {}
SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
Disconnect();
@@ -368,8 +368,9 @@ SSLClientSocketOpenSSL::~SSLClientSocketOpenSSL() {
void SSLClientSocketOpenSSL::GetSSLCertRequestInfo(
SSLCertRequestInfo* cert_request_info) {
- cert_request_info->host_and_port = host_and_port_.ToString();
+ cert_request_info->host_and_port = host_and_port_;
cert_request_info->cert_authorities = cert_authorities_;
+ cert_request_info->cert_key_types = cert_key_types_;
}
SSLClientSocket::NextProtoStatus SSLClientSocketOpenSSL::GetNextProto(
@@ -391,11 +392,9 @@ int SSLClientSocketOpenSSL::ExportKeyingMaterial(
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
int rv = SSL_export_keying_material(
- ssl_, out, outlen, const_cast<char*>(label.data()),
- label.size(),
- reinterpret_cast<unsigned char*>(const_cast<char*>(context.data())),
- context.length(),
- context.length() > 0);
+ ssl_, out, outlen, label.data(), label.size(),
+ reinterpret_cast<const unsigned char*>(context.data()),
+ context.length(), context.length() > 0);
if (rv != 1) {
int ssl_error = SSL_get_error(ssl_, rv);
@@ -408,6 +407,7 @@ int SSLClientSocketOpenSSL::ExportKeyingMaterial(
}
int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) {
+ NOTIMPLEMENTED();
return ERR_NOT_IMPLEMENTED;
}
@@ -415,17 +415,17 @@ int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) {
net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT);
// Set up new ssl object.
- if (!Init()) {
- int result = ERR_UNEXPECTED;
- net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, result);
- return result;
+ int rv = Init();
+ if (rv != OK) {
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_CONNECT, rv);
+ return rv;
}
// Set SSL to client mode. Handshake happens in the loop below.
SSL_set_connect_state(ssl_);
GotoState(STATE_HANDSHAKE);
- int rv = DoHandshakeLoop(net::OK);
+ rv = DoHandshakeLoop(OK);
if (rv == ERR_IO_PENDING) {
user_connect_callback_ = callback;
} else {
@@ -474,6 +474,7 @@ void SSLClientSocketOpenSSL::Disconnect() {
completed_handshake_ = false;
cert_authorities_.clear();
+ cert_key_types_.clear();
client_auth_cert_needed_ = false;
}
@@ -534,11 +535,7 @@ void SSLClientSocketOpenSSL::SetOmniboxSpeculation() {
}
bool SSLClientSocketOpenSSL::WasEverUsed() const {
- if (transport_.get() && transport_->socket())
- return transport_->socket()->WasEverUsed();
-
- NOTREACHED();
- return false;
+ return was_ever_used_;
}
bool SSLClientSocketOpenSSL::UsingTCPFastOpen() const {
@@ -609,6 +606,8 @@ int SSLClientSocketOpenSSL::Read(IOBuffer* buf,
if (rv == ERR_IO_PENDING) {
user_read_callback_ = callback;
} else {
+ if (rv > 0)
+ was_ever_used_ = true;
user_read_buf_ = NULL;
user_read_buf_len_ = 0;
}
@@ -627,6 +626,8 @@ int SSLClientSocketOpenSSL::Write(IOBuffer* buf,
if (rv == ERR_IO_PENDING) {
user_write_callback_ = callback;
} else {
+ if (rv > 0)
+ was_ever_used_ = true;
user_write_buf_ = NULL;
user_write_buf_len_ = 0;
}
@@ -634,15 +635,15 @@ int SSLClientSocketOpenSSL::Write(IOBuffer* buf,
return rv;
}
-bool SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) {
+int SSLClientSocketOpenSSL::SetReceiveBufferSize(int32 size) {
return transport_->socket()->SetReceiveBufferSize(size);
}
-bool SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) {
+int SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) {
return transport_->socket()->SetSendBufferSize(size);
}
-bool SSLClientSocketOpenSSL::Init() {
+int SSLClientSocketOpenSSL::Init() {
DCHECK(!ssl_);
DCHECK(!transport_bio_);
@@ -651,10 +652,10 @@ bool SSLClientSocketOpenSSL::Init() {
ssl_ = SSL_new(context->ssl_ctx());
if (!ssl_ || !context->SetClientSocketForSSL(ssl_, this))
- return false;
+ return ERR_UNEXPECTED;
if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str()))
- return false;
+ return ERR_UNEXPECTED;
trying_cached_session_ = context->session_cache()->SetSSLSessionWithKey(
ssl_, GetSocketSessionCacheKey(*this));
@@ -662,7 +663,7 @@ bool SSLClientSocketOpenSSL::Init() {
BIO* ssl_bio = NULL;
// 0 => use default buffer sizes.
if (!BIO_new_bio_pair(&ssl_bio, 0, &transport_bio_, 0))
- return false;
+ return ERR_UNEXPECTED;
DCHECK(ssl_bio);
DCHECK(transport_bio_);
@@ -677,22 +678,16 @@ bool SSLClientSocketOpenSSL::Init() {
bool tls1_enabled = (ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1 &&
ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1);
options.ConfigureFlag(SSL_OP_NO_TLSv1, !tls1_enabled);
-#if defined(SSL_OP_NO_TLSv1_1)
bool tls1_1_enabled =
(ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_1 &&
ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_1);
options.ConfigureFlag(SSL_OP_NO_TLSv1_1, !tls1_1_enabled);
-#endif
-#if defined(SSL_OP_NO_TLSv1_2)
bool tls1_2_enabled =
(ssl_config_.version_min <= SSL_PROTOCOL_VERSION_TLS1_2 &&
ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1_2);
options.ConfigureFlag(SSL_OP_NO_TLSv1_2, !tls1_2_enabled);
-#endif
-#if defined(SSL_OP_NO_COMPRESSION)
options.ConfigureFlag(SSL_OP_NO_COMPRESSION, true);
-#endif
// TODO(joth): Set this conditionally, see http://crbug.com/55410
options.ConfigureFlag(SSL_OP_LEGACY_SERVER_CONNECT, true);
@@ -703,13 +698,10 @@ bool SSLClientSocketOpenSSL::Init() {
// Same as above, this time for the SSL mode.
SslSetClearMask mode;
-#if defined(SSL_MODE_RELEASE_BUFFERS)
mode.ConfigureFlag(SSL_MODE_RELEASE_BUFFERS, true);
-#endif
-#if defined(SSL_MODE_SMALL_BUFFERS)
- mode.ConfigureFlag(SSL_MODE_SMALL_BUFFERS, true);
-#endif
+ mode.ConfigureFlag(SSL_MODE_HANDSHAKE_CUTTHROUGH,
+ ssl_config_.false_start_enabled);
SSL_set_mode(ssl_, mode.set_mask);
SSL_clear_mode(ssl_, mode.clear_mask);
@@ -760,12 +752,14 @@ bool SSLClientSocketOpenSSL::Init() {
SSL_enable_tls_channel_id(ssl_);
}
- return true;
+ return OK;
}
void SSLClientSocketOpenSSL::DoReadCallback(int rv) {
// Since Run may result in Read being called, clear |user_read_callback_|
// up front.
+ if (rv > 0)
+ was_ever_used_ = true;
user_read_buf_ = NULL;
user_read_buf_len_ = 0;
base::ResetAndReturn(&user_read_callback_).Run(rv);
@@ -774,6 +768,8 @@ void SSLClientSocketOpenSSL::DoReadCallback(int rv) {
void SSLClientSocketOpenSSL::DoWriteCallback(int rv) {
// Since Run may result in Write being called, clear |user_write_callback_|
// up front.
+ if (rv > 0)
+ was_ever_used_ = true;
user_write_buf_ = NULL;
user_write_buf_len_ = 0;
base::ResetAndReturn(&user_write_callback_).Run(rv);
@@ -796,7 +792,7 @@ bool SSLClientSocketOpenSSL::DoTransportIO() {
int SSLClientSocketOpenSSL::DoHandshake() {
crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE);
- int net_error = net::OK;
+ int net_error = OK;
int rv = SSL_do_handshake(ssl_);
if (client_auth_cert_needed_) {
@@ -914,25 +910,11 @@ void SSLClientSocketOpenSSL::DoConnectCallback(int rv) {
}
X509Certificate* SSLClientSocketOpenSSL::UpdateServerCert() {
- if (server_cert_.get())
- return server_cert_.get();
+ server_cert_chain_->Reset(SSL_get_peer_cert_chain(ssl_));
+ server_cert_ = server_cert_chain_->AsOSChain();
- crypto::ScopedOpenSSL<X509, X509_free> cert(SSL_get_peer_certificate(ssl_));
- if (!cert.get()) {
- LOG(WARNING) << "SSL_get_peer_certificate returned NULL";
- return NULL;
- }
-
- // Unlike SSL_get_peer_certificate, SSL_get_peer_cert_chain does not
- // increment the reference so sk_X509_free does not need to be called.
- STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl_);
- X509Certificate::OSCertHandles intermediates;
- if (chain) {
- for (int i = 0; i < sk_X509_num(chain); ++i)
- intermediates.push_back(sk_X509_value(chain, i));
- }
- server_cert_ = X509Certificate::CreateFromHandle(cert.get(), intermediates);
- DCHECK(server_cert_.get());
+ if (!server_cert_chain_->IsValid())
+ DVLOG(1) << "UpdateServerCert received invalid certificate chain from peer";
return server_cert_.get();
}
@@ -1270,10 +1252,7 @@ int SSLClientSocketOpenSSL::TransportReadComplete(int result) {
DCHECK(recv_buffer_.get());
int ret = BIO_write(transport_bio_, recv_buffer_->data(), result);
// A write into a memory BIO should always succeed.
- // Force values on the stack for http://crbug.com/335557
- base::debug::Alias(&result);
- base::debug::Alias(&ret);
- CHECK_EQ(result, ret);
+ DCHECK_EQ(result, ret);
}
recv_buffer_ = NULL;
transport_recv_busy_ = false;
@@ -1287,7 +1266,6 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
DCHECK(ssl == ssl_);
DCHECK(*x509 == NULL);
DCHECK(*pkey == NULL);
-
if (!ssl_config_.send_client_cert) {
// First pass: we know that a client certificate is needed, but we do not
// have one at hand.
@@ -1303,11 +1281,21 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
OPENSSL_free(str);
}
+ const unsigned char* client_cert_types;
+ size_t num_client_cert_types;
+ SSL_get_client_certificate_types(ssl, &client_cert_types,
+ &num_client_cert_types);
+ for (size_t i = 0; i < num_client_cert_types; i++) {
+ cert_key_types_.push_back(
+ static_cast<SSLClientCertType>(client_cert_types[i]));
+ }
+
return -1; // Suspends handshake.
}
// Second pass: a client certificate should have been selected.
if (ssl_config_.client_cert.get()) {
+#if defined(USE_OPENSSL_CERTS)
// A note about ownership: FetchClientCertPrivateKey() increments
// the reference count of the EVP_PKEY. Ownership of this reference
// is passed directly to OpenSSL, which will release the reference
@@ -1323,6 +1311,10 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl,
return 1;
}
LOG(WARNING) << "Client cert found without private key";
+#else // !defined(USE_OPENSSL_CERTS)
+ // OS handling of client certificates is not yet implemented.
+ NOTIMPLEMENTED();
+#endif // defined(USE_OPENSSL_CERTS)
}
// Send no client certificate.
@@ -1363,10 +1355,32 @@ void SSLClientSocketOpenSSL::ChannelIDRequestCallback(SSL* ssl,
ServerBoundCertService::kEPKIPassword,
encrypted_private_key_info,
subject_public_key_info));
+ if (!ec_private_key)
+ return;
set_channel_id_sent(true);
*pkey = EVP_PKEY_dup(ec_private_key->key());
}
+int SSLClientSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx) {
+ if (!completed_handshake_) {
+ // If the first handshake hasn't completed then we accept any certificates
+ // because we verify after the handshake.
+ return 1;
+ }
+
+ CHECK(server_cert_.get());
+
+ PeerCertificateChain chain(store_ctx->untrusted);
+ if (chain.IsValid() && server_cert_->Equals(chain.AsOSChain()))
+ return 1;
+
+ if (!chain.IsValid())
+ LOG(ERROR) << "Received invalid certificate chain between handshakes";
+ else
+ LOG(ERROR) << "Server certificate changed between handshakes";
+ return 0;
+}
+
// SelectNextProtoCallback is called by OpenSSL during the handshake. If the
// server supports NPN, selects a protocol from the list that the server
// provides. According to third_party/openssl/openssl/ssl/ssl_lib.c, the
@@ -1375,7 +1389,6 @@ int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out,
unsigned char* outlen,
const unsigned char* in,
unsigned int inlen) {
-#if defined(OPENSSL_NPN_NEGOTIATED)
if (ssl_config_.next_protos.empty()) {
*out = reinterpret_cast<uint8*>(
const_cast<char*>(kDefaultSupportedNPNProtocol));
@@ -1415,8 +1428,12 @@ int SSLClientSocketOpenSSL::SelectNextProtoCallback(unsigned char** out,
npn_proto_.assign(reinterpret_cast<const char*>(*out), *outlen);
server_protos_.assign(reinterpret_cast<const char*>(in), inlen);
DVLOG(2) << "next protocol: '" << npn_proto_ << "' status: " << npn_status_;
-#endif
return SSL_TLSEXT_ERR_OK;
}
+scoped_refptr<X509Certificate>
+SSLClientSocketOpenSSL::GetUnverifiedServerCertificateChain() const {
+ return server_cert_;
+}
+
} // namespace net