1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/netwerk/protocol/http/nsHttpDigestAuth.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,701 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +// HttpLog.h should generally be included first 1.11 +#include "HttpLog.h" 1.12 + 1.13 +#include "nsHttp.h" 1.14 +#include "nsHttpDigestAuth.h" 1.15 +#include "nsIHttpAuthenticableChannel.h" 1.16 +#include "nsISupportsPrimitives.h" 1.17 +#include "nsIURI.h" 1.18 +#include "nsString.h" 1.19 +#include "nsEscape.h" 1.20 +#include "nsNetCID.h" 1.21 +#include "prprf.h" 1.22 +#include "nsCRT.h" 1.23 +#include "nsICryptoHash.h" 1.24 + 1.25 +namespace mozilla { 1.26 +namespace net { 1.27 + 1.28 +//----------------------------------------------------------------------------- 1.29 +// nsHttpDigestAuth <public> 1.30 +//----------------------------------------------------------------------------- 1.31 + 1.32 +nsHttpDigestAuth::nsHttpDigestAuth() 1.33 +{} 1.34 + 1.35 +nsHttpDigestAuth::~nsHttpDigestAuth() 1.36 +{} 1.37 + 1.38 +//----------------------------------------------------------------------------- 1.39 +// nsHttpDigestAuth::nsISupports 1.40 +//----------------------------------------------------------------------------- 1.41 + 1.42 +NS_IMPL_ISUPPORTS(nsHttpDigestAuth, nsIHttpAuthenticator) 1.43 + 1.44 +//----------------------------------------------------------------------------- 1.45 +// nsHttpDigestAuth <protected> 1.46 +//----------------------------------------------------------------------------- 1.47 + 1.48 +nsresult 1.49 +nsHttpDigestAuth::MD5Hash(const char *buf, uint32_t len) 1.50 +{ 1.51 + nsresult rv; 1.52 + 1.53 + // Cache a reference to the nsICryptoHash instance since we'll be calling 1.54 + // this function frequently. 1.55 + if (!mVerifier) { 1.56 + mVerifier = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); 1.57 + if (NS_FAILED(rv)) { 1.58 + LOG(("nsHttpDigestAuth: no crypto hash!\n")); 1.59 + return rv; 1.60 + } 1.61 + } 1.62 + 1.63 + rv = mVerifier->Init(nsICryptoHash::MD5); 1.64 + if (NS_FAILED(rv)) return rv; 1.65 + 1.66 + rv = mVerifier->Update((unsigned char*)buf, len); 1.67 + if (NS_FAILED(rv)) return rv; 1.68 + 1.69 + nsAutoCString hashString; 1.70 + rv = mVerifier->Finish(false, hashString); 1.71 + if (NS_FAILED(rv)) return rv; 1.72 + 1.73 + NS_ENSURE_STATE(hashString.Length() == sizeof(mHashBuf)); 1.74 + memcpy(mHashBuf, hashString.get(), hashString.Length()); 1.75 + 1.76 + return rv; 1.77 +} 1.78 + 1.79 +nsresult 1.80 +nsHttpDigestAuth::GetMethodAndPath(nsIHttpAuthenticableChannel *authChannel, 1.81 + bool isProxyAuth, 1.82 + nsCString &httpMethod, 1.83 + nsCString &path) 1.84 +{ 1.85 + nsresult rv, rv2; 1.86 + nsCOMPtr<nsIURI> uri; 1.87 + rv = authChannel->GetURI(getter_AddRefs(uri)); 1.88 + if (NS_SUCCEEDED(rv)) { 1.89 + bool proxyMethodIsConnect; 1.90 + rv = authChannel->GetProxyMethodIsConnect(&proxyMethodIsConnect); 1.91 + if (NS_SUCCEEDED(rv)) { 1.92 + if (proxyMethodIsConnect && isProxyAuth) { 1.93 + httpMethod.AssignLiteral("CONNECT"); 1.94 + // 1.95 + // generate hostname:port string. (unfortunately uri->GetHostPort 1.96 + // leaves out the port if it matches the default value, so we can't 1.97 + // just call it.) 1.98 + // 1.99 + int32_t port; 1.100 + rv = uri->GetAsciiHost(path); 1.101 + rv2 = uri->GetPort(&port); 1.102 + if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2)) { 1.103 + path.Append(':'); 1.104 + path.AppendInt(port < 0 ? NS_HTTPS_DEFAULT_PORT : port); 1.105 + } 1.106 + } 1.107 + else { 1.108 + rv = authChannel->GetRequestMethod(httpMethod); 1.109 + rv2 = uri->GetPath(path); 1.110 + if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv2)) { 1.111 + // 1.112 + // strip any fragment identifier from the URL path. 1.113 + // 1.114 + int32_t ref = path.RFindChar('#'); 1.115 + if (ref != kNotFound) 1.116 + path.Truncate(ref); 1.117 + // 1.118 + // make sure we escape any UTF-8 characters in the URI path. the 1.119 + // digest auth uri attribute needs to match the request-URI. 1.120 + // 1.121 + // XXX we should really ask the HTTP channel for this string 1.122 + // instead of regenerating it here. 1.123 + // 1.124 + nsAutoCString buf; 1.125 + path = NS_EscapeURL(path, esc_OnlyNonASCII, buf); 1.126 + } 1.127 + } 1.128 + } 1.129 + } 1.130 + return rv; 1.131 +} 1.132 + 1.133 +//----------------------------------------------------------------------------- 1.134 +// nsHttpDigestAuth::nsIHttpAuthenticator 1.135 +//----------------------------------------------------------------------------- 1.136 + 1.137 +NS_IMETHODIMP 1.138 +nsHttpDigestAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel, 1.139 + const char *challenge, 1.140 + bool isProxyAuth, 1.141 + nsISupports **sessionState, 1.142 + nsISupports **continuationState, 1.143 + bool *result) 1.144 +{ 1.145 + nsAutoCString realm, domain, nonce, opaque; 1.146 + bool stale; 1.147 + uint16_t algorithm, qop; 1.148 + 1.149 + nsresult rv = ParseChallenge(challenge, realm, domain, nonce, opaque, 1.150 + &stale, &algorithm, &qop); 1.151 + if (NS_FAILED(rv)) return rv; 1.152 + 1.153 + // if the challenge has the "stale" flag set, then the user identity is not 1.154 + // necessarily invalid. by returning FALSE here we can suppress username 1.155 + // and password prompting that usually accompanies a 401/407 challenge. 1.156 + *result = !stale; 1.157 + 1.158 + // clear any existing nonce_count since we have a new challenge. 1.159 + NS_IF_RELEASE(*sessionState); 1.160 + return NS_OK; 1.161 +} 1.162 + 1.163 +NS_IMETHODIMP 1.164 +nsHttpDigestAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel, 1.165 + const char *challenge, 1.166 + bool isProxyAuth, 1.167 + const char16_t *userdomain, 1.168 + const char16_t *username, 1.169 + const char16_t *password, 1.170 + nsISupports **sessionState, 1.171 + nsISupports **continuationState, 1.172 + uint32_t *aFlags, 1.173 + char **creds) 1.174 + 1.175 +{ 1.176 + LOG(("nsHttpDigestAuth::GenerateCredentials [challenge=%s]\n", challenge)); 1.177 + 1.178 + NS_ENSURE_ARG_POINTER(creds); 1.179 + 1.180 + *aFlags = 0; 1.181 + 1.182 + bool isDigestAuth = !PL_strncasecmp(challenge, "digest ", 7); 1.183 + NS_ENSURE_TRUE(isDigestAuth, NS_ERROR_UNEXPECTED); 1.184 + 1.185 + // IIS implementation requires extra quotes 1.186 + bool requireExtraQuotes = false; 1.187 + { 1.188 + nsAutoCString serverVal; 1.189 + authChannel->GetServerResponseHeader(serverVal); 1.190 + if (!serverVal.IsEmpty()) { 1.191 + requireExtraQuotes = !PL_strncasecmp(serverVal.get(), "Microsoft-IIS", 13); 1.192 + } 1.193 + } 1.194 + 1.195 + nsresult rv; 1.196 + nsAutoCString httpMethod; 1.197 + nsAutoCString path; 1.198 + rv = GetMethodAndPath(authChannel, isProxyAuth, httpMethod, path); 1.199 + if (NS_FAILED(rv)) return rv; 1.200 + 1.201 + nsAutoCString realm, domain, nonce, opaque; 1.202 + bool stale; 1.203 + uint16_t algorithm, qop; 1.204 + 1.205 + rv = ParseChallenge(challenge, realm, domain, nonce, opaque, 1.206 + &stale, &algorithm, &qop); 1.207 + if (NS_FAILED(rv)) { 1.208 + LOG(("nsHttpDigestAuth::GenerateCredentials [ParseChallenge failed rv=%x]\n", rv)); 1.209 + return rv; 1.210 + } 1.211 + 1.212 + char ha1_digest[EXPANDED_DIGEST_LENGTH+1]; 1.213 + char ha2_digest[EXPANDED_DIGEST_LENGTH+1]; 1.214 + char response_digest[EXPANDED_DIGEST_LENGTH+1]; 1.215 + char upload_data_digest[EXPANDED_DIGEST_LENGTH+1]; 1.216 + 1.217 + if (qop & QOP_AUTH_INT) { 1.218 + // we do not support auth-int "quality of protection" currently 1.219 + qop &= ~QOP_AUTH_INT; 1.220 + 1.221 + NS_WARNING("no support for Digest authentication with data integrity quality of protection"); 1.222 + 1.223 + /* TODO: to support auth-int, we need to get an MD5 digest of 1.224 + * TODO: the data uploaded with this request. 1.225 + * TODO: however, i am not sure how to read in the file in without 1.226 + * TODO: disturbing the channel''s use of it. do i need to copy it 1.227 + * TODO: somehow? 1.228 + */ 1.229 +#if 0 1.230 + if (http_channel != nullptr) 1.231 + { 1.232 + nsIInputStream * upload; 1.233 + nsCOMPtr<nsIUploadChannel> uc = do_QueryInterface(http_channel); 1.234 + NS_ENSURE_TRUE(uc, NS_ERROR_UNEXPECTED); 1.235 + uc->GetUploadStream(&upload); 1.236 + if (upload) { 1.237 + char * upload_buffer; 1.238 + int upload_buffer_length = 0; 1.239 + //TODO: read input stream into buffer 1.240 + const char * digest = (const char*) 1.241 + nsNetwerkMD5Digest(upload_buffer, upload_buffer_length); 1.242 + ExpandToHex(digest, upload_data_digest); 1.243 + NS_RELEASE(upload); 1.244 + } 1.245 + } 1.246 +#endif 1.247 + } 1.248 + 1.249 + if (!(algorithm & ALGO_MD5 || algorithm & ALGO_MD5_SESS)) { 1.250 + // they asked only for algorithms that we do not support 1.251 + NS_WARNING("unsupported algorithm requested by Digest authentication"); 1.252 + return NS_ERROR_NOT_IMPLEMENTED; 1.253 + } 1.254 + 1.255 + // 1.256 + // the following are for increasing security. see RFC 2617 for more 1.257 + // information. 1.258 + // 1.259 + // nonce_count allows the server to keep track of auth challenges (to help 1.260 + // prevent spoofing). we increase this count every time. 1.261 + // 1.262 + char nonce_count[NONCE_COUNT_LENGTH+1] = "00000001"; // in hex 1.263 + if (*sessionState) { 1.264 + nsCOMPtr<nsISupportsPRUint32> v(do_QueryInterface(*sessionState)); 1.265 + if (v) { 1.266 + uint32_t nc; 1.267 + v->GetData(&nc); 1.268 + PR_snprintf(nonce_count, sizeof(nonce_count), "%08x", ++nc); 1.269 + v->SetData(nc); 1.270 + } 1.271 + } 1.272 + else { 1.273 + nsCOMPtr<nsISupportsPRUint32> v( 1.274 + do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID)); 1.275 + if (v) { 1.276 + v->SetData(1); 1.277 + NS_ADDREF(*sessionState = v); 1.278 + } 1.279 + } 1.280 + LOG((" nonce_count=%s\n", nonce_count)); 1.281 + 1.282 + // 1.283 + // this lets the client verify the server response (via a server 1.284 + // returned Authentication-Info header). also used for session info. 1.285 + // 1.286 + nsAutoCString cnonce; 1.287 + static const char hexChar[] = "0123456789abcdef"; 1.288 + for (int i=0; i<16; ++i) { 1.289 + cnonce.Append(hexChar[(int)(15.0 * rand()/(RAND_MAX + 1.0))]); 1.290 + } 1.291 + LOG((" cnonce=%s\n", cnonce.get())); 1.292 + 1.293 + // 1.294 + // calculate credentials 1.295 + // 1.296 + 1.297 + NS_ConvertUTF16toUTF8 cUser(username), cPass(password); 1.298 + rv = CalculateHA1(cUser, cPass, realm, algorithm, nonce, cnonce, ha1_digest); 1.299 + if (NS_FAILED(rv)) return rv; 1.300 + 1.301 + rv = CalculateHA2(httpMethod, path, qop, upload_data_digest, ha2_digest); 1.302 + if (NS_FAILED(rv)) return rv; 1.303 + 1.304 + rv = CalculateResponse(ha1_digest, ha2_digest, nonce, qop, nonce_count, 1.305 + cnonce, response_digest); 1.306 + if (NS_FAILED(rv)) return rv; 1.307 + 1.308 + // 1.309 + // Values that need to match the quoted-string production from RFC 2616: 1.310 + // 1.311 + // username 1.312 + // realm 1.313 + // nonce 1.314 + // opaque 1.315 + // cnonce 1.316 + // 1.317 + 1.318 + nsAutoCString authString; 1.319 + 1.320 + authString.AssignLiteral("Digest username="); 1.321 + rv = AppendQuotedString(cUser, authString); 1.322 + NS_ENSURE_SUCCESS(rv, rv); 1.323 + 1.324 + authString.AppendLiteral(", realm="); 1.325 + rv = AppendQuotedString(realm, authString); 1.326 + NS_ENSURE_SUCCESS(rv, rv); 1.327 + 1.328 + authString.AppendLiteral(", nonce="); 1.329 + rv = AppendQuotedString(nonce, authString); 1.330 + NS_ENSURE_SUCCESS(rv, rv); 1.331 + 1.332 + authString.AppendLiteral(", uri=\""); 1.333 + authString += path; 1.334 + if (algorithm & ALGO_SPECIFIED) { 1.335 + authString.AppendLiteral("\", algorithm="); 1.336 + if (algorithm & ALGO_MD5_SESS) 1.337 + authString.AppendLiteral("MD5-sess"); 1.338 + else 1.339 + authString.AppendLiteral("MD5"); 1.340 + } else { 1.341 + authString += '\"'; 1.342 + } 1.343 + authString.AppendLiteral(", response=\""); 1.344 + authString += response_digest; 1.345 + authString += '\"'; 1.346 + 1.347 + if (!opaque.IsEmpty()) { 1.348 + authString.AppendLiteral(", opaque="); 1.349 + rv = AppendQuotedString(opaque, authString); 1.350 + NS_ENSURE_SUCCESS(rv, rv); 1.351 + } 1.352 + 1.353 + if (qop) { 1.354 + authString.AppendLiteral(", qop="); 1.355 + if (requireExtraQuotes) 1.356 + authString += '\"'; 1.357 + authString.AppendLiteral("auth"); 1.358 + if (qop & QOP_AUTH_INT) 1.359 + authString.AppendLiteral("-int"); 1.360 + if (requireExtraQuotes) 1.361 + authString += '\"'; 1.362 + authString.AppendLiteral(", nc="); 1.363 + authString += nonce_count; 1.364 + 1.365 + authString.AppendLiteral(", cnonce="); 1.366 + rv = AppendQuotedString(cnonce, authString); 1.367 + NS_ENSURE_SUCCESS(rv, rv); 1.368 + } 1.369 + 1.370 + 1.371 + *creds = ToNewCString(authString); 1.372 + return NS_OK; 1.373 +} 1.374 + 1.375 +NS_IMETHODIMP 1.376 +nsHttpDigestAuth::GetAuthFlags(uint32_t *flags) 1.377 +{ 1.378 + *flags = REQUEST_BASED | REUSABLE_CHALLENGE | IDENTITY_ENCRYPTED; 1.379 + // 1.380 + // NOTE: digest auth credentials must be uniquely computed for each request, 1.381 + // so we do not set the REUSABLE_CREDENTIALS flag. 1.382 + // 1.383 + return NS_OK; 1.384 +} 1.385 + 1.386 +nsresult 1.387 +nsHttpDigestAuth::CalculateResponse(const char * ha1_digest, 1.388 + const char * ha2_digest, 1.389 + const nsAFlatCString & nonce, 1.390 + uint16_t qop, 1.391 + const char * nonce_count, 1.392 + const nsAFlatCString & cnonce, 1.393 + char * result) 1.394 +{ 1.395 + uint32_t len = 2*EXPANDED_DIGEST_LENGTH + nonce.Length() + 2; 1.396 + 1.397 + if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { 1.398 + len += cnonce.Length() + NONCE_COUNT_LENGTH + 3; 1.399 + if (qop & QOP_AUTH_INT) 1.400 + len += 8; // length of "auth-int" 1.401 + else 1.402 + len += 4; // length of "auth" 1.403 + } 1.404 + 1.405 + nsAutoCString contents; 1.406 + contents.SetCapacity(len); 1.407 + 1.408 + contents.Assign(ha1_digest, EXPANDED_DIGEST_LENGTH); 1.409 + contents.Append(':'); 1.410 + contents.Append(nonce); 1.411 + contents.Append(':'); 1.412 + 1.413 + if (qop & QOP_AUTH || qop & QOP_AUTH_INT) { 1.414 + contents.Append(nonce_count, NONCE_COUNT_LENGTH); 1.415 + contents.Append(':'); 1.416 + contents.Append(cnonce); 1.417 + contents.Append(':'); 1.418 + if (qop & QOP_AUTH_INT) 1.419 + contents.AppendLiteral("auth-int:"); 1.420 + else 1.421 + contents.AppendLiteral("auth:"); 1.422 + } 1.423 + 1.424 + contents.Append(ha2_digest, EXPANDED_DIGEST_LENGTH); 1.425 + 1.426 + nsresult rv = MD5Hash(contents.get(), contents.Length()); 1.427 + if (NS_SUCCEEDED(rv)) 1.428 + rv = ExpandToHex(mHashBuf, result); 1.429 + return rv; 1.430 +} 1.431 + 1.432 +nsresult 1.433 +nsHttpDigestAuth::ExpandToHex(const char * digest, char * result) 1.434 +{ 1.435 + int16_t index, value; 1.436 + 1.437 + for (index = 0; index < DIGEST_LENGTH; index++) { 1.438 + value = (digest[index] >> 4) & 0xf; 1.439 + if (value < 10) 1.440 + result[index*2] = value + '0'; 1.441 + else 1.442 + result[index*2] = value - 10 + 'a'; 1.443 + 1.444 + value = digest[index] & 0xf; 1.445 + if (value < 10) 1.446 + result[(index*2)+1] = value + '0'; 1.447 + else 1.448 + result[(index*2)+1] = value - 10 + 'a'; 1.449 + } 1.450 + 1.451 + result[EXPANDED_DIGEST_LENGTH] = 0; 1.452 + return NS_OK; 1.453 +} 1.454 + 1.455 +nsresult 1.456 +nsHttpDigestAuth::CalculateHA1(const nsAFlatCString & username, 1.457 + const nsAFlatCString & password, 1.458 + const nsAFlatCString & realm, 1.459 + uint16_t algorithm, 1.460 + const nsAFlatCString & nonce, 1.461 + const nsAFlatCString & cnonce, 1.462 + char * result) 1.463 +{ 1.464 + int16_t len = username.Length() + password.Length() + realm.Length() + 2; 1.465 + if (algorithm & ALGO_MD5_SESS) { 1.466 + int16_t exlen = EXPANDED_DIGEST_LENGTH + nonce.Length() + cnonce.Length() + 2; 1.467 + if (exlen > len) 1.468 + len = exlen; 1.469 + } 1.470 + 1.471 + nsAutoCString contents; 1.472 + contents.SetCapacity(len + 1); 1.473 + 1.474 + contents.Assign(username); 1.475 + contents.Append(':'); 1.476 + contents.Append(realm); 1.477 + contents.Append(':'); 1.478 + contents.Append(password); 1.479 + 1.480 + nsresult rv; 1.481 + rv = MD5Hash(contents.get(), contents.Length()); 1.482 + if (NS_FAILED(rv)) 1.483 + return rv; 1.484 + 1.485 + if (algorithm & ALGO_MD5_SESS) { 1.486 + char part1[EXPANDED_DIGEST_LENGTH+1]; 1.487 + ExpandToHex(mHashBuf, part1); 1.488 + 1.489 + contents.Assign(part1, EXPANDED_DIGEST_LENGTH); 1.490 + contents.Append(':'); 1.491 + contents.Append(nonce); 1.492 + contents.Append(':'); 1.493 + contents.Append(cnonce); 1.494 + 1.495 + rv = MD5Hash(contents.get(), contents.Length()); 1.496 + if (NS_FAILED(rv)) 1.497 + return rv; 1.498 + } 1.499 + 1.500 + return ExpandToHex(mHashBuf, result); 1.501 +} 1.502 + 1.503 +nsresult 1.504 +nsHttpDigestAuth::CalculateHA2(const nsAFlatCString & method, 1.505 + const nsAFlatCString & path, 1.506 + uint16_t qop, 1.507 + const char * bodyDigest, 1.508 + char * result) 1.509 +{ 1.510 + uint16_t methodLen = method.Length(); 1.511 + uint32_t pathLen = path.Length(); 1.512 + uint32_t len = methodLen + pathLen + 1; 1.513 + 1.514 + if (qop & QOP_AUTH_INT) { 1.515 + len += EXPANDED_DIGEST_LENGTH + 1; 1.516 + } 1.517 + 1.518 + nsAutoCString contents; 1.519 + contents.SetCapacity(len); 1.520 + 1.521 + contents.Assign(method); 1.522 + contents.Append(':'); 1.523 + contents.Append(path); 1.524 + 1.525 + if (qop & QOP_AUTH_INT) { 1.526 + contents.Append(':'); 1.527 + contents.Append(bodyDigest, EXPANDED_DIGEST_LENGTH); 1.528 + } 1.529 + 1.530 + nsresult rv = MD5Hash(contents.get(), contents.Length()); 1.531 + if (NS_SUCCEEDED(rv)) 1.532 + rv = ExpandToHex(mHashBuf, result); 1.533 + return rv; 1.534 +} 1.535 + 1.536 +nsresult 1.537 +nsHttpDigestAuth::ParseChallenge(const char * challenge, 1.538 + nsACString & realm, 1.539 + nsACString & domain, 1.540 + nsACString & nonce, 1.541 + nsACString & opaque, 1.542 + bool * stale, 1.543 + uint16_t * algorithm, 1.544 + uint16_t * qop) 1.545 +{ 1.546 + // put an absurd, but maximum, length cap on the challenge so 1.547 + // that calculations are 32 bit safe 1.548 + if (strlen(challenge) > 16000000) { 1.549 + return NS_ERROR_INVALID_ARG; 1.550 + } 1.551 + 1.552 + const char *p = challenge + 7; // first 7 characters are "Digest " 1.553 + 1.554 + *stale = false; 1.555 + *algorithm = ALGO_MD5; // default is MD5 1.556 + *qop = 0; 1.557 + 1.558 + for (;;) { 1.559 + while (*p && (*p == ',' || nsCRT::IsAsciiSpace(*p))) 1.560 + ++p; 1.561 + if (!*p) 1.562 + break; 1.563 + 1.564 + // name 1.565 + int32_t nameStart = (p - challenge); 1.566 + while (*p && !nsCRT::IsAsciiSpace(*p) && *p != '=') 1.567 + ++p; 1.568 + if (!*p) 1.569 + return NS_ERROR_INVALID_ARG; 1.570 + int32_t nameLength = (p - challenge) - nameStart; 1.571 + 1.572 + while (*p && (nsCRT::IsAsciiSpace(*p) || *p == '=')) 1.573 + ++p; 1.574 + if (!*p) 1.575 + return NS_ERROR_INVALID_ARG; 1.576 + 1.577 + bool quoted = false; 1.578 + if (*p == '"') { 1.579 + ++p; 1.580 + quoted = true; 1.581 + } 1.582 + 1.583 + // value 1.584 + int32_t valueStart = (p - challenge); 1.585 + int32_t valueLength = 0; 1.586 + if (quoted) { 1.587 + while (*p && *p != '"') 1.588 + ++p; 1.589 + if (*p != '"') 1.590 + return NS_ERROR_INVALID_ARG; 1.591 + valueLength = (p - challenge) - valueStart; 1.592 + ++p; 1.593 + } else { 1.594 + while (*p && !nsCRT::IsAsciiSpace(*p) && *p != ',') 1.595 + ++p; 1.596 + valueLength = (p - challenge) - valueStart; 1.597 + } 1.598 + 1.599 + // extract information 1.600 + if (nameLength == 5 && 1.601 + nsCRT::strncasecmp(challenge+nameStart, "realm", 5) == 0) 1.602 + { 1.603 + realm.Assign(challenge+valueStart, valueLength); 1.604 + } 1.605 + else if (nameLength == 6 && 1.606 + nsCRT::strncasecmp(challenge+nameStart, "domain", 6) == 0) 1.607 + { 1.608 + domain.Assign(challenge+valueStart, valueLength); 1.609 + } 1.610 + else if (nameLength == 5 && 1.611 + nsCRT::strncasecmp(challenge+nameStart, "nonce", 5) == 0) 1.612 + { 1.613 + nonce.Assign(challenge+valueStart, valueLength); 1.614 + } 1.615 + else if (nameLength == 6 && 1.616 + nsCRT::strncasecmp(challenge+nameStart, "opaque", 6) == 0) 1.617 + { 1.618 + opaque.Assign(challenge+valueStart, valueLength); 1.619 + } 1.620 + else if (nameLength == 5 && 1.621 + nsCRT::strncasecmp(challenge+nameStart, "stale", 5) == 0) 1.622 + { 1.623 + if (nsCRT::strncasecmp(challenge+valueStart, "true", 4) == 0) 1.624 + *stale = true; 1.625 + else 1.626 + *stale = false; 1.627 + } 1.628 + else if (nameLength == 9 && 1.629 + nsCRT::strncasecmp(challenge+nameStart, "algorithm", 9) == 0) 1.630 + { 1.631 + // we want to clear the default, so we use = not |= here 1.632 + *algorithm = ALGO_SPECIFIED; 1.633 + if (valueLength == 3 && 1.634 + nsCRT::strncasecmp(challenge+valueStart, "MD5", 3) == 0) 1.635 + *algorithm |= ALGO_MD5; 1.636 + else if (valueLength == 8 && 1.637 + nsCRT::strncasecmp(challenge+valueStart, "MD5-sess", 8) == 0) 1.638 + *algorithm |= ALGO_MD5_SESS; 1.639 + } 1.640 + else if (nameLength == 3 && 1.641 + nsCRT::strncasecmp(challenge+nameStart, "qop", 3) == 0) 1.642 + { 1.643 + int32_t ipos = valueStart; 1.644 + while (ipos < valueStart+valueLength) { 1.645 + while (ipos < valueStart+valueLength && 1.646 + (nsCRT::IsAsciiSpace(challenge[ipos]) || 1.647 + challenge[ipos] == ',')) 1.648 + ipos++; 1.649 + int32_t algostart = ipos; 1.650 + while (ipos < valueStart+valueLength && 1.651 + !nsCRT::IsAsciiSpace(challenge[ipos]) && 1.652 + challenge[ipos] != ',') 1.653 + ipos++; 1.654 + if ((ipos - algostart) == 4 && 1.655 + nsCRT::strncasecmp(challenge+algostart, "auth", 4) == 0) 1.656 + *qop |= QOP_AUTH; 1.657 + else if ((ipos - algostart) == 8 && 1.658 + nsCRT::strncasecmp(challenge+algostart, "auth-int", 8) == 0) 1.659 + *qop |= QOP_AUTH_INT; 1.660 + } 1.661 + } 1.662 + } 1.663 + return NS_OK; 1.664 +} 1.665 + 1.666 +nsresult 1.667 +nsHttpDigestAuth::AppendQuotedString(const nsACString & value, 1.668 + nsACString & aHeaderLine) 1.669 +{ 1.670 + nsAutoCString quoted; 1.671 + nsACString::const_iterator s, e; 1.672 + value.BeginReading(s); 1.673 + value.EndReading(e); 1.674 + 1.675 + // 1.676 + // Encode string according to RFC 2616 quoted-string production 1.677 + // 1.678 + quoted.Append('"'); 1.679 + for ( ; s != e; ++s) { 1.680 + // 1.681 + // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> 1.682 + // 1.683 + if (*s <= 31 || *s == 127) { 1.684 + return NS_ERROR_FAILURE; 1.685 + } 1.686 + 1.687 + // Escape two syntactically significant characters 1.688 + if (*s == '"' || *s == '\\') { 1.689 + quoted.Append('\\'); 1.690 + } 1.691 + 1.692 + quoted.Append(*s); 1.693 + } 1.694 + // FIXME: bug 41489 1.695 + // We should RFC2047-encode non-Latin-1 values according to spec 1.696 + quoted.Append('"'); 1.697 + aHeaderLine.Append(quoted); 1.698 + return NS_OK; 1.699 +} 1.700 + 1.701 +} // namespace mozilla::net 1.702 +} // namespace mozilla 1.703 + 1.704 +// vim: ts=2 sw=2