Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(380)

Side by Side Diff: webrtc/base/httpcommon.cc

Issue 2731673002: Removing HTTPS and SOCKS proxy server code. (Closed)
Patch Set: Adding back something still referenced by chromium. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « webrtc/base/httpcommon.h ('k') | webrtc/base/httpcommon-inl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include <time.h>
12
13 #if defined(WEBRTC_WIN)
14 #define WIN32_LEAN_AND_MEAN
15 #include <windows.h>
16 #include <winsock2.h>
17 #include <ws2tcpip.h>
18 #define SECURITY_WIN32
19 #include <security.h>
20 #endif
21
22 #include <algorithm>
23
24 #include "webrtc/base/arraysize.h"
25 #include "webrtc/base/base64.h"
26 #include "webrtc/base/checks.h"
27 #include "webrtc/base/cryptstring.h"
28 #include "webrtc/base/httpcommon-inl.h"
29 #include "webrtc/base/httpcommon.h"
30 #include "webrtc/base/messagedigest.h"
31 #include "webrtc/base/socketaddress.h"
32 #include "webrtc/base/stringencode.h"
33 #include "webrtc/base/stringutils.h"
34
35 namespace rtc {
36
37 #if defined(WEBRTC_WIN)
38 extern const ConstantLabel SECURITY_ERRORS[];
39 #endif
40
41 //////////////////////////////////////////////////////////////////////
42 // Enum - TODO: expose globally later?
43 //////////////////////////////////////////////////////////////////////
44
45 bool find_string(size_t& index, const std::string& needle,
46 const char* const haystack[], size_t max_index) {
47 for (index=0; index<max_index; ++index) {
48 if (_stricmp(needle.c_str(), haystack[index]) == 0) {
49 return true;
50 }
51 }
52 return false;
53 }
54
55 template<class E>
56 struct Enum {
57 static const char** Names;
58 static size_t Size;
59
60 static inline const char* Name(E val) { return Names[val]; }
61 static inline bool Parse(E& val, const std::string& name) {
62 size_t index;
63 if (!find_string(index, name, Names, Size))
64 return false;
65 val = static_cast<E>(index);
66 return true;
67 }
68
69 E val;
70
71 inline operator E&() { return val; }
72 inline Enum& operator=(E rhs) { val = rhs; return *this; }
73
74 inline const char* name() const { return Name(val); }
75 inline bool assign(const std::string& name) { return Parse(val, name); }
76 inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; }
77 };
78
79 #define ENUM(e,n) \
80 template<> const char** Enum<e>::Names = n; \
81 template<> size_t Enum<e>::Size = sizeof(n)/sizeof(n[0])
82
83 //////////////////////////////////////////////////////////////////////
84 // HttpCommon
85 //////////////////////////////////////////////////////////////////////
86
87 static const char* kHttpVersions[HVER_LAST+1] = {
88 "1.0", "1.1", "Unknown"
89 };
90 ENUM(HttpVersion, kHttpVersions);
91
92 static const char* kHttpVerbs[HV_LAST+1] = {
93 "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD"
94 };
95 ENUM(HttpVerb, kHttpVerbs);
96
97 static const char* kHttpHeaders[HH_LAST+1] = {
98 "Age",
99 "Cache-Control",
100 "Connection",
101 "Content-Disposition",
102 "Content-Length",
103 "Content-Range",
104 "Content-Type",
105 "Cookie",
106 "Date",
107 "ETag",
108 "Expires",
109 "Host",
110 "If-Modified-Since",
111 "If-None-Match",
112 "Keep-Alive",
113 "Last-Modified",
114 "Location",
115 "Proxy-Authenticate",
116 "Proxy-Authorization",
117 "Proxy-Connection",
118 "Range",
119 "Set-Cookie",
120 "TE",
121 "Trailers",
122 "Transfer-Encoding",
123 "Upgrade",
124 "User-Agent",
125 "WWW-Authenticate",
126 };
127 ENUM(HttpHeader, kHttpHeaders);
128
129 const char* ToString(HttpVersion version) {
130 return Enum<HttpVersion>::Name(version);
131 }
132
133 bool FromString(HttpVersion& version, const std::string& str) {
134 return Enum<HttpVersion>::Parse(version, str);
135 }
136
137 const char* ToString(HttpVerb verb) {
138 return Enum<HttpVerb>::Name(verb);
139 }
140
141 bool FromString(HttpVerb& verb, const std::string& str) {
142 return Enum<HttpVerb>::Parse(verb, str);
143 }
144
145 const char* ToString(HttpHeader header) {
146 return Enum<HttpHeader>::Name(header);
147 }
148
149 bool FromString(HttpHeader& header, const std::string& str) {
150 return Enum<HttpHeader>::Parse(header, str);
151 }
152
153 bool HttpCodeHasBody(uint32_t code) {
154 return !HttpCodeIsInformational(code)
155 && (code != HC_NO_CONTENT) && (code != HC_NOT_MODIFIED);
156 }
157
158 bool HttpCodeIsCacheable(uint32_t code) {
159 switch (code) {
160 case HC_OK:
161 case HC_NON_AUTHORITATIVE:
162 case HC_PARTIAL_CONTENT:
163 case HC_MULTIPLE_CHOICES:
164 case HC_MOVED_PERMANENTLY:
165 case HC_GONE:
166 return true;
167 default:
168 return false;
169 }
170 }
171
172 bool HttpHeaderIsEndToEnd(HttpHeader header) {
173 switch (header) {
174 case HH_CONNECTION:
175 case HH_KEEP_ALIVE:
176 case HH_PROXY_AUTHENTICATE:
177 case HH_PROXY_AUTHORIZATION:
178 case HH_PROXY_CONNECTION: // Note part of RFC... this is non-standard header
179 case HH_TE:
180 case HH_TRAILERS:
181 case HH_TRANSFER_ENCODING:
182 case HH_UPGRADE:
183 return false;
184 default:
185 return true;
186 }
187 }
188
189 bool HttpHeaderIsCollapsible(HttpHeader header) {
190 switch (header) {
191 case HH_SET_COOKIE:
192 case HH_PROXY_AUTHENTICATE:
193 case HH_WWW_AUTHENTICATE:
194 return false;
195 default:
196 return true;
197 }
198 }
199
200 bool HttpShouldKeepAlive(const HttpData& data) {
201 std::string connection;
202 if ((data.hasHeader(HH_PROXY_CONNECTION, &connection)
203 || data.hasHeader(HH_CONNECTION, &connection))) {
204 return (_stricmp(connection.c_str(), "Keep-Alive") == 0);
205 }
206 return (data.version >= HVER_1_1);
207 }
208
209 namespace {
210
211 inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) {
212 if (pos >= len)
213 return true;
214 if (isspace(static_cast<unsigned char>(data[pos])))
215 return true;
216 // The reason for this complexity is that some attributes may contain trailing
217 // equal signs (like base64 tokens in Negotiate auth headers)
218 if ((pos+1 < len) && (data[pos] == '=') &&
219 !isspace(static_cast<unsigned char>(data[pos+1])) &&
220 (data[pos+1] != '=')) {
221 return true;
222 }
223 return false;
224 }
225
226 // TODO: unittest for EscapeAttribute and HttpComposeAttributes.
227
228 std::string EscapeAttribute(const std::string& attribute) {
229 const size_t kMaxLength = attribute.length() * 2 + 1;
230 char* buffer = STACK_ARRAY(char, kMaxLength);
231 size_t len = escape(buffer, kMaxLength, attribute.data(), attribute.length(),
232 "\"", '\\');
233 return std::string(buffer, len);
234 }
235
236 } // anonymous namespace
237
238 void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
239 std::string* composed) {
240 std::stringstream ss;
241 for (size_t i=0; i<attributes.size(); ++i) {
242 if (i > 0) {
243 ss << separator << " ";
244 }
245 ss << attributes[i].first;
246 if (!attributes[i].second.empty()) {
247 ss << "=\"" << EscapeAttribute(attributes[i].second) << "\"";
248 }
249 }
250 *composed = ss.str();
251 }
252
253 void HttpParseAttributes(const char * data, size_t len,
254 HttpAttributeList& attributes) {
255 size_t pos = 0;
256 while (true) {
257 // Skip leading whitespace
258 while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) {
259 ++pos;
260 }
261
262 // End of attributes?
263 if (pos >= len)
264 return;
265
266 // Find end of attribute name
267 size_t start = pos;
268 while (!IsEndOfAttributeName(pos, len, data)) {
269 ++pos;
270 }
271
272 HttpAttribute attribute;
273 attribute.first.assign(data + start, data + pos);
274
275 // Attribute has value?
276 if ((pos < len) && (data[pos] == '=')) {
277 ++pos; // Skip '='
278 // Check if quoted value
279 if ((pos < len) && (data[pos] == '"')) {
280 while (++pos < len) {
281 if (data[pos] == '"') {
282 ++pos;
283 break;
284 }
285 if ((data[pos] == '\\') && (pos + 1 < len))
286 ++pos;
287 attribute.second.append(1, data[pos]);
288 }
289 } else {
290 while ((pos < len) &&
291 !isspace(static_cast<unsigned char>(data[pos])) &&
292 (data[pos] != ',')) {
293 attribute.second.append(1, data[pos++]);
294 }
295 }
296 }
297
298 attributes.push_back(attribute);
299 if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ','
300 }
301 }
302
303 bool HttpHasAttribute(const HttpAttributeList& attributes,
304 const std::string& name,
305 std::string* value) {
306 for (HttpAttributeList::const_iterator it = attributes.begin();
307 it != attributes.end(); ++it) {
308 if (it->first == name) {
309 if (value) {
310 *value = it->second;
311 }
312 return true;
313 }
314 }
315 return false;
316 }
317
318 bool HttpHasNthAttribute(HttpAttributeList& attributes,
319 size_t index,
320 std::string* name,
321 std::string* value) {
322 if (index >= attributes.size())
323 return false;
324
325 if (name)
326 *name = attributes[index].first;
327 if (value)
328 *value = attributes[index].second;
329 return true;
330 }
331
332 bool HttpDateToSeconds(const std::string& date, time_t* seconds) {
333 const char* const kTimeZones[] = {
334 "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT",
335 "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M",
336 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"
337 };
338 const int kTimeZoneOffsets[] = {
339 0, 0, -5, -4, -6, -5, -7, -6, -8, -7,
340 -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12,
341 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
342 };
343
344 RTC_DCHECK(nullptr != seconds);
345 struct tm tval;
346 memset(&tval, 0, sizeof(tval));
347 char month[4], zone[6];
348 memset(month, 0, sizeof(month));
349 memset(zone, 0, sizeof(zone));
350
351 if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c",
352 &tval.tm_mday, month, &tval.tm_year,
353 &tval.tm_hour, &tval.tm_min, &tval.tm_sec, zone)) {
354 return false;
355 }
356 switch (toupper(month[2])) {
357 case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break;
358 case 'B': tval.tm_mon = 1; break;
359 case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break;
360 case 'Y': tval.tm_mon = 4; break;
361 case 'L': tval.tm_mon = 6; break;
362 case 'G': tval.tm_mon = 7; break;
363 case 'P': tval.tm_mon = 8; break;
364 case 'T': tval.tm_mon = 9; break;
365 case 'V': tval.tm_mon = 10; break;
366 case 'C': tval.tm_mon = 11; break;
367 }
368 tval.tm_year -= 1900;
369 time_t gmt, non_gmt = mktime(&tval);
370 if ((zone[0] == '+') || (zone[0] == '-')) {
371 if (!isdigit(zone[1]) || !isdigit(zone[2])
372 || !isdigit(zone[3]) || !isdigit(zone[4])) {
373 return false;
374 }
375 int hours = (zone[1] - '0') * 10 + (zone[2] - '0');
376 int minutes = (zone[3] - '0') * 10 + (zone[4] - '0');
377 int offset = (hours * 60 + minutes) * 60;
378 gmt = non_gmt + ((zone[0] == '+') ? offset : -offset);
379 } else {
380 size_t zindex;
381 if (!find_string(zindex, zone, kTimeZones, arraysize(kTimeZones))) {
382 return false;
383 }
384 gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60;
385 }
386 // TODO: Android should support timezone, see b/2441195
387 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) || de fined(BSD)
388 tm *tm_for_timezone = localtime(&gmt);
389 *seconds = gmt + tm_for_timezone->tm_gmtoff;
390 #else
391 #if defined(_MSC_VER) && _MSC_VER >= 1900
392 long timezone = 0;
393 _get_timezone(&timezone);
394 #endif
395 *seconds = gmt - timezone;
396 #endif
397 return true;
398 }
399
400 std::string HttpAddress(const SocketAddress& address, bool secure) {
401 return (address.port() == HttpDefaultPort(secure))
402 ? address.hostname() : address.ToString();
403 }
404
405 //////////////////////////////////////////////////////////////////////
406 // HttpData
407 //////////////////////////////////////////////////////////////////////
408
409 HttpData::HttpData() : version(HVER_1_1) {
410 }
411
412 HttpData::~HttpData() = default;
413
414 void
415 HttpData::clear(bool release_document) {
416 // Clear headers first, since releasing a document may have far-reaching
417 // effects.
418 headers_.clear();
419 if (release_document) {
420 document.reset();
421 }
422 }
423
424 void
425 HttpData::copy(const HttpData& src) {
426 headers_ = src.headers_;
427 }
428
429 void
430 HttpData::changeHeader(const std::string& name, const std::string& value,
431 HeaderCombine combine) {
432 if (combine == HC_AUTO) {
433 HttpHeader header;
434 // Unrecognized headers are collapsible
435 combine = !FromString(header, name) || HttpHeaderIsCollapsible(header)
436 ? HC_YES : HC_NO;
437 } else if (combine == HC_REPLACE) {
438 headers_.erase(name);
439 combine = HC_NO;
440 }
441 // At this point, combine is one of (YES, NO, NEW)
442 if (combine != HC_NO) {
443 HeaderMap::iterator it = headers_.find(name);
444 if (it != headers_.end()) {
445 if (combine == HC_YES) {
446 it->second.append(",");
447 it->second.append(value);
448 }
449 return;
450 }
451 }
452 headers_.insert(HeaderMap::value_type(name, value));
453 }
454
455 size_t HttpData::clearHeader(const std::string& name) {
456 return headers_.erase(name);
457 }
458
459 HttpData::iterator HttpData::clearHeader(iterator header) {
460 iterator deprecated = header++;
461 headers_.erase(deprecated);
462 return header;
463 }
464
465 bool
466 HttpData::hasHeader(const std::string& name, std::string* value) const {
467 HeaderMap::const_iterator it = headers_.find(name);
468 if (it == headers_.end()) {
469 return false;
470 } else if (value) {
471 *value = it->second;
472 }
473 return true;
474 }
475
476 void HttpData::setContent(const std::string& content_type,
477 StreamInterface* document) {
478 setHeader(HH_CONTENT_TYPE, content_type);
479 setDocumentAndLength(document);
480 }
481
482 void HttpData::setDocumentAndLength(StreamInterface* document) {
483 // TODO: Consider calling Rewind() here?
484 RTC_DCHECK(!hasHeader(HH_CONTENT_LENGTH, nullptr));
485 RTC_DCHECK(!hasHeader(HH_TRANSFER_ENCODING, nullptr));
486 RTC_DCHECK(document != nullptr);
487 this->document.reset(document);
488 size_t content_length = 0;
489 if (this->document->GetAvailable(&content_length)) {
490 char buffer[32];
491 sprintfn(buffer, sizeof(buffer), "%d", content_length);
492 setHeader(HH_CONTENT_LENGTH, buffer);
493 } else {
494 setHeader(HH_TRANSFER_ENCODING, "chunked");
495 }
496 }
497
498 //
499 // HttpRequestData
500 //
501
502 void
503 HttpRequestData::clear(bool release_document) {
504 verb = HV_GET;
505 path.clear();
506 HttpData::clear(release_document);
507 }
508
509 void
510 HttpRequestData::copy(const HttpRequestData& src) {
511 verb = src.verb;
512 path = src.path;
513 HttpData::copy(src);
514 }
515
516 size_t
517 HttpRequestData::formatLeader(char* buffer, size_t size) const {
518 RTC_DCHECK(path.find(' ') == std::string::npos);
519 return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(),
520 path.data(), ToString(version));
521 }
522
523 HttpError
524 HttpRequestData::parseLeader(const char* line, size_t len) {
525 unsigned int vmajor, vminor;
526 int vend, dstart, dend;
527 // sscanf isn't safe with strings that aren't null-terminated, and there is
528 // no guarantee that |line| is. Create a local copy that is null-terminated.
529 std::string line_str(line, len);
530 line = line_str.c_str();
531 if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u",
532 &vend, &dstart, &dend, &vmajor, &vminor) != 2)
533 || (vmajor != 1)) {
534 return HE_PROTOCOL;
535 }
536 if (vminor == 0) {
537 version = HVER_1_0;
538 } else if (vminor == 1) {
539 version = HVER_1_1;
540 } else {
541 return HE_PROTOCOL;
542 }
543 std::string sverb(line, vend);
544 if (!FromString(verb, sverb.c_str())) {
545 return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED?
546 }
547 path.assign(line + dstart, line + dend);
548 return HE_NONE;
549 }
550
551 bool HttpRequestData::getAbsoluteUri(std::string* uri) const {
552 if (HV_CONNECT == verb)
553 return false;
554 Url<char> url(path);
555 if (url.valid()) {
556 uri->assign(path);
557 return true;
558 }
559 std::string host;
560 if (!hasHeader(HH_HOST, &host))
561 return false;
562 url.set_address(host);
563 url.set_full_path(path);
564 uri->assign(url.url());
565 return url.valid();
566 }
567
568 bool HttpRequestData::getRelativeUri(std::string* host,
569 std::string* path) const
570 {
571 if (HV_CONNECT == verb)
572 return false;
573 Url<char> url(this->path);
574 if (url.valid()) {
575 host->assign(url.address());
576 path->assign(url.full_path());
577 return true;
578 }
579 if (!hasHeader(HH_HOST, host))
580 return false;
581 path->assign(this->path);
582 return true;
583 }
584
585 //
586 // HttpResponseData
587 //
588
589 void
590 HttpResponseData::clear(bool release_document) {
591 scode = HC_INTERNAL_SERVER_ERROR;
592 message.clear();
593 HttpData::clear(release_document);
594 }
595
596 void
597 HttpResponseData::copy(const HttpResponseData& src) {
598 scode = src.scode;
599 message = src.message;
600 HttpData::copy(src);
601 }
602
603 void HttpResponseData::set_success(uint32_t scode) {
604 this->scode = scode;
605 message.clear();
606 setHeader(HH_CONTENT_LENGTH, "0", false);
607 }
608
609 void HttpResponseData::set_success(const std::string& content_type,
610 StreamInterface* document,
611 uint32_t scode) {
612 this->scode = scode;
613 message.erase(message.begin(), message.end());
614 setContent(content_type, document);
615 }
616
617 void HttpResponseData::set_redirect(const std::string& location,
618 uint32_t scode) {
619 this->scode = scode;
620 message.clear();
621 setHeader(HH_LOCATION, location);
622 setHeader(HH_CONTENT_LENGTH, "0", false);
623 }
624
625 void HttpResponseData::set_error(uint32_t scode) {
626 this->scode = scode;
627 message.clear();
628 setHeader(HH_CONTENT_LENGTH, "0", false);
629 }
630
631 size_t
632 HttpResponseData::formatLeader(char* buffer, size_t size) const {
633 size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode);
634 if (!message.empty()) {
635 len += sprintfn(buffer + len, size - len, " %.*s",
636 message.size(), message.data());
637 }
638 return len;
639 }
640
641 HttpError
642 HttpResponseData::parseLeader(const char* line, size_t len) {
643 size_t pos = 0;
644 unsigned int vmajor, vminor, temp_scode;
645 int temp_pos;
646 // sscanf isn't safe with strings that aren't null-terminated, and there is
647 // no guarantee that |line| is. Create a local copy that is null-terminated.
648 std::string line_str(line, len);
649 line = line_str.c_str();
650 if (sscanf(line, "HTTP %u%n",
651 &temp_scode, &temp_pos) == 1) {
652 // This server's response has no version. :( NOTE: This happens for every
653 // response to requests made from Chrome plugins, regardless of the server's
654 // behaviour.
655 LOG(LS_VERBOSE) << "HTTP version missing from response";
656 version = HVER_UNKNOWN;
657 } else if ((sscanf(line, "HTTP/%u.%u %u%n",
658 &vmajor, &vminor, &temp_scode, &temp_pos) == 3)
659 && (vmajor == 1)) {
660 // This server's response does have a version.
661 if (vminor == 0) {
662 version = HVER_1_0;
663 } else if (vminor == 1) {
664 version = HVER_1_1;
665 } else {
666 return HE_PROTOCOL;
667 }
668 } else {
669 return HE_PROTOCOL;
670 }
671 scode = temp_scode;
672 pos = static_cast<size_t>(temp_pos);
673 while ((pos < len) && isspace(static_cast<unsigned char>(line[pos]))) ++pos;
674 message.assign(line + pos, len - pos);
675 return HE_NONE;
676 }
677
678 //////////////////////////////////////////////////////////////////////
679 // Http Authentication
680 //////////////////////////////////////////////////////////////////////
681
682 std::string quote(const std::string& str) {
683 std::string result;
684 result.push_back('"');
685 for (size_t i=0; i<str.size(); ++i) {
686 if ((str[i] == '"') || (str[i] == '\\'))
687 result.push_back('\\');
688 result.push_back(str[i]);
689 }
690 result.push_back('"');
691 return result;
692 }
693
694 #if defined(WEBRTC_WIN)
695 struct NegotiateAuthContext : public HttpAuthContext {
696 CredHandle cred;
697 CtxtHandle ctx;
698 size_t steps;
699 bool specified_credentials;
700
701 NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
702 : HttpAuthContext(auth), cred(c1), ctx(c2), steps(0),
703 specified_credentials(false)
704 { }
705
706 virtual ~NegotiateAuthContext() {
707 DeleteSecurityContext(&ctx);
708 FreeCredentialsHandle(&cred);
709 }
710 };
711 #endif // WEBRTC_WIN
712
713 HttpAuthResult HttpAuthenticate(
714 const char * challenge, size_t len,
715 const SocketAddress& server,
716 const std::string& method, const std::string& uri,
717 const std::string& username, const CryptString& password,
718 HttpAuthContext *& context, std::string& response, std::string& auth_method)
719 {
720 HttpAttributeList args;
721 HttpParseAttributes(challenge, len, args);
722 HttpHasNthAttribute(args, 0, &auth_method, nullptr);
723
724 if (context && (context->auth_method != auth_method))
725 return HAR_IGNORE;
726
727 // BASIC
728 if (_stricmp(auth_method.c_str(), "basic") == 0) {
729 if (context)
730 return HAR_CREDENTIALS; // Bad credentials
731 if (username.empty())
732 return HAR_CREDENTIALS; // Missing credentials
733
734 context = new HttpAuthContext(auth_method);
735
736 // TODO: convert sensitive to a secure buffer that gets securely deleted
737 //std::string decoded = username + ":" + password;
738 size_t len = username.size() + password.GetLength() + 2;
739 char * sensitive = new char[len];
740 size_t pos = strcpyn(sensitive, len, username.data(), username.size());
741 pos += strcpyn(sensitive + pos, len - pos, ":");
742 password.CopyTo(sensitive + pos, true);
743
744 response = auth_method;
745 response.append(" ");
746 // TODO: create a sensitive-source version of Base64::encode
747 response.append(Base64::Encode(sensitive));
748 memset(sensitive, 0, len);
749 delete [] sensitive;
750 return HAR_RESPONSE;
751 }
752
753 // DIGEST
754 if (_stricmp(auth_method.c_str(), "digest") == 0) {
755 if (context)
756 return HAR_CREDENTIALS; // Bad credentials
757 if (username.empty())
758 return HAR_CREDENTIALS; // Missing credentials
759
760 context = new HttpAuthContext(auth_method);
761
762 std::string cnonce, ncount;
763 char buffer[256];
764 sprintf(buffer, "%d", static_cast<int>(time(0)));
765 cnonce = MD5(buffer);
766 ncount = "00000001";
767
768 std::string realm, nonce, qop, opaque;
769 HttpHasAttribute(args, "realm", &realm);
770 HttpHasAttribute(args, "nonce", &nonce);
771 bool has_qop = HttpHasAttribute(args, "qop", &qop);
772 bool has_opaque = HttpHasAttribute(args, "opaque", &opaque);
773
774 // TODO: convert sensitive to be secure buffer
775 //std::string A1 = username + ":" + realm + ":" + password;
776 size_t len = username.size() + realm.size() + password.GetLength() + 3;
777 char * sensitive = new char[len]; // A1
778 size_t pos = strcpyn(sensitive, len, username.data(), username.size());
779 pos += strcpyn(sensitive + pos, len - pos, ":");
780 pos += strcpyn(sensitive + pos, len - pos, realm.c_str());
781 pos += strcpyn(sensitive + pos, len - pos, ":");
782 password.CopyTo(sensitive + pos, true);
783
784 std::string A2 = method + ":" + uri;
785 std::string middle;
786 if (has_qop) {
787 qop = "auth";
788 middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop;
789 } else {
790 middle = nonce;
791 }
792 std::string HA1 = MD5(sensitive);
793 memset(sensitive, 0, len);
794 delete [] sensitive;
795 std::string HA2 = MD5(A2);
796 std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2);
797
798 std::stringstream ss;
799 ss << auth_method;
800 ss << " username=" << quote(username);
801 ss << ", realm=" << quote(realm);
802 ss << ", nonce=" << quote(nonce);
803 ss << ", uri=" << quote(uri);
804 if (has_qop) {
805 ss << ", qop=" << qop;
806 ss << ", nc=" << ncount;
807 ss << ", cnonce=" << quote(cnonce);
808 }
809 ss << ", response=\"" << dig_response << "\"";
810 if (has_opaque) {
811 ss << ", opaque=" << quote(opaque);
812 }
813 response = ss.str();
814 return HAR_RESPONSE;
815 }
816
817 #if defined(WEBRTC_WIN)
818 #if 1
819 bool want_negotiate = (_stricmp(auth_method.c_str(), "negotiate") == 0);
820 bool want_ntlm = (_stricmp(auth_method.c_str(), "ntlm") == 0);
821 // SPNEGO & NTLM
822 if (want_negotiate || want_ntlm) {
823 const size_t MAX_MESSAGE = 12000, MAX_SPN = 256;
824 char out_buf[MAX_MESSAGE], spn[MAX_SPN];
825
826 #if 0 // Requires funky windows versions
827 DWORD len = MAX_SPN;
828 if (DsMakeSpn("HTTP", server.HostAsURIString().c_str(), nullptr,
829 server.port(),
830 0, &len, spn) != ERROR_SUCCESS) {
831 LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed";
832 return HAR_IGNORE;
833 }
834 #else
835 sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str());
836 #endif
837
838 SecBuffer out_sec;
839 out_sec.pvBuffer = out_buf;
840 out_sec.cbBuffer = sizeof(out_buf);
841 out_sec.BufferType = SECBUFFER_TOKEN;
842
843 SecBufferDesc out_buf_desc;
844 out_buf_desc.ulVersion = 0;
845 out_buf_desc.cBuffers = 1;
846 out_buf_desc.pBuffers = &out_sec;
847
848 const ULONG NEG_FLAGS_DEFAULT =
849 //ISC_REQ_ALLOCATE_MEMORY
850 ISC_REQ_CONFIDENTIALITY
851 //| ISC_REQ_EXTENDED_ERROR
852 //| ISC_REQ_INTEGRITY
853 | ISC_REQ_REPLAY_DETECT
854 | ISC_REQ_SEQUENCE_DETECT
855 //| ISC_REQ_STREAM
856 //| ISC_REQ_USE_SUPPLIED_CREDS
857 ;
858
859 ::TimeStamp lifetime;
860 SECURITY_STATUS ret = S_OK;
861 ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT;
862
863 bool specify_credentials = !username.empty();
864 size_t steps = 0;
865
866 // uint32_t now = Time();
867
868 NegotiateAuthContext * neg = static_cast<NegotiateAuthContext *>(context);
869 if (neg) {
870 const size_t max_steps = 10;
871 if (++neg->steps >= max_steps) {
872 LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries";
873 return HAR_ERROR;
874 }
875 steps = neg->steps;
876
877 std::string challenge, decoded_challenge;
878 if (HttpHasNthAttribute(args, 1, &challenge, nullptr) &&
879 Base64::Decode(challenge, Base64::DO_STRICT, &decoded_challenge,
880 nullptr)) {
881 SecBuffer in_sec;
882 in_sec.pvBuffer = const_cast<char *>(decoded_challenge.data());
883 in_sec.cbBuffer = static_cast<unsigned long>(decoded_challenge.size()) ;
884 in_sec.BufferType = SECBUFFER_TOKEN;
885
886 SecBufferDesc in_buf_desc;
887 in_buf_desc.ulVersion = 0;
888 in_buf_desc.cBuffers = 1;
889 in_buf_desc.pBuffers = &in_sec;
890
891 ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, S ECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lif etime);
892 //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now);
893 if (FAILED(ret)) {
894 LOG(LS_ERROR) << "InitializeSecurityContext returned: "
895 << ErrorName(ret, SECURITY_ERRORS);
896 return HAR_ERROR;
897 }
898 } else if (neg->specified_credentials) {
899 // Try again with default credentials
900 specify_credentials = false;
901 delete context;
902 context = neg = 0;
903 } else {
904 return HAR_CREDENTIALS;
905 }
906 }
907
908 if (!neg) {
909 unsigned char userbuf[256], passbuf[256], domainbuf[16];
910 SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0;
911 if (specify_credentials) {
912 memset(&auth_id, 0, sizeof(auth_id));
913 size_t len = password.GetLength()+1;
914 char * sensitive = new char[len];
915 password.CopyTo(sensitive, true);
916 std::string::size_type pos = username.find('\\');
917 if (pos == std::string::npos) {
918 auth_id.UserLength = static_cast<unsigned long>(
919 std::min(sizeof(userbuf) - 1, username.size()));
920 memcpy(userbuf, username.c_str(), auth_id.UserLength);
921 userbuf[auth_id.UserLength] = 0;
922 auth_id.DomainLength = 0;
923 domainbuf[auth_id.DomainLength] = 0;
924 auth_id.PasswordLength = static_cast<unsigned long>(
925 std::min(sizeof(passbuf) - 1, password.GetLength()));
926 memcpy(passbuf, sensitive, auth_id.PasswordLength);
927 passbuf[auth_id.PasswordLength] = 0;
928 } else {
929 auth_id.UserLength = static_cast<unsigned long>(
930 std::min(sizeof(userbuf) - 1, username.size() - pos - 1));
931 memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength);
932 userbuf[auth_id.UserLength] = 0;
933 auth_id.DomainLength =
934 static_cast<unsigned long>(std::min(sizeof(domainbuf) - 1, pos));
935 memcpy(domainbuf, username.c_str(), auth_id.DomainLength);
936 domainbuf[auth_id.DomainLength] = 0;
937 auth_id.PasswordLength = static_cast<unsigned long>(
938 std::min(sizeof(passbuf) - 1, password.GetLength()));
939 memcpy(passbuf, sensitive, auth_id.PasswordLength);
940 passbuf[auth_id.PasswordLength] = 0;
941 }
942 memset(sensitive, 0, len);
943 delete [] sensitive;
944 auth_id.User = userbuf;
945 auth_id.Domain = domainbuf;
946 auth_id.Password = passbuf;
947 auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
948 pauth_id = &auth_id;
949 LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials";
950 } else {
951 LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials";
952 }
953
954 CredHandle cred;
955 ret = AcquireCredentialsHandleA(
956 0, const_cast<char*>(want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A),
957 SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime);
958 //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << TimeSince(now);
959 if (ret != SEC_E_OK) {
960 LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
961 << ErrorName(ret, SECURITY_ERRORS);
962 return HAR_IGNORE;
963 }
964
965 //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out;
966
967 CtxtHandle ctx;
968 ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_ DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime);
969 //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now);
970 if (FAILED(ret)) {
971 LOG(LS_ERROR) << "InitializeSecurityContext returned: "
972 << ErrorName(ret, SECURITY_ERRORS);
973 FreeCredentialsHandle(&cred);
974 return HAR_IGNORE;
975 }
976
977 RTC_DCHECK(!context);
978 context = neg = new NegotiateAuthContext(auth_method, cred, ctx);
979 neg->specified_credentials = specify_credentials;
980 neg->steps = steps;
981 }
982
983 if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) {
984 ret = CompleteAuthToken(&neg->ctx, &out_buf_desc);
985 //LOG(INFO) << "$$$ CompleteAuthToken @ " << TimeSince(now);
986 LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
987 << ErrorName(ret, SECURITY_ERRORS);
988 if (FAILED(ret)) {
989 return HAR_ERROR;
990 }
991 }
992
993 //LOG(INFO) << "$$$ NEGOTIATE took " << TimeSince(now) << "ms";
994
995 std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
996 response = auth_method;
997 response.append(" ");
998 response.append(Base64::Encode(decoded));
999 return HAR_RESPONSE;
1000 }
1001 #endif
1002 #endif // WEBRTC_WIN
1003
1004 return HAR_IGNORE;
1005 }
1006
1007 //////////////////////////////////////////////////////////////////////
1008
1009 } // namespace rtc
OLDNEW
« no previous file with comments | « webrtc/base/httpcommon.h ('k') | webrtc/base/httpcommon-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698