nsHttpDigestAuth.cpp (23616B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // HttpLog.h should generally be included first 8 #include "HttpLog.h" 9 10 #include "mozilla/ClearOnShutdown.h" 11 #include "mozilla/Sprintf.h" 12 #include "mozilla/StaticPrefs_network.h" 13 14 #include "nsHttp.h" 15 #include "nsHttpDigestAuth.h" 16 #include "nsIHttpAuthenticableChannel.h" 17 #include "nsISupportsPrimitives.h" 18 #include "nsIURI.h" 19 #include "nsString.h" 20 #include "nsEscape.h" 21 #include "nsNetCID.h" 22 #include "nsCRT.h" 23 #include "nsICryptoHash.h" 24 #include "nsComponentManagerUtils.h" 25 #include "pk11pub.h" 26 27 constexpr uint16_t DigestLength(uint16_t aAlgorithm) { 28 if (aAlgorithm & (ALGO_SHA256 | ALGO_SHA256_SESS)) { 29 return SHA256_DIGEST_LENGTH; 30 } 31 return MD5_DIGEST_LENGTH; 32 } 33 34 namespace mozilla { 35 namespace net { 36 37 StaticRefPtr<nsHttpDigestAuth> nsHttpDigestAuth::gSingleton; 38 39 already_AddRefed<nsIHttpAuthenticator> nsHttpDigestAuth::GetOrCreate() { 40 nsCOMPtr<nsIHttpAuthenticator> authenticator; 41 if (gSingleton) { 42 authenticator = gSingleton; 43 } else { 44 gSingleton = new nsHttpDigestAuth(); 45 ClearOnShutdown(&gSingleton); 46 authenticator = gSingleton; 47 } 48 49 return authenticator.forget(); 50 } 51 52 //----------------------------------------------------------------------------- 53 // nsHttpDigestAuth::nsISupports 54 //----------------------------------------------------------------------------- 55 56 NS_IMPL_ISUPPORTS(nsHttpDigestAuth, nsIHttpAuthenticator) 57 58 //----------------------------------------------------------------------------- 59 // nsHttpDigestAuth <protected> 60 //----------------------------------------------------------------------------- 61 62 nsresult nsHttpDigestAuth::DigestHash(const char* buf, uint32_t len, 63 uint16_t algorithm) { 64 nsresult rv; 65 66 // Cache a reference to the nsICryptoHash instance since we'll be calling 67 // this function frequently. 68 if (!mVerifier) { 69 mVerifier = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 70 if (NS_FAILED(rv)) { 71 LOG(("nsHttpDigestAuth: no crypto hash!\n")); 72 return rv; 73 } 74 } 75 76 uint32_t dlen; 77 if (algorithm & (ALGO_SHA256 | ALGO_SHA256_SESS)) { 78 rv = mVerifier->Init(nsICryptoHash::SHA256); 79 dlen = SHA256_DIGEST_LENGTH; 80 } else { 81 rv = mVerifier->Init(nsICryptoHash::MD5); 82 dlen = MD5_DIGEST_LENGTH; 83 } 84 if (NS_FAILED(rv)) return rv; 85 86 rv = mVerifier->Update((unsigned char*)buf, len); 87 if (NS_FAILED(rv)) return rv; 88 89 nsAutoCString hashString; 90 rv = mVerifier->Finish(false, hashString); 91 if (NS_FAILED(rv)) return rv; 92 93 NS_ENSURE_STATE(hashString.Length() == dlen); 94 memcpy(mHashBuf, hashString.get(), hashString.Length()); 95 96 return rv; 97 } 98 99 nsresult nsHttpDigestAuth::GetMethodAndPath( 100 nsIHttpAuthenticableChannel* authChannel, bool isProxyAuth, 101 nsCString& httpMethod, nsCString& path) { 102 nsresult rv, rv2; 103 nsCOMPtr<nsIURI> uri; 104 rv = authChannel->GetURI(getter_AddRefs(uri)); 105 if (NS_SUCCEEDED(rv)) { 106 bool proxyMethodIsConnect; 107 rv = authChannel->GetProxyMethodIsConnect(&proxyMethodIsConnect); 108 if (NS_SUCCEEDED(rv)) { 109 if (proxyMethodIsConnect && isProxyAuth) { 110 httpMethod.AssignLiteral("CONNECT"); 111 // 112 // generate hostname:port string. (unfortunately uri->GetHostPort 113 // leaves out the port if it matches the default value, so we can't 114 // just call it.) 115 // 116 int32_t port; 117 rv = uri->GetAsciiHost(path); 118 rv2 = uri->GetPort(&port); 119 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2)) { 120 path.Append(':'); 121 path.AppendInt(port < 0 ? NS_HTTPS_DEFAULT_PORT : port); 122 } 123 } else { 124 rv = authChannel->GetRequestMethod(httpMethod); 125 rv2 = uri->GetPathQueryRef(path); 126 if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2)) { 127 // 128 // strip any fragment identifier from the URL path. 129 // 130 int32_t ref = path.RFindChar('#'); 131 if (ref != kNotFound) path.Truncate(ref); 132 // 133 // make sure we escape any UTF-8 characters in the URI path. the 134 // digest auth uri attribute needs to match the request-URI. 135 // 136 // XXX we should really ask the HTTP channel for this string 137 // instead of regenerating it here. 138 // 139 nsAutoCString buf; 140 rv = NS_EscapeURL(path, esc_OnlyNonASCII | esc_Spaces, buf, 141 mozilla::fallible); 142 if (NS_SUCCEEDED(rv)) { 143 path = buf; 144 } 145 } 146 } 147 } 148 } 149 return rv; 150 } 151 152 //----------------------------------------------------------------------------- 153 // nsHttpDigestAuth::nsIHttpAuthenticator 154 //----------------------------------------------------------------------------- 155 156 NS_IMETHODIMP 157 nsHttpDigestAuth::ChallengeReceived(nsIHttpAuthenticableChannel* authChannel, 158 const nsACString& challenge, 159 bool isProxyAuth, 160 nsISupports** sessionState, 161 nsISupports** continuationState, 162 bool* result) { 163 nsAutoCString realm, domain, nonce, opaque; 164 bool stale; 165 uint16_t algorithm, qop; 166 167 nsresult rv = ParseChallenge(challenge, realm, domain, nonce, opaque, &stale, 168 &algorithm, &qop); 169 170 if (!(algorithm & 171 (ALGO_MD5 | ALGO_MD5_SESS | ALGO_SHA256 | ALGO_SHA256_SESS))) { 172 // they asked for an algorithm that we do not support yet (like SHA-512/256) 173 NS_WARNING("unsupported algorithm requested by Digest authentication"); 174 return NS_ERROR_NOT_IMPLEMENTED; 175 } 176 177 if (NS_FAILED(rv)) return rv; 178 179 // if the challenge has the "stale" flag set, then the user identity is not 180 // necessarily invalid. by returning FALSE here we can suppress username 181 // and password prompting that usually accompanies a 401/407 challenge. 182 *result = !stale; 183 184 // clear any existing nonce_count since we have a new challenge. 185 NS_IF_RELEASE(*sessionState); 186 return NS_OK; 187 } 188 189 NS_IMETHODIMP 190 nsHttpDigestAuth::GenerateCredentialsAsync( 191 nsIHttpAuthenticableChannel* authChannel, 192 nsIHttpAuthenticatorCallback* aCallback, const nsACString& challenge, 193 bool isProxyAuth, const nsAString& domain, const nsAString& username, 194 const nsAString& password, nsISupports* sessionState, 195 nsISupports* continuationState, nsICancelable** aCancellable) { 196 return NS_ERROR_NOT_IMPLEMENTED; 197 } 198 199 NS_IMETHODIMP 200 nsHttpDigestAuth::GenerateCredentials( 201 nsIHttpAuthenticableChannel* authChannel, const nsACString& aChallenge, 202 bool isProxyAuth, const nsAString& userdomain, const nsAString& username, 203 const nsAString& password, nsISupports** sessionState, 204 nsISupports** continuationState, uint32_t* aFlags, nsACString& creds) 205 206 { 207 LOG(("nsHttpDigestAuth::GenerateCredentials [challenge=%s]\n", 208 aChallenge.BeginReading())); 209 210 *aFlags = 0; 211 212 bool isDigestAuth = StringBeginsWith(aChallenge, "digest "_ns, 213 nsCaseInsensitiveCStringComparator); 214 NS_ENSURE_TRUE(isDigestAuth, NS_ERROR_UNEXPECTED); 215 216 // IIS implementation requires extra quotes 217 bool requireExtraQuotes = false; 218 { 219 nsAutoCString serverVal; 220 (void)authChannel->GetServerResponseHeader(serverVal); 221 if (!serverVal.IsEmpty()) { 222 requireExtraQuotes = 223 !nsCRT::strncasecmp(serverVal.get(), "Microsoft-IIS", 13); 224 } 225 } 226 227 nsresult rv; 228 nsAutoCString httpMethod; 229 nsAutoCString path; 230 rv = GetMethodAndPath(authChannel, isProxyAuth, httpMethod, path); 231 if (NS_FAILED(rv)) return rv; 232 233 nsAutoCString realm, domain, nonce, opaque; 234 bool stale; 235 uint16_t algorithm, qop; 236 237 rv = ParseChallenge(aChallenge, realm, domain, nonce, opaque, &stale, 238 &algorithm, &qop); 239 if (NS_FAILED(rv)) { 240 LOG( 241 ("nsHttpDigestAuth::GenerateCredentials [ParseChallenge failed " 242 "rv=%" PRIx32 "]\n", 243 static_cast<uint32_t>(rv))); 244 return rv; 245 } 246 247 const uint32_t dhexlen = 2 * DigestLength(algorithm) + 1; 248 char ha1_digest[dhexlen]; 249 char ha2_digest[dhexlen]; 250 char response_digest[dhexlen]; 251 char upload_data_digest[dhexlen]; 252 253 if (qop & QOP_AUTH_INT) { 254 // we do not support auth-int "quality of protection" currently 255 qop &= ~QOP_AUTH_INT; 256 257 NS_WARNING( 258 "no support for Digest authentication with data integrity quality of " 259 "protection"); 260 261 /* TODO: to support auth-int, we need to get an MD5 digest of 262 * TODO: the data uploaded with this request. 263 * TODO: however, i am not sure how to read in the file in without 264 * TODO: disturbing the channel''s use of it. do i need to copy it 265 * TODO: somehow? 266 */ 267 #if 0 268 if (http_channel != nullptr) 269 { 270 nsIInputStream * upload; 271 nsCOMPtr<nsIUploadChannel> uc = do_QueryInterface(http_channel); 272 NS_ENSURE_TRUE(uc, NS_ERROR_UNEXPECTED); 273 uc->GetUploadStream(&upload); 274 if (upload) { 275 char * upload_buffer; 276 int upload_buffer_length = 0; 277 //TODO: read input stream into buffer 278 const char * digest = (const char*) 279 nsNetwerkMD5Digest(upload_buffer, upload_buffer_length); 280 ExpandToHex(digest, upload_data_digest); 281 NS_RELEASE(upload); 282 } 283 } 284 #endif 285 } 286 287 if (!(algorithm & 288 (ALGO_MD5 | ALGO_MD5_SESS | ALGO_SHA256 | ALGO_SHA256_SESS))) { 289 // they asked only for algorithms that we do not support 290 NS_WARNING("unsupported algorithm requested by Digest authentication"); 291 return NS_ERROR_NOT_IMPLEMENTED; 292 } 293 294 // 295 // the following are for increasing security. see RFC 2617 for more 296 // information. 297 // 298 // nonce_count allows the server to keep track of auth challenges (to help 299 // prevent spoofing). we increase this count every time. 300 // 301 char nonce_count[NONCE_COUNT_LENGTH + 1] = "00000001"; // in hex 302 if (*sessionState) { 303 nsCOMPtr<nsISupportsPRUint32> v(do_QueryInterface(*sessionState)); 304 if (v) { 305 uint32_t nc; 306 v->GetData(&nc); 307 SprintfLiteral(nonce_count, "%08x", ++nc); 308 v->SetData(nc); 309 } 310 } else { 311 nsCOMPtr<nsISupportsPRUint32> v( 312 do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID)); 313 if (v) { 314 v->SetData(1); 315 v.forget(sessionState); 316 } 317 } 318 LOG((" nonce_count=%s\n", nonce_count)); 319 320 // 321 // this lets the client verify the server response (via a server 322 // returned Authentication-Info header). also used for session info. 323 // 324 nsAutoCString cnonce; 325 nsTArray<uint8_t> cnonceBuf; 326 cnonceBuf.SetLength(StaticPrefs::network_http_digest_auth_cnonce_length() / 327 2); 328 PK11_GenerateRandom(reinterpret_cast<unsigned char*>(cnonceBuf.Elements()), 329 cnonceBuf.Length()); 330 for (auto byte : cnonceBuf) { 331 cnonce.AppendPrintf("%02x", byte); 332 } 333 LOG((" cnonce=%s\n", cnonce.get())); 334 335 // 336 // calculate credentials 337 // 338 339 NS_ConvertUTF16toUTF8 cUser(username), cPass(password); 340 rv = CalculateHA1(cUser, cPass, realm, algorithm, nonce, cnonce, ha1_digest); 341 if (NS_FAILED(rv)) return rv; 342 343 rv = CalculateHA2(httpMethod, path, algorithm, qop, upload_data_digest, 344 ha2_digest); 345 if (NS_FAILED(rv)) return rv; 346 347 rv = CalculateResponse(ha1_digest, ha2_digest, algorithm, nonce, qop, 348 nonce_count, cnonce, response_digest); 349 if (NS_FAILED(rv)) return rv; 350 351 // 352 // Values that need to match the quoted-string production from RFC 2616: 353 // 354 // username 355 // realm 356 // nonce 357 // opaque 358 // cnonce 359 // 360 361 nsAutoCString authString; 362 363 authString.AssignLiteral("Digest username="); 364 rv = AppendQuotedString(cUser, authString); 365 NS_ENSURE_SUCCESS(rv, rv); 366 367 authString.AppendLiteral(", realm="); 368 rv = AppendQuotedString(realm, authString); 369 NS_ENSURE_SUCCESS(rv, rv); 370 371 authString.AppendLiteral(", nonce="); 372 rv = AppendQuotedString(nonce, authString); 373 NS_ENSURE_SUCCESS(rv, rv); 374 375 authString.AppendLiteral(", uri=\""); 376 authString += path; 377 if (algorithm & ALGO_SPECIFIED) { 378 authString.AppendLiteral("\", algorithm="); 379 if (algorithm & ALGO_MD5_SESS) { 380 authString.AppendLiteral("MD5-sess"); 381 } else if (algorithm & ALGO_SHA256) { 382 authString.AppendLiteral("SHA-256"); 383 } else if (algorithm & ALGO_SHA256_SESS) { 384 authString.AppendLiteral("SHA-256-sess"); 385 } else { 386 authString.AppendLiteral("MD5"); 387 } 388 } else { 389 authString += '\"'; 390 } 391 authString.AppendLiteral(", response=\""); 392 authString += response_digest; 393 authString += '\"'; 394 395 if (!opaque.IsEmpty()) { 396 authString.AppendLiteral(", opaque="); 397 rv = AppendQuotedString(opaque, authString); 398 NS_ENSURE_SUCCESS(rv, rv); 399 } 400 401 if (qop) { 402 authString.AppendLiteral(", qop="); 403 if (requireExtraQuotes) authString += '\"'; 404 authString.AppendLiteral("auth"); 405 if (qop & QOP_AUTH_INT) authString.AppendLiteral("-int"); 406 if (requireExtraQuotes) authString += '\"'; 407 authString.AppendLiteral(", nc="); 408 authString += nonce_count; 409 410 authString.AppendLiteral(", cnonce="); 411 rv = AppendQuotedString(cnonce, authString); 412 NS_ENSURE_SUCCESS(rv, rv); 413 } 414 415 creds = authString; 416 return NS_OK; 417 } 418 419 NS_IMETHODIMP 420 nsHttpDigestAuth::GetAuthFlags(uint32_t* flags) { 421 *flags = REQUEST_BASED | REUSABLE_CHALLENGE | IDENTITY_ENCRYPTED; 422 // 423 // NOTE: digest auth credentials must be uniquely computed for each request, 424 // so we do not set the REUSABLE_CREDENTIALS flag. 425 // 426 return NS_OK; 427 } 428 429 nsresult nsHttpDigestAuth::CalculateResponse( 430 const char* ha1_digest, const char* ha2_digest, uint16_t algorithm, 431 const nsCString& nonce, uint16_t qop, const char* nonce_count, 432 const nsCString& cnonce, char* result) { 433 const uint32_t dhexlen = 2 * DigestLength(algorithm); 434 uint32_t len = 2 * dhexlen + nonce.Length() + 2; 435 436 if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { 437 len += cnonce.Length() + NONCE_COUNT_LENGTH + 3; 438 if (qop & QOP_AUTH_INT) { 439 len += 8; // length of "auth-int" 440 } else { 441 len += 4; // length of "auth" 442 } 443 } 444 445 nsAutoCString contents; 446 contents.SetCapacity(len); 447 448 contents.Append(ha1_digest, dhexlen); 449 contents.Append(':'); 450 contents.Append(nonce); 451 contents.Append(':'); 452 453 if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { 454 contents.Append(nonce_count, NONCE_COUNT_LENGTH); 455 contents.Append(':'); 456 contents.Append(cnonce); 457 contents.Append(':'); 458 if (qop & QOP_AUTH_INT) { 459 contents.AppendLiteral("auth-int:"); 460 } else { 461 contents.AppendLiteral("auth:"); 462 } 463 } 464 465 contents.Append(ha2_digest, dhexlen); 466 467 nsresult rv = DigestHash(contents.get(), contents.Length(), algorithm); 468 if (NS_SUCCEEDED(rv)) rv = ExpandToHex(mHashBuf, result, algorithm); 469 return rv; 470 } 471 472 nsresult nsHttpDigestAuth::ExpandToHex(const char* digest, char* result, 473 uint16_t algorithm) { 474 int16_t index, value; 475 const int16_t dlen = DigestLength(algorithm); 476 477 for (index = 0; index < dlen; index++) { 478 value = (digest[index] >> 4) & 0xf; 479 if (value < 10) { 480 result[index * 2] = value + '0'; 481 } else { 482 result[index * 2] = value - 10 + 'a'; 483 } 484 485 value = digest[index] & 0xf; 486 if (value < 10) { 487 result[(index * 2) + 1] = value + '0'; 488 } else { 489 result[(index * 2) + 1] = value - 10 + 'a'; 490 } 491 } 492 493 result[2 * dlen] = 0; 494 return NS_OK; 495 } 496 497 nsresult nsHttpDigestAuth::CalculateHA1(const nsCString& username, 498 const nsCString& password, 499 const nsCString& realm, 500 uint16_t algorithm, 501 const nsCString& nonce, 502 const nsCString& cnonce, char* result) { 503 const int16_t dhexlen = 2 * DigestLength(algorithm); 504 int16_t len = username.Length() + password.Length() + realm.Length() + 2; 505 if (algorithm & (ALGO_MD5_SESS | ALGO_SHA256_SESS)) { 506 int16_t exlen = dhexlen + nonce.Length() + cnonce.Length() + 2; 507 if (exlen > len) len = exlen; 508 } 509 510 nsAutoCString contents; 511 contents.SetCapacity(len); 512 513 contents.Append(username); 514 contents.Append(':'); 515 contents.Append(realm); 516 contents.Append(':'); 517 contents.Append(password); 518 519 nsresult rv; 520 rv = DigestHash(contents.get(), contents.Length(), algorithm); 521 if (NS_FAILED(rv)) return rv; 522 523 if (algorithm & (ALGO_MD5_SESS | ALGO_SHA256_SESS)) { 524 char part1[dhexlen + 1]; 525 rv = ExpandToHex(mHashBuf, part1, algorithm); 526 MOZ_ASSERT(NS_SUCCEEDED(rv)); 527 528 contents.Assign(part1, dhexlen); 529 contents.Append(':'); 530 contents.Append(nonce); 531 contents.Append(':'); 532 contents.Append(cnonce); 533 534 rv = DigestHash(contents.get(), contents.Length(), algorithm); 535 if (NS_FAILED(rv)) return rv; 536 } 537 538 return ExpandToHex(mHashBuf, result, algorithm); 539 } 540 541 nsresult nsHttpDigestAuth::CalculateHA2(const nsCString& method, 542 const nsCString& path, 543 uint16_t algorithm, uint16_t qop, 544 const char* bodyDigest, char* result) { 545 uint16_t methodLen = method.Length(); 546 uint32_t pathLen = path.Length(); 547 uint32_t len = methodLen + pathLen + 1; 548 const uint32_t dhexlen = 2 * DigestLength(algorithm); 549 550 if (qop & QOP_AUTH_INT) { 551 len += dhexlen + 1; 552 } 553 554 nsAutoCString contents; 555 contents.SetCapacity(len); 556 557 contents.Assign(method); 558 contents.Append(':'); 559 contents.Append(path); 560 561 if (qop & QOP_AUTH_INT) { 562 contents.Append(':'); 563 contents.Append(bodyDigest, dhexlen); 564 } 565 566 nsresult rv = DigestHash(contents.get(), contents.Length(), algorithm); 567 if (NS_FAILED(rv)) { 568 return rv; 569 } 570 return ExpandToHex(mHashBuf, result, algorithm); 571 } 572 573 nsresult nsHttpDigestAuth::ParseChallenge(const nsACString& aChallenge, 574 nsACString& realm, nsACString& domain, 575 nsACString& nonce, nsACString& opaque, 576 bool* stale, uint16_t* algorithm, 577 uint16_t* qop) { 578 // put an absurd, but maximum, length cap on the challenge so 579 // that calculations are 32 bit safe 580 if (aChallenge.Length() > 16000000) { 581 return NS_ERROR_INVALID_ARG; 582 } 583 584 const char* challenge = aChallenge.BeginReading(); 585 const char* end = aChallenge.EndReading(); 586 const char* p = challenge + 6; // first 6 characters are "Digest" 587 if (p >= end) { 588 return NS_ERROR_INVALID_ARG; 589 } 590 591 *stale = false; 592 *algorithm = ALGO_MD5; // default is MD5 593 *qop = 0; 594 595 for (;;) { 596 while (p < end && (*p == ',' || nsCRT::IsAsciiSpace(*p))) { 597 ++p; 598 } 599 if (p >= end) { 600 break; 601 } 602 603 // name 604 int32_t nameStart = (p - challenge); 605 while (p < end && !nsCRT::IsAsciiSpace(*p) && *p != '=') { 606 ++p; 607 } 608 if (p >= end) { 609 return NS_ERROR_INVALID_ARG; 610 } 611 int32_t nameLength = (p - challenge) - nameStart; 612 613 while (p < end && (nsCRT::IsAsciiSpace(*p) || *p == '=')) { 614 ++p; 615 } 616 if (p >= end) { 617 return NS_ERROR_INVALID_ARG; 618 } 619 620 bool quoted = false; 621 if (*p == '"') { 622 ++p; 623 quoted = true; 624 } 625 626 // value 627 int32_t valueStart = (p - challenge); 628 int32_t valueLength = 0; 629 if (quoted) { 630 while (p < end && *p != '"') { 631 ++p; 632 } 633 if (p >= end || *p != '"') { 634 return NS_ERROR_INVALID_ARG; 635 } 636 valueLength = (p - challenge) - valueStart; 637 ++p; 638 } else { 639 while (p < end && !nsCRT::IsAsciiSpace(*p) && *p != ',') { 640 ++p; 641 } 642 valueLength = (p - challenge) - valueStart; 643 } 644 645 // extract information 646 if (nameLength == 5 && 647 nsCRT::strncasecmp(challenge + nameStart, "realm", 5) == 0) { 648 realm.Assign(challenge + valueStart, valueLength); 649 } else if (nameLength == 6 && 650 nsCRT::strncasecmp(challenge + nameStart, "domain", 6) == 0) { 651 domain.Assign(challenge + valueStart, valueLength); 652 } else if (nameLength == 5 && 653 nsCRT::strncasecmp(challenge + nameStart, "nonce", 5) == 0) { 654 nonce.Assign(challenge + valueStart, valueLength); 655 } else if (nameLength == 6 && 656 nsCRT::strncasecmp(challenge + nameStart, "opaque", 6) == 0) { 657 opaque.Assign(challenge + valueStart, valueLength); 658 } else if (nameLength == 5 && 659 nsCRT::strncasecmp(challenge + nameStart, "stale", 5) == 0) { 660 if (nsCRT::strncasecmp(challenge + valueStart, "true", 4) == 0) { 661 *stale = true; 662 } else { 663 *stale = false; 664 } 665 } else if (nameLength == 9 && 666 nsCRT::strncasecmp(challenge + nameStart, "algorithm", 9) == 0) { 667 // we want to clear the default, so we use = not |= here 668 *algorithm = ALGO_SPECIFIED; 669 if (valueLength == 3 && 670 nsCRT::strncasecmp(challenge + valueStart, "MD5", 3) == 0) { 671 *algorithm |= ALGO_MD5; 672 } else if (valueLength == 8 && nsCRT::strncasecmp(challenge + valueStart, 673 "MD5-sess", 8) == 0) { 674 *algorithm |= ALGO_MD5_SESS; 675 } else if (valueLength == 7 && nsCRT::strncasecmp(challenge + valueStart, 676 "SHA-256", 7) == 0) { 677 *algorithm |= ALGO_SHA256; 678 } else if (valueLength == 12 && 679 nsCRT::strncasecmp(challenge + valueStart, "SHA-256-sess", 680 12) == 0) { 681 *algorithm |= ALGO_SHA256_SESS; 682 } 683 } else if (nameLength == 3 && 684 nsCRT::strncasecmp(challenge + nameStart, "qop", 3) == 0) { 685 int32_t ipos = valueStart; 686 while (ipos < valueStart + valueLength) { 687 while ( 688 ipos < valueStart + valueLength && 689 (nsCRT::IsAsciiSpace(challenge[ipos]) || challenge[ipos] == ',')) { 690 ipos++; 691 } 692 int32_t algostart = ipos; 693 while (ipos < valueStart + valueLength && 694 !nsCRT::IsAsciiSpace(challenge[ipos]) && 695 challenge[ipos] != ',') { 696 ipos++; 697 } 698 if ((ipos - algostart) == 4 && 699 nsCRT::strncasecmp(challenge + algostart, "auth", 4) == 0) { 700 *qop |= QOP_AUTH; 701 } else if ((ipos - algostart) == 8 && 702 nsCRT::strncasecmp(challenge + algostart, "auth-int", 8) == 703 0) { 704 *qop |= QOP_AUTH_INT; 705 } 706 } 707 } 708 } 709 return NS_OK; 710 } 711 712 nsresult nsHttpDigestAuth::AppendQuotedString(const nsACString& value, 713 nsACString& aHeaderLine) { 714 nsAutoCString quoted; 715 nsACString::const_iterator s, e; 716 value.BeginReading(s); 717 value.EndReading(e); 718 719 // 720 // Encode string according to RFC 2616 quoted-string production 721 // 722 quoted.Append('"'); 723 for (; s != e; ++s) { 724 // 725 // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> 726 // 727 if (*s <= 31 || *s == 127) { 728 return NS_ERROR_FAILURE; 729 } 730 731 // Escape two syntactically significant characters 732 if (*s == '"' || *s == '\\') { 733 quoted.Append('\\'); 734 } 735 736 quoted.Append(*s); 737 } 738 // FIXME: bug 41489 739 // We should RFC2047-encode non-Latin-1 values according to spec 740 quoted.Append('"'); 741 aHeaderLine.Append(quoted); 742 return NS_OK; 743 } 744 745 } // namespace net 746 } // namespace mozilla 747 748 // vim: ts=2 sw=2