michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // HttpLog.h should generally be included first michael@0: #include "HttpLog.h" michael@0: michael@0: #include "nsHttp.h" michael@0: #include "nsHttpDigestAuth.h" michael@0: #include "nsIHttpAuthenticableChannel.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIURI.h" michael@0: #include "nsString.h" michael@0: #include "nsEscape.h" michael@0: #include "nsNetCID.h" michael@0: #include "prprf.h" michael@0: #include "nsCRT.h" michael@0: #include "nsICryptoHash.h" michael@0: michael@0: namespace mozilla { michael@0: namespace net { michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpDigestAuth michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsHttpDigestAuth::nsHttpDigestAuth() michael@0: {} michael@0: michael@0: nsHttpDigestAuth::~nsHttpDigestAuth() michael@0: {} michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpDigestAuth::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsHttpDigestAuth, nsIHttpAuthenticator) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpDigestAuth michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::MD5Hash(const char *buf, uint32_t len) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Cache a reference to the nsICryptoHash instance since we'll be calling michael@0: // this function frequently. michael@0: if (!mVerifier) { michael@0: mVerifier = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("nsHttpDigestAuth: no crypto hash!\n")); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: rv = mVerifier->Init(nsICryptoHash::MD5); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = mVerifier->Update((unsigned char*)buf, len); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoCString hashString; michael@0: rv = mVerifier->Finish(false, hashString); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: NS_ENSURE_STATE(hashString.Length() == sizeof(mHashBuf)); michael@0: memcpy(mHashBuf, hashString.get(), hashString.Length()); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::GetMethodAndPath(nsIHttpAuthenticableChannel *authChannel, michael@0: bool isProxyAuth, michael@0: nsCString &httpMethod, michael@0: nsCString &path) michael@0: { michael@0: nsresult rv, rv2; michael@0: nsCOMPtr uri; michael@0: rv = authChannel->GetURI(getter_AddRefs(uri)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: bool proxyMethodIsConnect; michael@0: rv = authChannel->GetProxyMethodIsConnect(&proxyMethodIsConnect); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: if (proxyMethodIsConnect && isProxyAuth) { michael@0: httpMethod.AssignLiteral("CONNECT"); michael@0: // michael@0: // generate hostname:port string. (unfortunately uri->GetHostPort michael@0: // leaves out the port if it matches the default value, so we can't michael@0: // just call it.) michael@0: // michael@0: int32_t port; michael@0: rv = uri->GetAsciiHost(path); michael@0: rv2 = uri->GetPort(&port); michael@0: if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2)) { michael@0: path.Append(':'); michael@0: path.AppendInt(port < 0 ? NS_HTTPS_DEFAULT_PORT : port); michael@0: } michael@0: } michael@0: else { michael@0: rv = authChannel->GetRequestMethod(httpMethod); michael@0: rv2 = uri->GetPath(path); michael@0: if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2)) { michael@0: // michael@0: // strip any fragment identifier from the URL path. michael@0: // michael@0: int32_t ref = path.RFindChar('#'); michael@0: if (ref != kNotFound) michael@0: path.Truncate(ref); michael@0: // michael@0: // make sure we escape any UTF-8 characters in the URI path. the michael@0: // digest auth uri attribute needs to match the request-URI. michael@0: // michael@0: // XXX we should really ask the HTTP channel for this string michael@0: // instead of regenerating it here. michael@0: // michael@0: nsAutoCString buf; michael@0: path = NS_EscapeURL(path, esc_OnlyNonASCII, buf); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsHttpDigestAuth::nsIHttpAuthenticator michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpDigestAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel, michael@0: const char *challenge, michael@0: bool isProxyAuth, michael@0: nsISupports **sessionState, michael@0: nsISupports **continuationState, michael@0: bool *result) michael@0: { michael@0: nsAutoCString realm, domain, nonce, opaque; michael@0: bool stale; michael@0: uint16_t algorithm, qop; michael@0: michael@0: nsresult rv = ParseChallenge(challenge, realm, domain, nonce, opaque, michael@0: &stale, &algorithm, &qop); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // if the challenge has the "stale" flag set, then the user identity is not michael@0: // necessarily invalid. by returning FALSE here we can suppress username michael@0: // and password prompting that usually accompanies a 401/407 challenge. michael@0: *result = !stale; michael@0: michael@0: // clear any existing nonce_count since we have a new challenge. michael@0: NS_IF_RELEASE(*sessionState); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpDigestAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel, michael@0: const char *challenge, michael@0: bool isProxyAuth, michael@0: const char16_t *userdomain, michael@0: const char16_t *username, michael@0: const char16_t *password, michael@0: nsISupports **sessionState, michael@0: nsISupports **continuationState, michael@0: uint32_t *aFlags, michael@0: char **creds) michael@0: michael@0: { michael@0: LOG(("nsHttpDigestAuth::GenerateCredentials [challenge=%s]\n", challenge)); michael@0: michael@0: NS_ENSURE_ARG_POINTER(creds); michael@0: michael@0: *aFlags = 0; michael@0: michael@0: bool isDigestAuth = !PL_strncasecmp(challenge, "digest ", 7); michael@0: NS_ENSURE_TRUE(isDigestAuth, NS_ERROR_UNEXPECTED); michael@0: michael@0: // IIS implementation requires extra quotes michael@0: bool requireExtraQuotes = false; michael@0: { michael@0: nsAutoCString serverVal; michael@0: authChannel->GetServerResponseHeader(serverVal); michael@0: if (!serverVal.IsEmpty()) { michael@0: requireExtraQuotes = !PL_strncasecmp(serverVal.get(), "Microsoft-IIS", 13); michael@0: } michael@0: } michael@0: michael@0: nsresult rv; michael@0: nsAutoCString httpMethod; michael@0: nsAutoCString path; michael@0: rv = GetMethodAndPath(authChannel, isProxyAuth, httpMethod, path); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: nsAutoCString realm, domain, nonce, opaque; michael@0: bool stale; michael@0: uint16_t algorithm, qop; michael@0: michael@0: rv = ParseChallenge(challenge, realm, domain, nonce, opaque, michael@0: &stale, &algorithm, &qop); michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("nsHttpDigestAuth::GenerateCredentials [ParseChallenge failed rv=%x]\n", rv)); michael@0: return rv; michael@0: } michael@0: michael@0: char ha1_digest[EXPANDED_DIGEST_LENGTH+1]; michael@0: char ha2_digest[EXPANDED_DIGEST_LENGTH+1]; michael@0: char response_digest[EXPANDED_DIGEST_LENGTH+1]; michael@0: char upload_data_digest[EXPANDED_DIGEST_LENGTH+1]; michael@0: michael@0: if (qop & QOP_AUTH_INT) { michael@0: // we do not support auth-int "quality of protection" currently michael@0: qop &= ~QOP_AUTH_INT; michael@0: michael@0: NS_WARNING("no support for Digest authentication with data integrity quality of protection"); michael@0: michael@0: /* TODO: to support auth-int, we need to get an MD5 digest of michael@0: * TODO: the data uploaded with this request. michael@0: * TODO: however, i am not sure how to read in the file in without michael@0: * TODO: disturbing the channel''s use of it. do i need to copy it michael@0: * TODO: somehow? michael@0: */ michael@0: #if 0 michael@0: if (http_channel != nullptr) michael@0: { michael@0: nsIInputStream * upload; michael@0: nsCOMPtr uc = do_QueryInterface(http_channel); michael@0: NS_ENSURE_TRUE(uc, NS_ERROR_UNEXPECTED); michael@0: uc->GetUploadStream(&upload); michael@0: if (upload) { michael@0: char * upload_buffer; michael@0: int upload_buffer_length = 0; michael@0: //TODO: read input stream into buffer michael@0: const char * digest = (const char*) michael@0: nsNetwerkMD5Digest(upload_buffer, upload_buffer_length); michael@0: ExpandToHex(digest, upload_data_digest); michael@0: NS_RELEASE(upload); michael@0: } michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: if (!(algorithm & ALGO_MD5 || algorithm & ALGO_MD5_SESS)) { michael@0: // they asked only for algorithms that we do not support michael@0: NS_WARNING("unsupported algorithm requested by Digest authentication"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: // michael@0: // the following are for increasing security. see RFC 2617 for more michael@0: // information. michael@0: // michael@0: // nonce_count allows the server to keep track of auth challenges (to help michael@0: // prevent spoofing). we increase this count every time. michael@0: // michael@0: char nonce_count[NONCE_COUNT_LENGTH+1] = "00000001"; // in hex michael@0: if (*sessionState) { michael@0: nsCOMPtr v(do_QueryInterface(*sessionState)); michael@0: if (v) { michael@0: uint32_t nc; michael@0: v->GetData(&nc); michael@0: PR_snprintf(nonce_count, sizeof(nonce_count), "%08x", ++nc); michael@0: v->SetData(nc); michael@0: } michael@0: } michael@0: else { michael@0: nsCOMPtr v( michael@0: do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID)); michael@0: if (v) { michael@0: v->SetData(1); michael@0: NS_ADDREF(*sessionState = v); michael@0: } michael@0: } michael@0: LOG((" nonce_count=%s\n", nonce_count)); michael@0: michael@0: // michael@0: // this lets the client verify the server response (via a server michael@0: // returned Authentication-Info header). also used for session info. michael@0: // michael@0: nsAutoCString cnonce; michael@0: static const char hexChar[] = "0123456789abcdef"; michael@0: for (int i=0; i<16; ++i) { michael@0: cnonce.Append(hexChar[(int)(15.0 * rand()/(RAND_MAX + 1.0))]); michael@0: } michael@0: LOG((" cnonce=%s\n", cnonce.get())); michael@0: michael@0: // michael@0: // calculate credentials michael@0: // michael@0: michael@0: NS_ConvertUTF16toUTF8 cUser(username), cPass(password); michael@0: rv = CalculateHA1(cUser, cPass, realm, algorithm, nonce, cnonce, ha1_digest); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = CalculateHA2(httpMethod, path, qop, upload_data_digest, ha2_digest); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: rv = CalculateResponse(ha1_digest, ha2_digest, nonce, qop, nonce_count, michael@0: cnonce, response_digest); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: // michael@0: // Values that need to match the quoted-string production from RFC 2616: michael@0: // michael@0: // username michael@0: // realm michael@0: // nonce michael@0: // opaque michael@0: // cnonce michael@0: // michael@0: michael@0: nsAutoCString authString; michael@0: michael@0: authString.AssignLiteral("Digest username="); michael@0: rv = AppendQuotedString(cUser, authString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: authString.AppendLiteral(", realm="); michael@0: rv = AppendQuotedString(realm, authString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: authString.AppendLiteral(", nonce="); michael@0: rv = AppendQuotedString(nonce, authString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: authString.AppendLiteral(", uri=\""); michael@0: authString += path; michael@0: if (algorithm & ALGO_SPECIFIED) { michael@0: authString.AppendLiteral("\", algorithm="); michael@0: if (algorithm & ALGO_MD5_SESS) michael@0: authString.AppendLiteral("MD5-sess"); michael@0: else michael@0: authString.AppendLiteral("MD5"); michael@0: } else { michael@0: authString += '\"'; michael@0: } michael@0: authString.AppendLiteral(", response=\""); michael@0: authString += response_digest; michael@0: authString += '\"'; michael@0: michael@0: if (!opaque.IsEmpty()) { michael@0: authString.AppendLiteral(", opaque="); michael@0: rv = AppendQuotedString(opaque, authString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (qop) { michael@0: authString.AppendLiteral(", qop="); michael@0: if (requireExtraQuotes) michael@0: authString += '\"'; michael@0: authString.AppendLiteral("auth"); michael@0: if (qop & QOP_AUTH_INT) michael@0: authString.AppendLiteral("-int"); michael@0: if (requireExtraQuotes) michael@0: authString += '\"'; michael@0: authString.AppendLiteral(", nc="); michael@0: authString += nonce_count; michael@0: michael@0: authString.AppendLiteral(", cnonce="); michael@0: rv = AppendQuotedString(cnonce, authString); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: michael@0: *creds = ToNewCString(authString); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsHttpDigestAuth::GetAuthFlags(uint32_t *flags) michael@0: { michael@0: *flags = REQUEST_BASED | REUSABLE_CHALLENGE | IDENTITY_ENCRYPTED; michael@0: // michael@0: // NOTE: digest auth credentials must be uniquely computed for each request, michael@0: // so we do not set the REUSABLE_CREDENTIALS flag. michael@0: // michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::CalculateResponse(const char * ha1_digest, michael@0: const char * ha2_digest, michael@0: const nsAFlatCString & nonce, michael@0: uint16_t qop, michael@0: const char * nonce_count, michael@0: const nsAFlatCString & cnonce, michael@0: char * result) michael@0: { michael@0: uint32_t len = 2*EXPANDED_DIGEST_LENGTH + nonce.Length() + 2; michael@0: michael@0: if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { michael@0: len += cnonce.Length() + NONCE_COUNT_LENGTH + 3; michael@0: if (qop & QOP_AUTH_INT) michael@0: len += 8; // length of "auth-int" michael@0: else michael@0: len += 4; // length of "auth" michael@0: } michael@0: michael@0: nsAutoCString contents; michael@0: contents.SetCapacity(len); michael@0: michael@0: contents.Assign(ha1_digest, EXPANDED_DIGEST_LENGTH); michael@0: contents.Append(':'); michael@0: contents.Append(nonce); michael@0: contents.Append(':'); michael@0: michael@0: if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { michael@0: contents.Append(nonce_count, NONCE_COUNT_LENGTH); michael@0: contents.Append(':'); michael@0: contents.Append(cnonce); michael@0: contents.Append(':'); michael@0: if (qop & QOP_AUTH_INT) michael@0: contents.AppendLiteral("auth-int:"); michael@0: else michael@0: contents.AppendLiteral("auth:"); michael@0: } michael@0: michael@0: contents.Append(ha2_digest, EXPANDED_DIGEST_LENGTH); michael@0: michael@0: nsresult rv = MD5Hash(contents.get(), contents.Length()); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = ExpandToHex(mHashBuf, result); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::ExpandToHex(const char * digest, char * result) michael@0: { michael@0: int16_t index, value; michael@0: michael@0: for (index = 0; index < DIGEST_LENGTH; index++) { michael@0: value = (digest[index] >> 4) & 0xf; michael@0: if (value < 10) michael@0: result[index*2] = value + '0'; michael@0: else michael@0: result[index*2] = value - 10 + 'a'; michael@0: michael@0: value = digest[index] & 0xf; michael@0: if (value < 10) michael@0: result[(index*2)+1] = value + '0'; michael@0: else michael@0: result[(index*2)+1] = value - 10 + 'a'; michael@0: } michael@0: michael@0: result[EXPANDED_DIGEST_LENGTH] = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::CalculateHA1(const nsAFlatCString & username, michael@0: const nsAFlatCString & password, michael@0: const nsAFlatCString & realm, michael@0: uint16_t algorithm, michael@0: const nsAFlatCString & nonce, michael@0: const nsAFlatCString & cnonce, michael@0: char * result) michael@0: { michael@0: int16_t len = username.Length() + password.Length() + realm.Length() + 2; michael@0: if (algorithm & ALGO_MD5_SESS) { michael@0: int16_t exlen = EXPANDED_DIGEST_LENGTH + nonce.Length() + cnonce.Length() + 2; michael@0: if (exlen > len) michael@0: len = exlen; michael@0: } michael@0: michael@0: nsAutoCString contents; michael@0: contents.SetCapacity(len + 1); michael@0: michael@0: contents.Assign(username); michael@0: contents.Append(':'); michael@0: contents.Append(realm); michael@0: contents.Append(':'); michael@0: contents.Append(password); michael@0: michael@0: nsresult rv; michael@0: rv = MD5Hash(contents.get(), contents.Length()); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (algorithm & ALGO_MD5_SESS) { michael@0: char part1[EXPANDED_DIGEST_LENGTH+1]; michael@0: ExpandToHex(mHashBuf, part1); michael@0: michael@0: contents.Assign(part1, EXPANDED_DIGEST_LENGTH); michael@0: contents.Append(':'); michael@0: contents.Append(nonce); michael@0: contents.Append(':'); michael@0: contents.Append(cnonce); michael@0: michael@0: rv = MD5Hash(contents.get(), contents.Length()); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: } michael@0: michael@0: return ExpandToHex(mHashBuf, result); michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::CalculateHA2(const nsAFlatCString & method, michael@0: const nsAFlatCString & path, michael@0: uint16_t qop, michael@0: const char * bodyDigest, michael@0: char * result) michael@0: { michael@0: uint16_t methodLen = method.Length(); michael@0: uint32_t pathLen = path.Length(); michael@0: uint32_t len = methodLen + pathLen + 1; michael@0: michael@0: if (qop & QOP_AUTH_INT) { michael@0: len += EXPANDED_DIGEST_LENGTH + 1; michael@0: } michael@0: michael@0: nsAutoCString contents; michael@0: contents.SetCapacity(len); michael@0: michael@0: contents.Assign(method); michael@0: contents.Append(':'); michael@0: contents.Append(path); michael@0: michael@0: if (qop & QOP_AUTH_INT) { michael@0: contents.Append(':'); michael@0: contents.Append(bodyDigest, EXPANDED_DIGEST_LENGTH); michael@0: } michael@0: michael@0: nsresult rv = MD5Hash(contents.get(), contents.Length()); michael@0: if (NS_SUCCEEDED(rv)) michael@0: rv = ExpandToHex(mHashBuf, result); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::ParseChallenge(const char * challenge, michael@0: nsACString & realm, michael@0: nsACString & domain, michael@0: nsACString & nonce, michael@0: nsACString & opaque, michael@0: bool * stale, michael@0: uint16_t * algorithm, michael@0: uint16_t * qop) michael@0: { michael@0: // put an absurd, but maximum, length cap on the challenge so michael@0: // that calculations are 32 bit safe michael@0: if (strlen(challenge) > 16000000) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: const char *p = challenge + 7; // first 7 characters are "Digest " michael@0: michael@0: *stale = false; michael@0: *algorithm = ALGO_MD5; // default is MD5 michael@0: *qop = 0; michael@0: michael@0: for (;;) { michael@0: while (*p && (*p == ',' || nsCRT::IsAsciiSpace(*p))) michael@0: ++p; michael@0: if (!*p) michael@0: break; michael@0: michael@0: // name michael@0: int32_t nameStart = (p - challenge); michael@0: while (*p && !nsCRT::IsAsciiSpace(*p) && *p != '=') michael@0: ++p; michael@0: if (!*p) michael@0: return NS_ERROR_INVALID_ARG; michael@0: int32_t nameLength = (p - challenge) - nameStart; michael@0: michael@0: while (*p && (nsCRT::IsAsciiSpace(*p) || *p == '=')) michael@0: ++p; michael@0: if (!*p) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: bool quoted = false; michael@0: if (*p == '"') { michael@0: ++p; michael@0: quoted = true; michael@0: } michael@0: michael@0: // value michael@0: int32_t valueStart = (p - challenge); michael@0: int32_t valueLength = 0; michael@0: if (quoted) { michael@0: while (*p && *p != '"') michael@0: ++p; michael@0: if (*p != '"') michael@0: return NS_ERROR_INVALID_ARG; michael@0: valueLength = (p - challenge) - valueStart; michael@0: ++p; michael@0: } else { michael@0: while (*p && !nsCRT::IsAsciiSpace(*p) && *p != ',') michael@0: ++p; michael@0: valueLength = (p - challenge) - valueStart; michael@0: } michael@0: michael@0: // extract information michael@0: if (nameLength == 5 && michael@0: nsCRT::strncasecmp(challenge+nameStart, "realm", 5) == 0) michael@0: { michael@0: realm.Assign(challenge+valueStart, valueLength); michael@0: } michael@0: else if (nameLength == 6 && michael@0: nsCRT::strncasecmp(challenge+nameStart, "domain", 6) == 0) michael@0: { michael@0: domain.Assign(challenge+valueStart, valueLength); michael@0: } michael@0: else if (nameLength == 5 && michael@0: nsCRT::strncasecmp(challenge+nameStart, "nonce", 5) == 0) michael@0: { michael@0: nonce.Assign(challenge+valueStart, valueLength); michael@0: } michael@0: else if (nameLength == 6 && michael@0: nsCRT::strncasecmp(challenge+nameStart, "opaque", 6) == 0) michael@0: { michael@0: opaque.Assign(challenge+valueStart, valueLength); michael@0: } michael@0: else if (nameLength == 5 && michael@0: nsCRT::strncasecmp(challenge+nameStart, "stale", 5) == 0) michael@0: { michael@0: if (nsCRT::strncasecmp(challenge+valueStart, "true", 4) == 0) michael@0: *stale = true; michael@0: else michael@0: *stale = false; michael@0: } michael@0: else if (nameLength == 9 && michael@0: nsCRT::strncasecmp(challenge+nameStart, "algorithm", 9) == 0) michael@0: { michael@0: // we want to clear the default, so we use = not |= here michael@0: *algorithm = ALGO_SPECIFIED; michael@0: if (valueLength == 3 && michael@0: nsCRT::strncasecmp(challenge+valueStart, "MD5", 3) == 0) michael@0: *algorithm |= ALGO_MD5; michael@0: else if (valueLength == 8 && michael@0: nsCRT::strncasecmp(challenge+valueStart, "MD5-sess", 8) == 0) michael@0: *algorithm |= ALGO_MD5_SESS; michael@0: } michael@0: else if (nameLength == 3 && michael@0: nsCRT::strncasecmp(challenge+nameStart, "qop", 3) == 0) michael@0: { michael@0: int32_t ipos = valueStart; michael@0: while (ipos < valueStart+valueLength) { michael@0: while (ipos < valueStart+valueLength && michael@0: (nsCRT::IsAsciiSpace(challenge[ipos]) || michael@0: challenge[ipos] == ',')) michael@0: ipos++; michael@0: int32_t algostart = ipos; michael@0: while (ipos < valueStart+valueLength && michael@0: !nsCRT::IsAsciiSpace(challenge[ipos]) && michael@0: challenge[ipos] != ',') michael@0: ipos++; michael@0: if ((ipos - algostart) == 4 && michael@0: nsCRT::strncasecmp(challenge+algostart, "auth", 4) == 0) michael@0: *qop |= QOP_AUTH; michael@0: else if ((ipos - algostart) == 8 && michael@0: nsCRT::strncasecmp(challenge+algostart, "auth-int", 8) == 0) michael@0: *qop |= QOP_AUTH_INT; michael@0: } michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsHttpDigestAuth::AppendQuotedString(const nsACString & value, michael@0: nsACString & aHeaderLine) michael@0: { michael@0: nsAutoCString quoted; michael@0: nsACString::const_iterator s, e; michael@0: value.BeginReading(s); michael@0: value.EndReading(e); michael@0: michael@0: // michael@0: // Encode string according to RFC 2616 quoted-string production michael@0: // michael@0: quoted.Append('"'); michael@0: for ( ; s != e; ++s) { michael@0: // michael@0: // CTL = michael@0: // michael@0: if (*s <= 31 || *s == 127) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Escape two syntactically significant characters michael@0: if (*s == '"' || *s == '\\') { michael@0: quoted.Append('\\'); michael@0: } michael@0: michael@0: quoted.Append(*s); michael@0: } michael@0: // FIXME: bug 41489 michael@0: // We should RFC2047-encode non-Latin-1 values according to spec michael@0: quoted.Append('"'); michael@0: aHeaderLine.Append(quoted); michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace mozilla::net michael@0: } // namespace mozilla michael@0: michael@0: // vim: ts=2 sw=2