Commit fd97353c authored by Sorin Jianu's avatar Sorin Jianu Committed by Commit Bot

Cup request sender: use x-cup-server-proof header when available.

After this change, the RequestSender is choosing between
x-cup-server-proof and etag value, and gives priority to the
x-cup-server-proof value if it is non-empty.

The values for both headers is read by the network stacks, and made
them available to the RequestSender, which implements the validation
mechanism in one place.

It's impractical to unit test this code, since the RequestSender
hardcodes the production public key and it can only transact CUP
with a production backend.

Bug: 606958
Change-Id: I5b0f2e9f2ba1e75ca4fd4c4becde848846541bfd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2266918Reviewed-by: default avatarJoshua Pawlicki <waffles@chromium.org>
Reviewed-by: default avatarS. Ganesh <ganesh@chromium.org>
Commit-Queue: Sorin Jianu <sorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#782987}
parent f343a89b
...@@ -155,6 +155,12 @@ using DownloadToFileCompleteCallback = ...@@ -155,6 +155,12 @@ using DownloadToFileCompleteCallback =
if ([headers objectForKey:headerEtag]) { if ([headers objectForKey:headerEtag]) {
etag = [headers objectForKey:headerEtag]; etag = [headers objectForKey:headerEtag];
} }
NSString* headerXCupServerProof = base::SysUTF8ToNSString(
update_client::NetworkFetcher::kHeaderXCupServerProof);
NSString* cupServerProof = @"";
if ([headers objectForKey:headerXCupServerProof]) {
cupServerProof = [headers objectForKey:headerXCupServerProof];
}
int64_t retryAfterResult = -1; int64_t retryAfterResult = -1;
NSString* xRetryAfter = [headers NSString* xRetryAfter = [headers
objectForKey:base::SysUTF8ToNSString( objectForKey:base::SysUTF8ToNSString(
...@@ -173,8 +179,8 @@ using DownloadToFileCompleteCallback = ...@@ -173,8 +179,8 @@ using DownloadToFileCompleteCallback =
base::BindOnce( base::BindOnce(
std::move(_postRequestCompleteCallback), std::move(_postRequestCompleteCallback),
std::make_unique<std::string>(base::SysNSStringToUTF8(dataToUse)), std::make_unique<std::string>(base::SysNSStringToUTF8(dataToUse)),
error.code, std::string(base::SysNSStringToUTF8(etag)), error.code, base::SysNSStringToUTF8(etag),
retryAfterResult)); base::SysNSStringToUTF8(cupServerProof), retryAfterResult));
} }
@end @end
......
...@@ -51,9 +51,11 @@ class ChromeUpdaterNetworkMacTest : public ::testing::Test { ...@@ -51,9 +51,11 @@ class ChromeUpdaterNetworkMacTest : public ::testing::Test {
void PostRequestCompleteCallback(std::unique_ptr<std::string> response_body, void PostRequestCompleteCallback(std::unique_ptr<std::string> response_body,
int net_error, int net_error,
const std::string& header_etag, const std::string& header_etag,
const std::string& header_x_cup_server_proof,
int64_t xheader_retry_after_sec) { int64_t xheader_retry_after_sec) {
EXPECT_EQ(net_error, 0); EXPECT_EQ(net_error, 0);
EXPECT_GT(header_etag.length(), 0u); EXPECT_STREQ(header_etag.c_str(), "Wfhw789h");
EXPECT_STREQ(header_x_cup_server_proof.c_str(), "server-proof");
EXPECT_EQ(xheader_retry_after_sec, 67); EXPECT_EQ(xheader_retry_after_sec, 67);
PostRequestCompleted(); PostRequestCompleted();
} }
...@@ -75,6 +77,7 @@ class ChromeUpdaterNetworkMacTest : public ::testing::Test { ...@@ -75,6 +77,7 @@ class ChromeUpdaterNetworkMacTest : public ::testing::Test {
http_response->set_content_type("text/plain"); http_response->set_content_type("text/plain");
http_response->AddCustomHeader("X-Retry-After", "67"); http_response->AddCustomHeader("X-Retry-After", "67");
http_response->AddCustomHeader("ETag", "Wfhw789h"); http_response->AddCustomHeader("ETag", "Wfhw789h");
http_response->AddCustomHeader("X-Cup-Server-Proof", "server-proof");
return http_response; return http_response;
} }
......
...@@ -66,7 +66,8 @@ void NetworkFetcher::PostRequestComplete() { ...@@ -66,7 +66,8 @@ void NetworkFetcher::PostRequestComplete() {
std::move(post_request_complete_callback_) std::move(post_request_complete_callback_)
.Run(std::make_unique<std::string>(network_fetcher_->GetResponseBody()), .Run(std::make_unique<std::string>(network_fetcher_->GetResponseBody()),
network_fetcher_->GetNetError(), network_fetcher_->GetHeaderETag(), network_fetcher_->GetNetError(), network_fetcher_->GetHeaderETag(),
network_fetcher_->GetXHeaderRetryAfterSec()); network_fetcher_->GetHeaderXCupServerProof(),
network_fetcher_->GetHeaderXRetryAfterSec());
} }
void NetworkFetcher::DownloadToFileComplete() { void NetworkFetcher::DownloadToFileComplete() {
......
...@@ -92,12 +92,17 @@ HRESULT NetworkFetcherWinHTTP::GetNetError() const { ...@@ -92,12 +92,17 @@ HRESULT NetworkFetcherWinHTTP::GetNetError() const {
std::string NetworkFetcherWinHTTP::GetHeaderETag() const { std::string NetworkFetcherWinHTTP::GetHeaderETag() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return etag_; return header_etag_;
} }
int64_t NetworkFetcherWinHTTP::GetXHeaderRetryAfterSec() const { std::string NetworkFetcherWinHTTP::GetHeaderXCupServerProof() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return xheader_retry_after_sec_; return header_x_cup_server_proof_;
}
int64_t NetworkFetcherWinHTTP::GetHeaderXRetryAfterSec() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return header_x_retry_after_sec_;
} }
base::FilePath NetworkFetcherWinHTTP::GetFilePath() const { base::FilePath NetworkFetcherWinHTTP::GetFilePath() const {
...@@ -299,7 +304,16 @@ void NetworkFetcherWinHTTP::HeadersAvailable() { ...@@ -299,7 +304,16 @@ void NetworkFetcherWinHTTP::HeadersAvailable() {
base::string16 etag; base::string16 etag;
if (SUCCEEDED(QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_ETAG, if (SUCCEEDED(QueryHeadersString(request_handle_.get(), WINHTTP_QUERY_ETAG,
WINHTTP_HEADER_NAME_BY_INDEX, &etag))) { WINHTTP_HEADER_NAME_BY_INDEX, &etag))) {
etag_ = base::SysWideToUTF8(etag); header_etag_ = base::SysWideToUTF8(etag);
}
base::string16 xheader_cup_server_proof;
if (SUCCEEDED(QueryHeadersString(
request_handle_.get(), WINHTTP_QUERY_CUSTOM,
base::SysUTF8ToWide(
update_client::NetworkFetcher::kHeaderXCupServerProof),
&xheader_cup_server_proof))) {
header_x_cup_server_proof_ = base::SysWideToUTF8(xheader_cup_server_proof);
} }
int xheader_retry_after_sec = 0; int xheader_retry_after_sec = 0;
...@@ -308,7 +322,7 @@ void NetworkFetcherWinHTTP::HeadersAvailable() { ...@@ -308,7 +322,7 @@ void NetworkFetcherWinHTTP::HeadersAvailable() {
base::SysUTF8ToWide( base::SysUTF8ToWide(
update_client::NetworkFetcher::kHeaderXRetryAfter), update_client::NetworkFetcher::kHeaderXRetryAfter),
&xheader_retry_after_sec))) { &xheader_retry_after_sec))) {
xheader_retry_after_sec_ = xheader_retry_after_sec; header_x_retry_after_sec_ = xheader_retry_after_sec;
} }
std::move(fetch_started_callback_).Run(response_code, content_length); std::move(fetch_started_callback_).Run(response_code, content_length);
......
...@@ -66,7 +66,8 @@ class NetworkFetcherWinHTTP ...@@ -66,7 +66,8 @@ class NetworkFetcherWinHTTP
std::string GetResponseBody() const; std::string GetResponseBody() const;
HRESULT GetNetError() const; HRESULT GetNetError() const;
std::string GetHeaderETag() const; std::string GetHeaderETag() const;
int64_t GetXHeaderRetryAfterSec() const; std::string GetHeaderXCupServerProof() const;
int64_t GetHeaderXRetryAfterSec() const;
base::FilePath GetFilePath() const; base::FilePath GetFilePath() const;
// Returns the number of bytes retrieved from the network. This may be // Returns the number of bytes retrieved from the network. This may be
...@@ -133,8 +134,9 @@ class NetworkFetcherWinHTTP ...@@ -133,8 +134,9 @@ class NetworkFetcherWinHTTP
base::string16 content_type_; base::string16 content_type_;
WriteDataCallback write_data_callback_; WriteDataCallback write_data_callback_;
HRESULT net_error_ = S_OK; HRESULT net_error_ = S_OK;
std::string etag_; std::string header_etag_;
int64_t xheader_retry_after_sec_ = -1; std::string header_x_cup_server_proof_;
int64_t header_x_retry_after_sec_ = -1;
std::vector<char> read_buffer_; std::vector<char> read_buffer_;
std::string post_response_body_; std::string post_response_body_;
base::FilePath file_path_; base::FilePath file_path_;
......
...@@ -139,6 +139,7 @@ void NetworkFetcherImpl::PostRequest( ...@@ -139,6 +139,7 @@ void NetworkFetcherImpl::PostRequest(
std::move(post_request_complete_callback) std::move(post_request_complete_callback)
.Run(std::move(response_body), simple_url_loader->NetError(), .Run(std::move(response_body), simple_url_loader->NetError(),
GetStringHeader(simple_url_loader, kHeaderEtag), GetStringHeader(simple_url_loader, kHeaderEtag),
GetStringHeader(simple_url_loader, kHeaderXCupServerProof),
GetInt64Header(simple_url_loader, kHeaderXRetryAfter)); GetInt64Header(simple_url_loader, kHeaderXRetryAfter));
}, },
simple_url_loader_.get(), std::move(post_request_complete_callback)), simple_url_loader_.get(), std::move(post_request_complete_callback)),
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace update_client { namespace update_client {
constexpr char NetworkFetcher::kHeaderEtag[]; constexpr char NetworkFetcher::kHeaderEtag[];
constexpr char NetworkFetcher::kHeaderXCupServerProof[];
constexpr char NetworkFetcher::kHeaderXRetryAfter[]; constexpr char NetworkFetcher::kHeaderXRetryAfter[];
} // namespace update_client } // namespace update_client
...@@ -31,6 +31,7 @@ class NetworkFetcher { ...@@ -31,6 +31,7 @@ class NetworkFetcher {
base::OnceCallback<void(std::unique_ptr<std::string> response_body, base::OnceCallback<void(std::unique_ptr<std::string> response_body,
int net_error, int net_error,
const std::string& header_etag, const std::string& header_etag,
const std::string& header_x_cup_server_proof,
int64_t xheader_retry_after_sec)>; int64_t xheader_retry_after_sec)>;
using DownloadToFileCompleteCallback = using DownloadToFileCompleteCallback =
base::OnceCallback<void(int net_error, int64_t content_size)>; base::OnceCallback<void(int net_error, int64_t content_size)>;
...@@ -38,9 +39,11 @@ class NetworkFetcher { ...@@ -38,9 +39,11 @@ class NetworkFetcher {
base::OnceCallback<void(int response_code, int64_t content_length)>; base::OnceCallback<void(int response_code, int64_t content_length)>;
using ProgressCallback = base::RepeatingCallback<void(int64_t current)>; using ProgressCallback = base::RepeatingCallback<void(int64_t current)>;
// The ETag header carries the ECSDA signature of the POST response, if // The following two headers carry the ECSDA signature of the POST response,
// signing has been used. // if signing has been used. Two headers are used for redundancy purposes.
// The value of the `X-Cup-Server-Proof` is preferred.
static constexpr char kHeaderEtag[] = "ETag"; static constexpr char kHeaderEtag[] = "ETag";
static constexpr char kHeaderXCupServerProof[] = "X-Cup-Server-Proof";
// The server uses the optional X-Retry-After header to indicate that the // The server uses the optional X-Retry-After header to indicate that the
// current request should not be attempted again. // current request should not be attempted again.
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/base64.h" #include "base/base64.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/location.h" #include "base/location.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
...@@ -32,6 +33,18 @@ constexpr char kKeyPubBytesBase64[] = ...@@ -32,6 +33,18 @@ constexpr char kKeyPubBytesBase64[] =
// The content type for all protocol requests. // The content type for all protocol requests.
constexpr char kContentType[] = "application/json"; constexpr char kContentType[] = "application/json";
// Returns the value of |response_cup_server_proof| or the value of
// |response_etag|, if the former value is empty.
const std::string& SelectCupServerProof(
const std::string& response_cup_server_proof,
const std::string& response_etag) {
if (response_cup_server_proof.empty()) {
DVLOG(3) << "Using etag as cup server proof.";
return response_etag;
}
return response_cup_server_proof;
}
} // namespace } // namespace
RequestSender::RequestSender(scoped_refptr<Configurator> config) RequestSender::RequestSender(scoped_refptr<Configurator> config)
...@@ -96,20 +109,22 @@ void RequestSender::SendInternal() { ...@@ -96,20 +109,22 @@ void RequestSender::SendInternal() {
base::BindOnce(&RequestSender::SendInternalComplete, base::BindOnce(&RequestSender::SendInternalComplete,
base::Unretained(this), base::Unretained(this),
static_cast<int>(ProtocolError::URL_FETCHER_FAILED), static_cast<int>(ProtocolError::URL_FETCHER_FAILED),
std::string(), std::string(), 0)); std::string(), std::string(), std::string(), 0));
} }
network_fetcher_->PostRequest( network_fetcher_->PostRequest(
url, request_body_, kContentType, request_extra_headers_, url, request_body_, kContentType, request_extra_headers_,
base::BindOnce(&RequestSender::OnResponseStarted, base::Unretained(this)), base::BindOnce(&RequestSender::OnResponseStarted, base::Unretained(this)),
base::BindRepeating([](int64_t current) {}), base::DoNothing(),
base::BindOnce(&RequestSender::OnNetworkFetcherComplete, base::BindOnce(&RequestSender::OnNetworkFetcherComplete,
base::Unretained(this), url)); base::Unretained(this), url));
} }
void RequestSender::SendInternalComplete(int error, void RequestSender::SendInternalComplete(
const std::string& response_body, int error,
const std::string& response_etag, const std::string& response_body,
int retry_after_sec) { const std::string& response_etag,
const std::string& response_cup_server_proof,
int retry_after_sec) {
VLOG(2) << "Omaha response received: " << response_body; VLOG(2) << "Omaha response received: " << response_body;
if (!error) { if (!error) {
...@@ -122,7 +137,9 @@ void RequestSender::SendInternalComplete(int error, ...@@ -122,7 +137,9 @@ void RequestSender::SendInternalComplete(int error,
DCHECK(use_signing_); DCHECK(use_signing_);
DCHECK(signer_); DCHECK(signer_);
if (signer_->ValidateResponse(response_body, response_etag)) { if (signer_->ValidateResponse(
response_body,
SelectCupServerProof(response_cup_server_proof, response_etag))) {
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(request_sender_callback_), 0, FROM_HERE, base::BindOnce(std::move(request_sender_callback_), 0,
response_body, retry_after_sec)); response_body, retry_after_sec));
...@@ -157,6 +174,7 @@ void RequestSender::OnNetworkFetcherComplete( ...@@ -157,6 +174,7 @@ void RequestSender::OnNetworkFetcherComplete(
std::unique_ptr<std::string> response_body, std::unique_ptr<std::string> response_body,
int net_error, int net_error,
const std::string& header_etag, const std::string& header_etag,
const std::string& xheader_cup_server_proof,
int64_t xheader_retry_after_sec) { int64_t xheader_retry_after_sec) {
DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(thread_checker_.CalledOnValidThread());
...@@ -175,10 +193,11 @@ void RequestSender::OnNetworkFetcherComplete( ...@@ -175,10 +193,11 @@ void RequestSender::OnNetworkFetcherComplete(
retry_after_sec = base::saturated_cast<int>(xheader_retry_after_sec); retry_after_sec = base::saturated_cast<int>(xheader_retry_after_sec);
base::ThreadTaskRunnerHandle::Get()->PostTask( base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&RequestSender::SendInternalComplete, FROM_HERE,
base::Unretained(this), error, base::BindOnce(&RequestSender::SendInternalComplete,
response_body ? *response_body : std::string(), base::Unretained(this), error,
header_etag, retry_after_sec)); response_body ? *response_body : std::string(),
header_etag, xheader_cup_server_proof, retry_after_sec));
} }
void RequestSender::HandleSendError(int error, int retry_after_sec) { void RequestSender::HandleSendError(int error, int retry_after_sec) {
......
...@@ -71,6 +71,7 @@ class RequestSender { ...@@ -71,6 +71,7 @@ class RequestSender {
std::unique_ptr<std::string> response_body, std::unique_ptr<std::string> response_body,
int net_error, int net_error,
const std::string& header_etag, const std::string& header_etag,
const std::string& xheader_cup_server_proof,
int64_t xheader_retry_after_sec); int64_t xheader_retry_after_sec);
// Implements the error handling and url fallback mechanism. // Implements the error handling and url fallback mechanism.
...@@ -81,6 +82,7 @@ class RequestSender { ...@@ -81,6 +82,7 @@ class RequestSender {
void SendInternalComplete(int error, void SendInternalComplete(int error,
const std::string& response_body, const std::string& response_body,
const std::string& response_etag, const std::string& response_etag,
const std::string& response_cup_server_proof,
int retry_after_sec); int retry_after_sec);
// Helper function to handle a non-continuable error in Send. // Helper function to handle a non-continuable error in Send.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment