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