113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.haxx.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS)
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include "urldata.h"
3013498266Sopenharmony_ci#include "strcase.h"
3113498266Sopenharmony_ci#include "strdup.h"
3213498266Sopenharmony_ci#include "http_aws_sigv4.h"
3313498266Sopenharmony_ci#include "curl_sha256.h"
3413498266Sopenharmony_ci#include "transfer.h"
3513498266Sopenharmony_ci#include "parsedate.h"
3613498266Sopenharmony_ci#include "sendf.h"
3713498266Sopenharmony_ci#include "escape.h"
3813498266Sopenharmony_ci
3913498266Sopenharmony_ci#include <time.h>
4013498266Sopenharmony_ci
4113498266Sopenharmony_ci/* The last 3 #include files should be in this order */
4213498266Sopenharmony_ci#include "curl_printf.h"
4313498266Sopenharmony_ci#include "curl_memory.h"
4413498266Sopenharmony_ci#include "memdebug.h"
4513498266Sopenharmony_ci
4613498266Sopenharmony_ci#include "slist.h"
4713498266Sopenharmony_ci
4813498266Sopenharmony_ci#define HMAC_SHA256(k, kl, d, dl, o)           \
4913498266Sopenharmony_ci  do {                                         \
5013498266Sopenharmony_ci    result = Curl_hmacit(Curl_HMAC_SHA256,     \
5113498266Sopenharmony_ci                         (unsigned char *)k,   \
5213498266Sopenharmony_ci                         kl,                   \
5313498266Sopenharmony_ci                         (unsigned char *)d,   \
5413498266Sopenharmony_ci                         dl, o);               \
5513498266Sopenharmony_ci    if(result) {                               \
5613498266Sopenharmony_ci      goto fail;                               \
5713498266Sopenharmony_ci    }                                          \
5813498266Sopenharmony_ci  } while(0)
5913498266Sopenharmony_ci
6013498266Sopenharmony_ci#define TIMESTAMP_SIZE 17
6113498266Sopenharmony_ci
6213498266Sopenharmony_ci/* hex-encoded with trailing null */
6313498266Sopenharmony_ci#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1)
6413498266Sopenharmony_ci
6513498266Sopenharmony_cistatic void sha256_to_hex(char *dst, unsigned char *sha)
6613498266Sopenharmony_ci{
6713498266Sopenharmony_ci  Curl_hexencode(sha, SHA256_DIGEST_LENGTH,
6813498266Sopenharmony_ci                 (unsigned char *)dst, SHA256_HEX_LENGTH);
6913498266Sopenharmony_ci}
7013498266Sopenharmony_ci
7113498266Sopenharmony_cistatic char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
7213498266Sopenharmony_ci{
7313498266Sopenharmony_ci  char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
7413498266Sopenharmony_ci
7513498266Sopenharmony_ci  if(tmp)
7613498266Sopenharmony_ci    return tmp;
7713498266Sopenharmony_ci  return Curl_checkheaders(data, STRCONST("Date"));
7813498266Sopenharmony_ci}
7913498266Sopenharmony_ci
8013498266Sopenharmony_ci/* remove whitespace, and lowercase all headers */
8113498266Sopenharmony_cistatic void trim_headers(struct curl_slist *head)
8213498266Sopenharmony_ci{
8313498266Sopenharmony_ci  struct curl_slist *l;
8413498266Sopenharmony_ci  for(l = head; l; l = l->next) {
8513498266Sopenharmony_ci    char *value; /* to read from */
8613498266Sopenharmony_ci    char *store;
8713498266Sopenharmony_ci    size_t colon = strcspn(l->data, ":");
8813498266Sopenharmony_ci    Curl_strntolower(l->data, l->data, colon);
8913498266Sopenharmony_ci
9013498266Sopenharmony_ci    value = &l->data[colon];
9113498266Sopenharmony_ci    if(!*value)
9213498266Sopenharmony_ci      continue;
9313498266Sopenharmony_ci    ++value;
9413498266Sopenharmony_ci    store = value;
9513498266Sopenharmony_ci
9613498266Sopenharmony_ci    /* skip leading whitespace */
9713498266Sopenharmony_ci    while(*value && ISBLANK(*value))
9813498266Sopenharmony_ci      value++;
9913498266Sopenharmony_ci
10013498266Sopenharmony_ci    while(*value) {
10113498266Sopenharmony_ci      int space = 0;
10213498266Sopenharmony_ci      while(*value && ISBLANK(*value)) {
10313498266Sopenharmony_ci        value++;
10413498266Sopenharmony_ci        space++;
10513498266Sopenharmony_ci      }
10613498266Sopenharmony_ci      if(space) {
10713498266Sopenharmony_ci        /* replace any number of consecutive whitespace with a single space,
10813498266Sopenharmony_ci           unless at the end of the string, then nothing */
10913498266Sopenharmony_ci        if(*value)
11013498266Sopenharmony_ci          *store++ = ' ';
11113498266Sopenharmony_ci      }
11213498266Sopenharmony_ci      else
11313498266Sopenharmony_ci        *store++ = *value++;
11413498266Sopenharmony_ci    }
11513498266Sopenharmony_ci    *store = 0; /* null terminate */
11613498266Sopenharmony_ci  }
11713498266Sopenharmony_ci}
11813498266Sopenharmony_ci
11913498266Sopenharmony_ci/* maximum length for the aws sivg4 parts */
12013498266Sopenharmony_ci#define MAX_SIGV4_LEN 64
12113498266Sopenharmony_ci#define MAX_SIGV4_LEN_TXT "64"
12213498266Sopenharmony_ci
12313498266Sopenharmony_ci#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
12413498266Sopenharmony_ci
12513498266Sopenharmony_ci#define MAX_HOST_LEN 255
12613498266Sopenharmony_ci/* FQDN + host: */
12713498266Sopenharmony_ci#define FULL_HOST_LEN (MAX_HOST_LEN + sizeof("host:"))
12813498266Sopenharmony_ci
12913498266Sopenharmony_ci/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
13013498266Sopenharmony_ci#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
13113498266Sopenharmony_ci
13213498266Sopenharmony_ci/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
13313498266Sopenharmony_cistatic CURLcode make_headers(struct Curl_easy *data,
13413498266Sopenharmony_ci                             const char *hostname,
13513498266Sopenharmony_ci                             char *timestamp,
13613498266Sopenharmony_ci                             char *provider1,
13713498266Sopenharmony_ci                             char **date_header,
13813498266Sopenharmony_ci                             char *content_sha256_header,
13913498266Sopenharmony_ci                             struct dynbuf *canonical_headers,
14013498266Sopenharmony_ci                             struct dynbuf *signed_headers)
14113498266Sopenharmony_ci{
14213498266Sopenharmony_ci  char date_hdr_key[DATE_HDR_KEY_LEN];
14313498266Sopenharmony_ci  char date_full_hdr[DATE_FULL_HDR_LEN];
14413498266Sopenharmony_ci  struct curl_slist *head = NULL;
14513498266Sopenharmony_ci  struct curl_slist *tmp_head = NULL;
14613498266Sopenharmony_ci  CURLcode ret = CURLE_OUT_OF_MEMORY;
14713498266Sopenharmony_ci  struct curl_slist *l;
14813498266Sopenharmony_ci  int again = 1;
14913498266Sopenharmony_ci
15013498266Sopenharmony_ci  /* provider1 mid */
15113498266Sopenharmony_ci  Curl_strntolower(provider1, provider1, strlen(provider1));
15213498266Sopenharmony_ci  provider1[0] = Curl_raw_toupper(provider1[0]);
15313498266Sopenharmony_ci
15413498266Sopenharmony_ci  msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
15513498266Sopenharmony_ci
15613498266Sopenharmony_ci  /* provider1 lowercase */
15713498266Sopenharmony_ci  Curl_strntolower(provider1, provider1, 1); /* first byte only */
15813498266Sopenharmony_ci  msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
15913498266Sopenharmony_ci            "x-%s-date:%s", provider1, timestamp);
16013498266Sopenharmony_ci
16113498266Sopenharmony_ci  if(Curl_checkheaders(data, STRCONST("Host"))) {
16213498266Sopenharmony_ci    head = NULL;
16313498266Sopenharmony_ci  }
16413498266Sopenharmony_ci  else {
16513498266Sopenharmony_ci    char full_host[FULL_HOST_LEN + 1];
16613498266Sopenharmony_ci
16713498266Sopenharmony_ci    if(data->state.aptr.host) {
16813498266Sopenharmony_ci      size_t pos;
16913498266Sopenharmony_ci
17013498266Sopenharmony_ci      if(strlen(data->state.aptr.host) > FULL_HOST_LEN) {
17113498266Sopenharmony_ci        ret = CURLE_URL_MALFORMAT;
17213498266Sopenharmony_ci        goto fail;
17313498266Sopenharmony_ci      }
17413498266Sopenharmony_ci      strcpy(full_host, data->state.aptr.host);
17513498266Sopenharmony_ci      /* remove /r/n as the separator for canonical request must be '\n' */
17613498266Sopenharmony_ci      pos = strcspn(full_host, "\n\r");
17713498266Sopenharmony_ci      full_host[pos] = 0;
17813498266Sopenharmony_ci    }
17913498266Sopenharmony_ci    else {
18013498266Sopenharmony_ci      if(strlen(hostname) > MAX_HOST_LEN) {
18113498266Sopenharmony_ci        ret = CURLE_URL_MALFORMAT;
18213498266Sopenharmony_ci        goto fail;
18313498266Sopenharmony_ci      }
18413498266Sopenharmony_ci      msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname);
18513498266Sopenharmony_ci    }
18613498266Sopenharmony_ci
18713498266Sopenharmony_ci    head = curl_slist_append(NULL, full_host);
18813498266Sopenharmony_ci    if(!head)
18913498266Sopenharmony_ci      goto fail;
19013498266Sopenharmony_ci  }
19113498266Sopenharmony_ci
19213498266Sopenharmony_ci
19313498266Sopenharmony_ci  if(*content_sha256_header) {
19413498266Sopenharmony_ci    tmp_head = curl_slist_append(head, content_sha256_header);
19513498266Sopenharmony_ci    if(!tmp_head)
19613498266Sopenharmony_ci      goto fail;
19713498266Sopenharmony_ci    head = tmp_head;
19813498266Sopenharmony_ci  }
19913498266Sopenharmony_ci
20013498266Sopenharmony_ci  /* copy user headers to our header list. the logic is based on how http.c
20113498266Sopenharmony_ci     handles user headers.
20213498266Sopenharmony_ci
20313498266Sopenharmony_ci     user headers in format 'name:' with no value are used to signal that an
20413498266Sopenharmony_ci     internal header of that name should be removed. those user headers are not
20513498266Sopenharmony_ci     added to this list.
20613498266Sopenharmony_ci
20713498266Sopenharmony_ci     user headers in format 'name;' with no value are used to signal that a
20813498266Sopenharmony_ci     header of that name with no value should be sent. those user headers are
20913498266Sopenharmony_ci     added to this list but in the format that they will be sent, ie the
21013498266Sopenharmony_ci     semi-colon is changed to a colon for format 'name:'.
21113498266Sopenharmony_ci
21213498266Sopenharmony_ci     user headers with a value of whitespace only, or without a colon or
21313498266Sopenharmony_ci     semi-colon, are not added to this list.
21413498266Sopenharmony_ci     */
21513498266Sopenharmony_ci  for(l = data->set.headers; l; l = l->next) {
21613498266Sopenharmony_ci    char *dupdata, *ptr;
21713498266Sopenharmony_ci    char *sep = strchr(l->data, ':');
21813498266Sopenharmony_ci    if(!sep)
21913498266Sopenharmony_ci      sep = strchr(l->data, ';');
22013498266Sopenharmony_ci    if(!sep || (*sep == ':' && !*(sep + 1)))
22113498266Sopenharmony_ci      continue;
22213498266Sopenharmony_ci    for(ptr = sep + 1; ISSPACE(*ptr); ++ptr)
22313498266Sopenharmony_ci      ;
22413498266Sopenharmony_ci    if(!*ptr && ptr != sep + 1) /* a value of whitespace only */
22513498266Sopenharmony_ci      continue;
22613498266Sopenharmony_ci    dupdata = strdup(l->data);
22713498266Sopenharmony_ci    if(!dupdata)
22813498266Sopenharmony_ci      goto fail;
22913498266Sopenharmony_ci    dupdata[sep - l->data] = ':';
23013498266Sopenharmony_ci    tmp_head = Curl_slist_append_nodup(head, dupdata);
23113498266Sopenharmony_ci    if(!tmp_head) {
23213498266Sopenharmony_ci      free(dupdata);
23313498266Sopenharmony_ci      goto fail;
23413498266Sopenharmony_ci    }
23513498266Sopenharmony_ci    head = tmp_head;
23613498266Sopenharmony_ci  }
23713498266Sopenharmony_ci
23813498266Sopenharmony_ci  trim_headers(head);
23913498266Sopenharmony_ci
24013498266Sopenharmony_ci  *date_header = find_date_hdr(data, date_hdr_key);
24113498266Sopenharmony_ci  if(!*date_header) {
24213498266Sopenharmony_ci    tmp_head = curl_slist_append(head, date_full_hdr);
24313498266Sopenharmony_ci    if(!tmp_head)
24413498266Sopenharmony_ci      goto fail;
24513498266Sopenharmony_ci    head = tmp_head;
24613498266Sopenharmony_ci    *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp);
24713498266Sopenharmony_ci  }
24813498266Sopenharmony_ci  else {
24913498266Sopenharmony_ci    char *value;
25013498266Sopenharmony_ci    char *endp;
25113498266Sopenharmony_ci    value = strchr(*date_header, ':');
25213498266Sopenharmony_ci    if(!value) {
25313498266Sopenharmony_ci      *date_header = NULL;
25413498266Sopenharmony_ci      goto fail;
25513498266Sopenharmony_ci    }
25613498266Sopenharmony_ci    ++value;
25713498266Sopenharmony_ci    while(ISBLANK(*value))
25813498266Sopenharmony_ci      ++value;
25913498266Sopenharmony_ci    endp = value;
26013498266Sopenharmony_ci    while(*endp && ISALNUM(*endp))
26113498266Sopenharmony_ci      ++endp;
26213498266Sopenharmony_ci    /* 16 bytes => "19700101T000000Z" */
26313498266Sopenharmony_ci    if((endp - value) == TIMESTAMP_SIZE - 1) {
26413498266Sopenharmony_ci      memcpy(timestamp, value, TIMESTAMP_SIZE - 1);
26513498266Sopenharmony_ci      timestamp[TIMESTAMP_SIZE - 1] = 0;
26613498266Sopenharmony_ci    }
26713498266Sopenharmony_ci    else
26813498266Sopenharmony_ci      /* bad timestamp length */
26913498266Sopenharmony_ci      timestamp[0] = 0;
27013498266Sopenharmony_ci    *date_header = NULL;
27113498266Sopenharmony_ci  }
27213498266Sopenharmony_ci
27313498266Sopenharmony_ci  /* alpha-sort in a case sensitive manner */
27413498266Sopenharmony_ci  do {
27513498266Sopenharmony_ci    again = 0;
27613498266Sopenharmony_ci    for(l = head; l; l = l->next) {
27713498266Sopenharmony_ci      struct curl_slist *next = l->next;
27813498266Sopenharmony_ci
27913498266Sopenharmony_ci      if(next && strcmp(l->data, next->data) > 0) {
28013498266Sopenharmony_ci        char *tmp = l->data;
28113498266Sopenharmony_ci
28213498266Sopenharmony_ci        l->data = next->data;
28313498266Sopenharmony_ci        next->data = tmp;
28413498266Sopenharmony_ci        again = 1;
28513498266Sopenharmony_ci      }
28613498266Sopenharmony_ci    }
28713498266Sopenharmony_ci  } while(again);
28813498266Sopenharmony_ci
28913498266Sopenharmony_ci  for(l = head; l; l = l->next) {
29013498266Sopenharmony_ci    char *tmp;
29113498266Sopenharmony_ci
29213498266Sopenharmony_ci    if(Curl_dyn_add(canonical_headers, l->data))
29313498266Sopenharmony_ci      goto fail;
29413498266Sopenharmony_ci    if(Curl_dyn_add(canonical_headers, "\n"))
29513498266Sopenharmony_ci      goto fail;
29613498266Sopenharmony_ci
29713498266Sopenharmony_ci    tmp = strchr(l->data, ':');
29813498266Sopenharmony_ci    if(tmp)
29913498266Sopenharmony_ci      *tmp = 0;
30013498266Sopenharmony_ci
30113498266Sopenharmony_ci    if(l != head) {
30213498266Sopenharmony_ci      if(Curl_dyn_add(signed_headers, ";"))
30313498266Sopenharmony_ci        goto fail;
30413498266Sopenharmony_ci    }
30513498266Sopenharmony_ci    if(Curl_dyn_add(signed_headers, l->data))
30613498266Sopenharmony_ci      goto fail;
30713498266Sopenharmony_ci  }
30813498266Sopenharmony_ci
30913498266Sopenharmony_ci  ret = CURLE_OK;
31013498266Sopenharmony_cifail:
31113498266Sopenharmony_ci  curl_slist_free_all(head);
31213498266Sopenharmony_ci
31313498266Sopenharmony_ci  return ret;
31413498266Sopenharmony_ci}
31513498266Sopenharmony_ci
31613498266Sopenharmony_ci#define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256"))
31713498266Sopenharmony_ci/* add 2 for ": " between header name and value */
31813498266Sopenharmony_ci#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \
31913498266Sopenharmony_ci                                SHA256_HEX_LENGTH)
32013498266Sopenharmony_ci
32113498266Sopenharmony_ci/* try to parse a payload hash from the content-sha256 header */
32213498266Sopenharmony_cistatic char *parse_content_sha_hdr(struct Curl_easy *data,
32313498266Sopenharmony_ci                                   const char *provider1,
32413498266Sopenharmony_ci                                   size_t *value_len)
32513498266Sopenharmony_ci{
32613498266Sopenharmony_ci  char key[CONTENT_SHA256_KEY_LEN];
32713498266Sopenharmony_ci  size_t key_len;
32813498266Sopenharmony_ci  char *value;
32913498266Sopenharmony_ci  size_t len;
33013498266Sopenharmony_ci
33113498266Sopenharmony_ci  key_len = msnprintf(key, sizeof(key), "x-%s-content-sha256", provider1);
33213498266Sopenharmony_ci
33313498266Sopenharmony_ci  value = Curl_checkheaders(data, key, key_len);
33413498266Sopenharmony_ci  if(!value)
33513498266Sopenharmony_ci    return NULL;
33613498266Sopenharmony_ci
33713498266Sopenharmony_ci  value = strchr(value, ':');
33813498266Sopenharmony_ci  if(!value)
33913498266Sopenharmony_ci    return NULL;
34013498266Sopenharmony_ci  ++value;
34113498266Sopenharmony_ci
34213498266Sopenharmony_ci  while(*value && ISBLANK(*value))
34313498266Sopenharmony_ci    ++value;
34413498266Sopenharmony_ci
34513498266Sopenharmony_ci  len = strlen(value);
34613498266Sopenharmony_ci  while(len > 0 && ISBLANK(value[len-1]))
34713498266Sopenharmony_ci    --len;
34813498266Sopenharmony_ci
34913498266Sopenharmony_ci  *value_len = len;
35013498266Sopenharmony_ci  return value;
35113498266Sopenharmony_ci}
35213498266Sopenharmony_ci
35313498266Sopenharmony_cistatic CURLcode calc_payload_hash(struct Curl_easy *data,
35413498266Sopenharmony_ci                                  unsigned char *sha_hash, char *sha_hex)
35513498266Sopenharmony_ci{
35613498266Sopenharmony_ci  const char *post_data = data->set.postfields;
35713498266Sopenharmony_ci  size_t post_data_len = 0;
35813498266Sopenharmony_ci  CURLcode result;
35913498266Sopenharmony_ci
36013498266Sopenharmony_ci  if(post_data) {
36113498266Sopenharmony_ci    if(data->set.postfieldsize < 0)
36213498266Sopenharmony_ci      post_data_len = strlen(post_data);
36313498266Sopenharmony_ci    else
36413498266Sopenharmony_ci      post_data_len = (size_t)data->set.postfieldsize;
36513498266Sopenharmony_ci  }
36613498266Sopenharmony_ci  result = Curl_sha256it(sha_hash, (const unsigned char *) post_data,
36713498266Sopenharmony_ci                         post_data_len);
36813498266Sopenharmony_ci  if(!result)
36913498266Sopenharmony_ci    sha256_to_hex(sha_hex, sha_hash);
37013498266Sopenharmony_ci  return result;
37113498266Sopenharmony_ci}
37213498266Sopenharmony_ci
37313498266Sopenharmony_ci#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD"
37413498266Sopenharmony_ci
37513498266Sopenharmony_cistatic CURLcode calc_s3_payload_hash(struct Curl_easy *data,
37613498266Sopenharmony_ci                                     Curl_HttpReq httpreq, char *provider1,
37713498266Sopenharmony_ci                                     unsigned char *sha_hash,
37813498266Sopenharmony_ci                                     char *sha_hex, char *header)
37913498266Sopenharmony_ci{
38013498266Sopenharmony_ci  bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD);
38113498266Sopenharmony_ci  /* The request method or filesize indicate no request payload */
38213498266Sopenharmony_ci  bool empty_payload = (empty_method || data->set.filesize == 0);
38313498266Sopenharmony_ci  /* The POST payload is in memory */
38413498266Sopenharmony_ci  bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields);
38513498266Sopenharmony_ci  CURLcode ret = CURLE_OUT_OF_MEMORY;
38613498266Sopenharmony_ci
38713498266Sopenharmony_ci  if(empty_payload || post_payload) {
38813498266Sopenharmony_ci    /* Calculate a real hash when we know the request payload */
38913498266Sopenharmony_ci    ret = calc_payload_hash(data, sha_hash, sha_hex);
39013498266Sopenharmony_ci    if(ret)
39113498266Sopenharmony_ci      goto fail;
39213498266Sopenharmony_ci  }
39313498266Sopenharmony_ci  else {
39413498266Sopenharmony_ci    /* Fall back to s3's UNSIGNED-PAYLOAD */
39513498266Sopenharmony_ci    size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1;
39613498266Sopenharmony_ci    DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */
39713498266Sopenharmony_ci    memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len);
39813498266Sopenharmony_ci    sha_hex[len] = 0;
39913498266Sopenharmony_ci  }
40013498266Sopenharmony_ci
40113498266Sopenharmony_ci  /* format the required content-sha256 header */
40213498266Sopenharmony_ci  msnprintf(header, CONTENT_SHA256_HDR_LEN,
40313498266Sopenharmony_ci            "x-%s-content-sha256: %s", provider1, sha_hex);
40413498266Sopenharmony_ci
40513498266Sopenharmony_ci  ret = CURLE_OK;
40613498266Sopenharmony_cifail:
40713498266Sopenharmony_ci  return ret;
40813498266Sopenharmony_ci}
40913498266Sopenharmony_ci
41013498266Sopenharmony_cistruct pair {
41113498266Sopenharmony_ci  const char *p;
41213498266Sopenharmony_ci  size_t len;
41313498266Sopenharmony_ci};
41413498266Sopenharmony_ci
41513498266Sopenharmony_cistatic int compare_func(const void *a, const void *b)
41613498266Sopenharmony_ci{
41713498266Sopenharmony_ci  const struct pair *aa = a;
41813498266Sopenharmony_ci  const struct pair *bb = b;
41913498266Sopenharmony_ci  /* If one element is empty, the other is always sorted higher */
42013498266Sopenharmony_ci  if(aa->len == 0)
42113498266Sopenharmony_ci    return -1;
42213498266Sopenharmony_ci  if(bb->len == 0)
42313498266Sopenharmony_ci    return 1;
42413498266Sopenharmony_ci  return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len);
42513498266Sopenharmony_ci}
42613498266Sopenharmony_ci
42713498266Sopenharmony_ci#define MAX_QUERYPAIRS 64
42813498266Sopenharmony_ci
42913498266Sopenharmony_cistatic CURLcode canon_query(struct Curl_easy *data,
43013498266Sopenharmony_ci                            const char *query, struct dynbuf *dq)
43113498266Sopenharmony_ci{
43213498266Sopenharmony_ci  CURLcode result = CURLE_OK;
43313498266Sopenharmony_ci  int entry = 0;
43413498266Sopenharmony_ci  int i;
43513498266Sopenharmony_ci  const char *p = query;
43613498266Sopenharmony_ci  struct pair array[MAX_QUERYPAIRS];
43713498266Sopenharmony_ci  struct pair *ap = &array[0];
43813498266Sopenharmony_ci  if(!query)
43913498266Sopenharmony_ci    return result;
44013498266Sopenharmony_ci
44113498266Sopenharmony_ci  /* sort the name=value pairs first */
44213498266Sopenharmony_ci  do {
44313498266Sopenharmony_ci    char *amp;
44413498266Sopenharmony_ci    entry++;
44513498266Sopenharmony_ci    ap->p = p;
44613498266Sopenharmony_ci    amp = strchr(p, '&');
44713498266Sopenharmony_ci    if(amp)
44813498266Sopenharmony_ci      ap->len = amp - p; /* excluding the ampersand */
44913498266Sopenharmony_ci    else {
45013498266Sopenharmony_ci      ap->len = strlen(p);
45113498266Sopenharmony_ci      break;
45213498266Sopenharmony_ci    }
45313498266Sopenharmony_ci    ap++;
45413498266Sopenharmony_ci    p = amp + 1;
45513498266Sopenharmony_ci  } while(entry < MAX_QUERYPAIRS);
45613498266Sopenharmony_ci  if(entry == MAX_QUERYPAIRS) {
45713498266Sopenharmony_ci    /* too many query pairs for us */
45813498266Sopenharmony_ci    failf(data, "aws-sigv4: too many query pairs in URL");
45913498266Sopenharmony_ci    return CURLE_URL_MALFORMAT;
46013498266Sopenharmony_ci  }
46113498266Sopenharmony_ci
46213498266Sopenharmony_ci  qsort(&array[0], entry, sizeof(struct pair), compare_func);
46313498266Sopenharmony_ci
46413498266Sopenharmony_ci  ap = &array[0];
46513498266Sopenharmony_ci  for(i = 0; !result && (i < entry); i++, ap++) {
46613498266Sopenharmony_ci    size_t len;
46713498266Sopenharmony_ci    const char *q = ap->p;
46813498266Sopenharmony_ci    bool found_equals = false;
46913498266Sopenharmony_ci    if(!ap->len)
47013498266Sopenharmony_ci      continue;
47113498266Sopenharmony_ci    for(len = ap->len; len && !result; q++, len--) {
47213498266Sopenharmony_ci      if(ISALNUM(*q))
47313498266Sopenharmony_ci        result = Curl_dyn_addn(dq, q, 1);
47413498266Sopenharmony_ci      else {
47513498266Sopenharmony_ci        switch(*q) {
47613498266Sopenharmony_ci        case '-':
47713498266Sopenharmony_ci        case '.':
47813498266Sopenharmony_ci        case '_':
47913498266Sopenharmony_ci        case '~':
48013498266Sopenharmony_ci          /* allowed as-is */
48113498266Sopenharmony_ci          result = Curl_dyn_addn(dq, q, 1);
48213498266Sopenharmony_ci          break;
48313498266Sopenharmony_ci        case '=':
48413498266Sopenharmony_ci          /* allowed as-is */
48513498266Sopenharmony_ci          result = Curl_dyn_addn(dq, q, 1);
48613498266Sopenharmony_ci          found_equals = true;
48713498266Sopenharmony_ci          break;
48813498266Sopenharmony_ci        case '%':
48913498266Sopenharmony_ci          /* uppercase the following if hexadecimal */
49013498266Sopenharmony_ci          if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) {
49113498266Sopenharmony_ci            char tmp[3]="%";
49213498266Sopenharmony_ci            tmp[1] = Curl_raw_toupper(q[1]);
49313498266Sopenharmony_ci            tmp[2] = Curl_raw_toupper(q[2]);
49413498266Sopenharmony_ci            result = Curl_dyn_addn(dq, tmp, 3);
49513498266Sopenharmony_ci            q += 2;
49613498266Sopenharmony_ci            len -= 2;
49713498266Sopenharmony_ci          }
49813498266Sopenharmony_ci          else
49913498266Sopenharmony_ci            /* '%' without a following two-digit hex, encode it */
50013498266Sopenharmony_ci            result = Curl_dyn_addn(dq, "%25", 3);
50113498266Sopenharmony_ci          break;
50213498266Sopenharmony_ci        default: {
50313498266Sopenharmony_ci          /* URL encode */
50413498266Sopenharmony_ci          const char hex[] = "0123456789ABCDEF";
50513498266Sopenharmony_ci          char out[3]={'%'};
50613498266Sopenharmony_ci          out[1] = hex[((unsigned char)*q)>>4];
50713498266Sopenharmony_ci          out[2] = hex[*q & 0xf];
50813498266Sopenharmony_ci          result = Curl_dyn_addn(dq, out, 3);
50913498266Sopenharmony_ci          break;
51013498266Sopenharmony_ci        }
51113498266Sopenharmony_ci        }
51213498266Sopenharmony_ci      }
51313498266Sopenharmony_ci    }
51413498266Sopenharmony_ci    if(!result && !found_equals) {
51513498266Sopenharmony_ci      /* queries without value still need an equals */
51613498266Sopenharmony_ci      result = Curl_dyn_addn(dq, "=", 1);
51713498266Sopenharmony_ci    }
51813498266Sopenharmony_ci    if(!result && i < entry - 1) {
51913498266Sopenharmony_ci      /* insert ampersands between query pairs */
52013498266Sopenharmony_ci      result = Curl_dyn_addn(dq, "&", 1);
52113498266Sopenharmony_ci    }
52213498266Sopenharmony_ci  }
52313498266Sopenharmony_ci  return result;
52413498266Sopenharmony_ci}
52513498266Sopenharmony_ci
52613498266Sopenharmony_ci
52713498266Sopenharmony_ciCURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
52813498266Sopenharmony_ci{
52913498266Sopenharmony_ci  CURLcode result = CURLE_OUT_OF_MEMORY;
53013498266Sopenharmony_ci  struct connectdata *conn = data->conn;
53113498266Sopenharmony_ci  size_t len;
53213498266Sopenharmony_ci  const char *arg;
53313498266Sopenharmony_ci  char provider0[MAX_SIGV4_LEN + 1]="";
53413498266Sopenharmony_ci  char provider1[MAX_SIGV4_LEN + 1]="";
53513498266Sopenharmony_ci  char region[MAX_SIGV4_LEN + 1]="";
53613498266Sopenharmony_ci  char service[MAX_SIGV4_LEN + 1]="";
53713498266Sopenharmony_ci  bool sign_as_s3 = false;
53813498266Sopenharmony_ci  const char *hostname = conn->host.name;
53913498266Sopenharmony_ci  time_t clock;
54013498266Sopenharmony_ci  struct tm tm;
54113498266Sopenharmony_ci  char timestamp[TIMESTAMP_SIZE];
54213498266Sopenharmony_ci  char date[9];
54313498266Sopenharmony_ci  struct dynbuf canonical_headers;
54413498266Sopenharmony_ci  struct dynbuf signed_headers;
54513498266Sopenharmony_ci  struct dynbuf canonical_query;
54613498266Sopenharmony_ci  char *date_header = NULL;
54713498266Sopenharmony_ci  Curl_HttpReq httpreq;
54813498266Sopenharmony_ci  const char *method = NULL;
54913498266Sopenharmony_ci  char *payload_hash = NULL;
55013498266Sopenharmony_ci  size_t payload_hash_len = 0;
55113498266Sopenharmony_ci  unsigned char sha_hash[SHA256_DIGEST_LENGTH];
55213498266Sopenharmony_ci  char sha_hex[SHA256_HEX_LENGTH];
55313498266Sopenharmony_ci  char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
55413498266Sopenharmony_ci  char *canonical_request = NULL;
55513498266Sopenharmony_ci  char *request_type = NULL;
55613498266Sopenharmony_ci  char *credential_scope = NULL;
55713498266Sopenharmony_ci  char *str_to_sign = NULL;
55813498266Sopenharmony_ci  const char *user = data->state.aptr.user ? data->state.aptr.user : "";
55913498266Sopenharmony_ci  char *secret = NULL;
56013498266Sopenharmony_ci  unsigned char sign0[SHA256_DIGEST_LENGTH] = {0};
56113498266Sopenharmony_ci  unsigned char sign1[SHA256_DIGEST_LENGTH] = {0};
56213498266Sopenharmony_ci  char *auth_headers = NULL;
56313498266Sopenharmony_ci
56413498266Sopenharmony_ci  DEBUGASSERT(!proxy);
56513498266Sopenharmony_ci  (void)proxy;
56613498266Sopenharmony_ci
56713498266Sopenharmony_ci  if(Curl_checkheaders(data, STRCONST("Authorization"))) {
56813498266Sopenharmony_ci    /* Authorization already present, Bailing out */
56913498266Sopenharmony_ci    return CURLE_OK;
57013498266Sopenharmony_ci  }
57113498266Sopenharmony_ci
57213498266Sopenharmony_ci  /* we init those buffers here, so goto fail will free initialized dynbuf */
57313498266Sopenharmony_ci  Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
57413498266Sopenharmony_ci  Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
57513498266Sopenharmony_ci  Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
57613498266Sopenharmony_ci
57713498266Sopenharmony_ci  /*
57813498266Sopenharmony_ci   * Parameters parsing
57913498266Sopenharmony_ci   * Google and Outscale use the same OSC or GOOG,
58013498266Sopenharmony_ci   * but Amazon uses AWS and AMZ for header arguments.
58113498266Sopenharmony_ci   * AWS is the default because most of non-amazon providers
58213498266Sopenharmony_ci   * are still using aws:amz as a prefix.
58313498266Sopenharmony_ci   */
58413498266Sopenharmony_ci  arg = data->set.str[STRING_AWS_SIGV4] ?
58513498266Sopenharmony_ci    data->set.str[STRING_AWS_SIGV4] : "aws:amz";
58613498266Sopenharmony_ci
58713498266Sopenharmony_ci  /* provider1[:provider2[:region[:service]]]
58813498266Sopenharmony_ci
58913498266Sopenharmony_ci     No string can be longer than N bytes of non-whitespace
59013498266Sopenharmony_ci  */
59113498266Sopenharmony_ci  (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
59213498266Sopenharmony_ci               ":%" MAX_SIGV4_LEN_TXT "[^:]"
59313498266Sopenharmony_ci               ":%" MAX_SIGV4_LEN_TXT "[^:]"
59413498266Sopenharmony_ci               ":%" MAX_SIGV4_LEN_TXT "s",
59513498266Sopenharmony_ci               provider0, provider1, region, service);
59613498266Sopenharmony_ci  if(!provider0[0]) {
59713498266Sopenharmony_ci    failf(data, "first aws-sigv4 provider can't be empty");
59813498266Sopenharmony_ci    result = CURLE_BAD_FUNCTION_ARGUMENT;
59913498266Sopenharmony_ci    goto fail;
60013498266Sopenharmony_ci  }
60113498266Sopenharmony_ci  else if(!provider1[0])
60213498266Sopenharmony_ci    strcpy(provider1, provider0);
60313498266Sopenharmony_ci
60413498266Sopenharmony_ci  if(!service[0]) {
60513498266Sopenharmony_ci    char *hostdot = strchr(hostname, '.');
60613498266Sopenharmony_ci    if(!hostdot) {
60713498266Sopenharmony_ci      failf(data, "aws-sigv4: service missing in parameters and hostname");
60813498266Sopenharmony_ci      result = CURLE_URL_MALFORMAT;
60913498266Sopenharmony_ci      goto fail;
61013498266Sopenharmony_ci    }
61113498266Sopenharmony_ci    len = hostdot - hostname;
61213498266Sopenharmony_ci    if(len > MAX_SIGV4_LEN) {
61313498266Sopenharmony_ci      failf(data, "aws-sigv4: service too long in hostname");
61413498266Sopenharmony_ci      result = CURLE_URL_MALFORMAT;
61513498266Sopenharmony_ci      goto fail;
61613498266Sopenharmony_ci    }
61713498266Sopenharmony_ci    memcpy(service, hostname, len);
61813498266Sopenharmony_ci    service[len] = '\0';
61913498266Sopenharmony_ci
62013498266Sopenharmony_ci    infof(data, "aws_sigv4: picked service %s from host", service);
62113498266Sopenharmony_ci
62213498266Sopenharmony_ci    if(!region[0]) {
62313498266Sopenharmony_ci      const char *reg = hostdot + 1;
62413498266Sopenharmony_ci      const char *hostreg = strchr(reg, '.');
62513498266Sopenharmony_ci      if(!hostreg) {
62613498266Sopenharmony_ci        failf(data, "aws-sigv4: region missing in parameters and hostname");
62713498266Sopenharmony_ci        result = CURLE_URL_MALFORMAT;
62813498266Sopenharmony_ci        goto fail;
62913498266Sopenharmony_ci      }
63013498266Sopenharmony_ci      len = hostreg - reg;
63113498266Sopenharmony_ci      if(len > MAX_SIGV4_LEN) {
63213498266Sopenharmony_ci        failf(data, "aws-sigv4: region too long in hostname");
63313498266Sopenharmony_ci        result = CURLE_URL_MALFORMAT;
63413498266Sopenharmony_ci        goto fail;
63513498266Sopenharmony_ci      }
63613498266Sopenharmony_ci      memcpy(region, reg, len);
63713498266Sopenharmony_ci      region[len] = '\0';
63813498266Sopenharmony_ci      infof(data, "aws_sigv4: picked region %s from host", region);
63913498266Sopenharmony_ci    }
64013498266Sopenharmony_ci  }
64113498266Sopenharmony_ci
64213498266Sopenharmony_ci  Curl_http_method(data, conn, &method, &httpreq);
64313498266Sopenharmony_ci
64413498266Sopenharmony_ci  /* AWS S3 requires a x-amz-content-sha256 header, and supports special
64513498266Sopenharmony_ci   * values like UNSIGNED-PAYLOAD */
64613498266Sopenharmony_ci  sign_as_s3 = (strcasecompare(provider0, "aws") &&
64713498266Sopenharmony_ci                strcasecompare(service, "s3"));
64813498266Sopenharmony_ci
64913498266Sopenharmony_ci  payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
65013498266Sopenharmony_ci
65113498266Sopenharmony_ci  if(!payload_hash) {
65213498266Sopenharmony_ci    if(sign_as_s3)
65313498266Sopenharmony_ci      result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
65413498266Sopenharmony_ci                                    sha_hex, content_sha256_hdr);
65513498266Sopenharmony_ci    else
65613498266Sopenharmony_ci      result = calc_payload_hash(data, sha_hash, sha_hex);
65713498266Sopenharmony_ci    if(result)
65813498266Sopenharmony_ci      goto fail;
65913498266Sopenharmony_ci
66013498266Sopenharmony_ci    payload_hash = sha_hex;
66113498266Sopenharmony_ci    /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */
66213498266Sopenharmony_ci    payload_hash_len = strlen(sha_hex);
66313498266Sopenharmony_ci  }
66413498266Sopenharmony_ci
66513498266Sopenharmony_ci#ifdef DEBUGBUILD
66613498266Sopenharmony_ci  {
66713498266Sopenharmony_ci    char *force_timestamp = getenv("CURL_FORCETIME");
66813498266Sopenharmony_ci    if(force_timestamp)
66913498266Sopenharmony_ci      clock = 0;
67013498266Sopenharmony_ci    else
67113498266Sopenharmony_ci      time(&clock);
67213498266Sopenharmony_ci  }
67313498266Sopenharmony_ci#else
67413498266Sopenharmony_ci  time(&clock);
67513498266Sopenharmony_ci#endif
67613498266Sopenharmony_ci  result = Curl_gmtime(clock, &tm);
67713498266Sopenharmony_ci  if(result) {
67813498266Sopenharmony_ci    goto fail;
67913498266Sopenharmony_ci  }
68013498266Sopenharmony_ci  if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
68113498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
68213498266Sopenharmony_ci    goto fail;
68313498266Sopenharmony_ci  }
68413498266Sopenharmony_ci
68513498266Sopenharmony_ci  result = make_headers(data, hostname, timestamp, provider1,
68613498266Sopenharmony_ci                        &date_header, content_sha256_hdr,
68713498266Sopenharmony_ci                        &canonical_headers, &signed_headers);
68813498266Sopenharmony_ci  if(result)
68913498266Sopenharmony_ci    goto fail;
69013498266Sopenharmony_ci
69113498266Sopenharmony_ci  if(*content_sha256_hdr) {
69213498266Sopenharmony_ci    /* make_headers() needed this without the \r\n for canonicalization */
69313498266Sopenharmony_ci    size_t hdrlen = strlen(content_sha256_hdr);
69413498266Sopenharmony_ci    DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr));
69513498266Sopenharmony_ci    memcpy(content_sha256_hdr + hdrlen, "\r\n", 3);
69613498266Sopenharmony_ci  }
69713498266Sopenharmony_ci
69813498266Sopenharmony_ci  memcpy(date, timestamp, sizeof(date));
69913498266Sopenharmony_ci  date[sizeof(date) - 1] = 0;
70013498266Sopenharmony_ci
70113498266Sopenharmony_ci  result = canon_query(data, data->state.up.query, &canonical_query);
70213498266Sopenharmony_ci  if(result)
70313498266Sopenharmony_ci    goto fail;
70413498266Sopenharmony_ci  result = CURLE_OUT_OF_MEMORY;
70513498266Sopenharmony_ci
70613498266Sopenharmony_ci  canonical_request =
70713498266Sopenharmony_ci    curl_maprintf("%s\n" /* HTTPRequestMethod */
70813498266Sopenharmony_ci                  "%s\n" /* CanonicalURI */
70913498266Sopenharmony_ci                  "%s\n" /* CanonicalQueryString */
71013498266Sopenharmony_ci                  "%s\n" /* CanonicalHeaders */
71113498266Sopenharmony_ci                  "%s\n" /* SignedHeaders */
71213498266Sopenharmony_ci                  "%.*s",  /* HashedRequestPayload in hex */
71313498266Sopenharmony_ci                  method,
71413498266Sopenharmony_ci                  data->state.up.path,
71513498266Sopenharmony_ci                  Curl_dyn_ptr(&canonical_query) ?
71613498266Sopenharmony_ci                  Curl_dyn_ptr(&canonical_query) : "",
71713498266Sopenharmony_ci                  Curl_dyn_ptr(&canonical_headers),
71813498266Sopenharmony_ci                  Curl_dyn_ptr(&signed_headers),
71913498266Sopenharmony_ci                  (int)payload_hash_len, payload_hash);
72013498266Sopenharmony_ci  if(!canonical_request)
72113498266Sopenharmony_ci    goto fail;
72213498266Sopenharmony_ci
72313498266Sopenharmony_ci  DEBUGF(infof(data, "Canonical request: %s", canonical_request));
72413498266Sopenharmony_ci
72513498266Sopenharmony_ci  /* provider 0 lowercase */
72613498266Sopenharmony_ci  Curl_strntolower(provider0, provider0, strlen(provider0));
72713498266Sopenharmony_ci  request_type = curl_maprintf("%s4_request", provider0);
72813498266Sopenharmony_ci  if(!request_type)
72913498266Sopenharmony_ci    goto fail;
73013498266Sopenharmony_ci
73113498266Sopenharmony_ci  credential_scope = curl_maprintf("%s/%s/%s/%s",
73213498266Sopenharmony_ci                                   date, region, service, request_type);
73313498266Sopenharmony_ci  if(!credential_scope)
73413498266Sopenharmony_ci    goto fail;
73513498266Sopenharmony_ci
73613498266Sopenharmony_ci  if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
73713498266Sopenharmony_ci                   strlen(canonical_request)))
73813498266Sopenharmony_ci    goto fail;
73913498266Sopenharmony_ci
74013498266Sopenharmony_ci  sha256_to_hex(sha_hex, sha_hash);
74113498266Sopenharmony_ci
74213498266Sopenharmony_ci  /* provider 0 uppercase */
74313498266Sopenharmony_ci  Curl_strntoupper(provider0, provider0, strlen(provider0));
74413498266Sopenharmony_ci
74513498266Sopenharmony_ci  /*
74613498266Sopenharmony_ci   * Google allows using RSA key instead of HMAC, so this code might change
74713498266Sopenharmony_ci   * in the future. For now we only support HMAC.
74813498266Sopenharmony_ci   */
74913498266Sopenharmony_ci  str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
75013498266Sopenharmony_ci                              "%s\n" /* RequestDateTime */
75113498266Sopenharmony_ci                              "%s\n" /* CredentialScope */
75213498266Sopenharmony_ci                              "%s",  /* HashedCanonicalRequest in hex */
75313498266Sopenharmony_ci                              provider0,
75413498266Sopenharmony_ci                              timestamp,
75513498266Sopenharmony_ci                              credential_scope,
75613498266Sopenharmony_ci                              sha_hex);
75713498266Sopenharmony_ci  if(!str_to_sign) {
75813498266Sopenharmony_ci    goto fail;
75913498266Sopenharmony_ci  }
76013498266Sopenharmony_ci
76113498266Sopenharmony_ci  /* provider 0 uppercase */
76213498266Sopenharmony_ci  secret = curl_maprintf("%s4%s", provider0,
76313498266Sopenharmony_ci                         data->state.aptr.passwd ?
76413498266Sopenharmony_ci                         data->state.aptr.passwd : "");
76513498266Sopenharmony_ci  if(!secret)
76613498266Sopenharmony_ci    goto fail;
76713498266Sopenharmony_ci
76813498266Sopenharmony_ci  HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
76913498266Sopenharmony_ci  HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
77013498266Sopenharmony_ci  HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0);
77113498266Sopenharmony_ci  HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
77213498266Sopenharmony_ci  HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
77313498266Sopenharmony_ci
77413498266Sopenharmony_ci  sha256_to_hex(sha_hex, sign0);
77513498266Sopenharmony_ci
77613498266Sopenharmony_ci  /* provider 0 uppercase */
77713498266Sopenharmony_ci  auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
77813498266Sopenharmony_ci                               "Credential=%s/%s, "
77913498266Sopenharmony_ci                               "SignedHeaders=%s, "
78013498266Sopenharmony_ci                               "Signature=%s\r\n"
78113498266Sopenharmony_ci                               /*
78213498266Sopenharmony_ci                                * date_header is added here, only if it wasn't
78313498266Sopenharmony_ci                                * user-specified (using CURLOPT_HTTPHEADER).
78413498266Sopenharmony_ci                                * date_header includes \r\n
78513498266Sopenharmony_ci                                */
78613498266Sopenharmony_ci                               "%s"
78713498266Sopenharmony_ci                               "%s", /* optional sha256 header includes \r\n */
78813498266Sopenharmony_ci                               provider0,
78913498266Sopenharmony_ci                               user,
79013498266Sopenharmony_ci                               credential_scope,
79113498266Sopenharmony_ci                               Curl_dyn_ptr(&signed_headers),
79213498266Sopenharmony_ci                               sha_hex,
79313498266Sopenharmony_ci                               date_header ? date_header : "",
79413498266Sopenharmony_ci                               content_sha256_hdr);
79513498266Sopenharmony_ci  if(!auth_headers) {
79613498266Sopenharmony_ci    goto fail;
79713498266Sopenharmony_ci  }
79813498266Sopenharmony_ci
79913498266Sopenharmony_ci  Curl_safefree(data->state.aptr.userpwd);
80013498266Sopenharmony_ci  data->state.aptr.userpwd = auth_headers;
80113498266Sopenharmony_ci  data->state.authhost.done = TRUE;
80213498266Sopenharmony_ci  result = CURLE_OK;
80313498266Sopenharmony_ci
80413498266Sopenharmony_cifail:
80513498266Sopenharmony_ci  Curl_dyn_free(&canonical_query);
80613498266Sopenharmony_ci  Curl_dyn_free(&canonical_headers);
80713498266Sopenharmony_ci  Curl_dyn_free(&signed_headers);
80813498266Sopenharmony_ci  free(canonical_request);
80913498266Sopenharmony_ci  free(request_type);
81013498266Sopenharmony_ci  free(credential_scope);
81113498266Sopenharmony_ci  free(str_to_sign);
81213498266Sopenharmony_ci  free(secret);
81313498266Sopenharmony_ci  free(date_header);
81413498266Sopenharmony_ci  return result;
81513498266Sopenharmony_ci}
81613498266Sopenharmony_ci
81713498266Sopenharmony_ci#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */
818