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 |