blob: 5ac1e42dbbe75a687cff00b7c89211236a45081e [file] [log] [blame]
deadbeeff137e972017-03-23 15:45:49 -07001/*
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)
deadbeeff137e972017-03-23 15:45:49 -070014#include <windows.h>
15#include <winsock2.h>
16#include <ws2tcpip.h>
17#define SECURITY_WIN32
18#include <security.h>
19#endif
20
21#include <algorithm>
22
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020023#include "rtc_base/arraysize.h"
24#include "rtc_base/base64.h"
25#include "rtc_base/checks.h"
26#include "rtc_base/cryptstring.h"
27#include "rtc_base/httpcommon-inl.h"
28#include "rtc_base/httpcommon.h"
29#include "rtc_base/messagedigest.h"
30#include "rtc_base/socketaddress.h"
Joachim Bauch5b32f232018-03-07 20:02:26 +010031#include "rtc_base/zero_memory.h"
deadbeeff137e972017-03-23 15:45:49 -070032
33namespace rtc {
Tommie51a0a82018-02-27 15:30:29 +010034namespace {
deadbeeff137e972017-03-23 15:45:49 -070035#if defined(WEBRTC_WIN)
Tommie51a0a82018-02-27 15:30:29 +010036///////////////////////////////////////////////////////////////////////////////
37// ConstantToLabel can be used to easily generate string names from constant
38// values. This can be useful for logging descriptive names of error messages.
39// Usage:
40// const ConstantToLabel LIBRARY_ERRORS[] = {
41// KLABEL(SOME_ERROR),
42// KLABEL(SOME_OTHER_ERROR),
43// ...
44// LASTLABEL
45// }
46//
47// int err = LibraryFunc();
48// LOG(LS_ERROR) << "LibraryFunc returned: "
49// << GetErrorName(err, LIBRARY_ERRORS);
Yves Gerey665174f2018-06-19 15:03:05 +020050struct ConstantToLabel {
51 int value;
52 const char* label;
53};
Tommie51a0a82018-02-27 15:30:29 +010054
55const char* LookupLabel(int value, const ConstantToLabel entries[]) {
56 for (int i = 0; entries[i].label; ++i) {
57 if (value == entries[i].value) {
58 return entries[i].label;
59 }
60 }
61 return 0;
62}
63
64std::string GetErrorName(int err, const ConstantToLabel* err_table) {
65 if (err == 0)
66 return "No error";
67
68 if (err_table != 0) {
69 if (const char* value = LookupLabel(err, err_table))
70 return value;
71 }
72
73 char buffer[16];
74 snprintf(buffer, sizeof(buffer), "0x%08x", err);
75 return buffer;
76}
77
Yves Gerey665174f2018-06-19 15:03:05 +020078#define KLABEL(x) \
79 { x, #x }
80#define LASTLABEL \
81 { 0, 0 }
Tommie51a0a82018-02-27 15:30:29 +010082
83const ConstantToLabel SECURITY_ERRORS[] = {
Yves Gerey665174f2018-06-19 15:03:05 +020084 KLABEL(SEC_I_COMPLETE_AND_CONTINUE),
85 KLABEL(SEC_I_COMPLETE_NEEDED),
86 KLABEL(SEC_I_CONTEXT_EXPIRED),
87 KLABEL(SEC_I_CONTINUE_NEEDED),
88 KLABEL(SEC_I_INCOMPLETE_CREDENTIALS),
89 KLABEL(SEC_I_RENEGOTIATE),
90 KLABEL(SEC_E_CERT_EXPIRED),
91 KLABEL(SEC_E_INCOMPLETE_MESSAGE),
92 KLABEL(SEC_E_INSUFFICIENT_MEMORY),
93 KLABEL(SEC_E_INTERNAL_ERROR),
94 KLABEL(SEC_E_INVALID_HANDLE),
95 KLABEL(SEC_E_INVALID_TOKEN),
96 KLABEL(SEC_E_LOGON_DENIED),
97 KLABEL(SEC_E_NO_AUTHENTICATING_AUTHORITY),
98 KLABEL(SEC_E_NO_CREDENTIALS),
99 KLABEL(SEC_E_NOT_OWNER),
100 KLABEL(SEC_E_OK),
101 KLABEL(SEC_E_SECPKG_NOT_FOUND),
102 KLABEL(SEC_E_TARGET_UNKNOWN),
103 KLABEL(SEC_E_UNKNOWN_CREDENTIALS),
104 KLABEL(SEC_E_UNSUPPORTED_FUNCTION),
105 KLABEL(SEC_E_UNTRUSTED_ROOT),
106 KLABEL(SEC_E_WRONG_PRINCIPAL),
107 LASTLABEL};
Tommie51a0a82018-02-27 15:30:29 +0100108#undef KLABEL
109#undef LASTLABEL
110#endif // defined(WEBRTC_WIN)
111} // namespace
deadbeeff137e972017-03-23 15:45:49 -0700112
113//////////////////////////////////////////////////////////////////////
114// Enum - TODO: expose globally later?
115//////////////////////////////////////////////////////////////////////
116
Yves Gerey665174f2018-06-19 15:03:05 +0200117bool find_string(size_t& index,
118 const std::string& needle,
119 const char* const haystack[],
120 size_t max_index) {
121 for (index = 0; index < max_index; ++index) {
deadbeeff137e972017-03-23 15:45:49 -0700122 if (_stricmp(needle.c_str(), haystack[index]) == 0) {
123 return true;
124 }
125 }
126 return false;
127}
128
Yves Gerey665174f2018-06-19 15:03:05 +0200129template <class E>
deadbeeff137e972017-03-23 15:45:49 -0700130struct Enum {
131 static const char** Names;
132 static size_t Size;
133
134 static inline const char* Name(E val) { return Names[val]; }
135 static inline bool Parse(E& val, const std::string& name) {
136 size_t index;
137 if (!find_string(index, name, Names, Size))
138 return false;
139 val = static_cast<E>(index);
140 return true;
141 }
142
143 E val;
144
145 inline operator E&() { return val; }
Yves Gerey665174f2018-06-19 15:03:05 +0200146 inline Enum& operator=(E rhs) {
147 val = rhs;
148 return *this;
149 }
deadbeeff137e972017-03-23 15:45:49 -0700150
151 inline const char* name() const { return Name(val); }
152 inline bool assign(const std::string& name) { return Parse(val, name); }
Yves Gerey665174f2018-06-19 15:03:05 +0200153 inline Enum& operator=(const std::string& rhs) {
154 assign(rhs);
155 return *this;
156 }
deadbeeff137e972017-03-23 15:45:49 -0700157};
158
Yves Gerey665174f2018-06-19 15:03:05 +0200159#define ENUM(e, n) \
160 template <> \
161 const char** Enum<e>::Names = n; \
162 template <> \
163 size_t Enum<e>::Size = sizeof(n) / sizeof(n[0])
deadbeeff137e972017-03-23 15:45:49 -0700164
165//////////////////////////////////////////////////////////////////////
166// HttpCommon
167//////////////////////////////////////////////////////////////////////
168
Yves Gerey665174f2018-06-19 15:03:05 +0200169static const char* kHttpVersions[HVER_LAST + 1] = {"1.0", "1.1", "Unknown"};
deadbeeff137e972017-03-23 15:45:49 -0700170ENUM(HttpVersion, kHttpVersions);
171
Yves Gerey665174f2018-06-19 15:03:05 +0200172static const char* kHttpVerbs[HV_LAST + 1] = {"GET", "POST", "PUT",
173 "DELETE", "CONNECT", "HEAD"};
deadbeeff137e972017-03-23 15:45:49 -0700174ENUM(HttpVerb, kHttpVerbs);
175
Yves Gerey665174f2018-06-19 15:03:05 +0200176static const char* kHttpHeaders[HH_LAST + 1] = {
177 "Age",
178 "Cache-Control",
179 "Connection",
180 "Content-Disposition",
181 "Content-Length",
182 "Content-Range",
183 "Content-Type",
184 "Cookie",
185 "Date",
186 "ETag",
187 "Expires",
188 "Host",
189 "If-Modified-Since",
190 "If-None-Match",
191 "Keep-Alive",
192 "Last-Modified",
193 "Location",
194 "Proxy-Authenticate",
195 "Proxy-Authorization",
196 "Proxy-Connection",
197 "Range",
198 "Set-Cookie",
199 "TE",
200 "Trailers",
201 "Transfer-Encoding",
202 "Upgrade",
203 "User-Agent",
204 "WWW-Authenticate",
deadbeeff137e972017-03-23 15:45:49 -0700205};
206ENUM(HttpHeader, kHttpHeaders);
207
208const char* ToString(HttpVersion version) {
209 return Enum<HttpVersion>::Name(version);
210}
211
212bool FromString(HttpVersion& version, const std::string& str) {
213 return Enum<HttpVersion>::Parse(version, str);
214}
215
216const char* ToString(HttpVerb verb) {
217 return Enum<HttpVerb>::Name(verb);
218}
219
220bool FromString(HttpVerb& verb, const std::string& str) {
221 return Enum<HttpVerb>::Parse(verb, str);
222}
223
224const char* ToString(HttpHeader header) {
225 return Enum<HttpHeader>::Name(header);
226}
227
228bool FromString(HttpHeader& header, const std::string& str) {
229 return Enum<HttpHeader>::Parse(header, str);
230}
231
232bool HttpCodeHasBody(uint32_t code) {
Yves Gerey665174f2018-06-19 15:03:05 +0200233 return !HttpCodeIsInformational(code) && (code != HC_NO_CONTENT) &&
234 (code != HC_NOT_MODIFIED);
deadbeeff137e972017-03-23 15:45:49 -0700235}
236
237bool HttpCodeIsCacheable(uint32_t code) {
238 switch (code) {
Yves Gerey665174f2018-06-19 15:03:05 +0200239 case HC_OK:
240 case HC_NON_AUTHORITATIVE:
241 case HC_PARTIAL_CONTENT:
242 case HC_MULTIPLE_CHOICES:
243 case HC_MOVED_PERMANENTLY:
244 case HC_GONE:
245 return true;
246 default:
247 return false;
deadbeeff137e972017-03-23 15:45:49 -0700248 }
249}
250
251bool HttpHeaderIsEndToEnd(HttpHeader header) {
252 switch (header) {
Yves Gerey665174f2018-06-19 15:03:05 +0200253 case HH_CONNECTION:
254 case HH_KEEP_ALIVE:
255 case HH_PROXY_AUTHENTICATE:
256 case HH_PROXY_AUTHORIZATION:
257 case HH_PROXY_CONNECTION: // Note part of RFC... this is non-standard
258 // header
259 case HH_TE:
260 case HH_TRAILERS:
261 case HH_TRANSFER_ENCODING:
262 case HH_UPGRADE:
263 return false;
264 default:
265 return true;
deadbeeff137e972017-03-23 15:45:49 -0700266 }
267}
268
269bool HttpHeaderIsCollapsible(HttpHeader header) {
270 switch (header) {
Yves Gerey665174f2018-06-19 15:03:05 +0200271 case HH_SET_COOKIE:
272 case HH_PROXY_AUTHENTICATE:
273 case HH_WWW_AUTHENTICATE:
274 return false;
275 default:
276 return true;
deadbeeff137e972017-03-23 15:45:49 -0700277 }
278}
279
280bool HttpShouldKeepAlive(const HttpData& data) {
281 std::string connection;
Yves Gerey665174f2018-06-19 15:03:05 +0200282 if ((data.hasHeader(HH_PROXY_CONNECTION, &connection) ||
283 data.hasHeader(HH_CONNECTION, &connection))) {
deadbeeff137e972017-03-23 15:45:49 -0700284 return (_stricmp(connection.c_str(), "Keep-Alive") == 0);
285 }
286 return (data.version >= HVER_1_1);
287}
288
289namespace {
290
Yves Gerey665174f2018-06-19 15:03:05 +0200291inline bool IsEndOfAttributeName(size_t pos, size_t len, const char* data) {
deadbeeff137e972017-03-23 15:45:49 -0700292 if (pos >= len)
293 return true;
294 if (isspace(static_cast<unsigned char>(data[pos])))
295 return true;
296 // The reason for this complexity is that some attributes may contain trailing
297 // equal signs (like base64 tokens in Negotiate auth headers)
Yves Gerey665174f2018-06-19 15:03:05 +0200298 if ((pos + 1 < len) && (data[pos] == '=') &&
299 !isspace(static_cast<unsigned char>(data[pos + 1])) &&
300 (data[pos + 1] != '=')) {
deadbeeff137e972017-03-23 15:45:49 -0700301 return true;
302 }
303 return false;
304}
305
deadbeeff137e972017-03-23 15:45:49 -0700306} // anonymous namespace
307
Yves Gerey665174f2018-06-19 15:03:05 +0200308void HttpParseAttributes(const char* data,
309 size_t len,
deadbeeff137e972017-03-23 15:45:49 -0700310 HttpAttributeList& attributes) {
311 size_t pos = 0;
312 while (true) {
313 // Skip leading whitespace
314 while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) {
315 ++pos;
316 }
317
318 // End of attributes?
319 if (pos >= len)
320 return;
321
322 // Find end of attribute name
323 size_t start = pos;
324 while (!IsEndOfAttributeName(pos, len, data)) {
325 ++pos;
326 }
327
328 HttpAttribute attribute;
329 attribute.first.assign(data + start, data + pos);
330
331 // Attribute has value?
332 if ((pos < len) && (data[pos] == '=')) {
Yves Gerey665174f2018-06-19 15:03:05 +0200333 ++pos; // Skip '='
deadbeeff137e972017-03-23 15:45:49 -0700334 // Check if quoted value
335 if ((pos < len) && (data[pos] == '"')) {
336 while (++pos < len) {
337 if (data[pos] == '"') {
338 ++pos;
339 break;
340 }
341 if ((data[pos] == '\\') && (pos + 1 < len))
342 ++pos;
343 attribute.second.append(1, data[pos]);
344 }
345 } else {
Yves Gerey665174f2018-06-19 15:03:05 +0200346 while ((pos < len) && !isspace(static_cast<unsigned char>(data[pos])) &&
347 (data[pos] != ',')) {
deadbeeff137e972017-03-23 15:45:49 -0700348 attribute.second.append(1, data[pos++]);
349 }
350 }
351 }
352
353 attributes.push_back(attribute);
Yves Gerey665174f2018-06-19 15:03:05 +0200354 if ((pos < len) && (data[pos] == ','))
355 ++pos; // Skip ','
deadbeeff137e972017-03-23 15:45:49 -0700356 }
357}
358
359bool HttpHasAttribute(const HttpAttributeList& attributes,
360 const std::string& name,
361 std::string* value) {
362 for (HttpAttributeList::const_iterator it = attributes.begin();
363 it != attributes.end(); ++it) {
364 if (it->first == name) {
365 if (value) {
366 *value = it->second;
367 }
368 return true;
369 }
370 }
371 return false;
372}
373
374bool HttpHasNthAttribute(HttpAttributeList& attributes,
375 size_t index,
376 std::string* name,
377 std::string* value) {
378 if (index >= attributes.size())
379 return false;
380
381 if (name)
382 *name = attributes[index].first;
383 if (value)
384 *value = attributes[index].second;
385 return true;
386}
387
388bool HttpDateToSeconds(const std::string& date, time_t* seconds) {
389 const char* const kTimeZones[] = {
Yves Gerey665174f2018-06-19 15:03:05 +0200390 "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST",
391 "PDT", "A", "B", "C", "D", "E", "F", "G", "H",
392 "I", "K", "L", "M", "N", "O", "P", "Q", "R",
393 "S", "T", "U", "V", "W", "X", "Y"};
deadbeeff137e972017-03-23 15:45:49 -0700394 const int kTimeZoneOffsets[] = {
Yves Gerey665174f2018-06-19 15:03:05 +0200395 0, 0, -5, -4, -6, -5, -7, -6, -8, -7, -1, -2, -3, -4, -5, -6, -7,
396 -8, -9, -10, -11, -12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
deadbeeff137e972017-03-23 15:45:49 -0700397
398 RTC_DCHECK(nullptr != seconds);
399 struct tm tval;
400 memset(&tval, 0, sizeof(tval));
401 char month[4], zone[6];
402 memset(month, 0, sizeof(month));
403 memset(zone, 0, sizeof(zone));
404
Yves Gerey665174f2018-06-19 15:03:05 +0200405 if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c", &tval.tm_mday,
406 month, &tval.tm_year, &tval.tm_hour, &tval.tm_min,
407 &tval.tm_sec, zone)) {
deadbeeff137e972017-03-23 15:45:49 -0700408 return false;
409 }
410 switch (toupper(month[2])) {
Yves Gerey665174f2018-06-19 15:03:05 +0200411 case 'N':
412 tval.tm_mon = (month[1] == 'A') ? 0 : 5;
413 break;
414 case 'B':
415 tval.tm_mon = 1;
416 break;
417 case 'R':
418 tval.tm_mon = (month[0] == 'M') ? 2 : 3;
419 break;
420 case 'Y':
421 tval.tm_mon = 4;
422 break;
423 case 'L':
424 tval.tm_mon = 6;
425 break;
426 case 'G':
427 tval.tm_mon = 7;
428 break;
429 case 'P':
430 tval.tm_mon = 8;
431 break;
432 case 'T':
433 tval.tm_mon = 9;
434 break;
435 case 'V':
436 tval.tm_mon = 10;
437 break;
438 case 'C':
439 tval.tm_mon = 11;
440 break;
deadbeeff137e972017-03-23 15:45:49 -0700441 }
442 tval.tm_year -= 1900;
443 time_t gmt, non_gmt = mktime(&tval);
444 if ((zone[0] == '+') || (zone[0] == '-')) {
Yves Gerey665174f2018-06-19 15:03:05 +0200445 if (!isdigit(zone[1]) || !isdigit(zone[2]) || !isdigit(zone[3]) ||
446 !isdigit(zone[4])) {
deadbeeff137e972017-03-23 15:45:49 -0700447 return false;
448 }
449 int hours = (zone[1] - '0') * 10 + (zone[2] - '0');
450 int minutes = (zone[3] - '0') * 10 + (zone[4] - '0');
451 int offset = (hours * 60 + minutes) * 60;
452 gmt = non_gmt + ((zone[0] == '+') ? offset : -offset);
453 } else {
454 size_t zindex;
455 if (!find_string(zindex, zone, kTimeZones, arraysize(kTimeZones))) {
456 return false;
457 }
458 gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60;
459 }
Yves Gerey665174f2018-06-19 15:03:05 +0200460// TODO: Android should support timezone, see b/2441195
461#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) || \
462 defined(BSD)
463 tm* tm_for_timezone = localtime(&gmt);
deadbeeff137e972017-03-23 15:45:49 -0700464 *seconds = gmt + tm_for_timezone->tm_gmtoff;
465#else
466#if defined(_MSC_VER) && _MSC_VER >= 1900
467 long timezone = 0;
468 _get_timezone(&timezone);
469#endif
470 *seconds = gmt - timezone;
471#endif
472 return true;
473}
474
475std::string HttpAddress(const SocketAddress& address, bool secure) {
Yves Gerey665174f2018-06-19 15:03:05 +0200476 return (address.port() == HttpDefaultPort(secure)) ? address.hostname()
477 : address.ToString();
deadbeeff137e972017-03-23 15:45:49 -0700478}
479
480//////////////////////////////////////////////////////////////////////
481// HttpData
482//////////////////////////////////////////////////////////////////////
483
Yves Gerey665174f2018-06-19 15:03:05 +0200484HttpData::HttpData() : version(HVER_1_1) {}
deadbeeff137e972017-03-23 15:45:49 -0700485
486HttpData::~HttpData() = default;
487
Yves Gerey665174f2018-06-19 15:03:05 +0200488void HttpData::clear(bool release_document) {
deadbeeff137e972017-03-23 15:45:49 -0700489 // Clear headers first, since releasing a document may have far-reaching
490 // effects.
491 headers_.clear();
492 if (release_document) {
493 document.reset();
494 }
495}
496
Yves Gerey665174f2018-06-19 15:03:05 +0200497void HttpData::copy(const HttpData& src) {
deadbeeff137e972017-03-23 15:45:49 -0700498 headers_ = src.headers_;
499}
500
Yves Gerey665174f2018-06-19 15:03:05 +0200501void HttpData::changeHeader(const std::string& name,
502 const std::string& value,
503 HeaderCombine combine) {
deadbeeff137e972017-03-23 15:45:49 -0700504 if (combine == HC_AUTO) {
505 HttpHeader header;
506 // Unrecognized headers are collapsible
507 combine = !FromString(header, name) || HttpHeaderIsCollapsible(header)
Yves Gerey665174f2018-06-19 15:03:05 +0200508 ? HC_YES
509 : HC_NO;
deadbeeff137e972017-03-23 15:45:49 -0700510 } else if (combine == HC_REPLACE) {
511 headers_.erase(name);
512 combine = HC_NO;
513 }
514 // At this point, combine is one of (YES, NO, NEW)
515 if (combine != HC_NO) {
516 HeaderMap::iterator it = headers_.find(name);
517 if (it != headers_.end()) {
518 if (combine == HC_YES) {
519 it->second.append(",");
520 it->second.append(value);
521 }
522 return;
523 }
524 }
525 headers_.insert(HeaderMap::value_type(name, value));
526}
527
528size_t HttpData::clearHeader(const std::string& name) {
529 return headers_.erase(name);
530}
531
532HttpData::iterator HttpData::clearHeader(iterator header) {
533 iterator deprecated = header++;
534 headers_.erase(deprecated);
535 return header;
536}
537
Yves Gerey665174f2018-06-19 15:03:05 +0200538bool HttpData::hasHeader(const std::string& name, std::string* value) const {
deadbeeff137e972017-03-23 15:45:49 -0700539 HeaderMap::const_iterator it = headers_.find(name);
540 if (it == headers_.end()) {
541 return false;
542 } else if (value) {
543 *value = it->second;
544 }
545 return true;
546}
547
548void HttpData::setContent(const std::string& content_type,
549 StreamInterface* document) {
550 setHeader(HH_CONTENT_TYPE, content_type);
551 setDocumentAndLength(document);
552}
553
554void HttpData::setDocumentAndLength(StreamInterface* document) {
555 // TODO: Consider calling Rewind() here?
556 RTC_DCHECK(!hasHeader(HH_CONTENT_LENGTH, nullptr));
557 RTC_DCHECK(!hasHeader(HH_TRANSFER_ENCODING, nullptr));
558 RTC_DCHECK(document != nullptr);
559 this->document.reset(document);
560 size_t content_length = 0;
561 if (this->document->GetAvailable(&content_length)) {
562 char buffer[32];
563 sprintfn(buffer, sizeof(buffer), "%d", content_length);
564 setHeader(HH_CONTENT_LENGTH, buffer);
565 } else {
566 setHeader(HH_TRANSFER_ENCODING, "chunked");
567 }
568}
569
570//
571// HttpRequestData
572//
573
Yves Gerey665174f2018-06-19 15:03:05 +0200574void HttpRequestData::clear(bool release_document) {
deadbeeff137e972017-03-23 15:45:49 -0700575 verb = HV_GET;
576 path.clear();
577 HttpData::clear(release_document);
578}
579
Yves Gerey665174f2018-06-19 15:03:05 +0200580void HttpRequestData::copy(const HttpRequestData& src) {
deadbeeff137e972017-03-23 15:45:49 -0700581 verb = src.verb;
582 path = src.path;
583 HttpData::copy(src);
584}
585
Yves Gerey665174f2018-06-19 15:03:05 +0200586size_t HttpRequestData::formatLeader(char* buffer, size_t size) const {
deadbeeff137e972017-03-23 15:45:49 -0700587 RTC_DCHECK(path.find(' ') == std::string::npos);
588 return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(),
589 path.data(), ToString(version));
590}
591
Yves Gerey665174f2018-06-19 15:03:05 +0200592HttpError HttpRequestData::parseLeader(const char* line, size_t len) {
deadbeeff137e972017-03-23 15:45:49 -0700593 unsigned int vmajor, vminor;
594 int vend, dstart, dend;
595 // sscanf isn't safe with strings that aren't null-terminated, and there is
596 // no guarantee that |line| is. Create a local copy that is null-terminated.
597 std::string line_str(line, len);
598 line = line_str.c_str();
Yves Gerey665174f2018-06-19 15:03:05 +0200599 if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u", &vend, &dstart, &dend, &vmajor,
600 &vminor) != 2) ||
601 (vmajor != 1)) {
deadbeeff137e972017-03-23 15:45:49 -0700602 return HE_PROTOCOL;
603 }
604 if (vminor == 0) {
605 version = HVER_1_0;
606 } else if (vminor == 1) {
607 version = HVER_1_1;
608 } else {
609 return HE_PROTOCOL;
610 }
611 std::string sverb(line, vend);
612 if (!FromString(verb, sverb.c_str())) {
Yves Gerey665174f2018-06-19 15:03:05 +0200613 return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED?
deadbeeff137e972017-03-23 15:45:49 -0700614 }
615 path.assign(line + dstart, line + dend);
616 return HE_NONE;
617}
618
619bool HttpRequestData::getAbsoluteUri(std::string* uri) const {
620 if (HV_CONNECT == verb)
621 return false;
622 Url<char> url(path);
623 if (url.valid()) {
624 uri->assign(path);
625 return true;
626 }
627 std::string host;
628 if (!hasHeader(HH_HOST, &host))
629 return false;
630 url.set_address(host);
631 url.set_full_path(path);
632 uri->assign(url.url());
633 return url.valid();
634}
635
636bool HttpRequestData::getRelativeUri(std::string* host,
Yves Gerey665174f2018-06-19 15:03:05 +0200637 std::string* path) const {
deadbeeff137e972017-03-23 15:45:49 -0700638 if (HV_CONNECT == verb)
639 return false;
640 Url<char> url(this->path);
641 if (url.valid()) {
642 host->assign(url.address());
643 path->assign(url.full_path());
644 return true;
645 }
646 if (!hasHeader(HH_HOST, host))
647 return false;
648 path->assign(this->path);
649 return true;
650}
651
652//
653// HttpResponseData
654//
655
Yves Gerey665174f2018-06-19 15:03:05 +0200656void HttpResponseData::clear(bool release_document) {
deadbeeff137e972017-03-23 15:45:49 -0700657 scode = HC_INTERNAL_SERVER_ERROR;
658 message.clear();
659 HttpData::clear(release_document);
660}
661
Yves Gerey665174f2018-06-19 15:03:05 +0200662void HttpResponseData::copy(const HttpResponseData& src) {
deadbeeff137e972017-03-23 15:45:49 -0700663 scode = src.scode;
664 message = src.message;
665 HttpData::copy(src);
666}
667
668void HttpResponseData::set_success(uint32_t scode) {
669 this->scode = scode;
670 message.clear();
671 setHeader(HH_CONTENT_LENGTH, "0", false);
672}
673
674void HttpResponseData::set_success(const std::string& content_type,
675 StreamInterface* document,
676 uint32_t scode) {
677 this->scode = scode;
678 message.erase(message.begin(), message.end());
679 setContent(content_type, document);
680}
681
682void HttpResponseData::set_redirect(const std::string& location,
683 uint32_t scode) {
684 this->scode = scode;
685 message.clear();
686 setHeader(HH_LOCATION, location);
687 setHeader(HH_CONTENT_LENGTH, "0", false);
688}
689
690void HttpResponseData::set_error(uint32_t scode) {
691 this->scode = scode;
692 message.clear();
693 setHeader(HH_CONTENT_LENGTH, "0", false);
694}
695
Yves Gerey665174f2018-06-19 15:03:05 +0200696size_t HttpResponseData::formatLeader(char* buffer, size_t size) const {
deadbeeff137e972017-03-23 15:45:49 -0700697 size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode);
698 if (!message.empty()) {
Yves Gerey665174f2018-06-19 15:03:05 +0200699 len += sprintfn(buffer + len, size - len, " %.*s", message.size(),
700 message.data());
deadbeeff137e972017-03-23 15:45:49 -0700701 }
702 return len;
703}
704
Yves Gerey665174f2018-06-19 15:03:05 +0200705HttpError HttpResponseData::parseLeader(const char* line, size_t len) {
deadbeeff137e972017-03-23 15:45:49 -0700706 size_t pos = 0;
707 unsigned int vmajor, vminor, temp_scode;
708 int temp_pos;
709 // sscanf isn't safe with strings that aren't null-terminated, and there is
710 // no guarantee that |line| is. Create a local copy that is null-terminated.
711 std::string line_str(line, len);
712 line = line_str.c_str();
Yves Gerey665174f2018-06-19 15:03:05 +0200713 if (sscanf(line, "HTTP %u%n", &temp_scode, &temp_pos) == 1) {
deadbeeff137e972017-03-23 15:45:49 -0700714 // This server's response has no version. :( NOTE: This happens for every
715 // response to requests made from Chrome plugins, regardless of the server's
716 // behaviour.
Mirko Bonadei675513b2017-11-09 11:09:25 +0100717 RTC_LOG(LS_VERBOSE) << "HTTP version missing from response";
deadbeeff137e972017-03-23 15:45:49 -0700718 version = HVER_UNKNOWN;
Yves Gerey665174f2018-06-19 15:03:05 +0200719 } else if ((sscanf(line, "HTTP/%u.%u %u%n", &vmajor, &vminor, &temp_scode,
720 &temp_pos) == 3) &&
721 (vmajor == 1)) {
deadbeeff137e972017-03-23 15:45:49 -0700722 // This server's response does have a version.
723 if (vminor == 0) {
724 version = HVER_1_0;
725 } else if (vminor == 1) {
726 version = HVER_1_1;
727 } else {
728 return HE_PROTOCOL;
729 }
730 } else {
731 return HE_PROTOCOL;
732 }
733 scode = temp_scode;
734 pos = static_cast<size_t>(temp_pos);
Yves Gerey665174f2018-06-19 15:03:05 +0200735 while ((pos < len) && isspace(static_cast<unsigned char>(line[pos])))
736 ++pos;
deadbeeff137e972017-03-23 15:45:49 -0700737 message.assign(line + pos, len - pos);
738 return HE_NONE;
739}
740
741//////////////////////////////////////////////////////////////////////
742// Http Authentication
743//////////////////////////////////////////////////////////////////////
744
745std::string quote(const std::string& str) {
746 std::string result;
747 result.push_back('"');
Yves Gerey665174f2018-06-19 15:03:05 +0200748 for (size_t i = 0; i < str.size(); ++i) {
deadbeeff137e972017-03-23 15:45:49 -0700749 if ((str[i] == '"') || (str[i] == '\\'))
750 result.push_back('\\');
751 result.push_back(str[i]);
752 }
753 result.push_back('"');
754 return result;
755}
756
757#if defined(WEBRTC_WIN)
758struct NegotiateAuthContext : public HttpAuthContext {
759 CredHandle cred;
760 CtxtHandle ctx;
761 size_t steps;
762 bool specified_credentials;
763
764 NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
Yves Gerey665174f2018-06-19 15:03:05 +0200765 : HttpAuthContext(auth),
766 cred(c1),
767 ctx(c2),
768 steps(0),
769 specified_credentials(false) {}
deadbeeff137e972017-03-23 15:45:49 -0700770
Steve Anton9de3aac2017-10-24 10:08:26 -0700771 ~NegotiateAuthContext() override {
deadbeeff137e972017-03-23 15:45:49 -0700772 DeleteSecurityContext(&ctx);
773 FreeCredentialsHandle(&cred);
774 }
775};
Yves Gerey665174f2018-06-19 15:03:05 +0200776#endif // WEBRTC_WIN
deadbeeff137e972017-03-23 15:45:49 -0700777
Yves Gerey665174f2018-06-19 15:03:05 +0200778HttpAuthResult HttpAuthenticate(const char* challenge,
779 size_t len,
780 const SocketAddress& server,
781 const std::string& method,
782 const std::string& uri,
783 const std::string& username,
784 const CryptString& password,
785 HttpAuthContext*& context,
786 std::string& response,
787 std::string& auth_method) {
deadbeeff137e972017-03-23 15:45:49 -0700788 HttpAttributeList args;
789 HttpParseAttributes(challenge, len, args);
790 HttpHasNthAttribute(args, 0, &auth_method, nullptr);
791
792 if (context && (context->auth_method != auth_method))
793 return HAR_IGNORE;
794
795 // BASIC
796 if (_stricmp(auth_method.c_str(), "basic") == 0) {
797 if (context)
Yves Gerey665174f2018-06-19 15:03:05 +0200798 return HAR_CREDENTIALS; // Bad credentials
deadbeeff137e972017-03-23 15:45:49 -0700799 if (username.empty())
Yves Gerey665174f2018-06-19 15:03:05 +0200800 return HAR_CREDENTIALS; // Missing credentials
deadbeeff137e972017-03-23 15:45:49 -0700801
802 context = new HttpAuthContext(auth_method);
803
Joachim Bauch5b32f232018-03-07 20:02:26 +0100804 // TODO(bugs.webrtc.org/8905): Convert sensitive to a CryptString and also
805 // return response as CryptString so contents get securely deleted
806 // automatically.
807 // std::string decoded = username + ":" + password;
deadbeeff137e972017-03-23 15:45:49 -0700808 size_t len = username.size() + password.GetLength() + 2;
Yves Gerey665174f2018-06-19 15:03:05 +0200809 char* sensitive = new char[len];
deadbeeff137e972017-03-23 15:45:49 -0700810 size_t pos = strcpyn(sensitive, len, username.data(), username.size());
811 pos += strcpyn(sensitive + pos, len - pos, ":");
812 password.CopyTo(sensitive + pos, true);
813
814 response = auth_method;
815 response.append(" ");
816 // TODO: create a sensitive-source version of Base64::encode
817 response.append(Base64::Encode(sensitive));
Joachim Bauch5b32f232018-03-07 20:02:26 +0100818 ExplicitZeroMemory(sensitive, len);
Yves Gerey665174f2018-06-19 15:03:05 +0200819 delete[] sensitive;
deadbeeff137e972017-03-23 15:45:49 -0700820 return HAR_RESPONSE;
821 }
822
823 // DIGEST
824 if (_stricmp(auth_method.c_str(), "digest") == 0) {
825 if (context)
Yves Gerey665174f2018-06-19 15:03:05 +0200826 return HAR_CREDENTIALS; // Bad credentials
deadbeeff137e972017-03-23 15:45:49 -0700827 if (username.empty())
Yves Gerey665174f2018-06-19 15:03:05 +0200828 return HAR_CREDENTIALS; // Missing credentials
deadbeeff137e972017-03-23 15:45:49 -0700829
830 context = new HttpAuthContext(auth_method);
831
832 std::string cnonce, ncount;
833 char buffer[256];
834 sprintf(buffer, "%d", static_cast<int>(time(0)));
835 cnonce = MD5(buffer);
836 ncount = "00000001";
837
838 std::string realm, nonce, qop, opaque;
839 HttpHasAttribute(args, "realm", &realm);
840 HttpHasAttribute(args, "nonce", &nonce);
841 bool has_qop = HttpHasAttribute(args, "qop", &qop);
842 bool has_opaque = HttpHasAttribute(args, "opaque", &opaque);
843
Joachim Bauch5b32f232018-03-07 20:02:26 +0100844 // TODO(bugs.webrtc.org/8905): Convert sensitive to a CryptString and also
845 // return response as CryptString so contents get securely deleted
846 // automatically.
847 // std::string A1 = username + ":" + realm + ":" + password;
deadbeeff137e972017-03-23 15:45:49 -0700848 size_t len = username.size() + realm.size() + password.GetLength() + 3;
Yves Gerey665174f2018-06-19 15:03:05 +0200849 char* sensitive = new char[len]; // A1
deadbeeff137e972017-03-23 15:45:49 -0700850 size_t pos = strcpyn(sensitive, len, username.data(), username.size());
851 pos += strcpyn(sensitive + pos, len - pos, ":");
852 pos += strcpyn(sensitive + pos, len - pos, realm.c_str());
853 pos += strcpyn(sensitive + pos, len - pos, ":");
854 password.CopyTo(sensitive + pos, true);
855
856 std::string A2 = method + ":" + uri;
857 std::string middle;
858 if (has_qop) {
859 qop = "auth";
860 middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop;
861 } else {
862 middle = nonce;
863 }
864 std::string HA1 = MD5(sensitive);
Joachim Bauch5b32f232018-03-07 20:02:26 +0100865 ExplicitZeroMemory(sensitive, len);
Yves Gerey665174f2018-06-19 15:03:05 +0200866 delete[] sensitive;
deadbeeff137e972017-03-23 15:45:49 -0700867 std::string HA2 = MD5(A2);
868 std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2);
869
870 std::stringstream ss;
871 ss << auth_method;
872 ss << " username=" << quote(username);
873 ss << ", realm=" << quote(realm);
874 ss << ", nonce=" << quote(nonce);
875 ss << ", uri=" << quote(uri);
876 if (has_qop) {
877 ss << ", qop=" << qop;
Yves Gerey665174f2018-06-19 15:03:05 +0200878 ss << ", nc=" << ncount;
deadbeeff137e972017-03-23 15:45:49 -0700879 ss << ", cnonce=" << quote(cnonce);
880 }
881 ss << ", response=\"" << dig_response << "\"";
882 if (has_opaque) {
883 ss << ", opaque=" << quote(opaque);
884 }
885 response = ss.str();
886 return HAR_RESPONSE;
887 }
888
889#if defined(WEBRTC_WIN)
890#if 1
891 bool want_negotiate = (_stricmp(auth_method.c_str(), "negotiate") == 0);
892 bool want_ntlm = (_stricmp(auth_method.c_str(), "ntlm") == 0);
893 // SPNEGO & NTLM
894 if (want_negotiate || want_ntlm) {
895 const size_t MAX_MESSAGE = 12000, MAX_SPN = 256;
896 char out_buf[MAX_MESSAGE], spn[MAX_SPN];
897
Yves Gerey665174f2018-06-19 15:03:05 +0200898#if 0 // Requires funky windows versions
deadbeeff137e972017-03-23 15:45:49 -0700899 DWORD len = MAX_SPN;
900 if (DsMakeSpn("HTTP", server.HostAsURIString().c_str(), nullptr,
901 server.port(),
902 0, &len, spn) != ERROR_SUCCESS) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100903 RTC_LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed";
deadbeeff137e972017-03-23 15:45:49 -0700904 return HAR_IGNORE;
905 }
906#else
907 sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str());
908#endif
909
910 SecBuffer out_sec;
Yves Gerey665174f2018-06-19 15:03:05 +0200911 out_sec.pvBuffer = out_buf;
912 out_sec.cbBuffer = sizeof(out_buf);
deadbeeff137e972017-03-23 15:45:49 -0700913 out_sec.BufferType = SECBUFFER_TOKEN;
914
915 SecBufferDesc out_buf_desc;
916 out_buf_desc.ulVersion = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200917 out_buf_desc.cBuffers = 1;
918 out_buf_desc.pBuffers = &out_sec;
deadbeeff137e972017-03-23 15:45:49 -0700919
920 const ULONG NEG_FLAGS_DEFAULT =
Yves Gerey665174f2018-06-19 15:03:05 +0200921 // ISC_REQ_ALLOCATE_MEMORY
922 ISC_REQ_CONFIDENTIALITY
923 //| ISC_REQ_EXTENDED_ERROR
924 //| ISC_REQ_INTEGRITY
925 | ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT
926 //| ISC_REQ_STREAM
927 //| ISC_REQ_USE_SUPPLIED_CREDS
928 ;
deadbeeff137e972017-03-23 15:45:49 -0700929
930 ::TimeStamp lifetime;
931 SECURITY_STATUS ret = S_OK;
932 ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT;
933
934 bool specify_credentials = !username.empty();
935 size_t steps = 0;
936
937 // uint32_t now = Time();
938
Yves Gerey665174f2018-06-19 15:03:05 +0200939 NegotiateAuthContext* neg = static_cast<NegotiateAuthContext*>(context);
deadbeeff137e972017-03-23 15:45:49 -0700940 if (neg) {
941 const size_t max_steps = 10;
942 if (++neg->steps >= max_steps) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100943 RTC_LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) "
944 "too many retries";
deadbeeff137e972017-03-23 15:45:49 -0700945 return HAR_ERROR;
946 }
947 steps = neg->steps;
948
949 std::string challenge, decoded_challenge;
950 if (HttpHasNthAttribute(args, 1, &challenge, nullptr) &&
951 Base64::Decode(challenge, Base64::DO_STRICT, &decoded_challenge,
952 nullptr)) {
953 SecBuffer in_sec;
Yves Gerey665174f2018-06-19 15:03:05 +0200954 in_sec.pvBuffer = const_cast<char*>(decoded_challenge.data());
955 in_sec.cbBuffer = static_cast<unsigned long>(decoded_challenge.size());
deadbeeff137e972017-03-23 15:45:49 -0700956 in_sec.BufferType = SECBUFFER_TOKEN;
957
958 SecBufferDesc in_buf_desc;
959 in_buf_desc.ulVersion = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200960 in_buf_desc.cBuffers = 1;
961 in_buf_desc.pBuffers = &in_sec;
deadbeeff137e972017-03-23 15:45:49 -0700962
Yves Gerey665174f2018-06-19 15:03:05 +0200963 ret = InitializeSecurityContextA(
964 &neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP,
965 &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime);
deadbeeff137e972017-03-23 15:45:49 -0700966 if (FAILED(ret)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100967 RTC_LOG(LS_ERROR) << "InitializeSecurityContext returned: "
Tommie51a0a82018-02-27 15:30:29 +0100968 << GetErrorName(ret, SECURITY_ERRORS);
deadbeeff137e972017-03-23 15:45:49 -0700969 return HAR_ERROR;
970 }
971 } else if (neg->specified_credentials) {
972 // Try again with default credentials
973 specify_credentials = false;
974 delete context;
975 context = neg = 0;
976 } else {
977 return HAR_CREDENTIALS;
978 }
979 }
980
981 if (!neg) {
982 unsigned char userbuf[256], passbuf[256], domainbuf[16];
Yves Gerey665174f2018-06-19 15:03:05 +0200983 SEC_WINNT_AUTH_IDENTITY_A auth_id, *pauth_id = 0;
deadbeeff137e972017-03-23 15:45:49 -0700984 if (specify_credentials) {
985 memset(&auth_id, 0, sizeof(auth_id));
Yves Gerey665174f2018-06-19 15:03:05 +0200986 size_t len = password.GetLength() + 1;
987 char* sensitive = new char[len];
deadbeeff137e972017-03-23 15:45:49 -0700988 password.CopyTo(sensitive, true);
989 std::string::size_type pos = username.find('\\');
990 if (pos == std::string::npos) {
991 auth_id.UserLength = static_cast<unsigned long>(
992 std::min(sizeof(userbuf) - 1, username.size()));
993 memcpy(userbuf, username.c_str(), auth_id.UserLength);
994 userbuf[auth_id.UserLength] = 0;
995 auth_id.DomainLength = 0;
996 domainbuf[auth_id.DomainLength] = 0;
997 auth_id.PasswordLength = static_cast<unsigned long>(
998 std::min(sizeof(passbuf) - 1, password.GetLength()));
999 memcpy(passbuf, sensitive, auth_id.PasswordLength);
1000 passbuf[auth_id.PasswordLength] = 0;
1001 } else {
1002 auth_id.UserLength = static_cast<unsigned long>(
1003 std::min(sizeof(userbuf) - 1, username.size() - pos - 1));
1004 memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength);
1005 userbuf[auth_id.UserLength] = 0;
1006 auth_id.DomainLength =
1007 static_cast<unsigned long>(std::min(sizeof(domainbuf) - 1, pos));
1008 memcpy(domainbuf, username.c_str(), auth_id.DomainLength);
1009 domainbuf[auth_id.DomainLength] = 0;
1010 auth_id.PasswordLength = static_cast<unsigned long>(
1011 std::min(sizeof(passbuf) - 1, password.GetLength()));
1012 memcpy(passbuf, sensitive, auth_id.PasswordLength);
1013 passbuf[auth_id.PasswordLength] = 0;
1014 }
Joachim Bauch5b32f232018-03-07 20:02:26 +01001015 ExplicitZeroMemory(sensitive, len);
Yves Gerey665174f2018-06-19 15:03:05 +02001016 delete[] sensitive;
deadbeeff137e972017-03-23 15:45:49 -07001017 auth_id.User = userbuf;
1018 auth_id.Domain = domainbuf;
1019 auth_id.Password = passbuf;
1020 auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
1021 pauth_id = &auth_id;
Mirko Bonadei675513b2017-11-09 11:09:25 +01001022 RTC_LOG(LS_VERBOSE)
1023 << "Negotiate protocol: Using specified credentials";
deadbeeff137e972017-03-23 15:45:49 -07001024 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001025 RTC_LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials";
deadbeeff137e972017-03-23 15:45:49 -07001026 }
1027
1028 CredHandle cred;
1029 ret = AcquireCredentialsHandleA(
1030 0, const_cast<char*>(want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A),
1031 SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime);
deadbeeff137e972017-03-23 15:45:49 -07001032 if (ret != SEC_E_OK) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001033 RTC_LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
Tommie51a0a82018-02-27 15:30:29 +01001034 << GetErrorName(ret, SECURITY_ERRORS);
deadbeeff137e972017-03-23 15:45:49 -07001035 return HAR_IGNORE;
1036 }
1037
Yves Gerey665174f2018-06-19 15:03:05 +02001038 // CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out;
deadbeeff137e972017-03-23 15:45:49 -07001039
1040 CtxtHandle ctx;
Yves Gerey665174f2018-06-19 15:03:05 +02001041 ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0,
1042 SECURITY_NATIVE_DREP, 0, 0, &ctx,
1043 &out_buf_desc, &ret_flags, &lifetime);
deadbeeff137e972017-03-23 15:45:49 -07001044 if (FAILED(ret)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +01001045 RTC_LOG(LS_ERROR) << "InitializeSecurityContext returned: "
Tommie51a0a82018-02-27 15:30:29 +01001046 << GetErrorName(ret, SECURITY_ERRORS);
deadbeeff137e972017-03-23 15:45:49 -07001047 FreeCredentialsHandle(&cred);
1048 return HAR_IGNORE;
1049 }
1050
1051 RTC_DCHECK(!context);
1052 context = neg = new NegotiateAuthContext(auth_method, cred, ctx);
1053 neg->specified_credentials = specify_credentials;
1054 neg->steps = steps;
1055 }
1056
Yves Gerey665174f2018-06-19 15:03:05 +02001057 if ((ret == SEC_I_COMPLETE_NEEDED) ||
1058 (ret == SEC_I_COMPLETE_AND_CONTINUE)) {
deadbeeff137e972017-03-23 15:45:49 -07001059 ret = CompleteAuthToken(&neg->ctx, &out_buf_desc);
Mirko Bonadei675513b2017-11-09 11:09:25 +01001060 RTC_LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
Tommie51a0a82018-02-27 15:30:29 +01001061 << GetErrorName(ret, SECURITY_ERRORS);
deadbeeff137e972017-03-23 15:45:49 -07001062 if (FAILED(ret)) {
1063 return HAR_ERROR;
1064 }
1065 }
1066
deadbeeff137e972017-03-23 15:45:49 -07001067 std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
1068 response = auth_method;
1069 response.append(" ");
1070 response.append(Base64::Encode(decoded));
1071 return HAR_RESPONSE;
1072 }
1073#endif
Yves Gerey665174f2018-06-19 15:03:05 +02001074#endif // WEBRTC_WIN
deadbeeff137e972017-03-23 15:45:49 -07001075
1076 return HAR_IGNORE;
1077}
1078
1079//////////////////////////////////////////////////////////////////////
1080
Yves Gerey665174f2018-06-19 15:03:05 +02001081} // namespace rtc