• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2018, 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   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     msnprintf(&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     msnprintf(&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     msnprintf(&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  */
Curl_auth_decode_digest_http_message(const char * chlg,struct digestdata * digest)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  * passwdp [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  */
_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 *))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 *cnonce = NULL;
700   size_t cnonce_sz = 0;
701   char *userp_quoted;
702   char *response = NULL;
703   char *tmp = NULL;
704 
705   if(!digest->nc)
706     digest->nc = 1;
707 
708   if(!digest->cnonce) {
709     char cnoncebuf[33];
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   if(!hashthis)
785     return CURLE_OUT_OF_MEMORY;
786 
787   if(digest->qop && strcasecompare(digest->qop, "auth-int")) {
788     /* We don't support auth-int for PUT or POST at the moment.
789        TODO: replace hash of empty string with entity-body for PUT/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