Coverage Report

Created: 2018-09-25 14:53

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