1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2019, 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 
Curl_auth_digest_get_pair(const char * str,char * value,char * content,const char ** endptr)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*/
auth_digest_md5_to_ascii(unsigned char * source,unsigned char * dest)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     msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
147 }
148 
149 /* Convert sha256 chunk to RFC7616 -suitable ascii string*/
auth_digest_sha256_to_ascii(unsigned char * source,unsigned char * dest)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     msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]);
156 }
157 
158 /* Perform quoted-string escaping as described in RFC2616 and its errata */
auth_digest_string_quoted(const char * source)159 static char *auth_digest_string_quoted(const char *source)
160 {
161   char *dest;
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     char *d = dest;
177     s = source;
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  */
auth_digest_get_key_value(const char * chlg,const char * key,char * value,size_t max_val_len,char end_char)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 
auth_digest_get_qop_values(const char * options,int * value)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  */
auth_decode_digest_md5_message(const char * chlg64,char * nonce,size_t nlen,char * realm,size_t rlen,char * alg,size_t alen,char * qop,size_t qlen)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  */
Curl_auth_is_digest_supported(void)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  * passwdp [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  */
Curl_auth_create_digest_md5_message(struct Curl_easy * data,const char * chlg64,const char * userp,const char * passwdp,const char * service,char ** outptr,size_t * outlen)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   size_t i;
361   MD5_context *ctxt;
362   char *response = NULL;
363   unsigned char digest[MD5_DIGEST_LEN];
364   char HA1_hex[2 * MD5_DIGEST_LEN + 1];
365   char HA2_hex[2 * MD5_DIGEST_LEN + 1];
366   char resp_hash_hex[2 * MD5_DIGEST_LEN + 1];
367   char nonce[64];
368   char realm[128];
369   char algorithm[64];
370   char qop_options[64];
371   int qop_values;
372   char cnonce[33];
373   char nonceCount[] = "00000001";
374   char method[]     = "AUTHENTICATE";
375   char qop[]        = DIGEST_QOP_VALUE_STRING_AUTH;
376   char *spn         = NULL;
377 
378   /* Decode the challenge message */
379   CURLcode result = auth_decode_digest_md5_message(chlg64, nonce,
380                                                    sizeof(nonce), realm,
381                                                    sizeof(realm), algorithm,
382                                                    sizeof(algorithm),
383                                                    qop_options,
384                                                    sizeof(qop_options));
385   if(result)
386     return result;
387 
388   /* We only support md5 sessions */
389   if(strcmp(algorithm, "md5-sess") != 0)
390     return CURLE_BAD_CONTENT_ENCODING;
391 
392   /* Get the qop-values from the qop-options */
393   result = auth_digest_get_qop_values(qop_options, &qop_values);
394   if(result)
395     return result;
396 
397   /* We only support auth quality-of-protection */
398   if(!(qop_values & DIGEST_QOP_VALUE_AUTH))
399     return CURLE_BAD_CONTENT_ENCODING;
400 
401   /* Generate 32 random hex chars, 32 bytes + 1 zero termination */
402   result = Curl_rand_hex(data, (unsigned char *)cnonce, sizeof(cnonce));
403   if(result)
404     return result;
405 
406   /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */
407   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
408   if(!ctxt)
409     return CURLE_OUT_OF_MEMORY;
410 
411   Curl_MD5_update(ctxt, (const unsigned char *) userp,
412                   curlx_uztoui(strlen(userp)));
413   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
414   Curl_MD5_update(ctxt, (const unsigned char *) realm,
415                   curlx_uztoui(strlen(realm)));
416   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
417   Curl_MD5_update(ctxt, (const unsigned char *) passwdp,
418                   curlx_uztoui(strlen(passwdp)));
419   Curl_MD5_final(ctxt, digest);
420 
421   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
422   if(!ctxt)
423     return CURLE_OUT_OF_MEMORY;
424 
425   Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN);
426   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
427   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
428                   curlx_uztoui(strlen(nonce)));
429   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
430   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
431                   curlx_uztoui(strlen(cnonce)));
432   Curl_MD5_final(ctxt, digest);
433 
434   /* Convert calculated 16 octet hex into 32 bytes string */
435   for(i = 0; i < MD5_DIGEST_LEN; i++)
436     msnprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]);
437 
438   /* Generate our SPN */
439   spn = Curl_auth_build_spn(service, realm, NULL);
440   if(!spn)
441     return CURLE_OUT_OF_MEMORY;
442 
443   /* Calculate H(A2) */
444   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
445   if(!ctxt) {
446     free(spn);
447 
448     return CURLE_OUT_OF_MEMORY;
449   }
450 
451   Curl_MD5_update(ctxt, (const unsigned char *) method,
452                   curlx_uztoui(strlen(method)));
453   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
454   Curl_MD5_update(ctxt, (const unsigned char *) spn,
455                   curlx_uztoui(strlen(spn)));
456   Curl_MD5_final(ctxt, digest);
457 
458   for(i = 0; i < MD5_DIGEST_LEN; i++)
459     msnprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]);
460 
461   /* Now calculate the response hash */
462   ctxt = Curl_MD5_init(Curl_DIGEST_MD5);
463   if(!ctxt) {
464     free(spn);
465 
466     return CURLE_OUT_OF_MEMORY;
467   }
468 
469   Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN);
470   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
471   Curl_MD5_update(ctxt, (const unsigned char *) nonce,
472                   curlx_uztoui(strlen(nonce)));
473   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
474 
475   Curl_MD5_update(ctxt, (const unsigned char *) nonceCount,
476                   curlx_uztoui(strlen(nonceCount)));
477   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
478   Curl_MD5_update(ctxt, (const unsigned char *) cnonce,
479                   curlx_uztoui(strlen(cnonce)));
480   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
481   Curl_MD5_update(ctxt, (const unsigned char *) qop,
482                   curlx_uztoui(strlen(qop)));
483   Curl_MD5_update(ctxt, (const unsigned char *) ":", 1);
484 
485   Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN);
486   Curl_MD5_final(ctxt, digest);
487 
488   for(i = 0; i < MD5_DIGEST_LEN; i++)
489     msnprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]);
490 
491   /* Generate the response */
492   response = aprintf("username=\"%s\",realm=\"%s\",nonce=\"%s\","
493                      "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s,"
494                      "qop=%s",
495                      userp, realm, nonce,
496                      cnonce, nonceCount, spn, resp_hash_hex, qop);
497   free(spn);
498   if(!response)
499     return CURLE_OUT_OF_MEMORY;
500 
501   /* Base64 encode the response */
502   result = Curl_base64_encode(data, response, 0, outptr, outlen);
503 
504   free(response);
505 
506   return result;
507 }
508 
509 /*
510  * Curl_auth_decode_digest_http_message()
511  *
512  * This is used to decode a HTTP DIGEST challenge message into the separate
513  * attributes.
514  *
515  * Parameters:
516  *
517  * chlg    [in]     - The challenge message.
518  * digest  [in/out] - The digest data struct being used and modified.
519  *
520  * Returns CURLE_OK on success.
521  */
Curl_auth_decode_digest_http_message(const char * chlg,struct digestdata * digest)522 CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
523                                               struct digestdata *digest)
524 {
525   bool before = FALSE; /* got a nonce before */
526   bool foundAuth = FALSE;
527   bool foundAuthInt = FALSE;
528   char *token = NULL;
529   char *tmp = NULL;
530 
531   /* If we already have received a nonce, keep that in mind */
532   if(digest->nonce)
533     before = TRUE;
534 
535   /* Clean up any former leftovers and initialise to defaults */
536   Curl_auth_digest_cleanup(digest);
537 
538   for(;;) {
539     char value[DIGEST_MAX_VALUE_LENGTH];
540     char content[DIGEST_MAX_CONTENT_LENGTH];
541 
542     /* Pass all additional spaces here */
543     while(*chlg && ISSPACE(*chlg))
544       chlg++;
545 
546     /* Extract a value=content pair */
547     if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) {
548       if(strcasecompare(value, "nonce")) {
549         free(digest->nonce);
550         digest->nonce = strdup(content);
551         if(!digest->nonce)
552           return CURLE_OUT_OF_MEMORY;
553       }
554       else if(strcasecompare(value, "stale")) {
555         if(strcasecompare(content, "true")) {
556           digest->stale = TRUE;
557           digest->nc = 1; /* we make a new nonce now */
558         }
559       }
560       else if(strcasecompare(value, "realm")) {
561         free(digest->realm);
562         digest->realm = strdup(content);
563         if(!digest->realm)
564           return CURLE_OUT_OF_MEMORY;
565       }
566       else if(strcasecompare(value, "opaque")) {
567         free(digest->opaque);
568         digest->opaque = strdup(content);
569         if(!digest->opaque)
570           return CURLE_OUT_OF_MEMORY;
571       }
572       else if(strcasecompare(value, "qop")) {
573         char *tok_buf = NULL;
574         /* Tokenize the list and choose auth if possible, use a temporary
575            clone of the buffer since strtok_r() ruins it */
576         tmp = strdup(content);
577         if(!tmp)
578           return CURLE_OUT_OF_MEMORY;
579 
580         token = strtok_r(tmp, ",", &tok_buf);
581         while(token != NULL) {
582           if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
583             foundAuth = TRUE;
584           }
585           else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
586             foundAuthInt = TRUE;
587           }
588           token = strtok_r(NULL, ",", &tok_buf);
589         }
590 
591         free(tmp);
592 
593         /* Select only auth or auth-int. Otherwise, ignore */
594         if(foundAuth) {
595           free(digest->qop);
596           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH);
597           if(!digest->qop)
598             return CURLE_OUT_OF_MEMORY;
599         }
600         else if(foundAuthInt) {
601           free(digest->qop);
602           digest->qop = strdup(DIGEST_QOP_VALUE_STRING_AUTH_INT);
603           if(!digest->qop)
604             return CURLE_OUT_OF_MEMORY;
605         }
606       }
607       else if(strcasecompare(value, "algorithm")) {
608         free(digest->algorithm);
609         digest->algorithm = strdup(content);
610         if(!digest->algorithm)
611           return CURLE_OUT_OF_MEMORY;
612 
613         if(strcasecompare(content, "MD5-sess"))
614           digest->algo = CURLDIGESTALGO_MD5SESS;
615         else if(strcasecompare(content, "MD5"))
616           digest->algo = CURLDIGESTALGO_MD5;
617         else if(strcasecompare(content, "SHA-256"))
618           digest->algo = CURLDIGESTALGO_SHA256;
619         else if(strcasecompare(content, "SHA-256-SESS"))
620           digest->algo = CURLDIGESTALGO_SHA256SESS;
621         else if(strcasecompare(content, "SHA-512-256"))
622           digest->algo = CURLDIGESTALGO_SHA512_256;
623         else if(strcasecompare(content, "SHA-512-256-SESS"))
624           digest->algo = CURLDIGESTALGO_SHA512_256SESS;
625         else
626           return CURLE_BAD_CONTENT_ENCODING;
627       }
628       else if(strcasecompare(value, "userhash")) {
629         if(strcasecompare(content, "true")) {
630           digest->userhash = TRUE;
631         }
632       }
633       else {
634         /* Unknown specifier, ignore it! */
635       }
636     }
637     else
638       break; /* We're done here */
639 
640     /* Pass all additional spaces here */
641     while(*chlg && ISSPACE(*chlg))
642       chlg++;
643 
644     /* Allow the list to be comma-separated */
645     if(',' == *chlg)
646       chlg++;
647   }
648 
649   /* We had a nonce since before, and we got another one now without
650      'stale=true'. This means we provided bad credentials in the previous
651      request */
652   if(before && !digest->stale)
653     return CURLE_BAD_CONTENT_ENCODING;
654 
655   /* We got this header without a nonce, that's a bad Digest line! */
656   if(!digest->nonce)
657     return CURLE_BAD_CONTENT_ENCODING;
658 
659   return CURLE_OK;
660 }
661 
662 /*
663  * _Curl_auth_create_digest_http_message()
664  *
665  * This is used to generate a HTTP DIGEST response message ready for sending
666  * to the recipient.
667  *
668  * Parameters:
669  *
670  * data    [in]     - The session handle.
671  * userp   [in]     - The user name.
672  * passwdp [in]     - The user's password.
673  * request [in]     - The HTTP request.
674  * uripath [in]     - The path of the HTTP uri.
675  * digest  [in/out] - The digest data struct being used and modified.
676  * outptr  [in/out] - The address where a pointer to newly allocated memory
677  *                    holding the result will be stored upon completion.
678  * outlen  [out]    - The length of the output message.
679  *
680  * Returns CURLE_OK on success.
681  */
_Curl_auth_create_digest_http_message(struct Curl_easy * data,const char * userp,const char * passwdp,const unsigned char * request,const unsigned char * uripath,struct digestdata * digest,char ** outptr,size_t * outlen,void (* convert_to_ascii)(unsigned char *,unsigned char *),void (* hash)(unsigned char *,const unsigned char *))682 static CURLcode _Curl_auth_create_digest_http_message(
683                   struct Curl_easy *data,
684                   const char *userp,
685                   const char *passwdp,
686                   const unsigned char *request,
687                   const unsigned char *uripath,
688                   struct digestdata *digest,
689                   char **outptr, size_t *outlen,
690                   void (*convert_to_ascii)(unsigned char *, unsigned char *),
691                   void (*hash)(unsigned char *, const unsigned char *))
692 {
693   CURLcode result;
694   unsigned char hashbuf[32]; /* 32 bytes/256 bits */
695   unsigned char request_digest[65];
696   unsigned char *hashthis;
697   unsigned char ha1[65];    /* 64 digits and 1 zero byte */
698   unsigned char ha2[65];    /* 64 digits and 1 zero byte */
699   char userh[65];
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     char cnoncebuf[33];
711     result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
712                            sizeof(cnoncebuf));
713     if(result)
714       return result;
715 
716     result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
717                                 &cnonce, &cnonce_sz);
718     if(result)
719       return result;
720 
721     digest->cnonce = cnonce;
722   }
723 
724   if(digest->userhash) {
725     hashthis = (unsigned char *) aprintf("%s:%s", userp, digest->realm);
726     if(!hashthis)
727       return CURLE_OUT_OF_MEMORY;
728 
729     CURL_OUTPUT_DIGEST_CONV(data, hashthis);
730     hash(hashbuf, hashthis);
731     free(hashthis);
732     convert_to_ascii(hashbuf, (unsigned char *)userh);
733   }
734 
735   /*
736     If the algorithm is "MD5" or unspecified (which then defaults to MD5):
737 
738       A1 = unq(username-value) ":" unq(realm-value) ":" passwd
739 
740     If the algorithm is "MD5-sess" then:
741 
742       A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
743            unq(nonce-value) ":" unq(cnonce-value)
744   */
745 
746   hashthis = (unsigned char *)
747     aprintf("%s:%s:%s", digest->userhash ? userh : userp,
748                                     digest->realm, passwdp);
749   if(!hashthis)
750     return CURLE_OUT_OF_MEMORY;
751 
752   CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
753   hash(hashbuf, hashthis);
754   free(hashthis);
755   convert_to_ascii(hashbuf, ha1);
756 
757   if(digest->algo == CURLDIGESTALGO_MD5SESS ||
758      digest->algo == CURLDIGESTALGO_SHA256SESS ||
759      digest->algo == CURLDIGESTALGO_SHA512_256SESS) {
760     /* nonce and cnonce are OUTSIDE the hash */
761     tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce);
762     if(!tmp)
763       return CURLE_OUT_OF_MEMORY;
764 
765     CURL_OUTPUT_DIGEST_CONV(data, tmp); /* Convert on non-ASCII machines */
766     hash(hashbuf, (unsigned char *) tmp);
767     free(tmp);
768     convert_to_ascii(hashbuf, ha1);
769   }
770 
771   /*
772     If the "qop" directive's value is "auth" or is unspecified, then A2 is:
773 
774       A2 = Method ":" digest-uri-value
775 
776     If the "qop" value is "auth-int", then A2 is:
777 
778       A2 = Method ":" digest-uri-value ":" H(entity-body)
779 
780     (The "Method" value is the HTTP request method as specified in section
781     5.1.1 of RFC 2616)
782   */
783 
784   hashthis = (unsigned char *) aprintf("%s:%s", request, uripath);
785   if(!hashthis)
786     return CURLE_OUT_OF_MEMORY;
787 
788   if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
789     /* We don't support auth-int for PUT or POST */
790     char hashed[65];
791     unsigned char *hashthis2;
792 
793     hash(hashbuf, (const unsigned char *)"");
794     convert_to_ascii(hashbuf, (unsigned char *)hashed);
795 
796     hashthis2 = (unsigned char *)aprintf("%s:%s", hashthis, hashed);
797     free(hashthis);
798     hashthis = hashthis2;
799   }
800 
801   if(!hashthis)
802     return CURLE_OUT_OF_MEMORY;
803 
804   CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
805   hash(hashbuf, hashthis);
806   free(hashthis);
807   convert_to_ascii(hashbuf, ha2);
808 
809   if(digest->qop) {
810     hashthis = (unsigned char *) aprintf("%s:%s:%08x:%s:%s:%s",
811                                         ha1,
812                                         digest->nonce,
813                                         digest->nc,
814                                         digest->cnonce,
815                                         digest->qop,
816                                         ha2);
817   }
818   else {
819     hashthis = (unsigned char *) aprintf("%s:%s:%s",
820                                         ha1,
821                                         digest->nonce,
822                                         ha2);
823   }
824 
825   if(!hashthis)
826     return CURLE_OUT_OF_MEMORY;
827 
828   CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
829   hash(hashbuf, hashthis);
830   free(hashthis);
831   convert_to_ascii(hashbuf, request_digest);
832 
833   /* For test case 64 (snooped from a Mozilla 1.3a request)
834 
835      Authorization: Digest username="testuser", realm="testrealm", \
836      nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
837 
838      Digest parameters are all quoted strings.  Username which is provided by
839      the user will need double quotes and backslashes within it escaped.  For
840      the other fields, this shouldn't be an issue.  realm, nonce, and opaque
841      are copied as is from the server, escapes and all.  cnonce is generated
842      with web-safe characters.  uri is already percent encoded.  nc is 8 hex
843      characters.  algorithm and qop with standard values only contain web-safe
844      characters.
845   */
846   userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
847   if(!userp_quoted)
848     return CURLE_OUT_OF_MEMORY;
849 
850   if(digest->qop) {
851     response = aprintf("username=\"%s\", "
852                        "realm=\"%s\", "
853                        "nonce=\"%s\", "
854                        "uri=\"%s\", "
855                        "cnonce=\"%s\", "
856                        "nc=%08x, "
857                        "qop=%s, "
858                        "response=\"%s\"",
859                        userp_quoted,
860                        digest->realm,
861                        digest->nonce,
862                        uripath,
863                        digest->cnonce,
864                        digest->nc,
865                        digest->qop,
866                        request_digest);
867 
868     if(strcasecompare(digest->qop, "auth"))
869       digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
870                        padded which tells to the server how many times you are
871                        using the same nonce in the qop=auth mode */
872   }
873   else {
874     response = aprintf("username=\"%s\", "
875                        "realm=\"%s\", "
876                        "nonce=\"%s\", "
877                        "uri=\"%s\", "
878                        "response=\"%s\"",
879                        userp_quoted,
880                        digest->realm,
881                        digest->nonce,
882                        uripath,
883                        request_digest);
884   }
885   free(userp_quoted);
886   if(!response)
887     return CURLE_OUT_OF_MEMORY;
888 
889   /* Add the optional fields */
890   if(digest->opaque) {
891     /* Append the opaque */
892     tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
893     free(response);
894     if(!tmp)
895       return CURLE_OUT_OF_MEMORY;
896 
897     response = tmp;
898   }
899 
900   if(digest->algorithm) {
901     /* Append the algorithm */
902     tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm);
903     free(response);
904     if(!tmp)
905       return CURLE_OUT_OF_MEMORY;
906 
907     response = tmp;
908   }
909 
910   if(digest->userhash) {
911     /* Append the userhash */
912     tmp = aprintf("%s, userhash=true", response);
913     free(response);
914     if(!tmp)
915       return CURLE_OUT_OF_MEMORY;
916 
917     response = tmp;
918   }
919 
920   /* Return the output */
921   *outptr = response;
922   *outlen = strlen(response);
923 
924   return CURLE_OK;
925 }
926 
927 /*
928  * Curl_auth_create_digest_http_message()
929  *
930  * This is used to generate a HTTP DIGEST response message ready for sending
931  * to the recipient.
932  *
933  * Parameters:
934  *
935  * data    [in]     - The session handle.
936  * userp   [in]     - The user name.
937  * passwdp [in]     - The user's password.
938  * request [in]     - The HTTP request.
939  * uripath [in]     - The path of the HTTP uri.
940  * digest  [in/out] - The digest data struct being used and modified.
941  * outptr  [in/out] - The address where a pointer to newly allocated memory
942  *                    holding the result will be stored upon completion.
943  * outlen  [out]    - The length of the output message.
944  *
945  * Returns CURLE_OK on success.
946  */
Curl_auth_create_digest_http_message(struct Curl_easy * data,const char * userp,const char * passwdp,const unsigned char * request,const unsigned char * uripath,struct digestdata * digest,char ** outptr,size_t * outlen)947 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
948                                               const char *userp,
949                                               const char *passwdp,
950                                               const unsigned char *request,
951                                               const unsigned char *uripath,
952                                               struct digestdata *digest,
953                                               char **outptr, size_t *outlen)
954 {
955   switch(digest->algo) {
956   case CURLDIGESTALGO_MD5:
957   case CURLDIGESTALGO_MD5SESS:
958     return _Curl_auth_create_digest_http_message(data, userp, passwdp,
959                                                  request, uripath, digest,
960                                                  outptr, outlen,
961                                                  auth_digest_md5_to_ascii,
962                                                  Curl_md5it);
963 
964   case CURLDIGESTALGO_SHA256:
965   case CURLDIGESTALGO_SHA256SESS:
966   case CURLDIGESTALGO_SHA512_256:
967   case CURLDIGESTALGO_SHA512_256SESS:
968     return _Curl_auth_create_digest_http_message(data, userp, passwdp,
969                                                  request, uripath, digest,
970                                                  outptr, outlen,
971                                                  auth_digest_sha256_to_ascii,
972                                                  Curl_sha256it);
973 
974   default:
975     return CURLE_UNSUPPORTED_PROTOCOL;
976   }
977 }
978 
979 /*
980  * Curl_auth_digest_cleanup()
981  *
982  * This is used to clean up the digest specific data.
983  *
984  * Parameters:
985  *
986  * digest    [in/out] - The digest data struct being cleaned up.
987  *
988  */
Curl_auth_digest_cleanup(struct digestdata * digest)989 void Curl_auth_digest_cleanup(struct digestdata *digest)
990 {
991   Curl_safefree(digest->nonce);
992   Curl_safefree(digest->cnonce);
993   Curl_safefree(digest->realm);
994   Curl_safefree(digest->opaque);
995   Curl_safefree(digest->qop);
996   Curl_safefree(digest->algorithm);
997 
998   digest->nc = 0;
999   digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
1000   digest->stale = FALSE; /* default means normal, not stale */
1001   digest->userhash = FALSE;
1002 }
1003 #endif  /* !USE_WINDOWS_SSPI */
1004 
1005 #endif  /* CURL_DISABLE_CRYPTO_AUTH */
1006