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