netwerk/protocol/http/nsHttpDigestAuth.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial