Chromium Code Reviews| Index: net/http/http_network_transaction.cc |
| diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc |
| index 8a8e7421e4bbd5d3e8f5390f3fd5f601c176b45d..90098eda064a823f948a8802079a1c765e4cd14e 100644 |
| --- a/net/http/http_network_transaction.cc |
| +++ b/net/http/http_network_transaction.cc |
| @@ -7,6 +7,7 @@ |
| #include <set> |
| #include <vector> |
| +#include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/compiler_specific.h" |
| @@ -60,6 +61,7 @@ |
| #include "net/spdy/spdy_session_pool.h" |
| #include "net/ssl/ssl_cert_request_info.h" |
| #include "net/ssl/ssl_connection_status_flags.h" |
| +#include "net/ssl/token_binding.h" |
| #include "url/gurl.h" |
| #include "url/url_canon.h" |
| @@ -198,6 +200,11 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info, |
| if (request_->privacy_mode == PRIVACY_MODE_ENABLED) |
| server_ssl_config_.channel_id_enabled = false; |
| + if (session_->params().enable_token_binding && |
| + session_->params().channel_id_service) { |
| + server_ssl_config_.token_binding_params.push_back(TB_PARAM_ECDSAP256); |
| + } |
| + |
| next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM; |
| int rv = DoLoop(OK); |
| if (rv == ERR_IO_PENDING) |
| @@ -614,6 +621,48 @@ bool HttpNetworkTransaction::IsSecureRequest() const { |
| return request_->url.SchemeIsCryptographic(); |
| } |
| +bool HttpNetworkTransaction::IsTokenBindingEnabled() const { |
| + if (!IsSecureRequest()) |
| + return false; |
| + SSLInfo ssl_info; |
| + stream_->GetSSLInfo(&ssl_info); |
| + if (!ssl_info.token_binding_negotiated) |
| + return false; |
| + if (ssl_info.token_binding_key_param != TB_PARAM_ECDSAP256) |
| + return false; |
| + if (!session_->params().channel_id_service) |
| + return false; |
| + return true; |
| +} |
| + |
| +void HttpNetworkTransaction::RecordTokenBindingSupport() const { |
| + enum { |
| + DISABLED = 0, |
| + CLIENT_ONLY = 1, |
| + CLIENT_AND_SERVER = 2, |
| + CLIENT_NO_CHANNEL_ID_SERVICE = 3, |
| + UNSUPPORTED_KEY_PARAM = 4, |
| + TOKEN_BINDING_SUPPORT_MAX |
| + } supported; |
| + if (!IsSecureRequest()) |
| + return; |
| + SSLInfo ssl_info; |
| + stream_->GetSSLInfo(&ssl_info); |
| + if (!session_->params().enable_token_binding) { |
| + supported = DISABLED; |
| + } else if (!session_->params().channel_id_service) { |
| + supported = CLIENT_NO_CHANNEL_ID_SERVICE; |
| + } else if (ssl_info.token_binding_key_param != TB_PARAM_ECDSAP256) { |
| + supported = UNSUPPORTED_KEY_PARAM; |
|
davidben
2015/11/18 20:49:00
This can never happen, no? You'll only negotiate t
nharper
2015/12/04 01:42:19
My thought here was if the server can negotiate to
|
| + } else if (ssl_info.token_binding_negotiated) { |
| + supported = CLIENT_AND_SERVER; |
| + } else { |
| + supported = CLIENT_ONLY; |
| + } |
| + UMA_HISTOGRAM_ENUMERATION("TokenBinding.Support", supported, |
| + TOKEN_BINDING_SUPPORT_MAX); |
| +} |
| + |
| bool HttpNetworkTransaction::UsingHttpProxyWithoutTunnel() const { |
| return (proxy_info_.is_http() || proxy_info_.is_https() || |
| proxy_info_.is_quic()) && |
| @@ -676,6 +725,13 @@ int HttpNetworkTransaction::DoLoop(int result) { |
| case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE: |
| rv = DoGenerateServerAuthTokenComplete(rv); |
| break; |
| + case STATE_GET_TOKEN_BINDING_KEY: |
| + DCHECK_EQ(OK, rv); |
| + rv = DoGetTokenBindingKey(); |
| + break; |
| + case STATE_GET_TOKEN_BINDING_KEY_COMPLETE: |
| + rv = DoGetTokenBindingKeyComplete(rv); |
| + break; |
| case STATE_INIT_REQUEST_BODY: |
| DCHECK_EQ(OK, rv); |
| rv = DoInitRequestBody(); |
| @@ -891,7 +947,34 @@ int HttpNetworkTransaction::DoGenerateServerAuthToken() { |
| int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) { |
| DCHECK_NE(ERR_IO_PENDING, rv); |
| if (rv == OK) |
| - next_state_ = STATE_INIT_REQUEST_BODY; |
| + next_state_ = STATE_GET_TOKEN_BINDING_KEY; |
| + return rv; |
| +} |
| + |
| +int HttpNetworkTransaction::DoGetTokenBindingKey() { |
| + next_state_ = STATE_GET_TOKEN_BINDING_KEY_COMPLETE; |
| + if (!IsTokenBindingEnabled()) |
| + return OK; |
| + |
| + net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY); |
| + ChannelIDService* channel_id_service = session_->params().channel_id_service; |
| + return channel_id_service->GetOrCreateChannelID( |
| + request_->url.host(), &token_binding_key_, |
| + base::Bind(&HttpNetworkTransaction::OnIOComplete, base::Unretained(this)), |
|
davidben
2015/11/18 20:49:00
io_callback_
nharper
2015/12/04 01:42:19
Done.
|
| + &token_binding_request_); |
| +} |
| + |
| +int HttpNetworkTransaction::DoGetTokenBindingKeyComplete(int rv) { |
| + DCHECK_NE(ERR_IO_PENDING, rv); |
| + next_state_ = STATE_INIT_REQUEST_BODY; |
| + if (!IsTokenBindingEnabled()) |
| + return OK; |
| + |
| + if (rv == OK && !token_binding_key_) |
| + rv = ERR_CHANNEL_ID_IMPORT_FAILED; |
|
davidben
2015/11/18 20:49:00
Is this possible?
nharper
2015/12/04 01:42:19
I can't see how this would be possible (GetOrCreat
|
| + |
| + net_log_.EndEventWithNetErrorCode( |
| + NetLog::TYPE_HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY, rv); |
| return rv; |
| } |
| @@ -928,6 +1011,15 @@ void HttpNetworkTransaction::BuildRequestHeaders( |
| request_headers_.SetHeader(HttpRequestHeaders::kContentLength, "0"); |
| } |
| + RecordTokenBindingSupport(); |
| + if (token_binding_key_) { |
|
davidben
2015/11/18 20:49:00
An HttpNetworkSession may run through a request mu
nharper
2015/12/04 01:42:19
Done.
|
| + std::string token_binding_header = BuildTokenBindingHeader(); |
| + if (token_binding_header != "") { |
| + request_headers_.SetHeader(HttpRequestHeaders::kTokenBinding, |
| + token_binding_header); |
| + } |
| + } |
| + |
| // Honor load flags that impact proxy caches. |
| if (request_->load_flags & LOAD_BYPASS_CACHE) { |
| request_headers_.SetHeader(HttpRequestHeaders::kPragma, "no-cache"); |
| @@ -954,6 +1046,25 @@ void HttpNetworkTransaction::BuildRequestHeaders( |
| request_headers_.HasHeader(HttpRequestHeaders::kProxyAuthorization); |
| } |
| +std::string HttpNetworkTransaction::BuildTokenBindingHeader() { |
| + std::string provided_token_binding; |
| + int rv = stream_->GetProvidedTokenBindingWithKey(token_binding_key_, |
| + &provided_token_binding); |
| + if (rv != OK) |
| + return ""; |
|
davidben
2015/11/18 20:49:00
If this or below fails, it should probably be fata
nharper
2015/12/04 01:42:19
Done.
|
| + std::vector<std::string> token_bindings; |
| + token_bindings.push_back(provided_token_binding); |
| + std::string header; |
| + rv = BuildTokenBindingMessageFromTokenBindings(token_bindings, &header); |
| + if (rv != OK) |
| + return ""; |
| + std::string base64_header; |
| + base::Base64Encode(header, &base64_header); |
| + base::ReplaceChars(base64_header, "+", "-", &base64_header); |
| + base::ReplaceChars(base64_header, "/", "_", &base64_header); |
|
davidben
2015/11/18 20:49:00
base/base64url.h
nharper
2015/12/04 01:42:19
Done.
|
| + return base64_header; |
| +} |
| + |
| int HttpNetworkTransaction::DoInitRequestBody() { |
| next_state_ = STATE_INIT_REQUEST_BODY_COMPLETE; |
| int rv = OK; |