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