tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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