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 c3af8b84d83e62340efb535f64a5e2d93da2ac86..bcf23eaf9c63c1411a4d982b13035e1bcc484b67 100644 |
--- a/net/socket/ssl_client_socket_openssl.cc |
+++ b/net/socket/ssl_client_socket_openssl.cc |
@@ -9,7 +9,9 @@ |
#include <errno.h> |
#include <openssl/bio.h> |
+#include <openssl/bytestring.h> |
#include <openssl/err.h> |
+#include <openssl/evp.h> |
#include <openssl/mem.h> |
#include <openssl/ssl.h> |
#include <string.h> |
@@ -255,6 +257,10 @@ class SSLClientSocketOpenSSL::SSLContext { |
SSL_CTX_set_keylog_bio(ssl_ctx_.get(), bio); |
} |
} |
+ |
+ if (!TokenBindingExtension::RegisterCallbacks(ssl_ctx_.get())) { |
+ NOTREACHED(); |
+ } |
} |
static int ClientCertRequestCallback(SSL* ssl, void* arg) { |
@@ -541,6 +547,11 @@ int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) { |
return rv; |
} |
+ // Set params for TokenBindingExtension object. |
+ if (IsTokenBindingEnabled(ssl_config_, channel_id_service_)) { |
+ token_binding_extension_.SetParams(ssl_config_.token_binding_params); |
+ } |
+ |
// Set SSL to client mode. Handshake happens in the loop below. |
SSL_set_connect_state(ssl_); |
@@ -704,6 +715,7 @@ bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { |
ssl_info->client_cert_sent = |
ssl_config_.send_client_cert && ssl_config_.client_cert.get(); |
ssl_info->channel_id_sent = channel_id_sent_; |
+ ssl_info->token_binding_negotiated = token_binding_extension_.WasNegotiated(); |
ssl_info->pinning_failure_log = pinning_failure_log_; |
AddSCTInfoToSSLInfo(ssl_info); |
@@ -1102,6 +1114,18 @@ int SSLClientSocketOpenSSL::DoHandshakeComplete(int result) { |
return ERR_SSL_FALLBACK_BEYOND_MINIMUM_VERSION; |
} |
+ // Check that if token binding was negotiated, then extended master secret |
+ // must also be negotiated. |
+ if (token_binding_extension_.WasNegotiated() && |
+ !ssl_->session->extended_master_secret) { |
+ return ERR_SSL_PROTOCOL_ERROR; |
+ } |
+ |
+ if (token_binding_extension_.WasNegotiated() && !channel_id_key_) { |
+ GotoState(STATE_TOKEN_BINDING_LOOKUP); |
+ return OK; |
+ } |
+ |
// SSL handshake is completed. If NPN wasn't negotiated, see if ALPN was. |
if (npn_status_ == kNextProtoUnsupported) { |
const uint8_t* alpn_proto = NULL; |
@@ -1118,6 +1142,8 @@ int SSLClientSocketOpenSSL::DoHandshakeComplete(int result) { |
RecordChannelIDSupport(channel_id_service_, channel_id_sent_, |
ssl_config_.channel_id_enabled, |
crypto::ECPrivateKey::IsSupported()); |
+ RecordTokenBindingSupport(ssl_config_, channel_id_service_, |
+ token_binding_extension_.WasNegotiated()); |
// Only record OCSP histograms if OCSP was requested. |
if (ssl_config_.signed_cert_timestamps_enabled || |
@@ -1180,6 +1206,31 @@ int SSLClientSocketOpenSSL::DoChannelIDLookupComplete(int result) { |
return OK; |
} |
+int SSLClientSocketOpenSSL::DoTokenBindingLookup() { |
+ net_log_.BeginEvent(NetLog::TYPE_SSL_GET_TOKEN_BINDING_KEY); |
+ GotoState(STATE_TOKEN_BINDING_LOOKUP_COMPLETE); |
+ return channel_id_service_->GetOrCreateChannelID( |
+ host_and_port_.host(), &channel_id_key_, |
+ base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete, |
+ base::Unretained(this)), |
+ &channel_id_request_); |
davidben
2015/10/01 16:15:17
What does this do? Do we even use this key for any
nharper
2015/10/01 19:12:23
We use this key once at the SSL layer to generate
|
+} |
+ |
+int SSLClientSocketOpenSSL::DoTokenBindingLookupComplete(int result) { |
+ if (!channel_id_key_) { |
+ LOG(ERROR) << "Failed to import Channel ID."; |
+ result = ERR_CHANNEL_ID_IMPORT_FAILED; |
+ } |
+ |
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_GET_TOKEN_BINDING_KEY, |
+ result); |
+ if (result < 0) |
+ return result; |
+ |
+ GotoState(STATE_HANDSHAKE_COMPLETE); |
+ return OK; |
+} |
+ |
int SSLClientSocketOpenSSL::DoVerifyCert(int result) { |
DCHECK(!server_cert_chain_->empty()); |
DCHECK(start_cert_verification_time_.is_null()); |
@@ -1410,6 +1461,13 @@ int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) { |
case STATE_CHANNEL_ID_LOOKUP_COMPLETE: |
rv = DoChannelIDLookupComplete(rv); |
break; |
+ case STATE_TOKEN_BINDING_LOOKUP: |
+ DCHECK_EQ(OK, rv); |
+ rv = DoTokenBindingLookup(); |
+ break; |
+ case STATE_TOKEN_BINDING_LOOKUP_COMPLETE: |
+ rv = DoTokenBindingLookupComplete(rv); |
+ break; |
case STATE_VERIFY_CERT: |
DCHECK_EQ(OK, rv); |
rv = DoVerifyCert(rv); |
@@ -2161,4 +2219,164 @@ void SSLClientSocketOpenSSL::OnPrivateKeySignComplete( |
PumpReadWriteEvents(); |
} |
+SSLClientSocketOpenSSL::TokenBindingExtension::TokenBindingExtension() |
+ : negotiated_(false), negotiated_param_(TokenBindingParam(0)) {} |
+ |
+SSLClientSocketOpenSSL::TokenBindingExtension::~TokenBindingExtension() {} |
+ |
+bool SSLClientSocketOpenSSL::TokenBindingExtension::WasNegotiated() const { |
+ return negotiated_; |
+} |
+ |
+TokenBindingParam |
+SSLClientSocketOpenSSL::TokenBindingExtension::NegotiationResult() const { |
+ return negotiated_param_; |
+}; |
+ |
+void SSLClientSocketOpenSSL::TokenBindingExtension::SetParams( |
+ const std::vector<TokenBindingParam>& params) { |
+ supported_params_.clear(); |
+ for (TokenBindingParam param : params) { |
+ supported_params_.push_back(param); |
+ } |
+}; |
+ |
+// static |
+bool SSLClientSocketOpenSSL::TokenBindingExtension::RegisterCallbacks( |
+ SSL_CTX* ssl_ctx) { |
+ return SSL_CTX_add_client_custom_ext( |
+ ssl_ctx, kExtNum, &TokenBindingExtension::ClientAddCallback, |
+ &TokenBindingExtension::ClientFreeCallback, nullptr, |
+ &TokenBindingExtension::ClientParseCallback, nullptr); |
+} |
+ |
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientAdd( |
+ const uint8_t** out, |
+ size_t* out_len, |
+ int* out_alert_value) { |
+ if (supported_params_.empty()) { |
+ return 0; |
+ } |
+ CBB output, parameters_list; |
+ int retval = -1; |
+ if (!CBB_init(&output, 7)) |
+ goto end; |
+ if (!CBB_add_u8(&output, kProtocolVersionMajor) || |
+ !CBB_add_u8(&output, kProtocolVersionMinor) || |
+ !CBB_add_u8_length_prefixed(&output, ¶meters_list)) { |
+ goto end; |
+ } |
+ for (size_t i = 0; i < supported_params_.size(); ++i) { |
+ if (!CBB_add_u8(¶meters_list, supported_params_[i])) { |
+ goto end; |
+ } |
+ } |
+ if (!CBB_finish(&output, const_cast<uint8_t**>(out), out_len)) |
+ goto end; |
+ |
+ retval = 1; |
+ |
+end: |
+ CBB_cleanup(&output); |
+ if (retval == -1) |
+ *out_alert_value = SSL_AD_INTERNAL_ERROR; |
+ return retval; |
+} |
+ |
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientParse( |
+ const uint8_t* contents, |
+ size_t contents_len, |
+ int* out_alert_value) { |
+ CBS extension; |
+ CBS_init(&extension, contents, contents_len); |
+ |
+ CBS parameters_list; |
+ uint8_t version_major, version_minor, param; |
+ if (!CBS_get_u8(&extension, &version_major) || |
+ !CBS_get_u8(&extension, &version_minor) || |
+ version_major != kProtocolVersionMajor || |
+ version_minor != kProtocolVersionMinor || |
+ !CBS_get_u8_length_prefixed(&extension, ¶meters_list) || |
+ !CBS_get_u8(¶meters_list, ¶m) || CBS_len(&extension) > 0) { |
+ *out_alert_value = SSL_AD_DECODE_ERROR; |
+ return 0; |
+ } |
+ bool valid_param = false; |
+ for (size_t i = 0; i < supported_params_.size(); ++i) { |
+ if (param == supported_params_[i]) { |
+ negotiated_param_ = TokenBindingParam(param); |
+ valid_param = true; |
+ break; |
+ } |
+ } |
+ |
+ // The server-negotiated version must be less than or equal to our version. |
+ if (version_major > kProtocolVersionMajor || |
+ (version_minor > kProtocolVersionMinor && |
+ version_major == kProtocolVersionMajor)) { |
+ *out_alert_value = SSL_AD_ILLEGAL_PARAMETER; |
+ return 0; |
+ } |
+ |
+ // Although the server returns the negotiated key param in a list, it must |
+ // only send one element in that list. |
+ if (CBS_len(¶meters_list) > 0 || !valid_param) { |
+ *out_alert_value = SSL_AD_ILLEGAL_PARAMETER; |
+ return 0; |
+ } |
+ |
+ // If the version the server negotiated is older than we support, don't fail |
+ // parsing the extension, but also don't set |negotiated_|. |
+ if (version_major < kMinProtocolVersionMajor || |
+ (version_minor < kMinProtocolVersionMinor && |
+ version_major == kMinProtocolVersionMajor)) { |
+ return 1; |
+ } |
+ |
+ negotiated_ = true; |
+ return 1; |
+} |
+ |
+// static |
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientAddCallback( |
+ SSL* ssl, |
+ unsigned int extension_value, |
+ const uint8_t** out, |
+ size_t* out_len, |
+ int* out_alert_value, |
+ void* add_arg) { |
+ DCHECK(extension_value == kExtNum); |
+ SSLClientSocketOpenSSL* socket = |
+ SSLClientSocketOpenSSL::SSLContext::GetInstance()->GetClientSocketFromSSL( |
+ ssl); |
+ return socket->token_binding_extension_.ClientAdd(out, out_len, |
+ out_alert_value); |
+} |
+ |
+// static |
+void SSLClientSocketOpenSSL::TokenBindingExtension::ClientFreeCallback( |
+ SSL* ssl, |
+ unsigned extension_value, |
+ const uint8_t* out, |
+ void* add_arg) { |
+ DCHECK(extension_value == kExtNum); |
+ OPENSSL_free(const_cast<unsigned char*>(out)); |
+} |
+ |
+// static |
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientParseCallback( |
+ SSL* ssl, |
+ unsigned int extension_value, |
+ const uint8_t* contents, |
+ size_t contents_len, |
+ int* out_alert_value, |
+ void* parse_arg) { |
+ DCHECK(extension_value == kExtNum); |
+ SSLClientSocketOpenSSL* socket = |
+ SSLClientSocketOpenSSL::SSLContext::GetInstance()->GetClientSocketFromSSL( |
+ ssl); |
+ return socket->token_binding_extension_.ClientParse(contents, contents_len, |
+ out_alert_value); |
+} |
+ |
} // namespace net |