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