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