diff -r dd2f2db1f87a netwerk/protocol/http/nsHttpDigestAuth.cpp --- a/netwerk/protocol/http/nsHttpDigestAuth.cpp Thu Mar 16 04:19:00 2017 +0100 +++ b/netwerk/protocol/http/nsHttpDigestAuth.cpp Thu Mar 16 21:42:51 2017 +0600 @@ -40,41 +40,48 @@ nsHttpDigestAuth::~nsHttpDigestAuth() NS_IMPL_ISUPPORTS(nsHttpDigestAuth, nsIHttpAuthenticator) //----------------------------------------------------------------------------- // nsHttpDigestAuth //----------------------------------------------------------------------------- nsresult -nsHttpDigestAuth::MD5Hash(const char *buf, uint32_t len) +nsHttpDigestAuth::DoHash(const char *buf, uint32_t len, uint16_t algorithm) { nsresult rv; // Cache a reference to the nsICryptoHash instance since we'll be calling // this function frequently. if (!mVerifier) { mVerifier = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); if (NS_FAILED(rv)) { LOG(("nsHttpDigestAuth: no crypto hash!\n")); return rv; } } - rv = mVerifier->Init(nsICryptoHash::MD5); + if (algorithm & (ALGO_SHA256 | ALGO_SHA256_SESS)) { + rv = mVerifier->Init(nsICryptoHash::SHA256); + } else if (algorithm & (ALGO_MD5 | ALGO_MD5_SESS)) { + rv = mVerifier->Init(nsICryptoHash::MD5); + } else { + NS_ERROR("Algorithm not set"); + rv = NS_ERROR_UNEXPECTED; + } if (NS_FAILED(rv)) return rv; rv = mVerifier->Update((unsigned char*)buf, len); if (NS_FAILED(rv)) return rv; nsAutoCString hashString; rv = mVerifier->Finish(false, hashString); if (NS_FAILED(rv)) return rv; - NS_ENSURE_STATE(hashString.Length() == sizeof(mHashBuf)); + NS_ENSURE_STATE(hashString.Length() <= sizeof(mHashBuf)); memcpy(mHashBuf, hashString.get(), hashString.Length()); return rv; } nsresult nsHttpDigestAuth::GetMethodAndPath(nsIHttpAuthenticableChannel *authChannel, bool isProxyAuth, @@ -141,20 +148,20 @@ nsHttpDigestAuth::ChallengeReceived(nsIH const char *challenge, bool isProxyAuth, nsISupports **sessionState, nsISupports **continuationState, bool *result) { nsAutoCString realm, domain, nonce, opaque; bool stale; - uint16_t algorithm, qop; + uint16_t algorithm, qop, charset, userhash; nsresult rv = ParseChallenge(challenge, realm, domain, nonce, opaque, - &stale, &algorithm, &qop); + &stale, &algorithm, &qop, &charset, &userhash); if (NS_FAILED(rv)) return rv; // if the challenge has the "stale" flag set, then the user identity is not // necessarily invalid. by returning FALSE here we can suppress username // and password prompting that usually accompanies a 401/407 challenge. *result = !stale; // clear any existing nonce_count since we have a new challenge. @@ -213,20 +220,20 @@ nsHttpDigestAuth::GenerateCredentials(ns nsresult rv; nsAutoCString httpMethod; nsAutoCString path; rv = GetMethodAndPath(authChannel, isProxyAuth, httpMethod, path); if (NS_FAILED(rv)) return rv; nsAutoCString realm, domain, nonce, opaque; bool stale; - uint16_t algorithm, qop; + uint16_t algorithm, qop, charset, userhash; rv = ParseChallenge(challenge, realm, domain, nonce, opaque, - &stale, &algorithm, &qop); + &stale, &algorithm, &qop, &charset, &userhash); if (NS_FAILED(rv)) { LOG(("nsHttpDigestAuth::GenerateCredentials [ParseChallenge failed rv=%" PRIx32 "]\n", static_cast(rv))); return rv; } char ha1_digest[EXPANDED_DIGEST_LENGTH+1]; char ha2_digest[EXPANDED_DIGEST_LENGTH+1]; @@ -260,17 +267,18 @@ nsHttpDigestAuth::GenerateCredentials(ns nsNetwerkMD5Digest(upload_buffer, upload_buffer_length); ExpandToHex(digest, upload_data_digest); NS_RELEASE(upload); } } #endif } - if (!(algorithm & ALGO_MD5 || algorithm & ALGO_MD5_SESS)) { + if (!(algorithm & (ALGO_MD5 | ALGO_MD5_SESS | ALGO_SHA256 | + ALGO_SHA256_SESS))) { // they asked only for algorithms that we do not support NS_WARNING("unsupported algorithm requested by Digest authentication"); return NS_ERROR_NOT_IMPLEMENTED; } // // the following are for increasing security. see RFC 2617 for more // information. @@ -308,59 +316,89 @@ nsHttpDigestAuth::GenerateCredentials(ns cnonce.Append(hexChar[(int)(15.0 * rand()/(RAND_MAX + 1.0))]); } LOG((" cnonce=%s\n", cnonce.get())); // // calculate credentials // + if (charset & CHARSET_SPECIFIED && !(charset & CHARSET_UTF8)) { + return NS_ERROR_UNEXPECTED; + } + rv = CheckControls(username); + NS_ENSURE_SUCCESS(rv, rv); + NS_ConvertUTF16toUTF8 cUser(username), cPass(password); rv = CalculateHA1(cUser, cPass, realm, algorithm, nonce, cnonce, ha1_digest); if (NS_FAILED(rv)) return rv; - rv = CalculateHA2(httpMethod, path, qop, upload_data_digest, ha2_digest); + rv = CalculateHA2(httpMethod, path, qop, algorithm, upload_data_digest, + ha2_digest); if (NS_FAILED(rv)) return rv; - rv = CalculateResponse(ha1_digest, ha2_digest, nonce, qop, nonce_count, - cnonce, response_digest); + rv = CalculateResponse(ha1_digest, ha2_digest, nonce, qop, algorithm, + nonce_count, cnonce, response_digest); if (NS_FAILED(rv)) return rv; // // Values that need to match the quoted-string production from RFC 2616: // // username // realm // nonce // opaque // cnonce // nsAutoCString authString; - authString.AssignLiteral("Digest username="); - rv = AppendQuotedString(cUser, authString); - NS_ENSURE_SUCCESS(rv, rv); + if (userhash == USERHASH_TRUE) { + char hashuser[EXPANDED_DIGEST_LENGTH+1]; + cUser.Append(':'); + cUser.Append(realm); + rv = DoHash(cUser.get(), cUser.Length(), algorithm); + NS_ENSURE_SUCCESS(rv, rv); + rv = ExpandToHex(mHashBuf, algorithm, hashuser); + NS_ENSURE_SUCCESS(rv, rv); + authString.AssignLiteral("Digest username=\""); + authString += hashuser; + authString += '\"'; + } else if (charset & CHARSET_UTF8) { + authString.AssignLiteral("Digest username*=UTF-8\'\'"); + nsAutoCString escUser; + NS_Escape(cUser, escUser, url_XAlphas); + authString.Append(escUser); + } else { + authString.AssignLiteral("Digest username="); + rv = AppendQuotedString(cUser, authString); + NS_ENSURE_SUCCESS(rv, rv); + } authString.AppendLiteral(", realm="); rv = AppendQuotedString(realm, authString); NS_ENSURE_SUCCESS(rv, rv); authString.AppendLiteral(", nonce="); rv = AppendQuotedString(nonce, authString); NS_ENSURE_SUCCESS(rv, rv); authString.AppendLiteral(", uri=\""); authString += path; if (algorithm & ALGO_SPECIFIED) { authString.AppendLiteral("\", algorithm="); - if (algorithm & ALGO_MD5_SESS) + if (algorithm & ALGO_SHA256_SESS) { + authString.AppendLiteral("SHA-256-sess"); + } else if (algorithm & ALGO_SHA256) { + authString.AppendLiteral("SHA-256"); + } else if (algorithm & ALGO_MD5_SESS) { authString.AppendLiteral("MD5-sess"); - else + } else { authString.AppendLiteral("MD5"); + } } else { authString += '\"'; } authString.AppendLiteral(", response=\""); authString += response_digest; authString += '\"'; if (!opaque.IsEmpty()) { @@ -381,16 +419,24 @@ nsHttpDigestAuth::GenerateCredentials(ns authString.AppendLiteral(", nc="); authString += nonce_count; authString.AppendLiteral(", cnonce="); rv = AppendQuotedString(cnonce, authString); NS_ENSURE_SUCCESS(rv, rv); } + if (userhash) { + authString.AppendLiteral(", userhash="); + if (userhash == USERHASH_TRUE) { + authString.AppendLiteral("true"); + } else { + authString.AppendLiteral("false"); + } + } *creds = ToNewCString(authString); return NS_OK; } NS_IMETHODIMP nsHttpDigestAuth::GetAuthFlags(uint32_t *flags) { @@ -402,183 +448,190 @@ nsHttpDigestAuth::GetAuthFlags(uint32_t return NS_OK; } nsresult nsHttpDigestAuth::CalculateResponse(const char * ha1_digest, const char * ha2_digest, const nsAFlatCString & nonce, uint16_t qop, + uint16_t algorithm, const char * nonce_count, const nsAFlatCString & cnonce, char * result) { - uint32_t len = 2*EXPANDED_DIGEST_LENGTH + nonce.Length() + 2; + uint32_t len = nonce.Length() + 2 + ExpandedDigestLength(algorithm) * 2; if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { len += cnonce.Length() + NONCE_COUNT_LENGTH + 3; if (qop & QOP_AUTH_INT) len += 8; // length of "auth-int" else len += 4; // length of "auth" } nsAutoCString contents; contents.SetCapacity(len); - contents.Assign(ha1_digest, EXPANDED_DIGEST_LENGTH); + contents.Assign(ha1_digest, ExpandedDigestLength(algorithm)); contents.Append(':'); contents.Append(nonce); contents.Append(':'); if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { contents.Append(nonce_count, NONCE_COUNT_LENGTH); contents.Append(':'); contents.Append(cnonce); contents.Append(':'); if (qop & QOP_AUTH_INT) contents.AppendLiteral("auth-int:"); else contents.AppendLiteral("auth:"); } - contents.Append(ha2_digest, EXPANDED_DIGEST_LENGTH); + contents.Append(ha2_digest, ExpandedDigestLength(algorithm)); - nsresult rv = MD5Hash(contents.get(), contents.Length()); + nsresult rv = DoHash(contents.get(), contents.Length(), algorithm); if (NS_SUCCEEDED(rv)) - rv = ExpandToHex(mHashBuf, result); + rv = ExpandToHex(mHashBuf, algorithm, result); return rv; } nsresult -nsHttpDigestAuth::ExpandToHex(const char * digest, char * result) +nsHttpDigestAuth::ExpandToHex(const char * digest, int16_t algorithm, char * result) { - int16_t index, value; + int16_t index, value, digestLen = DigestLength(algorithm); - for (index = 0; index < DIGEST_LENGTH; index++) { + for (index = 0; index < digestLen; index++) { value = (digest[index] >> 4) & 0xf; if (value < 10) result[index*2] = value + '0'; else result[index*2] = value - 10 + 'a'; value = digest[index] & 0xf; if (value < 10) result[(index*2)+1] = value + '0'; else result[(index*2)+1] = value - 10 + 'a'; } - result[EXPANDED_DIGEST_LENGTH] = 0; + result[ExpandedDigestLength(algorithm)] = 0; return NS_OK; } nsresult nsHttpDigestAuth::CalculateHA1(const nsAFlatCString & username, const nsAFlatCString & password, const nsAFlatCString & realm, uint16_t algorithm, const nsAFlatCString & nonce, const nsAFlatCString & cnonce, char * result) { int16_t len = username.Length() + password.Length() + realm.Length() + 2; - if (algorithm & ALGO_MD5_SESS) { - int16_t exlen = EXPANDED_DIGEST_LENGTH + nonce.Length() + cnonce.Length() + 2; + if (algorithm & (ALGO_MD5_SESS | ALGO_SHA256_SESS)) { + int16_t exlen = ExpandedDigestLength(algorithm) + nonce.Length() + + cnonce.Length() + 2; if (exlen > len) len = exlen; } nsAutoCString contents; contents.SetCapacity(len + 1); contents.Assign(username); contents.Append(':'); contents.Append(realm); contents.Append(':'); contents.Append(password); nsresult rv; - rv = MD5Hash(contents.get(), contents.Length()); + rv = DoHash(contents.get(), contents.Length(), algorithm); if (NS_FAILED(rv)) return rv; - if (algorithm & ALGO_MD5_SESS) { + if (algorithm & (ALGO_MD5_SESS | ALGO_SHA256_SESS)) { char part1[EXPANDED_DIGEST_LENGTH+1]; - rv = ExpandToHex(mHashBuf, part1); + rv = ExpandToHex(mHashBuf, algorithm, part1); MOZ_ASSERT(NS_SUCCEEDED(rv)); - contents.Assign(part1, EXPANDED_DIGEST_LENGTH); + contents.Assign(part1, ExpandedDigestLength(algorithm)); contents.Append(':'); contents.Append(nonce); contents.Append(':'); contents.Append(cnonce); - rv = MD5Hash(contents.get(), contents.Length()); + rv = DoHash(contents.get(), contents.Length(), algorithm); if (NS_FAILED(rv)) return rv; } - return ExpandToHex(mHashBuf, result); + return ExpandToHex(mHashBuf, algorithm, result); } nsresult nsHttpDigestAuth::CalculateHA2(const nsAFlatCString & method, const nsAFlatCString & path, uint16_t qop, + uint16_t algorithm, const char * bodyDigest, char * result) { uint16_t methodLen = method.Length(); uint32_t pathLen = path.Length(); uint32_t len = methodLen + pathLen + 1; if (qop & QOP_AUTH_INT) { - len += EXPANDED_DIGEST_LENGTH + 1; + len += ExpandedDigestLength(algorithm) + 1; } nsAutoCString contents; contents.SetCapacity(len); contents.Assign(method); contents.Append(':'); contents.Append(path); if (qop & QOP_AUTH_INT) { contents.Append(':'); - contents.Append(bodyDigest, EXPANDED_DIGEST_LENGTH); + contents.Append(bodyDigest, ExpandedDigestLength(algorithm)); } - nsresult rv = MD5Hash(contents.get(), contents.Length()); + nsresult rv = DoHash(contents.get(), contents.Length(), algorithm); if (NS_SUCCEEDED(rv)) - rv = ExpandToHex(mHashBuf, result); + rv = ExpandToHex(mHashBuf, algorithm, result); return rv; } nsresult nsHttpDigestAuth::ParseChallenge(const char * challenge, nsACString & realm, nsACString & domain, nsACString & nonce, nsACString & opaque, bool * stale, uint16_t * algorithm, - uint16_t * qop) + uint16_t * qop, + uint16_t * charset, + uint16_t * userhash) { // put an absurd, but maximum, length cap on the challenge so // that calculations are 32 bit safe if (strlen(challenge) > 16000000) { return NS_ERROR_INVALID_ARG; } const char *p = challenge + 6; // first 6 characters are "Digest" *stale = false; *algorithm = ALGO_MD5; // default is MD5 *qop = 0; + *charset = CHARSET_NOT_SET; + *userhash = USERHASH_NOT_SET; for (;;) { while (*p && (*p == ',' || nsCRT::IsAsciiSpace(*p))) ++p; if (!*p) break; // name @@ -632,35 +685,60 @@ nsHttpDigestAuth::ParseChallenge(const c { nonce.Assign(challenge+valueStart, valueLength); } else if (nameLength == 6 && nsCRT::strncasecmp(challenge+nameStart, "opaque", 6) == 0) { opaque.Assign(challenge+valueStart, valueLength); } + else if (nameLength == 7 && + nsCRT::strncasecmp(challenge+nameStart, "charset", 7) == 0) + { + *charset = CHARSET_SPECIFIED; + // UTF-8 or no + if (nsCRT::strncasecmp(challenge+valueStart, "UTF-8", 5) == 0) { + *charset |= CHARSET_UTF8; + } + } + else if (nameLength == 8 && + nsCRT::strncasecmp(challenge+nameStart, "userhash", 8) == 0) + { + if (nsCRT::strncasecmp(challenge+valueStart, "true", 4) == 0) { + *userhash = USERHASH_TRUE; + } else { + *userhash = USERHASH_FALSE; + } + } else if (nameLength == 5 && nsCRT::strncasecmp(challenge+nameStart, "stale", 5) == 0) { if (nsCRT::strncasecmp(challenge+valueStart, "true", 4) == 0) *stale = true; else *stale = false; } else if (nameLength == 9 && nsCRT::strncasecmp(challenge+nameStart, "algorithm", 9) == 0) { // we want to clear the default, so we use = not |= here *algorithm = ALGO_SPECIFIED; if (valueLength == 3 && - nsCRT::strncasecmp(challenge+valueStart, "MD5", 3) == 0) + nsCRT::strncasecmp(challenge+valueStart, "MD5", 3) == 0) { *algorithm |= ALGO_MD5; - else if (valueLength == 8 && - nsCRT::strncasecmp(challenge+valueStart, "MD5-sess", 8) == 0) + } else if (valueLength == 8 && + nsCRT::strncasecmp(challenge+valueStart, "MD5-sess", 8) == 0) { *algorithm |= ALGO_MD5_SESS; + } else if (valueLength == 7 && + nsCRT::strncasecmp(challenge+valueStart, "SHA-256", 7) == 0) { + *algorithm |= ALGO_SHA256; + } else if (valueLength == 12 && + nsCRT::strncasecmp(challenge+valueStart, "SHA-256-sess", 12) == 0) { + *algorithm |= ALGO_SHA256_SESS; + } } else if (nameLength == 3 && nsCRT::strncasecmp(challenge+nameStart, "qop", 3) == 0) { int32_t ipos = valueStart; while (ipos < valueStart+valueLength) { while (ipos < valueStart+valueLength && (nsCRT::IsAsciiSpace(challenge[ipos]) || @@ -706,19 +784,49 @@ nsHttpDigestAuth::AppendQuotedString(con // Escape two syntactically significant characters if (*s == '"' || *s == '\\') { quoted.Append('\\'); } quoted.Append(*s); } - // FIXME: bug 41489 - // We should RFC2047-encode non-Latin-1 values according to spec quoted.Append('"'); aHeaderLine.Append(quoted); return NS_OK; } +int16_t +nsHttpDigestAuth::DigestLength(int16_t algorithm) +{ + MOZ_ASSERT(algorithm >= ALGO_SPECIFIED && algorithm <= ALGO_SHA256_SESS + 1); + int16_t len; + if (algorithm & (ALGO_SHA256 | ALGO_SHA256_SESS)) { + len = DIGEST_SHA256_LENGTH; + } else { + len = DIGEST_MD5_LENGTH; + } + return len; +} + +int16_t +nsHttpDigestAuth::ExpandedDigestLength(int16_t algorithm) +{ + return DigestLength(algorithm) * 2; +} + +nsresult +nsHttpDigestAuth::CheckControls(const char16_t *username) +{ + const char16_t *p = username; + while(*p) { + if (*p < 9 || (*p > 9 && *p <= 31) || *p == 127) { + return NS_ERROR_FAILURE; + } + ++p; + } + return NS_OK; +} + } // namespace net } // namespace mozilla // vim: ts=2 sw=2 diff -r dd2f2db1f87a netwerk/protocol/http/nsHttpDigestAuth.h --- a/netwerk/protocol/http/nsHttpDigestAuth.h Thu Mar 16 04:19:00 2017 +0100 +++ b/netwerk/protocol/http/nsHttpDigestAuth.h Thu Mar 16 21:42:51 2017 +0600 @@ -14,82 +14,103 @@ class nsICryptoHash; namespace mozilla { namespace net { #define ALGO_SPECIFIED 0x01 #define ALGO_MD5 0x02 #define ALGO_MD5_SESS 0x04 +#define ALGO_SHA256 0x08 +#define ALGO_SHA256_SESS 0x10 #define QOP_AUTH 0x01 #define QOP_AUTH_INT 0x02 -#define DIGEST_LENGTH 16 -#define EXPANDED_DIGEST_LENGTH 32 +#define DIGEST_MD5_LENGTH 16 +#define DIGEST_SHA256_LENGTH 32 +#define EXPANDED_DIGEST_LENGTH 64 #define NONCE_COUNT_LENGTH 8 +#define USERHASH_NOT_SET 0x00 +#define USERHASH_FALSE 0x01 +#define USERHASH_TRUE 0x02 +#define CHARSET_NOT_SET 0x00 +#define CHARSET_SPECIFIED 0x01 +#define CHARSET_UTF8 0x02 //----------------------------------------------------------------------------- // nsHttpDigestAuth //----------------------------------------------------------------------------- class nsHttpDigestAuth final : public nsIHttpAuthenticator { public: NS_DECL_ISUPPORTS NS_DECL_NSIHTTPAUTHENTICATOR nsHttpDigestAuth(); protected: ~nsHttpDigestAuth(); - MOZ_MUST_USE nsresult ExpandToHex(const char * digest, char * result); + MOZ_MUST_USE nsresult ExpandToHex(const char * digest, + int16_t algorithm, + char * result); MOZ_MUST_USE nsresult CalculateResponse(const char * ha1_digest, const char * ha2_digest, const nsAFlatCString & nonce, uint16_t qop, + uint16_t algorithm, const char * nonce_count, const nsAFlatCString & cnonce, char * result); MOZ_MUST_USE nsresult CalculateHA1(const nsAFlatCString & username, const nsAFlatCString & password, const nsAFlatCString & realm, uint16_t algorithm, const nsAFlatCString & nonce, const nsAFlatCString & cnonce, char * result); MOZ_MUST_USE nsresult CalculateHA2(const nsAFlatCString & http_method, const nsAFlatCString & http_uri_path, uint16_t qop, + uint16_t algorithm, const char * body_digest, char * result); MOZ_MUST_USE nsresult ParseChallenge(const char * challenge, nsACString & realm, nsACString & domain, nsACString & nonce, nsACString & opaque, bool * stale, uint16_t * algorithm, - uint16_t * qop); + uint16_t * qop, + uint16_t * charset, + uint16_t * userhash); // result is in mHashBuf - MOZ_MUST_USE nsresult MD5Hash(const char *buf, uint32_t len); + MOZ_MUST_USE nsresult DoHash(const char *buf, + uint32_t len, + uint16_t algorithm); MOZ_MUST_USE nsresult GetMethodAndPath(nsIHttpAuthenticableChannel *, bool, nsCString &, nsCString &); // append the quoted version of value to aHeaderLine MOZ_MUST_USE nsresult AppendQuotedString(const nsACString & value, nsACString & aHeaderLine); + MOZ_MUST_USE nsresult CheckControls(const char16_t *username); + int16_t DigestLength(int16_t algorithm); + int16_t ExpandedDigestLength(int16_t algorithm); + protected: nsCOMPtr mVerifier; - char mHashBuf[DIGEST_LENGTH]; + char mHashBuf[DIGEST_SHA256_LENGTH]; }; } // namespace net } // namespace mozilla #endif // nsHttpDigestAuth_h__