d4616095da4cad2e502165e4e5bd9be06523764a
[platform/upstream/cmake.git] / Utilities / cmcurl / lib / vauth / digest.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2022, 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.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  * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication
23  *
24  ***************************************************************************/
25
26 #include "curl_setup.h"
27
28 #if !defined(CURL_DISABLE_CRYPTO_AUTH)
29
30 #include <curl/curl.h>
31
32 #include "vauth/vauth.h"
33 #include "vauth/digest.h"
34 #include "urldata.h"
35 #include "curl_base64.h"
36 #include "curl_hmac.h"
37 #include "curl_md5.h"
38 #include "curl_sha256.h"
39 #include "vtls/vtls.h"
40 #include "warnless.h"
41 #include "strtok.h"
42 #include "strcase.h"
43 #include "curl_printf.h"
44 #include "rand.h"
45
46 /* The last #include files should be: */
47 #include "curl_memory.h"
48 #include "memdebug.h"
49
50 #if !defined(USE_WINDOWS_SSPI)
51 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
52 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
53 #define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
54
55 #define DIGEST_QOP_VALUE_STRING_AUTH      "auth"
56 #define DIGEST_QOP_VALUE_STRING_AUTH_INT  "auth-int"
57 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf"
58 #endif
59
60 bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
61                                const char **endptr)
62 {
63   int c;
64   bool starts_with_quote = FALSE;
65   bool escape = FALSE;
66
67   for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);)
68     *value++ = *str++;
69   *value = 0;
70
71   if('=' != *str++)
72     /* eek, no match */
73     return FALSE;
74
75   if('\"' == *str) {
76     /* This starts with a quote so it must end with one as well! */
77     str++;
78     starts_with_quote = TRUE;
79   }
80
81   for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) {
82     switch(*str) {
83     case '\\':
84       if(!escape) {
85         /* possibly the start of an escaped quote */
86         escape = TRUE;
87         *content++ = '\\'; /* Even though this is an escape character, we still
88                               store it as-is in the target buffer */
89         continue;
90       }
91       break;
92
93     case ',':
94       if(!starts_with_quote) {
95         /* This signals the end of the content if we didn't get a starting
96            quote and then we do "sloppy" parsing */
97         c = 0; /* the end */
98         continue;
99       }
100       break;
101
102     case '\r':
103     case '\n':
104       /* end of string */
105       c = 0;
106       continue;
107
108     case '\"':
109       if(!escape && starts_with_quote) {
110         /* end of string */
111         c = 0;
112         continue;
113       }
114       break;
115     }
116
117     escape = FALSE;
118     *content++ = *str;
119   }
120
121   *content = 0;
122   *endptr = str;
123
124   return TRUE;
125 }
126
127 #if !defined(USE_WINDOWS_SSPI)
128 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/
129 static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
130                                      unsigned char *dest) /* 33 bytes */
131 {
132   int i;
133   for(i = 0; i < 16; i++)
134     msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
135 }
136
137 /* Convert sha256 chunk to RFC7616 -suitable ascii string*/
138 static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */
139                                      unsigned char *dest) /* 65 bytes */
140 {
141   int i;
142   for(i = 0; i < 32; i++)
143     msnprintf((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;
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     char *d = dest;
165     s = source;
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 = NULL;
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) {
220     if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
221       *value |= DIGEST_QOP_VALUE_AUTH;
222     else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
223       *value |= DIGEST_QOP_VALUE_AUTH_INT;
224     else if(strcasecompare(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 separate attributes.
240  *
241  * Parameters:
242  *
243  * chlgref [in]     - The 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 struct bufref *chlgref,
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   const char *chlg = (const char *) Curl_bufref_ptr(chlgref);
262
263   /* Ensure we have a valid challenge message */
264   if(!Curl_bufref_len(chlgref))
265     return CURLE_BAD_CONTENT_ENCODING;
266
267   /* Retrieve nonce string from the challenge */
268   if(!auth_digest_get_key_value(chlg, "nonce=\"", nonce, nlen, '\"'))
269     return CURLE_BAD_CONTENT_ENCODING;
270
271   /* Retrieve realm string from the challenge */
272   if(!auth_digest_get_key_value(chlg, "realm=\"", realm, rlen, '\"')) {
273     /* Challenge does not have a realm, set empty string [RFC2831] page 6 */
274     strcpy(realm, "");
275   }
276
277   /* Retrieve algorithm string from the challenge */
278   if(!auth_digest_get_key_value(chlg, "algorithm=", alg, alen, ','))
279     return CURLE_BAD_CONTENT_ENCODING;
280
281   /* Retrieve qop-options string from the challenge */
282   if(!auth_digest_get_key_value(chlg, "qop=\"", qop, qlen, '\"'))
283     return CURLE_BAD_CONTENT_ENCODING;
284
285   return CURLE_OK;
286 }
287
288 /*
289  * Curl_auth_is_digest_supported()
290  *
291  * This is used to evaluate if DIGEST is supported.
292  *
293  * Parameters: None
294  *
295  * Returns TRUE as DIGEST as handled by libcurl.
296  */
297 bool Curl_auth_is_digest_supported(void)
298 {
299   return TRUE;
300 }
301
302 /*
303  * Curl_auth_create_digest_md5_message()
304  *
305  * This is used to generate an already encoded DIGEST-MD5 response message
306  * ready for sending to the recipient.
307  *
308  * Parameters:
309  *
310  * data    [in]     - The session handle.
311  * chlg    [in]     - The challenge message.
312  * userp   [in]     - The user name.
313  * passwdp [in]     - The user's password.
314  * service [in]     - The service type such as http, smtp, pop or imap.
315  * out     [out]    - The result storage.
316  *
317  * Returns CURLE_OK on success.
318  */
319 CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
320                                              const struct bufref *chlg,
321                                              const char *userp,
322                                              const char *passwdp,
323                                              const char *service,
324                                              struct bufref *out)
325 {
326   size_t i;
327   struct MD5_context *ctxt;
328   char *response = NULL;
329   unsigned char digest[MD5_DIGEST_LEN];
330   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
331   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
332   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
333   char nonce[64];
334   char realm[128];
335   char algorithm[64];
336   char qop_options[64];
337   int qop_values;
338   char cnonce[33];
339   char nonceCount[] = "00000001";
340   char method[]     = "AUTHENTICATE";
341   char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
342   char *spn         = NULL;
343
344   /* Decode the challenge message */
345   CURLcode result = auth_decode_digest_md5_message(chlg,
346                                                    nonce, sizeof(nonce),
347                                                    realm, sizeof(realm),
348                                                    algorithm,
349                                                    sizeof(algorithm),
350                                                    qop_options,
351                                                    sizeof(qop_options));
352   if(result)
353     return result;
354
355   /* We only support md5 sessions */
356   if(strcmp(algorithm, "md5-sess") != 0)
357     return CURLE_BAD_CONTENT_ENCODING;
358
359   /* Get the qop-values from the qop-options */
360   result = auth_digest_get_qop_values(qop_options, &qop_values);
361   if(result)
362     return result;
363
364   /* We only support auth quality-of-protection */
365   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
366     return CURLE_BAD_CONTENT_ENCODING;
367
368   /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
369   result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
370   if(result)
371     return result;
372
373   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
374   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
375   if(!ctxt)
376     return CURLE_OUT_OF_MEMORY;
377
378   Curl_MD5_update(ctxt, (const unsigned char *) userp,
379                   curlx_uztoui(strlen(userp)));
380   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
381   Curl_MD5_update(ctxt, (const unsigned char *) realm,
382                   curlx_uztoui(strlen(realm)));
383   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
384   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
385                   curlx_uztoui(strlen(passwdp)));
386   Curl_MD5_final(ctxt, digest);
387
388   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
389   if(!ctxt)
390     return CURLE_OUT_OF_MEMORY;
391
392   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
393   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
394   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
395                   curlx_uztoui(strlen(nonce)));
396   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
397   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
398                   curlx_uztoui(strlen(cnonce)));
399   Curl_MD5_final(ctxt, digest);
400
401   /* Convert calculated 16 octet hex into 32 bytes string */
402   for(i = 0; i < MD5_DIGEST_LEN; i++)
403     msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
404
405   /* Generate our SPN */
406   spn = Curl_auth_build_spn(service, realm, NULL);
407   if(!spn)
408     return CURLE_OUT_OF_MEMORY;
409
410   /* Calculate H(A2) */
411   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
412   if(!ctxt) {
413     free(spn);
414
415     return CURLE_OUT_OF_MEMORY;
416   }
417
418   Curl_MD5_update(ctxt, (const unsigned char *) method,
419                   curlx_uztoui(strlen(method)));
420   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
421   Curl_MD5_update(ctxt, (const unsigned char *) spn,
422                   curlx_uztoui(strlen(spn)));
423   Curl_MD5_final(ctxt, digest);
424
425   for(i = 0; i < MD5_DIGEST_LEN; i++)
426     msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
427
428   /* Now calculate the response hash */
429   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
430   if(!ctxt) {
431     free(spn);
432
433     return CURLE_OUT_OF_MEMORY;
434   }
435
436   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
437   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
438   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
439                   curlx_uztoui(strlen(nonce)));
440   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
441
442   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
443                   curlx_uztoui(strlen(nonceCount)));
444   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
445   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
446                   curlx_uztoui(strlen(cnonce)));
447   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
448   Curl_MD5_update(ctxt, (const unsigned char *) qop,
449                   curlx_uztoui(strlen(qop)));
450   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
451
452   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
453   Curl_MD5_final(ctxt, digest);
454
455   for(i = 0; i < MD5_DIGEST_LEN; i++)
456     msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
457
458   /* Generate the response */
459   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
460                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
461                      "qop=%s",
462                      userp, realm, nonce,
463                      cnonce, nonceCount, spn, resp_hash_hex, qop);
464   free(spn);
465   if(!response)
466     return CURLE_OUT_OF_MEMORY;
467
468   /* Return the response. */
469   Curl_bufref_set(out, response, strlen(response), curl_free);
470   return result;
471 }
472
473 /*
474  * Curl_auth_decode_digest_http_message()
475  *
476  * This is used to decode a HTTP DIGEST challenge message into the separate
477  * attributes.
478  *
479  * Parameters:
480  *
481  * chlg    [in]     - The challenge message.
482  * digest  [in/out] - The digest data struct being used and modified.
483  *
484  * Returns CURLE_OK on success.
485  */
486 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
487                                               struct digestdata *digest)
488 {
489   bool before = FALSE; /* got a nonce before */
490   bool foundAuth = FALSE;
491   bool foundAuthInt = FALSE;
492   char *token = NULL;
493   char *tmp = NULL;
494
495   /* If we already have received a nonce, keep that in mind */
496   if(digest->nonce)
497     before = TRUE;
498
499   /* Clean up any former leftovers and initialise to defaults */
500   Curl_auth_digest_cleanup(digest);
501
502   for(;;) {
503     char value[DIGEST_MAX_VALUE_LENGTH];
504     char content[DIGEST_MAX_CONTENT_LENGTH];
505
506     /* Pass all additional spaces here */
507     while(*chlg && ISSPACE(*chlg))
508       chlg++;
509
510     /* Extract a value=content pair */
511     if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
512       if(strcasecompare(value, "nonce")) {
513         free(digest->nonce);
514         digest->nonce = strdup(content);
515         if(!digest->nonce)
516           return CURLE_OUT_OF_MEMORY;
517       }
518       else if(strcasecompare(value, "stale")) {
519         if(strcasecompare(content, "true")) {
520           digest->stale = TRUE;
521           digest->nc = 1; /* we make a new nonce now */
522         }
523       }
524       else if(strcasecompare(value, "realm")) {
525         free(digest->realm);
526         digest->realm = strdup(content);
527         if(!digest->realm)
528           return CURLE_OUT_OF_MEMORY;
529       }
530       else if(strcasecompare(value, "opaque")) {
531         free(digest->opaque);
532         digest->opaque = strdup(content);
533         if(!digest->opaque)
534           return CURLE_OUT_OF_MEMORY;
535       }
536       else if(strcasecompare(value, "qop")) {
537         char *tok_buf = NULL;
538         /* Tokenize the list and choose auth if possible, use a temporary
539            clone of the buffer since strtok_r() ruins it */
540         tmp = strdup(content);
541         if(!tmp)
542           return CURLE_OUT_OF_MEMORY;
543
544         token = strtok_r(tmp, ",", &tok_buf);
545         while(token) {
546           if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
547             foundAuth = TRUE;
548           }
549           else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
550             foundAuthInt = TRUE;
551           }
552           token = strtok_r(NULL, ",", &tok_buf);
553         }
554
555         free(tmp);
556
557         /* Select only auth or auth-int. Otherwise, ignore */
558         if(foundAuth) {
559           free(digest->qop);
560           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
561           if(!digest->qop)
562             return CURLE_OUT_OF_MEMORY;
563         }
564         else if(foundAuthInt) {
565           free(digest->qop);
566           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
567           if(!digest->qop)
568             return CURLE_OUT_OF_MEMORY;
569         }
570       }
571       else if(strcasecompare(value, "algorithm")) {
572         free(digest->algorithm);
573         digest->algorithm = strdup(content);
574         if(!digest->algorithm)
575           return CURLE_OUT_OF_MEMORY;
576
577         if(strcasecompare(content, "MD5-sess"))
578           digest->algo = CURLDIGESTALGO_MD5SESS;
579         else if(strcasecompare(content, "MD5"))
580           digest->algo = CURLDIGESTALGO_MD5;
581         else if(strcasecompare(content, "SHA-256"))
582           digest->algo = CURLDIGESTALGO_SHA256;
583         else if(strcasecompare(content, "SHA-256-SESS"))
584           digest->algo = CURLDIGESTALGO_SHA256SESS;
585         else if(strcasecompare(content, "SHA-512-256"))
586           digest->algo = CURLDIGESTALGO_SHA512_256;
587         else if(strcasecompare(content, "SHA-512-256-SESS"))
588           digest->algo = CURLDIGESTALGO_SHA512_256SESS;
589         else
590           return CURLE_BAD_CONTENT_ENCODING;
591       }
592       else if(strcasecompare(value, "userhash")) {
593         if(strcasecompare(content, "true")) {
594           digest->userhash = TRUE;
595         }
596       }
597       else {
598         /* Unknown specifier, ignore it! */
599       }
600     }
601     else
602       break; /* We're done here */
603
604     /* Pass all additional spaces here */
605     while(*chlg && ISSPACE(*chlg))
606       chlg++;
607
608     /* Allow the list to be comma-separated */
609     if(',' == *chlg)
610       chlg++;
611   }
612
613   /* We had a nonce since before, and we got another one now without
614      'stale=true'. This means we provided bad credentials in the previous
615      request */
616   if(before && !digest->stale)
617     return CURLE_BAD_CONTENT_ENCODING;
618
619   /* We got this header without a nonce, that's a bad Digest line! */
620   if(!digest->nonce)
621     return CURLE_BAD_CONTENT_ENCODING;
622
623   return CURLE_OK;
624 }
625
626 /*
627  * auth_create_digest_http_message()
628  *
629  * This is used to generate a HTTP DIGEST response message ready for sending
630  * to the recipient.
631  *
632  * Parameters:
633  *
634  * data    [in]     - The session handle.
635  * userp   [in]     - The user name.
636  * passwdp [in]     - The user's password.
637  * request [in]     - The HTTP request.
638  * uripath [in]     - The path of the HTTP uri.
639  * digest  [in/out] - The digest data struct being used and modified.
640  * outptr  [in/out] - The address where a pointer to newly allocated memory
641  *                    holding the result will be stored upon completion.
642  * outlen  [out]    - The length of the output message.
643  *
644  * Returns CURLE_OK on success.
645  */
646 static CURLcode auth_create_digest_http_message(
647                   struct Curl_easy *data,
648                   const char *userp,
649                   const char *passwdp,
650                   const unsigned char *request,
651                   const unsigned char *uripath,
652                   struct digestdata *digest,
653                   char **outptr, size_t *outlen,
654                   void (*convert_to_ascii)(unsigned char *, unsigned char *),
655                   CURLcode (*hash)(unsigned char *, const unsigned char *,
656                                    const size_t))
657 {
658   CURLcode result;
659   unsigned char hashbuf[32]; /* 32 bytes/256 bits */
660   unsigned char request_digest[65];
661   unsigned char ha1[65];    /* 64 digits and 1 zero byte */
662   unsigned char ha2[65];    /* 64 digits and 1 zero byte */
663   char userh[65];
664   char *cnonce = NULL;
665   size_t cnonce_sz = 0;
666   char *userp_quoted;
667   char *response = NULL;
668   char *hashthis = NULL;
669   char *tmp = NULL;
670
671   if(!digest->nc)
672     digest->nc = 1;
673
674   if(!digest->cnonce) {
675     char cnoncebuf[33];
676     result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
677                            sizeof(cnoncebuf));
678     if(result)
679       return result;
680
681     result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf),
682                                 &cnonce, &cnonce_sz);
683     if(result)
684       return result;
685
686     digest->cnonce = cnonce;
687   }
688
689   if(digest->userhash) {
690     hashthis = aprintf("%s:%s", userp, digest->realm);
691     if(!hashthis)
692       return CURLE_OUT_OF_MEMORY;
693
694     hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
695     free(hashthis);
696     convert_to_ascii(hashbuf, (unsigned char *)userh);
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   hashthis = aprintf("%s:%s:%s", userp, digest->realm, passwdp);
711   if(!hashthis)
712     return CURLE_OUT_OF_MEMORY;
713
714   hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
715   free(hashthis);
716   convert_to_ascii(hashbuf, ha1);
717
718   if(digest->algo == CURLDIGESTALGO_MD5SESS ||
719      digest->algo == CURLDIGESTALGO_SHA256SESS ||
720      digest->algo == CURLDIGESTALGO_SHA512_256SESS) {
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     hash(hashbuf, (unsigned char *) tmp, strlen(tmp));
727     free(tmp);
728     convert_to_ascii(hashbuf, ha1);
729   }
730
731   /*
732     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
733
734       A2 = Method ":" digest-uri-value
735
736     If the "qop" value is "auth-int", then A2 is:
737
738       A2 = Method ":" digest-uri-value ":" H(entity-body)
739
740     (The "Method" value is the HTTP request method as specified in section
741     5.1.1 of RFC 2616)
742   */
743
744   hashthis = aprintf("%s:%s", request, uripath);
745   if(!hashthis)
746     return CURLE_OUT_OF_MEMORY;
747
748   if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
749     /* We don't support auth-int for PUT or POST */
750     char hashed[65];
751     char *hashthis2;
752
753     hash(hashbuf, (const unsigned char *)"", 0);
754     convert_to_ascii(hashbuf, (unsigned char *)hashed);
755
756     hashthis2 = aprintf("%s:%s", hashthis, hashed);
757     free(hashthis);
758     hashthis = hashthis2;
759   }
760
761   if(!hashthis)
762     return CURLE_OUT_OF_MEMORY;
763
764   hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
765   free(hashthis);
766   convert_to_ascii(hashbuf, ha2);
767
768   if(digest->qop) {
769     hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc,
770                        digest->cnonce, digest->qop, ha2);
771   }
772   else {
773     hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2);
774   }
775
776   if(!hashthis)
777     return CURLE_OUT_OF_MEMORY;
778
779   hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
780   free(hashthis);
781   convert_to_ascii(hashbuf, request_digest);
782
783   /* For test case 64 (snooped from a Mozilla 1.3a request)
784
785      Authorization: Digest username="testuser", realm="testrealm", \
786      nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
787
788      Digest parameters are all quoted strings.  Username which is provided by
789      the user will need double quotes and backslashes within it escaped.  For
790      the other fields, this shouldn't be an issue.  realm, nonce, and opaque
791      are copied as is from the server, escapes and all.  cnonce is generated
792      with web-safe characters.  uri is already percent encoded.  nc is 8 hex
793      characters.  algorithm and qop with standard values only contain web-safe
794      characters.
795   */
796   userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
797   if(!userp_quoted)
798     return CURLE_OUT_OF_MEMORY;
799
800   if(digest->qop) {
801     response = aprintf("username=\"%s\", "
802                        "realm=\"%s\", "
803                        "nonce=\"%s\", "
804                        "uri=\"%s\", "
805                        "cnonce=\"%s\", "
806                        "nc=%08x, "
807                        "qop=%s, "
808                        "response=\"%s\"",
809                        userp_quoted,
810                        digest->realm,
811                        digest->nonce,
812                        uripath,
813                        digest->cnonce,
814                        digest->nc,
815                        digest->qop,
816                        request_digest);
817
818     if(strcasecompare(digest->qop, "auth"))
819       digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
820                        padded which tells to the server how many times you are
821                        using the same nonce in the qop=auth mode */
822   }
823   else {
824     response = aprintf("username=\"%s\", "
825                        "realm=\"%s\", "
826                        "nonce=\"%s\", "
827                        "uri=\"%s\", "
828                        "response=\"%s\"",
829                        userp_quoted,
830                        digest->realm,
831                        digest->nonce,
832                        uripath,
833                        request_digest);
834   }
835   free(userp_quoted);
836   if(!response)
837     return CURLE_OUT_OF_MEMORY;
838
839   /* Add the optional fields */
840   if(digest->opaque) {
841     /* Append the opaque */
842     tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
843     free(response);
844     if(!tmp)
845       return CURLE_OUT_OF_MEMORY;
846
847     response = tmp;
848   }
849
850   if(digest->algorithm) {
851     /* Append the algorithm */
852     tmp = aprintf("%s, algorithm=%s", response, digest->algorithm);
853     free(response);
854     if(!tmp)
855       return CURLE_OUT_OF_MEMORY;
856
857     response = tmp;
858   }
859
860   if(digest->userhash) {
861     /* Append the userhash */
862     tmp = aprintf("%s, userhash=true", response);
863     free(response);
864     if(!tmp)
865       return CURLE_OUT_OF_MEMORY;
866
867     response = tmp;
868   }
869
870   /* Return the output */
871   *outptr = response;
872   *outlen = strlen(response);
873
874   return CURLE_OK;
875 }
876
877 /*
878  * Curl_auth_create_digest_http_message()
879  *
880  * This is used to generate a HTTP DIGEST response message ready for sending
881  * to the recipient.
882  *
883  * Parameters:
884  *
885  * data    [in]     - The session handle.
886  * userp   [in]     - The user name.
887  * passwdp [in]     - The user's password.
888  * request [in]     - The HTTP request.
889  * uripath [in]     - The path of the HTTP uri.
890  * digest  [in/out] - The digest data struct being used and modified.
891  * outptr  [in/out] - The address where a pointer to newly allocated memory
892  *                    holding the result will be stored upon completion.
893  * outlen  [out]    - The length of the output message.
894  *
895  * Returns CURLE_OK on success.
896  */
897 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
898                                               const char *userp,
899                                               const char *passwdp,
900                                               const unsigned char *request,
901                                               const unsigned char *uripath,
902                                               struct digestdata *digest,
903                                               char **outptr, size_t *outlen)
904 {
905   switch(digest->algo) {
906   case CURLDIGESTALGO_MD5:
907   case CURLDIGESTALGO_MD5SESS:
908     return auth_create_digest_http_message(data, userp, passwdp,
909                                            request, uripath, digest,
910                                            outptr, outlen,
911                                            auth_digest_md5_to_ascii,
912                                            Curl_md5it);
913
914   case CURLDIGESTALGO_SHA256:
915   case CURLDIGESTALGO_SHA256SESS:
916   case CURLDIGESTALGO_SHA512_256:
917   case CURLDIGESTALGO_SHA512_256SESS:
918     return auth_create_digest_http_message(data, userp, passwdp,
919                                            request, uripath, digest,
920                                            outptr, outlen,
921                                            auth_digest_sha256_to_ascii,
922                                            Curl_sha256it);
923
924   default:
925     return CURLE_UNSUPPORTED_PROTOCOL;
926   }
927 }
928
929 /*
930  * Curl_auth_digest_cleanup()
931  *
932  * This is used to clean up the digest specific data.
933  *
934  * Parameters:
935  *
936  * digest    [in/out] - The digest data struct being cleaned up.
937  *
938  */
939 void Curl_auth_digest_cleanup(struct digestdata *digest)
940 {
941   Curl_safefree(digest->nonce);
942   Curl_safefree(digest->cnonce);
943   Curl_safefree(digest->realm);
944   Curl_safefree(digest->opaque);
945   Curl_safefree(digest->qop);
946   Curl_safefree(digest->algorithm);
947
948   digest->nc = 0;
949   digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
950   digest->stale = FALSE; /* default means normal, not stale */
951   digest->userhash = FALSE;
952 }
953 #endif  /* !USE_WINDOWS_SSPI */
954
955 #endif  /* CURL_DISABLE_CRYPTO_AUTH */