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