1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 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 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25#include "curl_setup.h" 26 27#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) 28 29#include "urldata.h" 30#include "strcase.h" 31#include "strdup.h" 32#include "http_aws_sigv4.h" 33#include "curl_sha256.h" 34#include "transfer.h" 35#include "parsedate.h" 36#include "sendf.h" 37#include "escape.h" 38 39#include <time.h> 40 41/* The last 3 #include files should be in this order */ 42#include "curl_printf.h" 43#include "curl_memory.h" 44#include "memdebug.h" 45 46#include "slist.h" 47 48#define HMAC_SHA256(k, kl, d, dl, o) \ 49 do { \ 50 result = Curl_hmacit(Curl_HMAC_SHA256, \ 51 (unsigned char *)k, \ 52 kl, \ 53 (unsigned char *)d, \ 54 dl, o); \ 55 if(result) { \ 56 goto fail; \ 57 } \ 58 } while(0) 59 60#define TIMESTAMP_SIZE 17 61 62/* hex-encoded with trailing null */ 63#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1) 64 65static void sha256_to_hex(char *dst, unsigned char *sha) 66{ 67 Curl_hexencode(sha, SHA256_DIGEST_LENGTH, 68 (unsigned char *)dst, SHA256_HEX_LENGTH); 69} 70 71static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr) 72{ 73 char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr)); 74 75 if(tmp) 76 return tmp; 77 return Curl_checkheaders(data, STRCONST("Date")); 78} 79 80/* remove whitespace, and lowercase all headers */ 81static void trim_headers(struct curl_slist *head) 82{ 83 struct curl_slist *l; 84 for(l = head; l; l = l->next) { 85 char *value; /* to read from */ 86 char *store; 87 size_t colon = strcspn(l->data, ":"); 88 Curl_strntolower(l->data, l->data, colon); 89 90 value = &l->data[colon]; 91 if(!*value) 92 continue; 93 ++value; 94 store = value; 95 96 /* skip leading whitespace */ 97 while(*value && ISBLANK(*value)) 98 value++; 99 100 while(*value) { 101 int space = 0; 102 while(*value && ISBLANK(*value)) { 103 value++; 104 space++; 105 } 106 if(space) { 107 /* replace any number of consecutive whitespace with a single space, 108 unless at the end of the string, then nothing */ 109 if(*value) 110 *store++ = ' '; 111 } 112 else 113 *store++ = *value++; 114 } 115 *store = 0; /* null terminate */ 116 } 117} 118 119/* maximum length for the aws sivg4 parts */ 120#define MAX_SIGV4_LEN 64 121#define MAX_SIGV4_LEN_TXT "64" 122 123#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date")) 124 125#define MAX_HOST_LEN 255 126/* FQDN + host: */ 127#define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:")) 128 129/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */ 130#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1) 131 132/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */ 133static CURLcode make_headers(struct Curl_easy *data, 134 const char *hostname, 135 char *timestamp, 136 char *provider1, 137 char **date_header, 138 char *content_sha256_header, 139 struct dynbuf *canonical_headers, 140 struct dynbuf *signed_headers) 141{ 142 char date_hdr_key[DATE_HDR_KEY_LEN]; 143 char date_full_hdr[DATE_FULL_HDR_LEN]; 144 struct curl_slist *head = NULL; 145 struct curl_slist *tmp_head = NULL; 146 CURLcode ret = CURLE_OUT_OF_MEMORY; 147 struct curl_slist *l; 148 int again = 1; 149 150 /* provider1 mid */ 151 Curl_strntolower(provider1, provider1, strlen(provider1)); 152 provider1[0] = Curl_raw_toupper(provider1[0]); 153 154 msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1); 155 156 /* provider1 lowercase */ 157 Curl_strntolower(provider1, provider1, 1); /* first byte only */ 158 msnprintf(date_full_hdr, DATE_FULL_HDR_LEN, 159 "x-%s-date:%s", provider1, timestamp); 160 161 if(Curl_checkheaders(data, STRCONST("Host"))) { 162 head = NULL; 163 } 164 else { 165 char full_host[FULL_HOST_LEN + 1]; 166 167 if(data->state.aptr.host) { 168 size_t pos; 169 170 if(strlen(data->state.aptr.host) > FULL_HOST_LEN) { 171 ret = CURLE_URL_MALFORMAT; 172 goto fail; 173 } 174 strcpy(full_host, data->state.aptr.host); 175 /* remove /r/n as the separator for canonical request must be '\n' */ 176 pos = strcspn(full_host, "\n\r"); 177 full_host[pos] = 0; 178 } 179 else { 180 if(strlen(hostname) > MAX_HOST_LEN) { 181 ret = CURLE_URL_MALFORMAT; 182 goto fail; 183 } 184 msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname); 185 } 186 187 head = curl_slist_append(NULL, full_host); 188 if(!head) 189 goto fail; 190 } 191 192 193 if(*content_sha256_header) { 194 tmp_head = curl_slist_append(head, content_sha256_header); 195 if(!tmp_head) 196 goto fail; 197 head = tmp_head; 198 } 199 200 /* copy user headers to our header list. the logic is based on how http.c 201 handles user headers. 202 203 user headers in format 'name:' with no value are used to signal that an 204 internal header of that name should be removed. those user headers are not 205 added to this list. 206 207 user headers in format 'name;' with no value are used to signal that a 208 header of that name with no value should be sent. those user headers are 209 added to this list but in the format that they will be sent, ie the 210 semi-colon is changed to a colon for format 'name:'. 211 212 user headers with a value of whitespace only, or without a colon or 213 semi-colon, are not added to this list. 214 */ 215 for(l = data->set.headers; l; l = l->next) { 216 char *dupdata, *ptr; 217 char *sep = strchr(l->data, ':'); 218 if(!sep) 219 sep = strchr(l->data, ';'); 220 if(!sep || (*sep == ':' && !*(sep + 1))) 221 continue; 222 for(ptr = sep + 1; ISSPACE(*ptr); ++ptr) 223 ; 224 if(!*ptr && ptr != sep + 1) /* a value of whitespace only */ 225 continue; 226 dupdata = strdup(l->data); 227 if(!dupdata) 228 goto fail; 229 dupdata[sep - l->data] = ':'; 230 tmp_head = Curl_slist_append_nodup(head, dupdata); 231 if(!tmp_head) { 232 free(dupdata); 233 goto fail; 234 } 235 head = tmp_head; 236 } 237 238 trim_headers(head); 239 240 *date_header = find_date_hdr(data, date_hdr_key); 241 if(!*date_header) { 242 tmp_head = curl_slist_append(head, date_full_hdr); 243 if(!tmp_head) 244 goto fail; 245 head = tmp_head; 246 *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp); 247 } 248 else { 249 char *value; 250 char *endp; 251 value = strchr(*date_header, ':'); 252 if(!value) { 253 *date_header = NULL; 254 goto fail; 255 } 256 ++value; 257 while(ISBLANK(*value)) 258 ++value; 259 endp = value; 260 while(*endp && ISALNUM(*endp)) 261 ++endp; 262 /* 16 bytes => "19700101T000000Z" */ 263 if((endp - value) == TIMESTAMP_SIZE - 1) { 264 memcpy(timestamp, value, TIMESTAMP_SIZE - 1); 265 timestamp[TIMESTAMP_SIZE - 1] = 0; 266 } 267 else 268 /* bad timestamp length */ 269 timestamp[0] = 0; 270 *date_header = NULL; 271 } 272 273 /* alpha-sort in a case sensitive manner */ 274 do { 275 again = 0; 276 for(l = head; l; l = l->next) { 277 struct curl_slist *next = l->next; 278 279 if(next && strcmp(l->data, next->data) > 0) { 280 char *tmp = l->data; 281 282 l->data = next->data; 283 next->data = tmp; 284 again = 1; 285 } 286 } 287 } while(again); 288 289 for(l = head; l; l = l->next) { 290 char *tmp; 291 292 if(Curl_dyn_add(canonical_headers, l->data)) 293 goto fail; 294 if(Curl_dyn_add(canonical_headers, "\n")) 295 goto fail; 296 297 tmp = strchr(l->data, ':'); 298 if(tmp) 299 *tmp = 0; 300 301 if(l != head) { 302 if(Curl_dyn_add(signed_headers, ";")) 303 goto fail; 304 } 305 if(Curl_dyn_add(signed_headers, l->data)) 306 goto fail; 307 } 308 309 ret = CURLE_OK; 310fail: 311 curl_slist_free_all(head); 312 313 return ret; 314} 315 316#define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256")) 317/* add 2 for ": " between header name and value */ 318#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \ 319 SHA256_HEX_LENGTH) 320 321/* try to parse a payload hash from the content-sha256 header */ 322static char *parse_content_sha_hdr(struct Curl_easy *data, 323 const char *provider1, 324 size_t *value_len) 325{ 326 char key[CONTENT_SHA256_KEY_LEN]; 327 size_t key_len; 328 char *value; 329 size_t len; 330 331 key_len = msnprintf(key, sizeof(key), "x-%s-content-sha256", provider1); 332 333 value = Curl_checkheaders(data, key, key_len); 334 if(!value) 335 return NULL; 336 337 value = strchr(value, ':'); 338 if(!value) 339 return NULL; 340 ++value; 341 342 while(*value && ISBLANK(*value)) 343 ++value; 344 345 len = strlen(value); 346 while(len > 0 && ISBLANK(value[len-1])) 347 --len; 348 349 *value_len = len; 350 return value; 351} 352 353static CURLcode calc_payload_hash(struct Curl_easy *data, 354 unsigned char *sha_hash, char *sha_hex) 355{ 356 const char *post_data = data->set.postfields; 357 size_t post_data_len = 0; 358 CURLcode result; 359 360 if(post_data) { 361 if(data->set.postfieldsize < 0) 362 post_data_len = strlen(post_data); 363 else 364 post_data_len = (size_t)data->set.postfieldsize; 365 } 366 result = Curl_sha256it(sha_hash, (const unsigned char *) post_data, 367 post_data_len); 368 if(!result) 369 sha256_to_hex(sha_hex, sha_hash); 370 return result; 371} 372 373#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD" 374 375static CURLcode calc_s3_payload_hash(struct Curl_easy *data, 376 Curl_HttpReq httpreq, char *provider1, 377 unsigned char *sha_hash, 378 char *sha_hex, char *header) 379{ 380 bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD); 381 /* The request method or filesize indicate no request payload */ 382 bool empty_payload = (empty_method || data->set.filesize == 0); 383 /* The POST payload is in memory */ 384 bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields); 385 CURLcode ret = CURLE_OUT_OF_MEMORY; 386 387 if(empty_payload || post_payload) { 388 /* Calculate a real hash when we know the request payload */ 389 ret = calc_payload_hash(data, sha_hash, sha_hex); 390 if(ret) 391 goto fail; 392 } 393 else { 394 /* Fall back to s3's UNSIGNED-PAYLOAD */ 395 size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1; 396 DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */ 397 memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len); 398 sha_hex[len] = 0; 399 } 400 401 /* format the required content-sha256 header */ 402 msnprintf(header, CONTENT_SHA256_HDR_LEN, 403 "x-%s-content-sha256: %s", provider1, sha_hex); 404 405 ret = CURLE_OK; 406fail: 407 return ret; 408} 409 410struct pair { 411 const char *p; 412 size_t len; 413}; 414 415static int compare_func(const void *a, const void *b) 416{ 417 const struct pair *aa = a; 418 const struct pair *bb = b; 419 /* If one element is empty, the other is always sorted higher */ 420 if(aa->len == 0) 421 return -1; 422 if(bb->len == 0) 423 return 1; 424 return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len); 425} 426 427#define MAX_QUERYPAIRS 64 428 429static CURLcode canon_query(struct Curl_easy *data, 430 const char *query, struct dynbuf *dq) 431{ 432 CURLcode result = CURLE_OK; 433 int entry = 0; 434 int i; 435 const char *p = query; 436 struct pair array[MAX_QUERYPAIRS]; 437 struct pair *ap = &array[0]; 438 if(!query) 439 return result; 440 441 /* sort the name=value pairs first */ 442 do { 443 char *amp; 444 entry++; 445 ap->p = p; 446 amp = strchr(p, '&'); 447 if(amp) 448 ap->len = amp - p; /* excluding the ampersand */ 449 else { 450 ap->len = strlen(p); 451 break; 452 } 453 ap++; 454 p = amp + 1; 455 } while(entry < MAX_QUERYPAIRS); 456 if(entry == MAX_QUERYPAIRS) { 457 /* too many query pairs for us */ 458 failf(data, "aws-sigv4: too many query pairs in URL"); 459 return CURLE_URL_MALFORMAT; 460 } 461 462 qsort(&array[0], entry, sizeof(struct pair), compare_func); 463 464 ap = &array[0]; 465 for(i = 0; !result && (i < entry); i++, ap++) { 466 size_t len; 467 const char *q = ap->p; 468 bool found_equals = false; 469 if(!ap->len) 470 continue; 471 for(len = ap->len; len && !result; q++, len--) { 472 if(ISALNUM(*q)) 473 result = Curl_dyn_addn(dq, q, 1); 474 else { 475 switch(*q) { 476 case '-': 477 case '.': 478 case '_': 479 case '~': 480 /* allowed as-is */ 481 result = Curl_dyn_addn(dq, q, 1); 482 break; 483 case '=': 484 /* allowed as-is */ 485 result = Curl_dyn_addn(dq, q, 1); 486 found_equals = true; 487 break; 488 case '%': 489 /* uppercase the following if hexadecimal */ 490 if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) { 491 char tmp[3]="%"; 492 tmp[1] = Curl_raw_toupper(q[1]); 493 tmp[2] = Curl_raw_toupper(q[2]); 494 result = Curl_dyn_addn(dq, tmp, 3); 495 q += 2; 496 len -= 2; 497 } 498 else 499 /* '%' without a following two-digit hex, encode it */ 500 result = Curl_dyn_addn(dq, "%25", 3); 501 break; 502 default: { 503 /* URL encode */ 504 const char hex[] = "0123456789ABCDEF"; 505 char out[3]={'%'}; 506 out[1] = hex[((unsigned char)*q)>>4]; 507 out[2] = hex[*q & 0xf]; 508 result = Curl_dyn_addn(dq, out, 3); 509 break; 510 } 511 } 512 } 513 } 514 if(!result && !found_equals) { 515 /* queries without value still need an equals */ 516 result = Curl_dyn_addn(dq, "=", 1); 517 } 518 if(!result && i < entry - 1) { 519 /* insert ampersands between query pairs */ 520 result = Curl_dyn_addn(dq, "&", 1); 521 } 522 } 523 return result; 524} 525 526 527CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy) 528{ 529 CURLcode result = CURLE_OUT_OF_MEMORY; 530 struct connectdata *conn = data->conn; 531 size_t len; 532 const char *arg; 533 char provider0[MAX_SIGV4_LEN + 1]=""; 534 char provider1[MAX_SIGV4_LEN + 1]=""; 535 char region[MAX_SIGV4_LEN + 1]=""; 536 char service[MAX_SIGV4_LEN + 1]=""; 537 bool sign_as_s3 = false; 538 const char *hostname = conn->host.name; 539 time_t clock; 540 struct tm tm; 541 char timestamp[TIMESTAMP_SIZE]; 542 char date[9]; 543 struct dynbuf canonical_headers; 544 struct dynbuf signed_headers; 545 struct dynbuf canonical_query; 546 char *date_header = NULL; 547 Curl_HttpReq httpreq; 548 const char *method = NULL; 549 char *payload_hash = NULL; 550 size_t payload_hash_len = 0; 551 unsigned char sha_hash[SHA256_DIGEST_LENGTH]; 552 char sha_hex[SHA256_HEX_LENGTH]; 553 char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */ 554 char *canonical_request = NULL; 555 char *request_type = NULL; 556 char *credential_scope = NULL; 557 char *str_to_sign = NULL; 558 const char *user = data->state.aptr.user ? data->state.aptr.user : ""; 559 char *secret = NULL; 560 unsigned char sign0[SHA256_DIGEST_LENGTH] = {0}; 561 unsigned char sign1[SHA256_DIGEST_LENGTH] = {0}; 562 char *auth_headers = NULL; 563 564 DEBUGASSERT(!proxy); 565 (void)proxy; 566 567 if(Curl_checkheaders(data, STRCONST("Authorization"))) { 568 /* Authorization already present, Bailing out */ 569 return CURLE_OK; 570 } 571 572 /* we init those buffers here, so goto fail will free initialized dynbuf */ 573 Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER); 574 Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER); 575 Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER); 576 577 /* 578 * Parameters parsing 579 * Google and Outscale use the same OSC or GOOG, 580 * but Amazon uses AWS and AMZ for header arguments. 581 * AWS is the default because most of non-amazon providers 582 * are still using aws:amz as a prefix. 583 */ 584 arg = data->set.str[STRING_AWS_SIGV4] ? 585 data->set.str[STRING_AWS_SIGV4] : "aws:amz"; 586 587 /* provider1[:provider2[:region[:service]]] 588 589 No string can be longer than N bytes of non-whitespace 590 */ 591 (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]" 592 ":%" MAX_SIGV4_LEN_TXT "[^:]" 593 ":%" MAX_SIGV4_LEN_TXT "[^:]" 594 ":%" MAX_SIGV4_LEN_TXT "s", 595 provider0, provider1, region, service); 596 if(!provider0[0]) { 597 failf(data, "first aws-sigv4 provider can't be empty"); 598 result = CURLE_BAD_FUNCTION_ARGUMENT; 599 goto fail; 600 } 601 else if(!provider1[0]) 602 strcpy(provider1, provider0); 603 604 if(!service[0]) { 605 char *hostdot = strchr(hostname, '.'); 606 if(!hostdot) { 607 failf(data, "aws-sigv4: service missing in parameters and hostname"); 608 result = CURLE_URL_MALFORMAT; 609 goto fail; 610 } 611 len = hostdot - hostname; 612 if(len > MAX_SIGV4_LEN) { 613 failf(data, "aws-sigv4: service too long in hostname"); 614 result = CURLE_URL_MALFORMAT; 615 goto fail; 616 } 617 memcpy(service, hostname, len); 618 service[len] = '\0'; 619 620 infof(data, "aws_sigv4: picked service %s from host", service); 621 622 if(!region[0]) { 623 const char *reg = hostdot + 1; 624 const char *hostreg = strchr(reg, '.'); 625 if(!hostreg) { 626 failf(data, "aws-sigv4: region missing in parameters and hostname"); 627 result = CURLE_URL_MALFORMAT; 628 goto fail; 629 } 630 len = hostreg - reg; 631 if(len > MAX_SIGV4_LEN) { 632 failf(data, "aws-sigv4: region too long in hostname"); 633 result = CURLE_URL_MALFORMAT; 634 goto fail; 635 } 636 memcpy(region, reg, len); 637 region[len] = '\0'; 638 infof(data, "aws_sigv4: picked region %s from host", region); 639 } 640 } 641 642 Curl_http_method(data, conn, &method, &httpreq); 643 644 /* AWS S3 requires a x-amz-content-sha256 header, and supports special 645 * values like UNSIGNED-PAYLOAD */ 646 sign_as_s3 = (strcasecompare(provider0, "aws") && 647 strcasecompare(service, "s3")); 648 649 payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len); 650 651 if(!payload_hash) { 652 if(sign_as_s3) 653 result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash, 654 sha_hex, content_sha256_hdr); 655 else 656 result = calc_payload_hash(data, sha_hash, sha_hex); 657 if(result) 658 goto fail; 659 660 payload_hash = sha_hex; 661 /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */ 662 payload_hash_len = strlen(sha_hex); 663 } 664 665#ifdef DEBUGBUILD 666 { 667 char *force_timestamp = getenv("CURL_FORCETIME"); 668 if(force_timestamp) 669 clock = 0; 670 else 671 time(&clock); 672 } 673#else 674 time(&clock); 675#endif 676 result = Curl_gmtime(clock, &tm); 677 if(result) { 678 goto fail; 679 } 680 if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { 681 result = CURLE_OUT_OF_MEMORY; 682 goto fail; 683 } 684 685 result = make_headers(data, hostname, timestamp, provider1, 686 &date_header, content_sha256_hdr, 687 &canonical_headers, &signed_headers); 688 if(result) 689 goto fail; 690 691 if(*content_sha256_hdr) { 692 /* make_headers() needed this without the \r\n for canonicalization */ 693 size_t hdrlen = strlen(content_sha256_hdr); 694 DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr)); 695 memcpy(content_sha256_hdr + hdrlen, "\r\n", 3); 696 } 697 698 memcpy(date, timestamp, sizeof(date)); 699 date[sizeof(date) - 1] = 0; 700 701 result = canon_query(data, data->state.up.query, &canonical_query); 702 if(result) 703 goto fail; 704 result = CURLE_OUT_OF_MEMORY; 705 706 canonical_request = 707 curl_maprintf("%s\n" /* HTTPRequestMethod */ 708 "%s\n" /* CanonicalURI */ 709 "%s\n" /* CanonicalQueryString */ 710 "%s\n" /* CanonicalHeaders */ 711 "%s\n" /* SignedHeaders */ 712 "%.*s", /* HashedRequestPayload in hex */ 713 method, 714 data->state.up.path, 715 Curl_dyn_ptr(&canonical_query) ? 716 Curl_dyn_ptr(&canonical_query) : "", 717 Curl_dyn_ptr(&canonical_headers), 718 Curl_dyn_ptr(&signed_headers), 719 (int)payload_hash_len, payload_hash); 720 if(!canonical_request) 721 goto fail; 722 723 DEBUGF(infof(data, "Canonical request: %s", canonical_request)); 724 725 /* provider 0 lowercase */ 726 Curl_strntolower(provider0, provider0, strlen(provider0)); 727 request_type = curl_maprintf("%s4_request", provider0); 728 if(!request_type) 729 goto fail; 730 731 credential_scope = curl_maprintf("%s/%s/%s/%s", 732 date, region, service, request_type); 733 if(!credential_scope) 734 goto fail; 735 736 if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request, 737 strlen(canonical_request))) 738 goto fail; 739 740 sha256_to_hex(sha_hex, sha_hash); 741 742 /* provider 0 uppercase */ 743 Curl_strntoupper(provider0, provider0, strlen(provider0)); 744 745 /* 746 * Google allows using RSA key instead of HMAC, so this code might change 747 * in the future. For now we only support HMAC. 748 */ 749 str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */ 750 "%s\n" /* RequestDateTime */ 751 "%s\n" /* CredentialScope */ 752 "%s", /* HashedCanonicalRequest in hex */ 753 provider0, 754 timestamp, 755 credential_scope, 756 sha_hex); 757 if(!str_to_sign) { 758 goto fail; 759 } 760 761 /* provider 0 uppercase */ 762 secret = curl_maprintf("%s4%s", provider0, 763 data->state.aptr.passwd ? 764 data->state.aptr.passwd : ""); 765 if(!secret) 766 goto fail; 767 768 HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0); 769 HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1); 770 HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0); 771 HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1); 772 HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0); 773 774 sha256_to_hex(sha_hex, sign0); 775 776 /* provider 0 uppercase */ 777 auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 " 778 "Credential=%s/%s, " 779 "SignedHeaders=%s, " 780 "Signature=%s\r\n" 781 /* 782 * date_header is added here, only if it wasn't 783 * user-specified (using CURLOPT_HTTPHEADER). 784 * date_header includes \r\n 785 */ 786 "%s" 787 "%s", /* optional sha256 header includes \r\n */ 788 provider0, 789 user, 790 credential_scope, 791 Curl_dyn_ptr(&signed_headers), 792 sha_hex, 793 date_header ? date_header : "", 794 content_sha256_hdr); 795 if(!auth_headers) { 796 goto fail; 797 } 798 799 Curl_safefree(data->state.aptr.userpwd); 800 data->state.aptr.userpwd = auth_headers; 801 data->state.authhost.done = TRUE; 802 result = CURLE_OK; 803 804fail: 805 Curl_dyn_free(&canonical_query); 806 Curl_dyn_free(&canonical_headers); 807 Curl_dyn_free(&signed_headers); 808 free(canonical_request); 809 free(request_type); 810 free(credential_scope); 811 free(str_to_sign); 812 free(secret); 813 free(date_header); 814 return result; 815} 816 817#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */ 818