| 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 0d39f6483f2b3bcfed6a543a61e78995d917828a..59d170778bf2cbfec29ff2134455ae71d87dadde 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>
|
| @@ -82,6 +84,15 @@ const char kDefaultSupportedNPNProtocol[] = "http/1.1";
|
| // Default size of the internal BoringSSL buffers.
|
| const int KDefaultOpenSSLBufferSize = 17 * 1024;
|
|
|
| +// TLS extension number use for Token Binding.
|
| +const unsigned int kTbExtNum = 30033;
|
| +
|
| +// Token Binding ProtocolVersions supported.
|
| +const uint8_t kTbProtocolVersionMajor = 0;
|
| +const uint8_t kTbProtocolVersionMinor = 3;
|
| +const uint8_t kTbMinProtocolVersionMajor = 0;
|
| +const uint8_t kTbMinProtocolVersionMinor = 2;
|
| +
|
| void FreeX509Stack(STACK_OF(X509)* ptr) {
|
| sk_X509_pop_free(ptr, X509_free);
|
| }
|
| @@ -194,6 +205,18 @@ base::LazyInstance<PlatformKeyTaskRunner>::Leaky g_platform_key_task_runner =
|
| LAZY_INSTANCE_INITIALIZER;
|
| #endif
|
|
|
| +class ScopedCBB {
|
| + public:
|
| + ScopedCBB() { CBB_zero(&cbb_); }
|
| + ~ScopedCBB() { CBB_cleanup(&cbb_); }
|
| +
|
| + CBB* get() { return &cbb_; }
|
| +
|
| + private:
|
| + CBB cbb_;
|
| + DISALLOW_COPY_AND_ASSIGN(ScopedCBB);
|
| +};
|
| +
|
| } // namespace
|
|
|
| class SSLClientSocketOpenSSL::SSLContext {
|
| @@ -243,6 +266,47 @@ class SSLClientSocketOpenSSL::SSLContext {
|
| SSL_CTX_set_session_cache_mode(
|
| ssl_ctx_.get(), SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
|
| SSL_CTX_sess_set_new_cb(ssl_ctx_.get(), NewSessionCallback);
|
| +
|
| + if (!SSL_CTX_add_client_custom_ext(ssl_ctx_.get(), kTbExtNum,
|
| + &TokenBindingAddCallback,
|
| + &TokenBindingFreeCallback, nullptr,
|
| + &TokenBindingParseCallback, nullptr)) {
|
| + NOTREACHED();
|
| + }
|
| + }
|
| +
|
| + static int TokenBindingAddCallback(SSL* ssl,
|
| + unsigned int extension_value,
|
| + const uint8_t** out,
|
| + size_t* out_len,
|
| + int* out_alert_value,
|
| + void* add_arg) {
|
| + DCHECK_EQ(extension_value, kTbExtNum);
|
| + SSLClientSocketOpenSSL* socket =
|
| + SSLClientSocketOpenSSL::SSLContext::GetInstance()
|
| + ->GetClientSocketFromSSL(ssl);
|
| + return socket->TokenBindingAdd(out, out_len, out_alert_value);
|
| + }
|
| +
|
| + static void TokenBindingFreeCallback(SSL* ssl,
|
| + unsigned extension_value,
|
| + const uint8_t* out,
|
| + void* add_arg) {
|
| + DCHECK_EQ(extension_value, kTbExtNum);
|
| + OPENSSL_free(const_cast<unsigned char*>(out));
|
| + }
|
| +
|
| + static int TokenBindingParseCallback(SSL* ssl,
|
| + unsigned int extension_value,
|
| + const uint8_t* contents,
|
| + size_t contents_len,
|
| + int* out_alert_value,
|
| + void* parse_arg) {
|
| + DCHECK_EQ(extension_value, kTbExtNum);
|
| + SSLClientSocketOpenSSL* socket =
|
| + SSLClientSocketOpenSSL::SSLContext::GetInstance()
|
| + ->GetClientSocketFromSSL(ssl);
|
| + return socket->TokenBindingParse(contents, contents_len, out_alert_value);
|
| }
|
|
|
| static int ClientCertRequestCallback(SSL* ssl, void* arg) {
|
| @@ -431,6 +495,8 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
|
| cert_verifier_(context.cert_verifier),
|
| cert_transparency_verifier_(context.cert_transparency_verifier),
|
| channel_id_service_(context.channel_id_service),
|
| + tb_was_negotiated_(false),
|
| + tb_negotiated_param_(TB_PARAM_ECDSAP256),
|
| ssl_(NULL),
|
| transport_bio_(NULL),
|
| transport_(transport_socket.Pass()),
|
| @@ -607,6 +673,7 @@ void SSLClientSocketOpenSSL::Disconnect() {
|
| npn_proto_.clear();
|
|
|
| channel_id_sent_ = false;
|
| + tb_was_negotiated_ = false;
|
| session_pending_ = false;
|
| certificate_verified_ = false;
|
| channel_id_request_.Cancel();
|
| @@ -704,6 +771,8 @@ 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 = tb_was_negotiated_;
|
| + ssl_info->token_binding_key_param = tb_negotiated_param_;
|
| ssl_info->pinning_failure_log = pinning_failure_log_;
|
|
|
| AddSCTInfoToSSLInfo(ssl_info);
|
| @@ -1099,6 +1168,11 @@ 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 (tb_was_negotiated_ && !SSL_get_extms_support(ssl_))
|
| + return ERR_SSL_PROTOCOL_ERROR;
|
| +
|
| // SSL handshake is completed. If NPN wasn't negotiated, see if ALPN was.
|
| if (npn_status_ == kNextProtoUnsupported) {
|
| const uint8_t* alpn_proto = NULL;
|
| @@ -2061,6 +2135,9 @@ std::string SSLClientSocketOpenSSL::GetSessionCacheKey() const {
|
| }
|
|
|
| bool SSLClientSocketOpenSSL::IsRenegotiationAllowed() const {
|
| + if (tb_was_negotiated_)
|
| + return false;
|
| +
|
| if (npn_status_ == kNextProtoUnsupported)
|
| return ssl_config_.renego_allowed_default;
|
|
|
| @@ -2161,4 +2238,83 @@ void SSLClientSocketOpenSSL::OnPrivateKeySignComplete(
|
| PumpReadWriteEvents();
|
| }
|
|
|
| +int SSLClientSocketOpenSSL::TokenBindingAdd(const uint8_t** out,
|
| + size_t* out_len,
|
| + int* out_alert_value) {
|
| + if (ssl_config_.token_binding_params.empty()) {
|
| + return 0;
|
| + }
|
| + ScopedCBB output;
|
| + CBB parameters_list;
|
| + if (!CBB_init(output.get(), 7) ||
|
| + !CBB_add_u8(output.get(), kTbProtocolVersionMajor) ||
|
| + !CBB_add_u8(output.get(), kTbProtocolVersionMinor) ||
|
| + !CBB_add_u8_length_prefixed(output.get(), ¶meters_list)) {
|
| + *out_alert_value = SSL_AD_INTERNAL_ERROR;
|
| + return -1;
|
| + }
|
| + for (size_t i = 0; i < ssl_config_.token_binding_params.size(); ++i) {
|
| + if (!CBB_add_u8(¶meters_list, ssl_config_.token_binding_params[i])) {
|
| + *out_alert_value = SSL_AD_INTERNAL_ERROR;
|
| + return -1;
|
| + }
|
| + }
|
| + // |*out| will be freed by TokenBindingFreeCallback.
|
| + if (!CBB_finish(output.get(), const_cast<uint8_t**>(out), out_len)) {
|
| + *out_alert_value = SSL_AD_INTERNAL_ERROR;
|
| + return -1;
|
| + }
|
| +
|
| + return 1;
|
| +}
|
| +
|
| +int SSLClientSocketOpenSSL::TokenBindingParse(const uint8_t* contents,
|
| + size_t contents_len,
|
| + int* out_alert_value) {
|
| + if (completed_connect_) {
|
| + // Token Binding may only be negotiated on the initial handshake.
|
| + *out_alert_value = SSL_AD_ILLEGAL_PARAMETER;
|
| + return 0;
|
| + }
|
| +
|
| + 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) ||
|
| + !CBS_get_u8_length_prefixed(&extension, ¶meters_list) ||
|
| + !CBS_get_u8(¶meters_list, ¶m) || CBS_len(¶meters_list) > 0 ||
|
| + CBS_len(&extension) > 0) {
|
| + *out_alert_value = SSL_AD_DECODE_ERROR;
|
| + return 0;
|
| + }
|
| + // The server-negotiated version must be less than or equal to our version.
|
| + if (version_major > kTbProtocolVersionMajor ||
|
| + (version_minor > kTbProtocolVersionMinor &&
|
| + version_major == kTbProtocolVersionMajor)) {
|
| + *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 < kTbMinProtocolVersionMajor ||
|
| + (version_minor < kTbMinProtocolVersionMinor &&
|
| + version_major == kTbMinProtocolVersionMajor)) {
|
| + return 1;
|
| + }
|
| +
|
| + for (size_t i = 0; i < ssl_config_.token_binding_params.size(); ++i) {
|
| + if (param == ssl_config_.token_binding_params[i]) {
|
| + tb_negotiated_param_ = ssl_config_.token_binding_params[i];
|
| + tb_was_negotiated_ = true;
|
| + return 1;
|
| + }
|
| + }
|
| +
|
| + *out_alert_value = SSL_AD_ILLEGAL_PARAMETER;
|
| + return 0;
|
| +}
|
| +
|
| } // namespace net
|
|
|