2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
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.
13 #if defined(WEBRTC_WIN)
14 #define WIN32_LEAN_AND_MEAN
18 #define SECURITY_WIN32
22 #include "webrtc/base/httpcommon-inl.h"
24 #include "webrtc/base/base64.h"
25 #include "webrtc/base/common.h"
26 #include "webrtc/base/cryptstring.h"
27 #include "webrtc/base/httpcommon.h"
28 #include "webrtc/base/socketaddress.h"
29 #include "webrtc/base/stringdigest.h"
30 #include "webrtc/base/stringencode.h"
31 #include "webrtc/base/stringutils.h"
35 #if defined(WEBRTC_WIN)
36 extern const ConstantLabel SECURITY_ERRORS[];
39 //////////////////////////////////////////////////////////////////////
40 // Enum - TODO: expose globally later?
41 //////////////////////////////////////////////////////////////////////
43 bool find_string(size_t& index, const std::string& needle,
44 const char* const haystack[], size_t max_index) {
45 for (index=0; index<max_index; ++index) {
46 if (_stricmp(needle.c_str(), haystack[index]) == 0) {
55 static const char** Names;
58 static inline const char* Name(E val) { return Names[val]; }
59 static inline bool Parse(E& val, const std::string& name) {
61 if (!find_string(index, name, Names, Size))
63 val = static_cast<E>(index);
69 inline operator E&() { return val; }
70 inline Enum& operator=(E rhs) { val = rhs; return *this; }
72 inline const char* name() const { return Name(val); }
73 inline bool assign(const std::string& name) { return Parse(val, name); }
74 inline Enum& operator=(const std::string& rhs) { assign(rhs); return *this; }
78 template<> const char** Enum<e>::Names = n; \
79 template<> size_t Enum<e>::Size = sizeof(n)/sizeof(n[0])
81 //////////////////////////////////////////////////////////////////////
83 //////////////////////////////////////////////////////////////////////
85 static const char* kHttpVersions[HVER_LAST+1] = {
86 "1.0", "1.1", "Unknown"
88 ENUM(HttpVersion, kHttpVersions);
90 static const char* kHttpVerbs[HV_LAST+1] = {
91 "GET", "POST", "PUT", "DELETE", "CONNECT", "HEAD"
93 ENUM(HttpVerb, kHttpVerbs);
95 static const char* kHttpHeaders[HH_LAST+1] = {
99 "Content-Disposition",
113 "Proxy-Authenticate",
114 "Proxy-Authorization",
125 ENUM(HttpHeader, kHttpHeaders);
127 const char* ToString(HttpVersion version) {
128 return Enum<HttpVersion>::Name(version);
131 bool FromString(HttpVersion& version, const std::string& str) {
132 return Enum<HttpVersion>::Parse(version, str);
135 const char* ToString(HttpVerb verb) {
136 return Enum<HttpVerb>::Name(verb);
139 bool FromString(HttpVerb& verb, const std::string& str) {
140 return Enum<HttpVerb>::Parse(verb, str);
143 const char* ToString(HttpHeader header) {
144 return Enum<HttpHeader>::Name(header);
147 bool FromString(HttpHeader& header, const std::string& str) {
148 return Enum<HttpHeader>::Parse(header, str);
151 bool HttpCodeHasBody(uint32 code) {
152 return !HttpCodeIsInformational(code)
153 && (code != HC_NO_CONTENT) && (code != HC_NOT_MODIFIED);
156 bool HttpCodeIsCacheable(uint32 code) {
159 case HC_NON_AUTHORITATIVE:
160 case HC_PARTIAL_CONTENT:
161 case HC_MULTIPLE_CHOICES:
162 case HC_MOVED_PERMANENTLY:
170 bool HttpHeaderIsEndToEnd(HttpHeader header) {
174 case HH_PROXY_AUTHENTICATE:
175 case HH_PROXY_AUTHORIZATION:
176 case HH_PROXY_CONNECTION: // Note part of RFC... this is non-standard header
179 case HH_TRANSFER_ENCODING:
187 bool HttpHeaderIsCollapsible(HttpHeader header) {
190 case HH_PROXY_AUTHENTICATE:
191 case HH_WWW_AUTHENTICATE:
198 bool HttpShouldKeepAlive(const HttpData& data) {
199 std::string connection;
200 if ((data.hasHeader(HH_PROXY_CONNECTION, &connection)
201 || data.hasHeader(HH_CONNECTION, &connection))) {
202 return (_stricmp(connection.c_str(), "Keep-Alive") == 0);
204 return (data.version >= HVER_1_1);
209 inline bool IsEndOfAttributeName(size_t pos, size_t len, const char * data) {
212 if (isspace(static_cast<unsigned char>(data[pos])))
214 // The reason for this complexity is that some attributes may contain trailing
215 // equal signs (like base64 tokens in Negotiate auth headers)
216 if ((pos+1 < len) && (data[pos] == '=') &&
217 !isspace(static_cast<unsigned char>(data[pos+1])) &&
218 (data[pos+1] != '=')) {
224 // TODO: unittest for EscapeAttribute and HttpComposeAttributes.
226 std::string EscapeAttribute(const std::string& attribute) {
227 const size_t kMaxLength = attribute.length() * 2 + 1;
228 char* buffer = STACK_ARRAY(char, kMaxLength);
229 size_t len = escape(buffer, kMaxLength, attribute.data(), attribute.length(),
231 return std::string(buffer, len);
234 } // anonymous namespace
236 void HttpComposeAttributes(const HttpAttributeList& attributes, char separator,
237 std::string* composed) {
238 std::stringstream ss;
239 for (size_t i=0; i<attributes.size(); ++i) {
241 ss << separator << " ";
243 ss << attributes[i].first;
244 if (!attributes[i].second.empty()) {
245 ss << "=\"" << EscapeAttribute(attributes[i].second) << "\"";
248 *composed = ss.str();
251 void HttpParseAttributes(const char * data, size_t len,
252 HttpAttributeList& attributes) {
255 // Skip leading whitespace
256 while ((pos < len) && isspace(static_cast<unsigned char>(data[pos]))) {
260 // End of attributes?
264 // Find end of attribute name
266 while (!IsEndOfAttributeName(pos, len, data)) {
270 HttpAttribute attribute;
271 attribute.first.assign(data + start, data + pos);
273 // Attribute has value?
274 if ((pos < len) && (data[pos] == '=')) {
276 // Check if quoted value
277 if ((pos < len) && (data[pos] == '"')) {
278 while (++pos < len) {
279 if (data[pos] == '"') {
283 if ((data[pos] == '\\') && (pos + 1 < len))
285 attribute.second.append(1, data[pos]);
288 while ((pos < len) &&
289 !isspace(static_cast<unsigned char>(data[pos])) &&
290 (data[pos] != ',')) {
291 attribute.second.append(1, data[pos++]);
296 attributes.push_back(attribute);
297 if ((pos < len) && (data[pos] == ',')) ++pos; // Skip ','
301 bool HttpHasAttribute(const HttpAttributeList& attributes,
302 const std::string& name,
303 std::string* value) {
304 for (HttpAttributeList::const_iterator it = attributes.begin();
305 it != attributes.end(); ++it) {
306 if (it->first == name) {
316 bool HttpHasNthAttribute(HttpAttributeList& attributes,
319 std::string* value) {
320 if (index >= attributes.size())
324 *name = attributes[index].first;
326 *value = attributes[index].second;
330 bool HttpDateToSeconds(const std::string& date, time_t* seconds) {
331 const char* const kTimeZones[] = {
332 "UT", "GMT", "EST", "EDT", "CST", "CDT", "MST", "MDT", "PST", "PDT",
333 "A", "B", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M",
334 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y"
336 const int kTimeZoneOffsets[] = {
337 0, 0, -5, -4, -6, -5, -7, -6, -8, -7,
338 -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -11, -12,
339 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
342 ASSERT(NULL != seconds);
344 memset(&tval, 0, sizeof(tval));
345 char month[4], zone[6];
346 memset(month, 0, sizeof(month));
347 memset(zone, 0, sizeof(zone));
349 if (7 != sscanf(date.c_str(), "%*3s, %d %3s %d %d:%d:%d %5c",
350 &tval.tm_mday, month, &tval.tm_year,
351 &tval.tm_hour, &tval.tm_min, &tval.tm_sec, zone)) {
354 switch (toupper(month[2])) {
355 case 'N': tval.tm_mon = (month[1] == 'A') ? 0 : 5; break;
356 case 'B': tval.tm_mon = 1; break;
357 case 'R': tval.tm_mon = (month[0] == 'M') ? 2 : 3; break;
358 case 'Y': tval.tm_mon = 4; break;
359 case 'L': tval.tm_mon = 6; break;
360 case 'G': tval.tm_mon = 7; break;
361 case 'P': tval.tm_mon = 8; break;
362 case 'T': tval.tm_mon = 9; break;
363 case 'V': tval.tm_mon = 10; break;
364 case 'C': tval.tm_mon = 11; break;
366 tval.tm_year -= 1900;
367 time_t gmt, non_gmt = mktime(&tval);
368 if ((zone[0] == '+') || (zone[0] == '-')) {
369 if (!isdigit(zone[1]) || !isdigit(zone[2])
370 || !isdigit(zone[3]) || !isdigit(zone[4])) {
373 int hours = (zone[1] - '0') * 10 + (zone[2] - '0');
374 int minutes = (zone[3] - '0') * 10 + (zone[4] - '0');
375 int offset = (hours * 60 + minutes) * 60;
376 gmt = non_gmt + ((zone[0] == '+') ? offset : -offset);
379 if (!find_string(zindex, zone, kTimeZones, ARRAY_SIZE(kTimeZones))) {
382 gmt = non_gmt + kTimeZoneOffsets[zindex] * 60 * 60;
384 // TODO: Android should support timezone, see b/2441195
385 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) || defined(WEBRTC_ANDROID) || defined(BSD)
386 tm *tm_for_timezone = localtime(&gmt);
387 *seconds = gmt + tm_for_timezone->tm_gmtoff;
389 *seconds = gmt - timezone;
394 std::string HttpAddress(const SocketAddress& address, bool secure) {
395 return (address.port() == HttpDefaultPort(secure))
396 ? address.hostname() : address.ToString();
399 //////////////////////////////////////////////////////////////////////
401 //////////////////////////////////////////////////////////////////////
404 HttpData::clear(bool release_document) {
405 // Clear headers first, since releasing a document may have far-reaching
408 if (release_document) {
414 HttpData::copy(const HttpData& src) {
415 headers_ = src.headers_;
419 HttpData::changeHeader(const std::string& name, const std::string& value,
420 HeaderCombine combine) {
421 if (combine == HC_AUTO) {
423 // Unrecognized headers are collapsible
424 combine = !FromString(header, name) || HttpHeaderIsCollapsible(header)
426 } else if (combine == HC_REPLACE) {
427 headers_.erase(name);
430 // At this point, combine is one of (YES, NO, NEW)
431 if (combine != HC_NO) {
432 HeaderMap::iterator it = headers_.find(name);
433 if (it != headers_.end()) {
434 if (combine == HC_YES) {
435 it->second.append(",");
436 it->second.append(value);
441 headers_.insert(HeaderMap::value_type(name, value));
444 size_t HttpData::clearHeader(const std::string& name) {
445 return headers_.erase(name);
448 HttpData::iterator HttpData::clearHeader(iterator header) {
449 iterator deprecated = header++;
450 headers_.erase(deprecated);
455 HttpData::hasHeader(const std::string& name, std::string* value) const {
456 HeaderMap::const_iterator it = headers_.find(name);
457 if (it == headers_.end()) {
465 void HttpData::setContent(const std::string& content_type,
466 StreamInterface* document) {
467 setHeader(HH_CONTENT_TYPE, content_type);
468 setDocumentAndLength(document);
471 void HttpData::setDocumentAndLength(StreamInterface* document) {
472 // TODO: Consider calling Rewind() here?
473 ASSERT(!hasHeader(HH_CONTENT_LENGTH, NULL));
474 ASSERT(!hasHeader(HH_TRANSFER_ENCODING, NULL));
475 ASSERT(document != NULL);
476 this->document.reset(document);
477 size_t content_length = 0;
478 if (this->document->GetAvailable(&content_length)) {
480 sprintfn(buffer, sizeof(buffer), "%d", content_length);
481 setHeader(HH_CONTENT_LENGTH, buffer);
483 setHeader(HH_TRANSFER_ENCODING, "chunked");
492 HttpRequestData::clear(bool release_document) {
495 HttpData::clear(release_document);
499 HttpRequestData::copy(const HttpRequestData& src) {
506 HttpRequestData::formatLeader(char* buffer, size_t size) const {
507 ASSERT(path.find(' ') == std::string::npos);
508 return sprintfn(buffer, size, "%s %.*s HTTP/%s", ToString(verb), path.size(),
509 path.data(), ToString(version));
513 HttpRequestData::parseLeader(const char* line, size_t len) {
514 unsigned int vmajor, vminor;
515 int vend, dstart, dend;
516 // sscanf isn't safe with strings that aren't null-terminated, and there is
517 // no guarantee that |line| is. Create a local copy that is null-terminated.
518 std::string line_str(line, len);
519 line = line_str.c_str();
520 if ((sscanf(line, "%*s%n %n%*s%n HTTP/%u.%u",
521 &vend, &dstart, &dend, &vmajor, &vminor) != 2)
527 } else if (vminor == 1) {
532 std::string sverb(line, vend);
533 if (!FromString(verb, sverb.c_str())) {
534 return HE_PROTOCOL; // !?! HC_METHOD_NOT_SUPPORTED?
536 path.assign(line + dstart, line + dend);
540 bool HttpRequestData::getAbsoluteUri(std::string* uri) const {
541 if (HV_CONNECT == verb)
549 if (!hasHeader(HH_HOST, &host))
551 url.set_address(host);
552 url.set_full_path(path);
553 uri->assign(url.url());
557 bool HttpRequestData::getRelativeUri(std::string* host,
558 std::string* path) const
560 if (HV_CONNECT == verb)
562 Url<char> url(this->path);
564 host->assign(url.address());
565 path->assign(url.full_path());
568 if (!hasHeader(HH_HOST, host))
570 path->assign(this->path);
579 HttpResponseData::clear(bool release_document) {
580 scode = HC_INTERNAL_SERVER_ERROR;
582 HttpData::clear(release_document);
586 HttpResponseData::copy(const HttpResponseData& src) {
588 message = src.message;
593 HttpResponseData::set_success(uint32 scode) {
596 setHeader(HH_CONTENT_LENGTH, "0", false);
600 HttpResponseData::set_success(const std::string& content_type,
601 StreamInterface* document,
604 message.erase(message.begin(), message.end());
605 setContent(content_type, document);
609 HttpResponseData::set_redirect(const std::string& location, uint32 scode) {
612 setHeader(HH_LOCATION, location);
613 setHeader(HH_CONTENT_LENGTH, "0", false);
617 HttpResponseData::set_error(uint32 scode) {
620 setHeader(HH_CONTENT_LENGTH, "0", false);
624 HttpResponseData::formatLeader(char* buffer, size_t size) const {
625 size_t len = sprintfn(buffer, size, "HTTP/%s %lu", ToString(version), scode);
626 if (!message.empty()) {
627 len += sprintfn(buffer + len, size - len, " %.*s",
628 message.size(), message.data());
634 HttpResponseData::parseLeader(const char* line, size_t len) {
636 unsigned int vmajor, vminor, temp_scode;
638 // sscanf isn't safe with strings that aren't null-terminated, and there is
639 // no guarantee that |line| is. Create a local copy that is null-terminated.
640 std::string line_str(line, len);
641 line = line_str.c_str();
642 if (sscanf(line, "HTTP %u%n",
643 &temp_scode, &temp_pos) == 1) {
644 // This server's response has no version. :( NOTE: This happens for every
645 // response to requests made from Chrome plugins, regardless of the server's
647 LOG(LS_VERBOSE) << "HTTP version missing from response";
648 version = HVER_UNKNOWN;
649 } else if ((sscanf(line, "HTTP/%u.%u %u%n",
650 &vmajor, &vminor, &temp_scode, &temp_pos) == 3)
652 // This server's response does have a version.
655 } else if (vminor == 1) {
664 pos = static_cast<size_t>(temp_pos);
665 while ((pos < len) && isspace(static_cast<unsigned char>(line[pos]))) ++pos;
666 message.assign(line + pos, len - pos);
670 //////////////////////////////////////////////////////////////////////
671 // Http Authentication
672 //////////////////////////////////////////////////////////////////////
674 #define TEST_DIGEST 0
677 const char * const DIGEST_CHALLENGE =
678 "Digest realm=\"testrealm@host.com\","
679 " qop=\"auth,auth-int\","
680 " nonce=\"dcd98b7102dd2f0e8b11d0f600bfb0c093\","
681 " opaque=\"5ccc069c403ebaf9f0171e9517f40e41\"";
682 const char * const DIGEST_METHOD = "GET";
683 const char * const DIGEST_URI =
685 const char * const DIGEST_CNONCE =
687 const char * const DIGEST_RESPONSE =
688 "6629fae49393a05397450978507c4ef1";
690 //pass_ = "Circle Of Life";
692 const char * const DIGEST_CHALLENGE =
693 "Digest realm=\"Squid proxy-caching web server\","
694 " nonce=\"Nny4QuC5PwiSDixJ\","
697 const char * const DIGEST_URI =
699 const char * const DIGEST_CNONCE =
700 "6501d58e9a21cee1e7b5fec894ded024";
701 const char * const DIGEST_RESPONSE =
702 "edffcb0829e755838b073a4a42de06bc";
705 std::string quote(const std::string& str) {
707 result.push_back('"');
708 for (size_t i=0; i<str.size(); ++i) {
709 if ((str[i] == '"') || (str[i] == '\\'))
710 result.push_back('\\');
711 result.push_back(str[i]);
713 result.push_back('"');
717 #if defined(WEBRTC_WIN)
718 struct NegotiateAuthContext : public HttpAuthContext {
722 bool specified_credentials;
724 NegotiateAuthContext(const std::string& auth, CredHandle c1, CtxtHandle c2)
725 : HttpAuthContext(auth), cred(c1), ctx(c2), steps(0),
726 specified_credentials(false)
729 virtual ~NegotiateAuthContext() {
730 DeleteSecurityContext(&ctx);
731 FreeCredentialsHandle(&cred);
736 HttpAuthResult HttpAuthenticate(
737 const char * challenge, size_t len,
738 const SocketAddress& server,
739 const std::string& method, const std::string& uri,
740 const std::string& username, const CryptString& password,
741 HttpAuthContext *& context, std::string& response, std::string& auth_method)
744 challenge = DIGEST_CHALLENGE;
745 len = strlen(challenge);
748 HttpAttributeList args;
749 HttpParseAttributes(challenge, len, args);
750 HttpHasNthAttribute(args, 0, &auth_method, NULL);
752 if (context && (context->auth_method != auth_method))
756 if (_stricmp(auth_method.c_str(), "basic") == 0) {
758 return HAR_CREDENTIALS; // Bad credentials
759 if (username.empty())
760 return HAR_CREDENTIALS; // Missing credentials
762 context = new HttpAuthContext(auth_method);
764 // TODO: convert sensitive to a secure buffer that gets securely deleted
765 //std::string decoded = username + ":" + password;
766 size_t len = username.size() + password.GetLength() + 2;
767 char * sensitive = new char[len];
768 size_t pos = strcpyn(sensitive, len, username.data(), username.size());
769 pos += strcpyn(sensitive + pos, len - pos, ":");
770 password.CopyTo(sensitive + pos, true);
772 response = auth_method;
773 response.append(" ");
774 // TODO: create a sensitive-source version of Base64::encode
775 response.append(Base64::Encode(sensitive));
776 memset(sensitive, 0, len);
782 if (_stricmp(auth_method.c_str(), "digest") == 0) {
784 return HAR_CREDENTIALS; // Bad credentials
785 if (username.empty())
786 return HAR_CREDENTIALS; // Missing credentials
788 context = new HttpAuthContext(auth_method);
790 std::string cnonce, ncount;
792 method = DIGEST_METHOD;
794 cnonce = DIGEST_CNONCE;
797 sprintf(buffer, "%d", static_cast<int>(time(0)));
798 cnonce = MD5(buffer);
802 std::string realm, nonce, qop, opaque;
803 HttpHasAttribute(args, "realm", &realm);
804 HttpHasAttribute(args, "nonce", &nonce);
805 bool has_qop = HttpHasAttribute(args, "qop", &qop);
806 bool has_opaque = HttpHasAttribute(args, "opaque", &opaque);
808 // TODO: convert sensitive to be secure buffer
809 //std::string A1 = username + ":" + realm + ":" + password;
810 size_t len = username.size() + realm.size() + password.GetLength() + 3;
811 char * sensitive = new char[len]; // A1
812 size_t pos = strcpyn(sensitive, len, username.data(), username.size());
813 pos += strcpyn(sensitive + pos, len - pos, ":");
814 pos += strcpyn(sensitive + pos, len - pos, realm.c_str());
815 pos += strcpyn(sensitive + pos, len - pos, ":");
816 password.CopyTo(sensitive + pos, true);
818 std::string A2 = method + ":" + uri;
822 middle = nonce + ":" + ncount + ":" + cnonce + ":" + qop;
826 std::string HA1 = MD5(sensitive);
827 memset(sensitive, 0, len);
829 std::string HA2 = MD5(A2);
830 std::string dig_response = MD5(HA1 + ":" + middle + ":" + HA2);
833 ASSERT(strcmp(dig_response.c_str(), DIGEST_RESPONSE) == 0);
836 std::stringstream ss;
838 ss << " username=" << quote(username);
839 ss << ", realm=" << quote(realm);
840 ss << ", nonce=" << quote(nonce);
841 ss << ", uri=" << quote(uri);
843 ss << ", qop=" << qop;
844 ss << ", nc=" << ncount;
845 ss << ", cnonce=" << quote(cnonce);
847 ss << ", response=\"" << dig_response << "\"";
849 ss << ", opaque=" << quote(opaque);
855 #if defined(WEBRTC_WIN)
857 bool want_negotiate = (_stricmp(auth_method.c_str(), "negotiate") == 0);
858 bool want_ntlm = (_stricmp(auth_method.c_str(), "ntlm") == 0);
860 if (want_negotiate || want_ntlm) {
861 const size_t MAX_MESSAGE = 12000, MAX_SPN = 256;
862 char out_buf[MAX_MESSAGE], spn[MAX_SPN];
864 #if 0 // Requires funky windows versions
866 if (DsMakeSpn("HTTP", server.HostAsURIString().c_str(), NULL,
868 0, &len, spn) != ERROR_SUCCESS) {
869 LOG_F(WARNING) << "(Negotiate) - DsMakeSpn failed";
873 sprintfn(spn, MAX_SPN, "HTTP/%s", server.ToString().c_str());
877 out_sec.pvBuffer = out_buf;
878 out_sec.cbBuffer = sizeof(out_buf);
879 out_sec.BufferType = SECBUFFER_TOKEN;
881 SecBufferDesc out_buf_desc;
882 out_buf_desc.ulVersion = 0;
883 out_buf_desc.cBuffers = 1;
884 out_buf_desc.pBuffers = &out_sec;
886 const ULONG NEG_FLAGS_DEFAULT =
887 //ISC_REQ_ALLOCATE_MEMORY
888 ISC_REQ_CONFIDENTIALITY
889 //| ISC_REQ_EXTENDED_ERROR
890 //| ISC_REQ_INTEGRITY
891 | ISC_REQ_REPLAY_DETECT
892 | ISC_REQ_SEQUENCE_DETECT
894 //| ISC_REQ_USE_SUPPLIED_CREDS
897 ::TimeStamp lifetime;
898 SECURITY_STATUS ret = S_OK;
899 ULONG ret_flags = 0, flags = NEG_FLAGS_DEFAULT;
901 bool specify_credentials = !username.empty();
904 //uint32 now = Time();
906 NegotiateAuthContext * neg = static_cast<NegotiateAuthContext *>(context);
908 const size_t max_steps = 10;
909 if (++neg->steps >= max_steps) {
910 LOG(WARNING) << "AsyncHttpsProxySocket::Authenticate(Negotiate) too many retries";
915 std::string challenge, decoded_challenge;
916 if (HttpHasNthAttribute(args, 1, &challenge, NULL)
917 && Base64::Decode(challenge, Base64::DO_STRICT,
918 &decoded_challenge, NULL)) {
920 in_sec.pvBuffer = const_cast<char *>(decoded_challenge.data());
921 in_sec.cbBuffer = static_cast<unsigned long>(decoded_challenge.size());
922 in_sec.BufferType = SECBUFFER_TOKEN;
924 SecBufferDesc in_buf_desc;
925 in_buf_desc.ulVersion = 0;
926 in_buf_desc.cBuffers = 1;
927 in_buf_desc.pBuffers = &in_sec;
929 ret = InitializeSecurityContextA(&neg->cred, &neg->ctx, spn, flags, 0, SECURITY_NATIVE_DREP, &in_buf_desc, 0, &neg->ctx, &out_buf_desc, &ret_flags, &lifetime);
930 //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now);
932 LOG(LS_ERROR) << "InitializeSecurityContext returned: "
933 << ErrorName(ret, SECURITY_ERRORS);
936 } else if (neg->specified_credentials) {
937 // Try again with default credentials
938 specify_credentials = false;
942 return HAR_CREDENTIALS;
947 unsigned char userbuf[256], passbuf[256], domainbuf[16];
948 SEC_WINNT_AUTH_IDENTITY_A auth_id, * pauth_id = 0;
949 if (specify_credentials) {
950 memset(&auth_id, 0, sizeof(auth_id));
951 size_t len = password.GetLength()+1;
952 char * sensitive = new char[len];
953 password.CopyTo(sensitive, true);
954 std::string::size_type pos = username.find('\\');
955 if (pos == std::string::npos) {
956 auth_id.UserLength = static_cast<unsigned long>(
957 _min(sizeof(userbuf) - 1, username.size()));
958 memcpy(userbuf, username.c_str(), auth_id.UserLength);
959 userbuf[auth_id.UserLength] = 0;
960 auth_id.DomainLength = 0;
961 domainbuf[auth_id.DomainLength] = 0;
962 auth_id.PasswordLength = static_cast<unsigned long>(
963 _min(sizeof(passbuf) - 1, password.GetLength()));
964 memcpy(passbuf, sensitive, auth_id.PasswordLength);
965 passbuf[auth_id.PasswordLength] = 0;
967 auth_id.UserLength = static_cast<unsigned long>(
968 _min(sizeof(userbuf) - 1, username.size() - pos - 1));
969 memcpy(userbuf, username.c_str() + pos + 1, auth_id.UserLength);
970 userbuf[auth_id.UserLength] = 0;
971 auth_id.DomainLength = static_cast<unsigned long>(
972 _min(sizeof(domainbuf) - 1, pos));
973 memcpy(domainbuf, username.c_str(), auth_id.DomainLength);
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;
980 memset(sensitive, 0, len);
982 auth_id.User = userbuf;
983 auth_id.Domain = domainbuf;
984 auth_id.Password = passbuf;
985 auth_id.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
987 LOG(LS_VERBOSE) << "Negotiate protocol: Using specified credentials";
989 LOG(LS_VERBOSE) << "Negotiate protocol: Using default credentials";
993 ret = AcquireCredentialsHandleA(
994 0, const_cast<char*>(want_negotiate ? NEGOSSP_NAME_A : NTLMSP_NAME_A),
995 SECPKG_CRED_OUTBOUND, 0, pauth_id, 0, 0, &cred, &lifetime);
996 //LOG(INFO) << "$$$ AcquireCredentialsHandle @ " << TimeSince(now);
997 if (ret != SEC_E_OK) {
998 LOG(LS_ERROR) << "AcquireCredentialsHandle error: "
999 << ErrorName(ret, SECURITY_ERRORS);
1003 //CSecBufferBundle<5, CSecBufferBase::FreeSSPI> sb_out;
1006 ret = InitializeSecurityContextA(&cred, 0, spn, flags, 0, SECURITY_NATIVE_DREP, 0, 0, &ctx, &out_buf_desc, &ret_flags, &lifetime);
1007 //LOG(INFO) << "$$$ InitializeSecurityContext @ " << TimeSince(now);
1009 LOG(LS_ERROR) << "InitializeSecurityContext returned: "
1010 << ErrorName(ret, SECURITY_ERRORS);
1011 FreeCredentialsHandle(&cred);
1016 context = neg = new NegotiateAuthContext(auth_method, cred, ctx);
1017 neg->specified_credentials = specify_credentials;
1021 if ((ret == SEC_I_COMPLETE_NEEDED) || (ret == SEC_I_COMPLETE_AND_CONTINUE)) {
1022 ret = CompleteAuthToken(&neg->ctx, &out_buf_desc);
1023 //LOG(INFO) << "$$$ CompleteAuthToken @ " << TimeSince(now);
1024 LOG(LS_VERBOSE) << "CompleteAuthToken returned: "
1025 << ErrorName(ret, SECURITY_ERRORS);
1031 //LOG(INFO) << "$$$ NEGOTIATE took " << TimeSince(now) << "ms";
1033 std::string decoded(out_buf, out_buf + out_sec.cbBuffer);
1034 response = auth_method;
1035 response.append(" ");
1036 response.append(Base64::Encode(decoded));
1037 return HAR_RESPONSE;
1040 #endif // WEBRTC_WIN
1045 //////////////////////////////////////////////////////////////////////