1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel (at) haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.haxx.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * RFC2831 DIGEST-MD5 authentication 22 * RFC7616 DIGEST-SHA256, DIGEST-SHA512-256 authentication 23 * 24 ***************************************************************************/ 25 26 #include "curl_setup.h" 27 28 #if !defined(CURL_DISABLE_CRYPTO_AUTH) 29 30 #include <curl/curl.h> 31 32 #include "vauth/vauth.h" 33 #include "vauth/digest.h" 34 #include "urldata.h" 35 #include "curl_base64.h" 36 #include "curl_hmac.h" 37 #include "curl_md5.h" 38 #include "curl_sha256.h" 39 #include "vtls/vtls.h" 40 #include "warnless.h" 41 #include "strtok.h" 42 #include "strcase.h" 43 #include "non-ascii.h" /* included for Curl_convert_... prototypes */ 44 #include "curl_printf.h" 45 #include "rand.h" 46 47 /* The last #include files should be: */ 48 #include "curl_memory.h" 49 #include "memdebug.h" 50 51 #if !defined(USE_WINDOWS_SSPI) 52 #define DIGEST_QOP_VALUE_AUTH (1 << 0) 53 #define DIGEST_QOP_VALUE_AUTH_INT (1 << 1) 54 #define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2) 55 56 #define DIGEST_QOP_VALUE_STRING_AUTH "auth" 57 #define DIGEST_QOP_VALUE_STRING_AUTH_INT "auth-int" 58 #define DIGEST_QOP_VALUE_STRING_AUTH_CONF "auth-conf" 59 60 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines. 61 It converts digest text to ASCII so the MD5 will be correct for 62 what ultimately goes over the network. 63 */ 64 #define CURL_OUTPUT_DIGEST_CONV(a, b) \ 65 result = Curl_convert_to_network(a, (char *)b, strlen((const char *)b)); \ 66 if(result) { \ 67 free(b); \ 68 return result; \ 69 } 70 #endif /* !USE_WINDOWS_SSPI */ 71 72 bool Curl_auth_digest_get_pair(const char *str, char *value, char *content, 73 const char **endptr) 74 { 75 int c; 76 bool starts_with_quote = FALSE; 77 bool escape = FALSE; 78 79 for(c = DIGEST_MAX_VALUE_LENGTH - 1; (*str && (*str != '=') && c--);) 80 *value++ = *str++; 81 *value = 0; 82 83 if('=' != *str++) 84 /* eek, no match */ 85 return FALSE; 86 87 if('\"' == *str) { 88 /* This starts with a quote so it must end with one as well! */ 89 str++; 90 starts_with_quote = TRUE; 91 } 92 93 for(c = DIGEST_MAX_CONTENT_LENGTH - 1; *str && c--; str++) { 94 switch(*str) { 95 case '\\': 96 if(!escape) { 97 /* possibly the start of an escaped quote */ 98 escape = TRUE; 99 *content++ = '\\'; /* Even though this is an escape character, we still 100 store it as-is in the target buffer */ 101 continue; 102 } 103 break; 104 105 case ',': 106 if(!starts_with_quote) { 107 /* This signals the end of the content if we didn't get a starting 108 quote and then we do "sloppy" parsing */ 109 c = 0; /* the end */ 110 continue; 111 } 112 break; 113 114 case '\r': 115 case '\n': 116 /* end of string */ 117 c = 0; 118 continue; 119 120 case '\"': 121 if(!escape && starts_with_quote) { 122 /* end of string */ 123 c = 0; 124 continue; 125 } 126 break; 127 } 128 129 escape = FALSE; 130 *content++ = *str; 131 } 132 133 *content = 0; 134 *endptr = str; 135 136 return TRUE; 137 } 138 139 #if !defined(USE_WINDOWS_SSPI) 140 /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ 141 static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ 142 unsigned char *dest) /* 33 bytes */ 143 { 144 int i; 145 for(i = 0; i < 16; i++) 146 msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); 147 } 148 149 /* Convert sha256 chunk to RFC7616 -suitable ascii string*/ 150 static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ 151 unsigned char *dest) /* 65 bytes */ 152 { 153 int i; 154 for(i = 0; i < 32; i++) 155 msnprintf((char *) &dest[i * 2], 3, "%02x", source[i]); 156 } 157 158 /* Perform quoted-string escaping as described in RFC2616 and its errata */ 159 static char *auth_digest_string_quoted(const char *source) 160 { 161 char *dest; 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 */ 193 static bool auth_digest_get_key_value(const char *chlg, 194 const char *key, 195 char *value, 196 size_t max_val_len, 197 char end_char) 198 { 199 char *find_pos; 200 size_t i; 201 202 find_pos = strstr(chlg, key); 203 if(!find_pos) 204 return FALSE; 205 206 find_pos += strlen(key); 207 208 for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i) 209 value[i] = *find_pos++; 210 value[i] = '\0'; 211 212 return TRUE; 213 } 214 215 static CURLcode auth_digest_get_qop_values(const char *options, int *value) 216 { 217 char *tmp; 218 char *token; 219 char *tok_buf = NULL; 220 221 /* Initialise the output */ 222 *value = 0; 223 224 /* Tokenise the list of qop values. Use a temporary clone of the buffer since 225 strtok_r() ruins it. */ 226 tmp = strdup(options); 227 if(!tmp) 228 return CURLE_OUT_OF_MEMORY; 229 230 token = strtok_r(tmp, ",", &tok_buf); 231 while(token != NULL) { 232 if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) 233 *value |= DIGEST_QOP_VALUE_AUTH; 234 else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) 235 *value |= DIGEST_QOP_VALUE_AUTH_INT; 236 else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF)) 237 *value |= DIGEST_QOP_VALUE_AUTH_CONF; 238 239 token = strtok_r(NULL, ",", &tok_buf); 240 } 241 242 free(tmp); 243 244 return CURLE_OK; 245 } 246 247 /* 248 * auth_decode_digest_md5_message() 249 * 250 * This is used internally to decode an already encoded DIGEST-MD5 challenge 251 * message into the separate attributes. 252 * 253 * Parameters: 254 * 255 * chlg64 [in] - The base64 encoded challenge message. 256 * nonce [in/out] - The buffer where the nonce will be stored. 257 * nlen [in] - The length of the nonce buffer. 258 * realm [in/out] - The buffer where the realm will be stored. 259 * rlen [in] - The length of the realm buffer. 260 * alg [in/out] - The buffer where the algorithm will be stored. 261 * alen [in] - The length of the algorithm buffer. 262 * qop [in/out] - The buffer where the qop-options will be stored. 263 * qlen [in] - The length of the qop buffer. 264 * 265 * Returns CURLE_OK on success. 266 */ 267 static CURLcode auth_decode_digest_md5_message(const char *chlg64, 268 char *nonce, size_t nlen, 269 char *realm, size_t rlen, 270 char *alg, size_t alen, 271 char *qop, size_t qlen) 272 { 273 CURLcode result = CURLE_OK; 274 unsigned char *chlg = NULL; 275 size_t chlglen = 0; 276 size_t chlg64len = strlen(chlg64); 277 278 /* Decode the base-64 encoded challenge message */ 279 if(chlg64len && *chlg64 != '=') { 280 result = Curl_base64_decode(chlg64, &chlg, &chlglen); 281 if(result) 282 return result; 283 } 284 285 /* Ensure we have a valid challenge message */ 286 if(!chlg) 287 return CURLE_BAD_CONTENT_ENCODING; 288 289 /* Retrieve nonce string from the challenge */ 290 if(!auth_digest_get_key_value((char *) chlg, "nonce=\"", nonce, nlen, 291 '\"')) { 292 free(chlg); 293 return CURLE_BAD_CONTENT_ENCODING; 294 } 295 296 /* Retrieve realm string from the challenge */ 297 if(!auth_digest_get_key_value((char *) chlg, "realm=\"", realm, rlen, 298 '\"')) { 299 /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ 300 strcpy(realm, ""); 301 } 302 303 /* Retrieve algorithm string from the challenge */ 304 if(!auth_digest_get_key_value((char *) chlg, "algorithm=", alg, alen, ',')) { 305 free(chlg); 306 return CURLE_BAD_CONTENT_ENCODING; 307 } 308 309 /* Retrieve qop-options string from the challenge */ 310 if(!auth_digest_get_key_value((char *) chlg, "qop=\"", qop, qlen, '\"')) { 311 free(chlg); 312 return CURLE_BAD_CONTENT_ENCODING; 313 } 314 315 free(chlg); 316 317 return CURLE_OK; 318 } 319 320 /* 321 * Curl_auth_is_digest_supported() 322 * 323 * This is used to evaluate if DIGEST is supported. 324 * 325 * Parameters: None 326 * 327 * Returns TRUE as DIGEST as handled by libcurl. 328 */ 329 bool Curl_auth_is_digest_supported(void) 330 { 331 return TRUE; 332 } 333 334 /* 335 * Curl_auth_create_digest_md5_message() 336 * 337 * This is used to generate an already encoded DIGEST-MD5 response message 338 * ready for sending to the recipient. 339 * 340 * Parameters: 341 * 342 * data [in] - The session handle. 343 * chlg64 [in] - The base64 encoded challenge message. 344 * userp [in] - The user name. 345 * 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 */ 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 */ 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 */ 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 */ 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 */ 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