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