| OLD | NEW |
| (Empty) |
| 1 // Copyright 2008-2010 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 // | |
| 16 // TODO(omaha): to minimize conversions from UNICODE to UTF-8 consider an | |
| 17 // API that takes the request body as UTF-8 and it stores it as UTF-8. | |
| 18 | |
| 19 #include "omaha/net/cup_request.h" | |
| 20 | |
| 21 #include <atlconv.h> | |
| 22 #include <atlstr.h> | |
| 23 #include <vector> | |
| 24 #include "omaha/base/const_addresses.h" | |
| 25 #include "omaha/base/debug.h" | |
| 26 #include "omaha/base/error.h" | |
| 27 #include "omaha/base/encrypt.h" | |
| 28 #include "omaha/base/logging.h" | |
| 29 #include "omaha/base/path.h" | |
| 30 #include "omaha/base/safe_format.h" | |
| 31 #include "omaha/base/security/b64.h" | |
| 32 #include "omaha/base/security/hmac.h" | |
| 33 #include "omaha/base/security/rsa.h" | |
| 34 #include "omaha/base/security/sha.h" | |
| 35 #include "omaha/base/string.h" | |
| 36 #include "omaha/base/utils.h" | |
| 37 #include "omaha/net/cup_utils.h" | |
| 38 #include "omaha/net/http_client.h" | |
| 39 #include "omaha/net/net_utils.h" | |
| 40 #include "omaha/net/network_config.h" | |
| 41 | |
| 42 using omaha::encrypt::EncryptData; | |
| 43 using omaha::encrypt::DecryptData; | |
| 44 | |
| 45 namespace omaha { | |
| 46 | |
| 47 namespace detail { | |
| 48 | |
| 49 class CupRequestImpl { | |
| 50 public: | |
| 51 explicit CupRequestImpl(HttpRequestInterface* http_request); | |
| 52 ~CupRequestImpl(); | |
| 53 | |
| 54 HRESULT Close(); | |
| 55 HRESULT Send(); | |
| 56 HRESULT Cancel(); | |
| 57 HRESULT Pause(); | |
| 58 HRESULT Resume(); | |
| 59 std::vector<uint8> GetResponse() const; | |
| 60 HRESULT QueryHeadersString(uint32 info_level, | |
| 61 const TCHAR* name, | |
| 62 CString* value) const; | |
| 63 CString GetResponseHeaders() const; | |
| 64 int GetHttpStatusCode() const; | |
| 65 CString ToString() const; | |
| 66 void SetEntropy(const void* data, size_t data_length); | |
| 67 | |
| 68 void set_session_handle(HINTERNET session_handle); | |
| 69 void set_url(const CString& url); | |
| 70 void set_request_buffer(const void* buffer, size_t buffer_length); | |
| 71 void set_proxy_configuration(const ProxyConfig& proxy_config); | |
| 72 void set_filename(const CString& filename); | |
| 73 void set_low_priority(bool low_priority); | |
| 74 void set_callback(NetworkRequestCallback* callback); | |
| 75 void set_additional_headers(const CString& additional_headers); | |
| 76 void set_preserve_protocol(bool preserve_protocol); | |
| 77 CString user_agent() const; | |
| 78 void set_user_agent(const CString& user_agent); | |
| 79 void set_proxy_auth_config(const ProxyAuthConfig& proxy_auth_config); | |
| 80 | |
| 81 private: | |
| 82 HRESULT DoSend(); | |
| 83 HRESULT BuildRequest(); | |
| 84 HRESULT BuildChallengeHash(); | |
| 85 HRESULT BuildClientProof(); | |
| 86 HRESULT InitializeEntropy(); | |
| 87 HRESULT AuthenticateResponse(); | |
| 88 | |
| 89 // Loads the {sk, c} credentials from persistent storage. | |
| 90 HRESULT LoadCredentials(std::vector<uint8>* sk, CStringA* c); | |
| 91 | |
| 92 // Saves the {sk, c} credentials. The key is encrypted before saving it. | |
| 93 HRESULT SaveCredentials(const std::vector<uint8>& sk, const CStringA& c); | |
| 94 | |
| 95 // Replaces the https protocol scheme with http if changing protocol is | |
| 96 // allowed. | |
| 97 HRESULT BuildInnerRequestUrl(const CString& url, CString* inner_url); | |
| 98 | |
| 99 // The transient state of the request, so that we can start always with a | |
| 100 // clean slate even though the same instance is being reuse across requests. | |
| 101 struct TransientCupState { | |
| 102 std::vector<uint8> entropy; | |
| 103 std::vector<uint8> r; // Random bytes (r). | |
| 104 std::vector<uint8> sk; // Cached shared key (sk) | |
| 105 std::vector<uint8> new_sk; // Current shared_key (sk'). | |
| 106 std::vector<uint8> hw; // Challenge hash (hw). | |
| 107 std::vector<uint8> hm; // Response hash (hm). | |
| 108 | |
| 109 CStringA cp; // Client proof (cp). | |
| 110 CStringA sp; // Server proof (sp). | |
| 111 CStringA vw; // Versioned challenge (v|w). | |
| 112 std::vector<uint8> response; // The received response. | |
| 113 CStringA c; // Cached client cookie (c) | |
| 114 CStringA new_cookie; // The cookie returned by the server (c'). | |
| 115 | |
| 116 // The url of the request. It includes the original url plus the versioned | |
| 117 // challenge (v|w). | |
| 118 CStringA request_url; | |
| 119 }; | |
| 120 scoped_ptr<TransientCupState> cup_; | |
| 121 | |
| 122 CStringA url_; // The original url. | |
| 123 CString additional_headers_; | |
| 124 bool preserve_protocol_; // Should not change request scheme if | |
| 125 // true. | |
| 126 const void* request_buffer_; // Contains the request body for POST. | |
| 127 size_t request_buffer_length_; // Length of the request body. | |
| 128 std::vector<uint8> entropy_; // Optional entropy pass by the caller, | |
| 129 // mostly for testing purpose. | |
| 130 | |
| 131 // {sk, c} credentials. They are persisted in the registry when the object is | |
| 132 // destroyed. The vast majority of network code runs impersonated. Writing | |
| 133 // through the credentials is likely to fail. However, due to how the | |
| 134 // CUP object is being used by the upper layers of the network code, a | |
| 135 // write back policy is possible. | |
| 136 std::vector<uint8> persisted_sk_; | |
| 137 CStringA persisted_c_; | |
| 138 | |
| 139 scoped_ptr<RSA> rsa_; | |
| 140 RSA::PublicKey public_key_; // Server public key (pk[v]). | |
| 141 scoped_ptr<HttpRequestInterface> http_request_; // Inner http request. | |
| 142 | |
| 143 static const RSA::PublicKeyInstance kCupProductionPublicKey; | |
| 144 static const RSA::PublicKeyInstance kCupTestPublicKey; | |
| 145 | |
| 146 DISALLOW_EVIL_CONSTRUCTORS(CupRequestImpl); | |
| 147 }; | |
| 148 | |
| 149 const RSA::PublicKeyInstance CupRequestImpl::kCupProductionPublicKey = | |
| 150 #include "omaha/net/cup_pubkey.3.h" | |
| 151 ; // NOLINT | |
| 152 | |
| 153 const RSA::PublicKeyInstance CupRequestImpl::kCupTestPublicKey = | |
| 154 #include "omaha/net/cup_pubkey.2.h" | |
| 155 ; // NOLINT | |
| 156 | |
| 157 CupRequestImpl::CupRequestImpl(HttpRequestInterface* http_request) | |
| 158 : preserve_protocol_(false), | |
| 159 request_buffer_(NULL), | |
| 160 request_buffer_length_(0), | |
| 161 public_key_(NULL) { | |
| 162 ASSERT1(http_request); | |
| 163 bool is_using_cup_test_keys = NetworkConfig::IsUsingCupTestKeys(); | |
| 164 public_key_ = is_using_cup_test_keys ? kCupTestPublicKey : | |
| 165 kCupProductionPublicKey; | |
| 166 | |
| 167 // Try to retrieve the credentials if we have any. If we have succeeded, then | |
| 168 // we must have a {sk, c} pair. If we have failed, then we will generate | |
| 169 // a fresh set of credentials later on. | |
| 170 HRESULT hr = LoadCredentials(&persisted_sk_, &persisted_c_); | |
| 171 if (FAILED(hr)) { | |
| 172 ASSERT1(persisted_sk_.empty()); | |
| 173 ASSERT1(persisted_c_.IsEmpty()); | |
| 174 } | |
| 175 | |
| 176 rsa_.reset(new RSA(public_key_)); | |
| 177 http_request_.reset(http_request); | |
| 178 | |
| 179 // Decorate the user agent by appending the "CUP" suffix. This overrides | |
| 180 // the user agent of the inner http request. | |
| 181 CString user_agent(http_request_->user_agent()); | |
| 182 user_agent += _T(";cup"); | |
| 183 http_request_->set_user_agent(user_agent); | |
| 184 } | |
| 185 | |
| 186 CupRequestImpl::~CupRequestImpl() { | |
| 187 Close(); | |
| 188 } | |
| 189 | |
| 190 HRESULT CupRequestImpl::Close() { | |
| 191 cup_.reset(); | |
| 192 | |
| 193 // TODO(omaha): optimize so that if the credentials did not change then | |
| 194 // there would be not need to write back. | |
| 195 if (!persisted_sk_.empty() && !persisted_c_.IsEmpty()) { | |
| 196 VERIFY1(SUCCEEDED(SaveCredentials(persisted_sk_, persisted_c_))); | |
| 197 } | |
| 198 return http_request_->Close(); | |
| 199 } | |
| 200 | |
| 201 HRESULT CupRequestImpl::Send() { | |
| 202 // Start with a fresh CUP state. This is important as the client may | |
| 203 // reuse the same CUP request for subsequent requests. | |
| 204 cup_.reset(new TransientCupState); | |
| 205 | |
| 206 // First, build a request, send it, and then authenticate the response. | |
| 207 HRESULT hr = BuildRequest(); | |
| 208 if (FAILED(hr)) { | |
| 209 return hr; | |
| 210 } | |
| 211 hr = DoSend(); | |
| 212 if (FAILED(hr)) { | |
| 213 return hr; | |
| 214 } | |
| 215 hr = AuthenticateResponse(); | |
| 216 if (FAILED(hr)) { | |
| 217 return hr; | |
| 218 } | |
| 219 return S_OK; | |
| 220 } | |
| 221 | |
| 222 HRESULT CupRequestImpl::BuildRequest() { | |
| 223 HRESULT hr(InitializeEntropy()); | |
| 224 if (FAILED(hr)) { | |
| 225 return hr; | |
| 226 } | |
| 227 // Start with some random bytes. | |
| 228 cup_->r = cup_utils::RsaPad(rsa_->size(), | |
| 229 &cup_->entropy.front(), cup_->entropy.size()); | |
| 230 | |
| 231 // Derive a new shared key (sk') as the hash of the random bytes. | |
| 232 // TODO(omaha): consider protecting the key using ::CryptProtectmemory when | |
| 233 // not being used. | |
| 234 cup_->new_sk = cup_utils::Hash(cup_->r); | |
| 235 | |
| 236 // Compute the challenge (w) by encrypting in place (r) with the server | |
| 237 // public key pk[v]. | |
| 238 size_t encrypted_size = rsa_->raw(&cup_->r.front(), cup_->r.size()); | |
| 239 ASSERT1(encrypted_size == cup_->r.size()); | |
| 240 | |
| 241 // Compute the versioned challenge (v|w) as | |
| 242 // decimal-v:base64-encoded-rsa-wrapper. | |
| 243 SafeCStringAFormat(&cup_->vw, "%d:%s", | |
| 244 rsa_->version(), | |
| 245 cup_utils::B64Encode(&cup_->r.front(), cup_->r.size())); | |
| 246 | |
| 247 // Compute the url of the CUP request. | |
| 248 // Append a query string or append to the existing query string if any. | |
| 249 const char* format_string = url_.Find('?') != -1 ? "%1&w=%2" : "%1?w=%2"; | |
| 250 cup_->request_url.FormatMessage(format_string, url_, cup_->vw); | |
| 251 | |
| 252 // Compute the challenge hash (hw) as HASH(HASH(v|w)|HASH(req)) | |
| 253 hr = BuildChallengeHash(); | |
| 254 if (FAILED(hr)) { | |
| 255 return hr; | |
| 256 } | |
| 257 | |
| 258 // Compute the client proof as SYMsign[sk](0|hw|HASH(c)) if we have a | |
| 259 // {sk, c} pair or as SYMsign[sk'](3|hw) if we do not. | |
| 260 hr = BuildClientProof(); | |
| 261 if (FAILED(hr)) { | |
| 262 return hr; | |
| 263 } | |
| 264 | |
| 265 NET_LOG(L4, (_T("[hw: %s]"), BytesToHex(cup_->hw))); | |
| 266 | |
| 267 // This is what the client sends up along with the request: a versioned | |
| 268 // challenge, a client proof, and a client cookie if it has one. | |
| 269 | |
| 270 NET_LOG(L4, (_T("[request: %s]"), CA2T(cup_->request_url))); | |
| 271 NET_LOG(L4, (_T("[ifmatch: %s]"), CA2T(cup_->cp))); | |
| 272 NET_LOG(L4, (_T("[cookie: %s]"), CA2T(cup_->c))); | |
| 273 | |
| 274 return S_OK; | |
| 275 } | |
| 276 | |
| 277 HRESULT CupRequestImpl::BuildChallengeHash() { | |
| 278 // The hash of the request if carried through all the cryptographic proofs. | |
| 279 // The challenge hash hw is computed as: | |
| 280 // HASH(HASH(v|w)|HASH(request_url)|HASH(body)). | |
| 281 // For simplicity, the protocol scheme and the port are dropped before | |
| 282 // hashing. The hash is computed over the CUP request url, which includes | |
| 283 // the versioned hash. | |
| 284 scoped_ptr<HttpClient> http_client(CreateHttpClient()); | |
| 285 if (!http_client.get()) { | |
| 286 return OMAHA_NET_E_CUP_NO_HTTP_CLIENT; | |
| 287 } | |
| 288 HRESULT hr = http_client->Initialize(); | |
| 289 if (FAILED(hr)) { | |
| 290 return hr; | |
| 291 } | |
| 292 ASSERT1(!cup_->request_url.IsEmpty()); | |
| 293 CString url(cup_->request_url); | |
| 294 CString scheme, server, url_path, query_string; | |
| 295 hr = http_client->CrackUrl(url, | |
| 296 0, | |
| 297 &scheme, | |
| 298 &server, | |
| 299 NULL, | |
| 300 &url_path, | |
| 301 &query_string); | |
| 302 if (FAILED(hr)) { | |
| 303 return hr; | |
| 304 } | |
| 305 | |
| 306 ASSERT1(!scheme.IsEmpty()); | |
| 307 ASSERT1(!server.IsEmpty()); | |
| 308 ASSERT1(!url_path.IsEmpty()); | |
| 309 url.FormatMessage(_T("//%1%2%3"), server, url_path, query_string); | |
| 310 | |
| 311 CStringA req(url); | |
| 312 | |
| 313 // Compute hw as HASH(HASH(v|w)|HASH(request_url)|HASH(body)). | |
| 314 ASSERT1(!cup_->vw.IsEmpty()); | |
| 315 ASSERT1(!url.IsEmpty()); | |
| 316 cup_->hw = cup_utils::HashBuffers(cup_->vw.GetString(), cup_->vw.GetLength(), | |
| 317 req.GetString(), req.GetLength(), | |
| 318 request_buffer_, request_buffer_length_); | |
| 319 return S_OK; | |
| 320 } | |
| 321 | |
| 322 HRESULT CupRequestImpl::BuildClientProof() { | |
| 323 // Use persisted {sk, c} or start with empty credentials otherwise. | |
| 324 ASSERT1(cup_->sk.empty()); | |
| 325 ASSERT1(cup_->c.IsEmpty()); | |
| 326 if (!persisted_sk_.empty() && !persisted_c_.IsEmpty()) { | |
| 327 cup_->sk = persisted_sk_; | |
| 328 cup_->c = persisted_c_; | |
| 329 } | |
| 330 | |
| 331 std::vector<uint8> hcp; // hmac of the client proof. | |
| 332 if (!cup_->sk.empty() && !cup_->c.IsEmpty()) { | |
| 333 // Use the cached shared key (sk) and the cookie (c) if we have them. | |
| 334 ASSERT1(cup_->sk.size() == SHA_DIGEST_SIZE); | |
| 335 NET_LOG(L4, (_T("[using sk: %s]"), BytesToHex(cup_->sk))); | |
| 336 | |
| 337 // Compute 'cp' as SYMsign[sk](0|HASH(w)|HASH(c)) | |
| 338 std::vector<uint8> hc = cup_utils::Hash(cup_->c); | |
| 339 hcp = cup_utils::SymSign(cup_->sk, 0, &cup_->hw, &hc, NULL); | |
| 340 } else { | |
| 341 // There is no saved shared key. Use current shared key (new_sk). | |
| 342 ASSERT1(cup_->new_sk.size() == SHA_DIGEST_SIZE); | |
| 343 NET_LOG(L4, (_T("[using sk': %s]"), BytesToHex(cup_->new_sk))); | |
| 344 | |
| 345 // Compute 'cp' as SYMsign[sk'](3|HASH(w)) | |
| 346 hcp = cup_utils::SymSign(cup_->new_sk, 3, &cup_->hw, NULL, NULL); | |
| 347 } | |
| 348 | |
| 349 NET_LOG(L4, (_T("[client proof hmac: %s]"), BytesToHex(hcp))); | |
| 350 cup_->cp = cup_utils::B64Encode(hcp); | |
| 351 return S_OK; | |
| 352 } | |
| 353 | |
| 354 HRESULT CupRequestImpl::AuthenticateResponse() { | |
| 355 // This is what the server has sent down along with the response: | |
| 356 // a server proof, and optionally a new cookie for the client. | |
| 357 NET_LOG(L4, (_T("[etag: %s]"), CA2T(cup_->sp))); | |
| 358 NET_LOG(L4, (_T("[svr cookie: %s]"), CA2T(cup_->new_cookie))); | |
| 359 | |
| 360 if (cup_->sp.IsEmpty()) { | |
| 361 // We can't authenticate anything without the server proof. | |
| 362 return OMAHA_NET_E_CUP_NO_SERVER_PROOF; | |
| 363 } | |
| 364 | |
| 365 // Compute the hash of the response (hm). | |
| 366 cup_->hm = cup_utils::Hash(cup_->response); | |
| 367 NET_LOG(L4, (_T("[hm: %s]"), BytesToHex(cup_->hm))); | |
| 368 | |
| 369 // Verify the response and the challenge w are authenticated by the | |
| 370 // client shared key. The validation has at least one subtle aspect: the | |
| 371 // client must try two signatures. First, it tries to authenticate using | |
| 372 // the new sk and the new cookie. If the response does not authenticate but | |
| 373 // the client has an old key, it should try authenticating with the old key. | |
| 374 // This second step is important in the case of an attacker or server | |
| 375 // misconfiguration where the server is signing with the old key but still | |
| 376 // sending a cookie. | |
| 377 std::vector<uint8> hmac; | |
| 378 if (!cup_->new_cookie.IsEmpty() && !cup_->new_sk.empty()) { | |
| 379 // The server has sent down a new cookie because the client proof or the | |
| 380 // client cookie were not good or missing. Try to authenticate using the | |
| 381 // new shared key and the new cookie {sk', c'}. | |
| 382 std::vector<uint8> hnew_c = cup_utils::Hash(cup_->new_cookie); // HASH(c') | |
| 383 | |
| 384 // Compute the server proof (sp) as SYMsign[sk'](1|hw|hm|hc'). | |
| 385 hmac = cup_utils::SymSign(cup_->new_sk, 1, &cup_->hw, &cup_->hm, &hnew_c); | |
| 386 CStringA expected_sp = cup_utils::B64Encode(hmac); | |
| 387 | |
| 388 if (expected_sp == cup_->sp) { | |
| 389 // Copy the credentials to write them back when this object is destroyed. | |
| 390 persisted_sk_ = cup_->new_sk; | |
| 391 persisted_c_ = cup_->new_cookie; | |
| 392 return S_OK; | |
| 393 } | |
| 394 } | |
| 395 | |
| 396 if (!cup_->sk.empty()) { | |
| 397 // The server has accepted our client proof (cp) and consequently the sk. | |
| 398 | |
| 399 // Compute the server proof as SYMsign[sk](2|hw|hm). | |
| 400 hmac = cup_utils::SymSign(cup_->sk, 2, &cup_->hw, &cup_->hm, NULL); | |
| 401 CStringA expected_sp = cup_utils::B64Encode(hmac); | |
| 402 | |
| 403 if (expected_sp == cup_->sp) { | |
| 404 return S_OK; | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 NET_LOG(L4, (_T("[expected server proof: %s]"), BytesToHex(hmac))); | |
| 409 | |
| 410 // The server proof does not authenticate. We reject this response. | |
| 411 return OMAHA_NET_E_CUP_NOT_TRUSTED; | |
| 412 } | |
| 413 | |
| 414 HRESULT CupRequestImpl::LoadCredentials(std::vector<uint8>* sk, CStringA* c) { | |
| 415 ASSERT1(sk); | |
| 416 ASSERT1(c); | |
| 417 NetworkConfig* network_config = NULL; | |
| 418 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
| 419 HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); | |
| 420 if (FAILED(hr)) { | |
| 421 return hr; | |
| 422 } | |
| 423 CupCredentials cup_credentials; | |
| 424 hr = network_config->GetCupCredentials(&cup_credentials); | |
| 425 if (SUCCEEDED(hr)) { | |
| 426 sk->swap(cup_credentials.sk); | |
| 427 *c = cup_credentials.c; | |
| 428 } | |
| 429 return hr; | |
| 430 } | |
| 431 | |
| 432 HRESULT CupRequestImpl::SaveCredentials(const std::vector<uint8>& sk, | |
| 433 const CStringA& c) { | |
| 434 NetworkConfig* network_config = NULL; | |
| 435 NetworkConfigManager& network_manager = NetworkConfigManager::Instance(); | |
| 436 HRESULT hr = network_manager.GetUserNetworkConfig(&network_config); | |
| 437 if (FAILED(hr)) { | |
| 438 return hr; | |
| 439 } | |
| 440 | |
| 441 CupCredentials cup_credentials; | |
| 442 cup_credentials.sk = sk; | |
| 443 cup_credentials.c = c; | |
| 444 return network_config->SetCupCredentials(&cup_credentials); | |
| 445 } | |
| 446 | |
| 447 HRESULT CupRequestImpl::DoSend() { | |
| 448 // The url of the inner request includes the versioned challenge. | |
| 449 // It replaces the protocol from https to http since CUP over https is | |
| 450 // not supported. | |
| 451 CString inner_request_url; | |
| 452 HRESULT hr = BuildInnerRequestUrl(CString(cup_->request_url), | |
| 453 &inner_request_url); | |
| 454 if (FAILED(hr)) { | |
| 455 return hr; | |
| 456 } | |
| 457 http_request_->set_url(inner_request_url); | |
| 458 | |
| 459 // Prepare additional headers to send. | |
| 460 CString additional_headers(additional_headers_); | |
| 461 | |
| 462 // The client proof (cp) is sent as the "If-Match" header. | |
| 463 ASSERT1(!cup_->cp.IsEmpty()); | |
| 464 CStringA if_match_header; | |
| 465 SafeCStringAFormat(&if_match_header, "\"%s\"", cup_->cp); | |
| 466 additional_headers += HttpClient::BuildRequestHeader(_T("If-Match"), | |
| 467 CA2T(if_match_header)); | |
| 468 | |
| 469 // Send the client cookie (c) if we have one. | |
| 470 if (!cup_->c.IsEmpty()) { | |
| 471 additional_headers += HttpClient::BuildRequestHeader(_T("Cookie"), | |
| 472 CA2T(cup_->c)); | |
| 473 } | |
| 474 http_request_->set_additional_headers(additional_headers); | |
| 475 | |
| 476 NET_LOG(L5, (_T("[CUP request][%s]"), | |
| 477 BufferToPrintableString(request_buffer_, | |
| 478 request_buffer_length_))); | |
| 479 hr = http_request_->Send(); | |
| 480 if (FAILED(hr)) { | |
| 481 return hr; | |
| 482 } | |
| 483 http_request_->GetResponse().swap(cup_->response); | |
| 484 int status_code(http_request_->GetHttpStatusCode()); | |
| 485 if (status_code != HTTP_STATUS_OK && | |
| 486 status_code != HTTP_STATUS_PARTIAL_CONTENT) { | |
| 487 return HRESULTFromHttpStatusCode(status_code); | |
| 488 } | |
| 489 NET_LOG(L5, (_T("[CUP response][%s]"), | |
| 490 VectorToPrintableString(cup_->response))); | |
| 491 | |
| 492 // Get the server proof (sp). | |
| 493 CString etag_header; | |
| 494 http_request_->QueryHeadersString(WINHTTP_QUERY_ETAG, NULL, &etag_header); | |
| 495 UnenclosePath(&etag_header); // Remove the quotes. | |
| 496 cup_->sp = CT2A(etag_header); | |
| 497 | |
| 498 // Get the client cookie c'. The cookie may or may not be there. If the | |
| 499 // server has accepted both client proof (sp) then no | |
| 500 // new cookie is sent down. This is an indication for the client to | |
| 501 // use its {sk, c} pair. | |
| 502 CString set_cookie_header; | |
| 503 http_request_->QueryHeadersString(WINHTTP_QUERY_SET_COOKIE, | |
| 504 NULL, &set_cookie_header); | |
| 505 | |
| 506 // Parse the cookie header to extract c=xxx cookie. | |
| 507 cup_->new_cookie = CT2A(cup_utils::ParseCupCookie(set_cookie_header)); | |
| 508 return S_OK; | |
| 509 } | |
| 510 | |
| 511 HRESULT CupRequestImpl::Cancel() { | |
| 512 return http_request_->Cancel(); | |
| 513 } | |
| 514 | |
| 515 HRESULT CupRequestImpl::Pause() { | |
| 516 return http_request_->Pause(); | |
| 517 } | |
| 518 | |
| 519 HRESULT CupRequestImpl::Resume() { | |
| 520 return http_request_->Resume(); | |
| 521 } | |
| 522 | |
| 523 std::vector<uint8> CupRequestImpl::GetResponse() const { | |
| 524 return http_request_->GetResponse(); | |
| 525 } | |
| 526 | |
| 527 HRESULT CupRequestImpl::QueryHeadersString(uint32 info_level, | |
| 528 const TCHAR* name, | |
| 529 CString* value) const { | |
| 530 return http_request_->QueryHeadersString(info_level, name, value); | |
| 531 } | |
| 532 | |
| 533 CString CupRequestImpl::GetResponseHeaders() const { | |
| 534 return http_request_->GetResponseHeaders(); | |
| 535 } | |
| 536 | |
| 537 int CupRequestImpl::GetHttpStatusCode() const { | |
| 538 return http_request_->GetHttpStatusCode(); | |
| 539 } | |
| 540 | |
| 541 CString CupRequestImpl::ToString() const { | |
| 542 return CString("CUP:") + http_request_->ToString(); | |
| 543 } | |
| 544 | |
| 545 void CupRequestImpl::set_session_handle(HINTERNET session_handle) { | |
| 546 http_request_->set_session_handle(session_handle); | |
| 547 } | |
| 548 | |
| 549 void CupRequestImpl::set_url(const CString& url) { | |
| 550 url_ = CT2A(url, CP_UTF8); | |
| 551 } | |
| 552 | |
| 553 void CupRequestImpl::set_request_buffer(const void* buffer, | |
| 554 size_t buffer_length) { | |
| 555 request_buffer_ = buffer; | |
| 556 request_buffer_length_ = buffer_length; | |
| 557 http_request_->set_request_buffer(request_buffer_, request_buffer_length_); | |
| 558 } | |
| 559 | |
| 560 void CupRequestImpl::set_proxy_configuration(const ProxyConfig& proxy_config) { | |
| 561 http_request_->set_proxy_configuration(proxy_config); | |
| 562 } | |
| 563 | |
| 564 void CupRequestImpl::set_filename(const CString& filename) { | |
| 565 http_request_->set_filename(filename); | |
| 566 } | |
| 567 | |
| 568 void CupRequestImpl::set_low_priority(bool low_priority) { | |
| 569 http_request_->set_low_priority(low_priority); | |
| 570 } | |
| 571 | |
| 572 void CupRequestImpl::set_callback(NetworkRequestCallback* callback) { | |
| 573 http_request_->set_callback(callback); | |
| 574 } | |
| 575 | |
| 576 void CupRequestImpl::set_additional_headers(const CString& additional_headers) { | |
| 577 additional_headers_ = additional_headers; | |
| 578 } | |
| 579 | |
| 580 void CupRequestImpl::set_preserve_protocol(bool preserve_protocol) { | |
| 581 preserve_protocol_ = preserve_protocol; | |
| 582 } | |
| 583 | |
| 584 CString CupRequestImpl::user_agent() const { | |
| 585 return http_request_->user_agent(); | |
| 586 } | |
| 587 | |
| 588 void CupRequestImpl::set_user_agent(const CString& user_agent) { | |
| 589 http_request_->set_user_agent(user_agent); | |
| 590 } | |
| 591 | |
| 592 void CupRequestImpl::set_proxy_auth_config(const ProxyAuthConfig& config) { | |
| 593 http_request_->set_proxy_auth_config(config); | |
| 594 } | |
| 595 | |
| 596 void CupRequestImpl::SetEntropy(const void* data, size_t data_length) { | |
| 597 ASSERT(false, (_T("Do not call from production code"))); | |
| 598 ASSERT1(data); | |
| 599 const uint8* first(static_cast<const uint8*>(data)); | |
| 600 const uint8* last(first + data_length); | |
| 601 std::vector<uint8> new_entropy(first, last); | |
| 602 entropy_.swap(new_entropy); | |
| 603 } | |
| 604 | |
| 605 HRESULT CupRequestImpl::InitializeEntropy() { | |
| 606 if (entropy_.empty()) { | |
| 607 cup_->entropy.resize(rsa_->size() - SHA_DIGEST_SIZE); | |
| 608 if (!GenRandom(&cup_->entropy.front(), cup_->entropy.size())) { | |
| 609 return OMAHA_NET_E_CUP_NO_ENTROPY; | |
| 610 } | |
| 611 } else { | |
| 612 cup_->entropy = entropy_; | |
| 613 } | |
| 614 return S_OK; | |
| 615 } | |
| 616 | |
| 617 HRESULT CupRequestImpl::BuildInnerRequestUrl(const CString& url, | |
| 618 CString* inner_url) { | |
| 619 ASSERT1(inner_url); | |
| 620 if (!String_StartsWith(url, kHttpsProtoScheme, true)) { | |
| 621 *inner_url = url; | |
| 622 return S_OK; | |
| 623 } | |
| 624 | |
| 625 if (preserve_protocol_) { | |
| 626 // Request must be sent through https. So return error here so we can | |
| 627 // fall back to the next request type in the chain without CUP. | |
| 628 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); | |
| 629 } | |
| 630 | |
| 631 *inner_url = url.Mid(_tcslen(kHttpsProtoScheme)); | |
| 632 inner_url->Insert(0, kHttpProtoScheme); | |
| 633 return S_OK; | |
| 634 } | |
| 635 | |
| 636 } // namespace detail | |
| 637 | |
| 638 | |
| 639 CupRequest::CupRequest(HttpRequestInterface* http_request) { | |
| 640 ASSERT1(http_request); | |
| 641 impl_.reset(new detail::CupRequestImpl(http_request)); | |
| 642 } | |
| 643 | |
| 644 CupRequest::~CupRequest() { | |
| 645 } | |
| 646 | |
| 647 HRESULT CupRequest::Close() { | |
| 648 return impl_->Close(); | |
| 649 } | |
| 650 | |
| 651 HRESULT CupRequest::Send() { | |
| 652 return impl_->Send(); | |
| 653 } | |
| 654 | |
| 655 HRESULT CupRequest::Cancel() { | |
| 656 return impl_->Cancel(); | |
| 657 } | |
| 658 | |
| 659 HRESULT CupRequest::Pause() { | |
| 660 return impl_->Pause(); | |
| 661 } | |
| 662 | |
| 663 HRESULT CupRequest::Resume() { | |
| 664 return impl_->Resume(); | |
| 665 } | |
| 666 | |
| 667 std::vector<uint8> CupRequest::GetResponse() const { | |
| 668 return impl_->GetResponse(); | |
| 669 } | |
| 670 | |
| 671 int CupRequest::GetHttpStatusCode() const { | |
| 672 return impl_->GetHttpStatusCode(); | |
| 673 } | |
| 674 | |
| 675 HRESULT CupRequest::QueryHeadersString(uint32 info_level, | |
| 676 const TCHAR* name, | |
| 677 CString* value) const { | |
| 678 return impl_->QueryHeadersString(info_level, name, value); | |
| 679 } | |
| 680 | |
| 681 CString CupRequest::GetResponseHeaders() const { | |
| 682 return impl_->GetResponseHeaders(); | |
| 683 } | |
| 684 | |
| 685 CString CupRequest::ToString() const { | |
| 686 return impl_->ToString(); | |
| 687 } | |
| 688 | |
| 689 void CupRequest::set_session_handle(HINTERNET session_handle) { | |
| 690 return impl_->set_session_handle(session_handle); | |
| 691 } | |
| 692 | |
| 693 void CupRequest::set_url(const CString& url) { | |
| 694 impl_->set_url(url); | |
| 695 } | |
| 696 | |
| 697 void CupRequest::set_request_buffer(const void* buffer, size_t buffer_length) { | |
| 698 impl_->set_request_buffer(buffer, buffer_length); | |
| 699 } | |
| 700 | |
| 701 void CupRequest::set_proxy_configuration(const ProxyConfig& proxy_config) { | |
| 702 impl_->set_proxy_configuration(proxy_config); | |
| 703 } | |
| 704 | |
| 705 void CupRequest::set_filename(const CString& filename) { | |
| 706 impl_->set_filename(filename); | |
| 707 } | |
| 708 | |
| 709 void CupRequest::set_low_priority(bool low_priority) { | |
| 710 impl_->set_low_priority(low_priority); | |
| 711 } | |
| 712 | |
| 713 void CupRequest::set_callback(NetworkRequestCallback* callback) { | |
| 714 impl_->set_callback(callback); | |
| 715 } | |
| 716 | |
| 717 void CupRequest::set_additional_headers(const CString& additional_headers) { | |
| 718 impl_->set_additional_headers(additional_headers); | |
| 719 } | |
| 720 | |
| 721 void CupRequest::set_preserve_protocol(bool preserve_protocol) { | |
| 722 impl_->set_preserve_protocol(preserve_protocol); | |
| 723 } | |
| 724 | |
| 725 CString CupRequest::user_agent() const { | |
| 726 return impl_->user_agent(); | |
| 727 } | |
| 728 | |
| 729 void CupRequest::set_user_agent(const CString& user_agent) { | |
| 730 impl_->set_user_agent(user_agent); | |
| 731 } | |
| 732 | |
| 733 void CupRequest::set_proxy_auth_config(const ProxyAuthConfig& config) { | |
| 734 impl_->set_proxy_auth_config(config); | |
| 735 } | |
| 736 | |
| 737 void CupRequest::SetEntropy(const void* data, size_t data_length) { | |
| 738 return impl_->SetEntropy(data, data_length); | |
| 739 } | |
| 740 | |
| 741 } // namespace omaha | |
| 742 | |
| OLD | NEW |