1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2020, 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, b, strlen(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 struct 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 * 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 */
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 *,const size_t))682 static CURLcode 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 const size_t))
693 {
694 CURLcode result;
695 unsigned char hashbuf[32]; /* 32 bytes/256 bits */
696 unsigned char request_digest[65];
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 *hashthis = NULL;
705 char *tmp = NULL;
706
707 if(!digest->nc)
708 digest->nc = 1;
709
710 if(!digest->cnonce) {
711 char cnoncebuf[33];
712 result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
713 sizeof(cnoncebuf));
714 if(result)
715 return result;
716
717 result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf),
718 &cnonce, &cnonce_sz);
719 if(result)
720 return result;
721
722 digest->cnonce = cnonce;
723 }
724
725 if(digest->userhash) {
726 hashthis = aprintf("%s:%s", userp, digest->realm);
727 if(!hashthis)
728 return CURLE_OUT_OF_MEMORY;
729
730 CURL_OUTPUT_DIGEST_CONV(data, hashthis);
731 hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
732 free(hashthis);
733 convert_to_ascii(hashbuf, (unsigned char *)userh);
734 }
735
736 /*
737 If the algorithm is "MD5" or unspecified (which then defaults to MD5):
738
739 A1 = unq(username-value) ":" unq(realm-value) ":" passwd
740
741 If the algorithm is "MD5-sess" then:
742
743 A1 = H(unq(username-value) ":" unq(realm-value) ":" passwd) ":"
744 unq(nonce-value) ":" unq(cnonce-value)
745 */
746
747 hashthis = 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, (unsigned char *) hashthis, strlen(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, strlen(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 = 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 char *hashthis2;
792
793 hash(hashbuf, (const unsigned char *)"", 0);
794 convert_to_ascii(hashbuf, (unsigned char *)hashed);
795
796 hashthis2 = 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, (unsigned char *) hashthis, strlen(hashthis));
806 free(hashthis);
807 convert_to_ascii(hashbuf, ha2);
808
809 if(digest->qop) {
810 hashthis = aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc,
811 digest->cnonce, digest->qop, ha2);
812 }
813 else {
814 hashthis = aprintf("%s:%s:%s", ha1, digest->nonce, ha2);
815 }
816
817 if(!hashthis)
818 return CURLE_OUT_OF_MEMORY;
819
820 CURL_OUTPUT_DIGEST_CONV(data, hashthis); /* convert on non-ASCII machines */
821 hash(hashbuf, (unsigned char *) hashthis, strlen(hashthis));
822 free(hashthis);
823 convert_to_ascii(hashbuf, request_digest);
824
825 /* For test case 64 (snooped from a Mozilla 1.3a request)
826
827 Authorization: Digest username="testuser", realm="testrealm", \
828 nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca"
829
830 Digest parameters are all quoted strings. Username which is provided by
831 the user will need double quotes and backslashes within it escaped. For
832 the other fields, this shouldn't be an issue. realm, nonce, and opaque
833 are copied as is from the server, escapes and all. cnonce is generated
834 with web-safe characters. uri is already percent encoded. nc is 8 hex
835 characters. algorithm and qop with standard values only contain web-safe
836 characters.
837 */
838 userp_quoted = auth_digest_string_quoted(digest->userhash ? userh : userp);
839 if(!userp_quoted)
840 return CURLE_OUT_OF_MEMORY;
841
842 if(digest->qop) {
843 response = aprintf("username=\"%s\", "
844 "realm=\"%s\", "
845 "nonce=\"%s\", "
846 "uri=\"%s\", "
847 "cnonce=\"%s\", "
848 "nc=%08x, "
849 "qop=%s, "
850 "response=\"%s\"",
851 userp_quoted,
852 digest->realm,
853 digest->nonce,
854 uripath,
855 digest->cnonce,
856 digest->nc,
857 digest->qop,
858 request_digest);
859
860 if(strcasecompare(digest->qop, "auth"))
861 digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0
862 padded which tells to the server how many times you are
863 using the same nonce in the qop=auth mode */
864 }
865 else {
866 response = aprintf("username=\"%s\", "
867 "realm=\"%s\", "
868 "nonce=\"%s\", "
869 "uri=\"%s\", "
870 "response=\"%s\"",
871 userp_quoted,
872 digest->realm,
873 digest->nonce,
874 uripath,
875 request_digest);
876 }
877 free(userp_quoted);
878 if(!response)
879 return CURLE_OUT_OF_MEMORY;
880
881 /* Add the optional fields */
882 if(digest->opaque) {
883 /* Append the opaque */
884 tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque);
885 free(response);
886 if(!tmp)
887 return CURLE_OUT_OF_MEMORY;
888
889 response = tmp;
890 }
891
892 if(digest->algorithm) {
893 /* Append the algorithm */
894 tmp = aprintf("%s, algorithm=%s", response, digest->algorithm);
895 free(response);
896 if(!tmp)
897 return CURLE_OUT_OF_MEMORY;
898
899 response = tmp;
900 }
901
902 if(digest->userhash) {
903 /* Append the userhash */
904 tmp = aprintf("%s, userhash=true", response);
905 free(response);
906 if(!tmp)
907 return CURLE_OUT_OF_MEMORY;
908
909 response = tmp;
910 }
911
912 /* Return the output */
913 *outptr = response;
914 *outlen = strlen(response);
915
916 return CURLE_OK;
917 }
918
919 /*
920 * Curl_auth_create_digest_http_message()
921 *
922 * This is used to generate a HTTP DIGEST response message ready for sending
923 * to the recipient.
924 *
925 * Parameters:
926 *
927 * data [in] - The session handle.
928 * userp [in] - The user name.
929 * passwdp [in] - The user's password.
930 * request [in] - The HTTP request.
931 * uripath [in] - The path of the HTTP uri.
932 * digest [in/out] - The digest data struct being used and modified.
933 * outptr [in/out] - The address where a pointer to newly allocated memory
934 * holding the result will be stored upon completion.
935 * outlen [out] - The length of the output message.
936 *
937 * Returns CURLE_OK on success.
938 */
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)939 CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
940 const char *userp,
941 const char *passwdp,
942 const unsigned char *request,
943 const unsigned char *uripath,
944 struct digestdata *digest,
945 char **outptr, size_t *outlen)
946 {
947 switch(digest->algo) {
948 case CURLDIGESTALGO_MD5:
949 case CURLDIGESTALGO_MD5SESS:
950 return auth_create_digest_http_message(data, userp, passwdp,
951 request, uripath, digest,
952 outptr, outlen,
953 auth_digest_md5_to_ascii,
954 Curl_md5it);
955
956 case CURLDIGESTALGO_SHA256:
957 case CURLDIGESTALGO_SHA256SESS:
958 case CURLDIGESTALGO_SHA512_256:
959 case CURLDIGESTALGO_SHA512_256SESS:
960 return auth_create_digest_http_message(data, userp, passwdp,
961 request, uripath, digest,
962 outptr, outlen,
963 auth_digest_sha256_to_ascii,
964 Curl_sha256it);
965
966 default:
967 return CURLE_UNSUPPORTED_PROTOCOL;
968 }
969 }
970
971 /*
972 * Curl_auth_digest_cleanup()
973 *
974 * This is used to clean up the digest specific data.
975 *
976 * Parameters:
977 *
978 * digest [in/out] - The digest data struct being cleaned up.
979 *
980 */
Curl_auth_digest_cleanup(struct digestdata * digest)981 void Curl_auth_digest_cleanup(struct digestdata *digest)
982 {
983 Curl_safefree(digest->nonce);
984 Curl_safefree(digest->cnonce);
985 Curl_safefree(digest->realm);
986 Curl_safefree(digest->opaque);
987 Curl_safefree(digest->qop);
988 Curl_safefree(digest->algorithm);
989
990 digest->nc = 0;
991 digest->algo = CURLDIGESTALGO_MD5; /* default algorithm */
992 digest->stale = FALSE; /* default means normal, not stale */
993 digest->userhash = FALSE;
994 }
995 #endif /* !USE_WINDOWS_SSPI */
996
997 #endif /* CURL_DISABLE_CRYPTO_AUTH */
998