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 c3af8b84d83e62340efb535f64a5e2d93da2ac86..97ef5b6bcb082a52c9e5185b88a727fbf534f720 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); | 
| 
 
Ryan Sleevi
2015/10/01 21:46:15
Why do we need to do the lookup in the SSL socket?
 
 | 
| + return channel_id_service_->GetOrCreateChannelID( | 
| + host_and_port_.host(), &channel_id_key_, | 
| + base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete, | 
| + base::Unretained(this)), | 
| + &channel_id_request_); | 
| +} | 
| + | 
| +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) != 0; | 
| +} | 
| + | 
| +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 |