chiark / gitweb /
Import curl_7.56.1.orig.tar.gz
[curl.git] / lib / vauth / digest.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * RFC2831 DIGEST-MD5 authentication
22  *
23  ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
28
29 #include <curl/curl.h>
30
31 #include "vauth/vauth.h"
32 #include "vauth/digest.h"
33 #include "urldata.h"
34 #include "curl_base64.h"
35 #include "curl_hmac.h"
36 #include "curl_md5.h"
37 #include "vtls/vtls.h"
38 #include "warnless.h"
39 #include "strtok.h"
40 #include "strcase.h"
41 #include "non-ascii.h" /* included for Curl_convert_... prototypes */
42 #include "curl_printf.h"
43 #include "rand.h"
44
45 /* The last #include files should be: */
46 #include "curl_memory.h"
47 #include "memdebug.h"
48
49 #if !defined(USE_WINDOWS_SSPI)
50 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
51 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
52 #define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
53
54 #define DIGEST_QOP_VALUE_STRING_AUTH      "auth"
55 #define DIGEST_QOP_VALUE_STRING_AUTH_INT  "auth-int"
56 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
57
58 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines.
59    It converts digest text to ASCII so the MD5 will be correct for
60    what ultimately goes over the network.
61 */
62 #define CURL_OUTPUT_DIGEST_CONV(a, b) \
63   result = Curl_convert_to_network(a, (char *)b, strlen((const char *)b)); \
64   if(result) { \
65     free(b); \
66     return result; \
67   }
68 #endif /* !USE_WINDOWS_SSPI */
69
70 bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
71                                const char **endptr)
72 {
73   int c;
74   bool starts_with_quote = FALSE;
75   bool escape = FALSE;
76
77   for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
78     *value++ = *str++;
79   *value = 0;
80
81   if('=' != *str++)
82     /* eek, no match */
83     return FALSE;
84
85   if('\"' == *str) {
86     /* This starts with a quote so it must end with one as well! */
87     str++;
88     starts_with_quote = TRUE;
89   }
90
91   for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
92     switch(*str) {
93     case '\\':
94       if(!escape) {
95         /* possibly the start of an escaped quote */
96         escape = TRUE;
97         *content++ = '\\'; /* Even though this is an escape character, we still
98                               store it as-is in the target buffer */
99         continue;
100       }
101       break;
102
103     case ',':
104       if(!starts_with_quote) {
105         /* This signals the end of the content if we didn't get a starting
106            quote and then we do "sloppy" parsing */
107         c = 0; /* the end */
108         continue;
109       }
110       break;
111
112     case '\r':
113     case '\n':
114       /* end of string */
115       c = 0;
116       continue;
117
118     case '\"':
119       if(!escape && starts_with_quote) {
120         /* end of string */
121         c = 0;
122         continue;
123       }
124       break;
125     }
126
127     escape = FALSE;
128     *content++ = *str;
129   }
130
131   *content = 0;
132   *endptr = str;
133
134   return TRUE;
135 }
136
137 #if !defined(USE_WINDOWS_SSPI)
138 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
139 static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
140                                      unsigned char *dest) /* 33 bytes */
141 {
142   int i;
143   for(i = 0; i < 16; i++)
144     snprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
145 }
146
147 /* Perform quoted-string escaping as described in RFC2616 and its errata */
148 static char *auth_digest_string_quoted(const char *source)
149 {
150   char *dest, *d;
151   const char *s = source;
152   size_t n = 1; /* null terminator */
153
154   /* Calculate size needed */
155   while(*s) {
156     ++n;
157     if(*s == '"' || *s == '\\') {
158       ++n;
159     }
160     ++s;
161   }
162
163   dest = malloc(n);
164   if(dest) {
165     s = source;
166     d = dest;
167     while(*s) {
168       if(*s == '"' || *s == '\\') {
169         *d++ = '\\';
170       }
171       *d++ = *s++;
172     }
173     *d = 0;
174   }
175
176   return dest;
177 }
178
179 /* Retrieves the value for a corresponding key from the challenge string
180  * returns TRUE if the key could be found, FALSE if it does not exists
181  */
182 static bool auth_digest_get_key_value(const char *chlg,
183                                       const char *key,
184                                       char *value,
185                                       size_t max_val_len,
186                                       char end_char)
187 {
188   char *find_pos;
189   size_t i;
190
191   find_pos = strstr(chlg, key);
192   if(!find_pos)
193     return FALSE;
194
195   find_pos += strlen(key);
196
197   for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i)
198     value[i] = *find_pos++;
199   value[i] = '\0';
200
201   return TRUE;
202 }
203
204 static CURLcode auth_digest_get_qop_values(const char *options, int *value)
205 {
206   char *tmp;
207   char *token;
208   char *tok_buf = NULL;
209
210   /* Initialise the output */
211   *value = 0;
212
213   /* Tokenise the list of qop values. Use a temporary clone of the buffer since
214      strtok_r() ruins it. */
215   tmp = strdup(options);
216   if(!tmp)
217     return CURLE_OUT_OF_MEMORY;
218
219   token = strtok_r(tmp, ",", &tok_buf);
220   while(token != NULL) {
221     if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
222       *value |= DIGEST_QOP_VALUE_AUTH;
223     else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
224       *value |= DIGEST_QOP_VALUE_AUTH_INT;
225     else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
226       *value |= DIGEST_QOP_VALUE_AUTH_CONF;
227
228     token = strtok_r(NULL, ",", &tok_buf);
229   }
230
231   free(tmp);
232
233   return CURLE_OK;
234 }
235
236 /*
237  * auth_decode_digest_md5_message()
238  *
239  * This is used internally to decode an already encoded DIGEST-MD5 challenge
240  * message into the separate attributes.
241  *
242  * Parameters:
243  *
244  * chlg64  [in]     - The base64 encoded challenge message.
245  * nonce   [in/out] - The buffer where the nonce will be stored.
246  * nlen    [in]     - The length of the nonce buffer.
247  * realm   [in/out] - The buffer where the realm will be stored.
248  * rlen    [in]     - The length of the realm buffer.
249  * alg     [in/out] - The buffer where the algorithm will be stored.
250  * alen    [in]     - The length of the algorithm buffer.
251  * qop     [in/out] - The buffer where the qop-options will be stored.
252  * qlen    [in]     - The length of the qop buffer.
253  *
254  * Returns CURLE_OK on success.
255  */
256 static CURLcode auth_decode_digest_md5_message(const char *chlg64,
257                                                char *nonce, size_t nlen,
258                                                char *realm, size_t rlen,
259                                                char *alg, size_t alen,
260                                                char *qop, size_t qlen)
261 {
262   CURLcode result = CURLE_OK;
263   unsigned char *chlg = NULL;
264   size_t chlglen = 0;
265   size_t chlg64len = strlen(chlg64);
266
267   /* Decode the base-64 encoded challenge message */
268   if(chlg64len && *chlg64 != '=') {
269     result = Curl_base64_decode(chlg64, &chlg, &chlglen);
270     if(result)
271       return result;
272   }
273
274   /* Ensure we have a valid challenge message */
275   if(!chlg)
276     return CURLE_BAD_CONTENT_ENCODING;
277
278   /* Retrieve nonce string from the challenge */
279   if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen,
280                                 '\"')) {
281     free(chlg);
282     return CURLE_BAD_CONTENT_ENCODING;
283   }
284
285   /* Retrieve realm string from the challenge */
286   if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen,
287                                 '\"')) {
288     /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
289     strcpy(realm, "");
290   }
291
292   /* Retrieve algorithm string from the challenge */
293   if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) {
294     free(chlg);
295     return CURLE_BAD_CONTENT_ENCODING;
296   }
297
298   /* Retrieve qop-options string from the challenge */
299   if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) {
300     free(chlg);
301     return CURLE_BAD_CONTENT_ENCODING;
302   }
303
304   free(chlg);
305
306   return CURLE_OK;
307 }
308
309 /*
310  * Curl_auth_is_digest_supported()
311  *
312  * This is used to evaluate if DIGEST is supported.
313  *
314  * Parameters: None
315  *
316  * Returns TRUE as DIGEST as handled by libcurl.
317  */
318 bool Curl_auth_is_digest_supported(void)
319 {
320   return TRUE;
321 }
322
323 /*
324  * Curl_auth_create_digest_md5_message()
325  *
326  * This is used to generate an already encoded DIGEST-MD5 response message
327  * ready for sending to the recipient.
328  *
329  * Parameters:
330  *
331  * data    [in]     - The session handle.
332  * chlg64  [in]     - The base64 encoded challenge message.
333  * userp   [in]     - The user name.
334  * passdwp [in]     - The user's password.
335  * service [in]     - The service type such as http, smtp, pop or imap.
336  * outptr  [in/out] - The address where a pointer to newly allocated memory
337  *                    holding the result will be stored upon completion.
338  * outlen  [out]    - The length of the output message.
339  *
340  * Returns CURLE_OK on success.
341  */
342 CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
343                                              const char *chlg64,
344                                              const char *userp,
345                                              const char *passwdp,
346                                              const char *service,
347                                              char **outptr, size_t *outlen)
348 {
349   CURLcode result = CURLE_OK;
350   size_t i;
351   MD5_context *ctxt;
352   char *response = NULL;
353   unsigned char digest[MD5_DIGEST_LEN];
354   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
355   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
356   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
357   char nonce[64];
358   char realm[128];
359   char algorithm[64];
360   char qop_options[64];
361   int qop_values;
362   char cnonce[33];
363   char nonceCount[] = "00000001";
364   char method[]     = "AUTHENTICATE";
365   char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
366   char *spn         = NULL;
367
368   /* Decode the challenge message */
369   result = auth_decode_digest_md5_message(chlg64, nonce, sizeof(nonce),
370                                           realm, sizeof(realm),
371                                           algorithm, sizeof(algorithm),
372                                           qop_options, sizeof(qop_options));
373   if(result)
374     return result;
375
376   /* We only support md5 sessions */
377   if(strcmp(algorithm, "md5-sess") != 0)
378     return CURLE_BAD_CONTENT_ENCODING;
379
380   /* Get the qop-values from the qop-options */
381   result = auth_digest_get_qop_values(qop_options, &qop_values);
382   if(result)
383     return result;
384
385   /* We only support auth quality-of-protection */
386   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
387     return CURLE_BAD_CONTENT_ENCODING;
388
389   /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
390   result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
391   if(result)
392     return result;
393
394   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
395   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
396   if(!ctxt)
397     return CURLE_OUT_OF_MEMORY;
398
399   Curl_MD5_update(ctxt, (const unsigned char *) userp,
400                   curlx_uztoui(strlen(userp)));
401   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
402   Curl_MD5_update(ctxt, (const unsigned char *) realm,
403                   curlx_uztoui(strlen(realm)));
404   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
405   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
406                   curlx_uztoui(strlen(passwdp)));
407   Curl_MD5_final(ctxt, digest);
408
409   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
410   if(!ctxt)
411     return CURLE_OUT_OF_MEMORY;
412
413   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
414   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
415   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
416                   curlx_uztoui(strlen(nonce)));
417   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
418   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
419                   curlx_uztoui(strlen(cnonce)));
420   Curl_MD5_final(ctxt, digest);
421
422   /* Convert calculated 16 octet hex into 32 bytes string */
423   for(i = 0; i < MD5_DIGEST_LEN; i++)
424     snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
425
426   /* Generate our SPN */
427   spn = Curl_auth_build_spn(service, realm, NULL);
428   if(!spn)
429     return CURLE_OUT_OF_MEMORY;
430
431   /* Calculate H(A2) */
432   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
433   if(!ctxt) {
434     free(spn);
435
436     return CURLE_OUT_OF_MEMORY;
437   }
438
439   Curl_MD5_update(ctxt, (const unsigned char *) method,
440                   curlx_uztoui(strlen(method)));
441   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
442   Curl_MD5_update(ctxt, (const unsigned char *) spn,
443                   curlx_uztoui(strlen(spn)));
444   Curl_MD5_final(ctxt, digest);
445
446   for(i = 0; i < MD5_DIGEST_LEN; i++)
447     snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
448
449   /* Now calculate the response hash */
450   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
451   if(!ctxt) {
452     free(spn);
453
454     return CURLE_OUT_OF_MEMORY;
455   }
456
457   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
458   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
459   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
460                   curlx_uztoui(strlen(nonce)));
461   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
462
463   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
464                   curlx_uztoui(strlen(nonceCount)));
465   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
466   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
467                   curlx_uztoui(strlen(cnonce)));
468   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
469   Curl_MD5_update(ctxt, (const unsigned char *) qop,
470                   curlx_uztoui(strlen(qop)));
471   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
472
473   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
474   Curl_MD5_final(ctxt, digest);
475
476   for(i = 0; i < MD5_DIGEST_LEN; i++)
477     snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
478
479   /* Generate the response */
480   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
481                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
482                      "qop=%s",
483                      userp, realm, nonce,
484                      cnonce, nonceCount, spn, resp_hash_hex, qop);
485   free(spn);
486   if(!response)
487     return CURLE_OUT_OF_MEMORY;
488
489   /* Base64 encode the response */
490   result = Curl_base64_encode(data, response, 0, outptr, outlen);
491
492   free(response);
493
494   return result;
495 }
496
497 /*
498  * Curl_auth_decode_digest_http_message()
499  *
500  * This is used to decode a HTTP DIGEST challenge message into the separate
501  * attributes.
502  *
503  * Parameters:
504  *
505  * chlg    [in]     - The challenge message.
506  * digest  [in/out] - The digest data struct being used and modified.
507  *
508  * Returns CURLE_OK on success.
509  */
510 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
511                                               struct digestdata *digest)
512 {
513   bool before = FALSE; /* got a nonce before */
514   bool foundAuth = FALSE;
515   bool foundAuthInt = FALSE;
516   char *token = NULL;
517   char *tmp = NULL;
518
519   /* If we already have received a nonce, keep that in mind */
520   if(digest->nonce)
521     before = TRUE;
522
523   /* Clean up any former leftovers and initialise to defaults */
524   Curl_auth_digest_cleanup(digest);
525
526   for(;;) {
527     char value[DIGEST_MAX_VALUE_LENGTH];
528     char content[DIGEST_MAX_CONTENT_LENGTH];
529
530     /* Pass all additional spaces here */
531     while(*chlg && ISSPACE(*chlg))
532       chlg++;
533
534     /* Extract a value=content pair */
535     if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
536       if(strcasecompare(value, "nonce")) {
537         free(digest->nonce);
538         digest->nonce = strdup(content);
539         if(!digest->nonce)
540           return CURLE_OUT_OF_MEMORY;
541       }
542       else if(strcasecompare(value, "stale")) {
543         if(strcasecompare(content, "true")) {
544           digest->stale = TRUE;
545           digest->nc = 1; /* we make a new nonce now */
546         }
547       }
548       else if(strcasecompare(value, "realm")) {
549         free(digest->realm);
550         digest->realm = strdup(content);
551         if(!digest->realm)
552           return CURLE_OUT_OF_MEMORY;
553       }
554       else if(strcasecompare(value, "opaque")) {
555         free(digest->opaque);
556         digest->opaque = strdup(content);
557         if(!digest->opaque)
558           return CURLE_OUT_OF_MEMORY;
559       }
560       else if(strcasecompare(value, "qop")) {
561         char *tok_buf = NULL;
562         /* Tokenize the list and choose auth if possible, use a temporary
563            clone of the buffer since strtok_r() ruins it */
564         tmp = strdup(content);
565         if(!tmp)
566           return CURLE_OUT_OF_MEMORY;
567
568         token = strtok_r(tmp, ",", &tok_buf);
569         while(token != NULL) {
570           if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
571             foundAuth = TRUE;
572           }
573           else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
574             foundAuthInt = TRUE;
575           }
576           token = strtok_r(NULL, ",", &tok_buf);
577         }
578
579         free(tmp);
580
581         /* Select only auth or auth-int. Otherwise, ignore */
582         if(foundAuth) {
583           free(digest->qop);
584           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
585           if(!digest->qop)
586             return CURLE_OUT_OF_MEMORY;
587         }
588         else if(foundAuthInt) {
589           free(digest->qop);
590           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
591           if(!digest->qop)
592             return CURLE_OUT_OF_MEMORY;
593         }
594       }
595       else if(strcasecompare(value, "algorithm")) {
596         free(digest->algorithm);
597         digest->algorithm = strdup(content);
598         if(!digest->algorithm)
599           return CURLE_OUT_OF_MEMORY;
600
601         if(strcasecompare(content, "MD5-sess"))
602           digest->algo = CURLDIGESTALGO_MD5SESS;
603         else if(strcasecompare(content, "MD5"))
604           digest->algo = CURLDIGESTALGO_MD5;
605         else
606           return CURLE_BAD_CONTENT_ENCODING;
607       }
608       else {
609         /* Unknown specifier, ignore it! */
610       }
611     }
612     else
613       break; /* We're done here */
614
615     /* Pass all additional spaces here */
616     while(*chlg && ISSPACE(*chlg))
617       chlg++;
618
619     /* Allow the list to be comma-separated */
620     if(',' == *chlg)
621       chlg++;
622   }
623
624   /* We had a nonce since before, and we got another one now without
625      'stale=true'. This means we provided bad credentials in the previous
626      request */
627   if(before && !digest->stale)
628     return CURLE_BAD_CONTENT_ENCODING;
629
630   /* We got this header without a nonce, that's a bad Digest line! */
631   if(!digest->nonce)
632     return CURLE_BAD_CONTENT_ENCODING;
633
634   return CURLE_OK;
635 }
636
637 /*
638  * Curl_auth_create_digest_http_message()
639  *
640  * This is used to generate a HTTP DIGEST response message ready for sending
641  * to the recipient.
642  *
643  * Parameters:
644  *
645  * data    [in]     - The session handle.
646  * userp   [in]     - The user name.
647  * passdwp [in]     - The user's password.
648  * request [in]     - The HTTP request.
649  * uripath [in]     - The path of the HTTP uri.
650  * digest  [in/out] - The digest data struct being used and modified.
651  * outptr  [in/out] - The address where a pointer to newly allocated memory
652  *                    holding the result will be stored upon completion.
653  * outlen  [out]    - The length of the output message.
654  *
655  * Returns CURLE_OK on success.
656  */
657 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
658                                               const char *userp,
659                                               const char *passwdp,
660                                               const unsigned char *request,
661                                               const unsigned char *uripath,
662                                               struct digestdata *digest,
663                                               char **outptr, size_t *outlen)
664 {
665   CURLcode result;
666   unsigned char md5buf[16]; /* 16 bytes/128 bits */
667   unsigned char request_digest[33];
668   unsigned char *md5this;
669   unsigned char ha1[33];    /* 32 digits and 1 zero byte */
670   unsigned char ha2[33];    /* 32 digits and 1 zero byte */
671   char cnoncebuf[33];
672   char *cnonce = NULL;
673   size_t cnonce_sz = 0;
674   char *userp_quoted;
675   char *response = NULL;
676   char *tmp = NULL;
677
678   if(!digest->nc)
679     digest->nc = 1;
680
681   if(!digest->cnonce) {
682     result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
683                            sizeof(cnoncebuf));
684     if(result)
685       return result;
686
687     result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
688                                 &cnonce, &cnonce_sz);
689     if(result)
690       return result;
691
692     digest->cnonce = cnonce;
693   }
694
695   /*
696     If the algorithm is "MD5" or unspecified (which then defaults to MD5):
697
698       A1 = unq(username-value) ":" unq(realm-value) ":" passwd
699
700     If the algorithm is "MD5-sess" then:
701
702       A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
703            unq(nonce-value) ":" unq(cnonce-value)
704   */
705
706   md5this = (unsigned char *)
707     aprintf("%s:%s:%s", userp, digest->realm, passwdp);
708   if(!md5this)
709     return CURLE_OUT_OF_MEMORY;
710
711   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
712   Curl_md5it(md5buf, md5this);
713   free(md5this);
714   auth_digest_md5_to_ascii(md5buf, ha1);
715
716   if(digest->algo == CURLDIGESTALGO_MD5SESS) {
717     /* nonce and cnonce are OUTSIDE the hash */
718     tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
719     if(!tmp)
720       return CURLE_OUT_OF_MEMORY;
721
722     CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
723     Curl_md5it(md5buf, (unsigned char *) tmp);
724     free(tmp);
725     auth_digest_md5_to_ascii(md5buf, ha1);
726   }
727
728   /*
729     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
730
731       A2 = Method ":" digest-uri-value
732
733     If the "qop" value is "auth-int", then A2 is:
734
735       A2 = Method ":" digest-uri-value ":" H(entity-body)
736
737     (The "Method" value is the HTTP request method as specified in section
738     5.1.1 of RFC 2616)
739   */
740
741   md5this = (unsigned char *) aprintf("%s:%s", request, uripath);
742
743   if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
744     /* We don't support auth-int for PUT or POST at the moment.
745        TODO: replace md5 of empty string with entity-body for PUT/POST */
746     unsigned char *md5this2 = (unsigned char *)
747       aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e");
748     free(md5this);
749     md5this = md5this2;
750   }
751
752   if(!md5this)
753     return CURLE_OUT_OF_MEMORY;
754
755   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
756   Curl_md5it(md5buf, md5this);
757   free(md5this);
758   auth_digest_md5_to_ascii(md5buf, ha2);
759
760   if(digest->qop) {
761     md5this = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
762                                         ha1,
763                                         digest->nonce,
764                                         digest->nc,
765                                         digest->cnonce,
766                                         digest->qop,
767                                         ha2);
768   }
769   else {
770     md5this = (unsigned char *) aprintf("%s:%s:%s",
771                                         ha1,
772                                         digest->nonce,
773                                         ha2);
774   }
775
776   if(!md5this)
777     return CURLE_OUT_OF_MEMORY;
778
779   CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */
780   Curl_md5it(md5buf, md5this);
781   free(md5this);
782   auth_digest_md5_to_ascii(md5buf, request_digest);
783
784   /* For test case 64 (snooped from a Mozilla 1.3a request)
785
786      Authorization: Digest username="testuser", realm="testrealm", \
787      nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
788
789      Digest parameters are all quoted strings.  Username which is provided by
790      the user will need double quotes and backslashes within it escaped.  For
791      the other fields, this shouldn't be an issue.  realm, nonce, and opaque
792      are copied as is from the server, escapes and all.  cnonce is generated
793      with web-safe characters.  uri is already percent encoded.  nc is 8 hex
794      characters.  algorithm and qop with standard values only contain web-safe
795      characters.
796   */
797   userp_quoted = auth_digest_string_quoted(userp);
798   if(!userp_quoted)
799     return CURLE_OUT_OF_MEMORY;
800
801   if(digest->qop) {
802     response = aprintf("username=\"%s\", "
803                        "realm=\"%s\", "
804                        "nonce=\"%s\", "
805                        "uri=\"%s\", "
806                        "cnonce=\"%s\", "
807                        "nc=%08x, "
808                        "qop=%s, "
809                        "response=\"%s\"",
810                        userp_quoted,
811                        digest->realm,
812                        digest->nonce,
813                        uripath,
814                        digest->cnonce,
815                        digest->nc,
816                        digest->qop,
817                        request_digest);
818
819     if(strcasecompare(digest->qop, "auth"))
820       digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
821                        padded which tells to the server how many times you are
822                        using the same nonce in the qop=auth mode */
823   }
824   else {
825     response = aprintf("username=\"%s\", "
826                        "realm=\"%s\", "
827                        "nonce=\"%s\", "
828                        "uri=\"%s\", "
829                        "response=\"%s\"",
830                        userp_quoted,
831                        digest->realm,
832                        digest->nonce,
833                        uripath,
834                        request_digest);
835   }
836   free(userp_quoted);
837   if(!response)
838     return CURLE_OUT_OF_MEMORY;
839
840   /* Add the optional fields */
841   if(digest->opaque) {
842     /* Append the opaque */
843     tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
844     free(response);
845     if(!tmp)
846       return CURLE_OUT_OF_MEMORY;
847
848     response = tmp;
849   }
850
851   if(digest->algorithm) {
852     /* Append the algorithm */
853     tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
854     free(response);
855     if(!tmp)
856       return CURLE_OUT_OF_MEMORY;
857
858     response = tmp;
859   }
860
861   /* Return the output */
862   *outptr = response;
863   *outlen = strlen(response);
864
865   return CURLE_OK;
866 }
867
868 /*
869  * Curl_auth_digest_cleanup()
870  *
871  * This is used to clean up the digest specific data.
872  *
873  * Parameters:
874  *
875  * digest    [in/out] - The digest data struct being cleaned up.
876  *
877  */
878 void Curl_auth_digest_cleanup(struct digestdata *digest)
879 {
880   Curl_safefree(digest->nonce);
881   Curl_safefree(digest->cnonce);
882   Curl_safefree(digest->realm);
883   Curl_safefree(digest->opaque);
884   Curl_safefree(digest->qop);
885   Curl_safefree(digest->algorithm);
886
887   digest->nc = 0;
888   digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
889   digest->stale = FALSE; /* default means normal, not stale */
890 }
891 #endif  /* !USE_WINDOWS_SSPI */
892
893 #endif  /* CURL_DISABLE_CRYPTO_AUTH */