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 "rtc_base/arraysize.h"
25 #include "rtc_base/base64.h"
26 #include "rtc_base/checks.h"
27 #include "rtc_base/cryptstring.h"
28 #include "rtc_base/httpcommon-inl.h"
29 #include "rtc_base/httpcommon.h"
30 #include "rtc_base/messagedigest.h"
31 #include "rtc_base/socketaddress.h"
32 #include "rtc_base/stringencode.h"
33 #include "rtc_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 
find_string(size_t & index,const std::string & needle,const char * const haystack[],size_t max_index)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 
Namertc::Enum60   static inline const char* Name(E val) { return Names[val]; }
Parsertc::Enum61   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 
operator E&rtc::Enum71   inline operator E&() { return val; }
operator =rtc::Enum72   inline Enum& operator=(E rhs) { val = rhs; return *this; }
73 
namertc::Enum74   inline const char* name() const { return Name(val); }
assignrtc::Enum75   inline bool assign(const std::string& name) { return Parse(val, name); }
operator =rtc::Enum76   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 
ToString(HttpVersion version)129 const char* ToString(HttpVersion version) {
130   return Enum<HttpVersion>::Name(version);
131 }
132 
FromString(HttpVersion & version,const std::string & str)133 bool FromString(HttpVersion& version, const std::string& str) {
134   return Enum<HttpVersion>::Parse(version, str);
135 }
136 
ToString(HttpVerb verb)137 const char* ToString(HttpVerb verb) {
138   return Enum<HttpVerb>::Name(verb);
139 }
140 
FromString(HttpVerb & verb,const std::string & str)141 bool FromString(HttpVerb& verb, const std::string& str) {
142   return Enum<HttpVerb>::Parse(verb, str);
143 }
144 
ToString(HttpHeader header)145 const char* ToString(HttpHeader header) {
146   return Enum<HttpHeader>::Name(header);
147 }
148 
FromString(HttpHeader & header,const std::string & str)149 bool FromString(HttpHeader& header, const std::string& str) {
150   return Enum<HttpHeader>::Parse(header, str);
151 }
152 
HttpCodeHasBody(uint32_t code)153 bool HttpCodeHasBody(uint32_t code) {
154   return !HttpCodeIsInformational(code)
155          && (code != HC_NO_CONTENT) && (code != HC_NOT_MODIFIED);
156 }
157 
HttpCodeIsCacheable(uint32_t code)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 
HttpHeaderIsEndToEnd(HttpHeader header)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 
HttpHeaderIsCollapsible(HttpHeader header)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 
HttpShouldKeepAlive(const HttpData & data)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 
IsEndOfAttributeName(size_t pos,size_t len,const char * data)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 
EscapeAttribute(const std::string & attribute)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 
HttpComposeAttributes(const HttpAttributeList & attributes,char separator,std::string * composed)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 
HttpParseAttributes(const char * data,size_t len,HttpAttributeList & attributes)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 
HttpHasAttribute(const HttpAttributeList & attributes,const std::string & name,std::string * value)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 
HttpHasNthAttribute(HttpAttributeList & attributes,size_t index,std::string * name,std::string * value)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 
HttpDateToSeconds(const std::string & date,time_t * seconds)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) || defined(WEBRTC_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 
HttpAddress(const SocketAddress & address,bool secure)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 
HttpData()409 HttpData::HttpData() : version(HVER_1_1) {
410 }
411 
412 HttpData::~HttpData() = default;
413 
414 void
clear(bool release_document)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
copy(const HttpData & src)425 HttpData::copy(const HttpData& src) {
426   headers_ = src.headers_;
427 }
428 
429 void
changeHeader(const std::string & name,const std::string & value,HeaderCombine combine)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 
clearHeader(const std::string & name)455 size_t HttpData::clearHeader(const std::string& name) {
456   return headers_.erase(name);
457 }
458 
clearHeader(iterator header)459 HttpData::iterator HttpData::clearHeader(iterator header) {
460   iterator deprecated = header++;
461   headers_.erase(deprecated);
462   return header;
463 }
464 
465 bool
hasHeader(const std::string & name,std::string * value) const466 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 
setContent(const std::string & content_type,StreamInterface * document)476 void HttpData::setContent(const std::string& content_type,
477                           StreamInterface* document) {
478   setHeader(HH_CONTENT_TYPE, content_type);
479   setDocumentAndLength(document);
480 }
481 
setDocumentAndLength(StreamInterface * document)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
clear(bool release_document)503 HttpRequestData::clear(bool release_document) {
504   verb = HV_GET;
505   path.clear();
506   HttpData::clear(release_document);
507 }
508 
509 void
copy(const HttpRequestData & src)510 HttpRequestData::copy(const HttpRequestData& src) {
511   verb = src.verb;
512   path = src.path;
513   HttpData::copy(src);
514 }
515 
516 size_t
formatLeader(char * buffer,size_t size) const517 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
parseLeader(const char * line,size_t len)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 
getAbsoluteUri(std::string * uri) const551 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 
getRelativeUri(std::string * host,std::string * path) const568 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
clear(bool release_document)590 HttpResponseData::clear(bool release_document) {
591   scode = HC_INTERNAL_SERVER_ERROR;
592   message.clear();
593   HttpData::clear(release_document);
594 }
595 
596 void
copy(const HttpResponseData & src)597 HttpResponseData::copy(const HttpResponseData& src) {
598   scode = src.scode;
599   message = src.message;
600   HttpData::copy(src);
601 }
602 
set_success(uint32_t scode)603 void HttpResponseData::set_success(uint32_t scode) {
604   this->scode = scode;
605   message.clear();
606   setHeader(HH_CONTENT_LENGTH, "0", false);
607 }
608 
set_success(const std::string & content_type,StreamInterface * document,uint32_t scode)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 
set_redirect(const std::string & location,uint32_t scode)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 
set_error(uint32_t scode)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
formatLeader(char * buffer,size_t size) const632 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
parseLeader(const char * line,size_t len)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     RTC_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 
quote(const std::string & str)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 
NegotiateAuthContextrtc::NegotiateAuthContext701   NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
702   : HttpAuthContext(auth), cred(c1), ctx(c2), steps(0),
703     specified_credentials(false)
704   { }
705 
~NegotiateAuthContextrtc::NegotiateAuthContext706   ~NegotiateAuthContext() override {
707     DeleteSecurityContext(&ctx);
708     FreeCredentialsHandle(&cred);
709   }
710 };
711 #endif // WEBRTC_WIN
712 
HttpAuthenticate(const char * challenge,size_t len,const SocketAddress & server,const std::string & method,const std::string & uri,const std::string & username,const CryptString & password,HttpAuthContext * & context,std::string & response,std::string & auth_method)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       RTC_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         RTC_LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) "
873                             "too many retries";
874         return HAR_ERROR;
875       }
876       steps = neg->steps;
877 
878       std::string challenge, decoded_challenge;
879       if (HttpHasNthAttribute(args, 1, &challenge, nullptr) &&
880           Base64::Decode(challenge, Base64::DO_STRICT, &decoded_challenge,
881                          nullptr)) {
882         SecBuffer in_sec;
883         in_sec.pvBuffer   = const_cast<char *>(decoded_challenge.data());
884         in_sec.cbBuffer   = static_cast<unsigned long>(decoded_challenge.size());
885         in_sec.BufferType = SECBUFFER_TOKEN;
886 
887         SecBufferDesc in_buf_desc;
888         in_buf_desc.ulVersion = 0;
889         in_buf_desc.cBuffers  = 1;
890         in_buf_desc.pBuffers  = &in_sec;
891 
892         ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime);
893         // RTC_LOG(INFO) << "$$$ InitializeSecurityContext @ " <<
894         // TimeSince(now);
895         if (FAILED(ret)) {
896           RTC_LOG(LS_ERROR) << "InitializeSecurityContext returned: "
897                             << ErrorName(ret, SECURITY_ERRORS);
898           return HAR_ERROR;
899         }
900       } else if (neg->specified_credentials) {
901         // Try again with default credentials
902         specify_credentials = false;
903         delete context;
904         context = neg = 0;
905       } else {
906         return HAR_CREDENTIALS;
907       }
908     }
909 
910     if (!neg) {
911       unsigned char userbuf[256], passbuf[256], domainbuf[16];
912       SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0;
913       if (specify_credentials) {
914         memset(&auth_id, 0, sizeof(auth_id));
915         size_t len = password.GetLength()+1;
916         char * sensitive = new char[len];
917         password.CopyTo(sensitive, true);
918         std::string::size_type pos = username.find('\\');
919         if (pos == std::string::npos) {
920           auth_id.UserLength = static_cast<unsigned long>(
921               std::min(sizeof(userbuf) - 1, username.size()));
922           memcpy(userbuf, username.c_str(), auth_id.UserLength);
923           userbuf[auth_id.UserLength] = 0;
924           auth_id.DomainLength = 0;
925           domainbuf[auth_id.DomainLength] = 0;
926           auth_id.PasswordLength = static_cast<unsigned long>(
927               std::min(sizeof(passbuf) - 1, password.GetLength()));
928           memcpy(passbuf, sensitive, auth_id.PasswordLength);
929           passbuf[auth_id.PasswordLength] = 0;
930         } else {
931           auth_id.UserLength = static_cast<unsigned long>(
932               std::min(sizeof(userbuf) - 1, username.size() - pos - 1));
933           memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength);
934           userbuf[auth_id.UserLength] = 0;
935           auth_id.DomainLength =
936               static_cast<unsigned long>(std::min(sizeof(domainbuf) - 1, pos));
937           memcpy(domainbuf, username.c_str(), auth_id.DomainLength);
938           domainbuf[auth_id.DomainLength] = 0;
939           auth_id.PasswordLength = static_cast<unsigned long>(
940               std::min(sizeof(passbuf) - 1, password.GetLength()));
941           memcpy(passbuf, sensitive, auth_id.PasswordLength);
942           passbuf[auth_id.PasswordLength] = 0;
943         }
944         memset(sensitive, 0, len);
945         delete [] sensitive;
946         auth_id.User = userbuf;
947         auth_id.Domain = domainbuf;
948         auth_id.Password = passbuf;
949         auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
950         pauth_id = &auth_id;
951         RTC_LOG(LS_VERBOSE)
952             << "Negotiate protocol: Using specified credentials";
953       } else {
954         RTC_LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials";
955       }
956 
957       CredHandle cred;
958       ret = AcquireCredentialsHandleA(
959           0, const_cast<char*>(want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A),
960           SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime);
961       // RTC_LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << TimeSince(now);
962       if (ret != SEC_E_OK) {
963         RTC_LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
964                           << ErrorName(ret, SECURITY_ERRORS);
965         return HAR_IGNORE;
966       }
967 
968       //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out;
969 
970       CtxtHandle ctx;
971       ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime);
972       // RTC_LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now);
973       if (FAILED(ret)) {
974         RTC_LOG(LS_ERROR) << "InitializeSecurityContext returned: "
975                           << ErrorName(ret, SECURITY_ERRORS);
976         FreeCredentialsHandle(&cred);
977         return HAR_IGNORE;
978       }
979 
980       RTC_DCHECK(!context);
981       context = neg = new NegotiateAuthContext(auth_method, cred, ctx);
982       neg->specified_credentials = specify_credentials;
983       neg->steps = steps;
984     }
985 
986     if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) {
987       ret = CompleteAuthToken(&neg->ctx, &out_buf_desc);
988       // RTC_LOG(INFO) << "$$$ CompleteAuthToken @ " << TimeSince(now);
989       RTC_LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
990                           << ErrorName(ret, SECURITY_ERRORS);
991       if (FAILED(ret)) {
992         return HAR_ERROR;
993       }
994     }
995 
996     // RTC_LOG(INFO) << "$$$ NEGOTIATE took " << TimeSince(now) << "ms";
997 
998     std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
999     response = auth_method;
1000     response.append(" ");
1001     response.append(Base64::Encode(decoded));
1002     return HAR_RESPONSE;
1003   }
1004 #endif
1005 #endif // WEBRTC_WIN
1006 
1007   return HAR_IGNORE;
1008 }
1009 
1010 //////////////////////////////////////////////////////////////////////
1011 
1012 } // namespace rtc
1013