Browse Source

aws_sigv4: fall back to UNSIGNED-PAYLOAD for sign_as_s3

all s3 requests default to UNSIGNED-PAYLOAD and add the required
x-amz-content-sha256 header. this allows CURLAUTH_AWS_SIGV4 to correctly
sign s3 requests to amazon with no additional configuration

Signed-off-by: Casey Bodley <cbodley@redhat.com>

Closes #9995
Casey Bodley 1 year ago
parent
commit
495d09810a

+ 4 - 0
docs/libcurl/opts/CURLOPT_AWS_SIGV4.3

@@ -101,5 +101,9 @@ calculation.  For POST requests, this is a checksum of the provided
 \fICURLOPT_POSTFIELDS(3)\fP.  Otherwise, it's the checksum of an empty buffer.
 For requests like PUT, you can provide your own checksum in a HTTP header named
 \fBx-provider2-content-sha256\fP.
+.PP
+For \fBaws:s3\fP, a \fBx-amz-content-sha256\fP header is added to every request
+if not already present. For s3 requests with unknown payload, this header takes
+the special value "UNSIGNED-PAYLOAD".
 .SH "SEE ALSO"
 .BR CURLOPT_HEADEROPT "(3), " CURLOPT_HTTPHEADER "(3), "

+ 140 - 55
lib/http_aws_sigv4.c

@@ -58,13 +58,15 @@
 
 #define TIMESTAMP_SIZE 17
 
-static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
+/* hex-encoded with trailing null */
+#define SHA256_HEX_LENGTH (2 * SHA256_DIGEST_LENGTH + 1)
+
+static void sha256_to_hex(char *dst, unsigned char *sha)
 {
   int i;
 
-  DEBUGASSERT(dst_l >= 65);
-  for(i = 0; i < 32; ++i) {
-    msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
+  for(i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
+    msnprintf(dst + (i * 2), SHA256_HEX_LENGTH - (i * 2), "%02x", sha[i]);
   }
 }
 
@@ -135,6 +137,7 @@ static CURLcode make_headers(struct Curl_easy *data,
                              char *timestamp,
                              char *provider1,
                              char **date_header,
+                             char *content_sha256_header,
                              struct dynbuf *canonical_headers,
                              struct dynbuf *signed_headers)
 {
@@ -189,6 +192,13 @@ static CURLcode make_headers(struct Curl_easy *data,
   }
 
 
+  if (*content_sha256_header) {
+    tmp_head = curl_slist_append(head, content_sha256_header);
+    if(!tmp_head)
+      goto fail;
+    head = tmp_head;
+  }
+
   for(l = data->set.headers; l; l = l->next) {
     tmp_head = curl_slist_append(head, l->data);
     if(!tmp_head)
@@ -267,6 +277,9 @@ fail:
 }
 
 #define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256"))
+/* add 2 for ": " between header name and value */
+#define CONTENT_SHA256_HDR_LEN (CONTENT_SHA256_KEY_LEN + 2 + \
+                                SHA256_HEX_LENGTH)
 
 /* try to parse a payload hash from the content-sha256 header */
 static char *parse_content_sha_hdr(struct Curl_easy *data,
@@ -300,6 +313,67 @@ static char *parse_content_sha_hdr(struct Curl_easy *data,
   return value;
 }
 
+static CURLcode calc_payload_hash(struct Curl_easy *data,
+                                  unsigned char *sha_hash, char *sha_hex)
+{
+  const char *post_data = data->set.postfields;
+  size_t post_data_len = 0;
+  CURLcode ret = CURLE_OUT_OF_MEMORY;
+
+  if(post_data) {
+    if(data->set.postfieldsize < 0)
+      post_data_len = strlen(post_data);
+    else
+      post_data_len = (size_t)data->set.postfieldsize;
+  }
+  ret = Curl_sha256it(sha_hash, (const unsigned char *) post_data,
+                      post_data_len);
+  if(ret)
+    goto fail;
+
+  sha256_to_hex(sha_hex, sha_hash);
+fail:
+  return ret;
+}
+
+#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD"
+
+static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
+                                     Curl_HttpReq httpreq, char *provider1,
+                                     unsigned char *sha_hash,
+                                     char *sha_hex, char *header)
+{
+  bool empty_method = (httpreq == HTTPREQ_GET || httpreq == HTTPREQ_HEAD);
+  /* The request method or filesize indicate no request payload */
+  bool empty_payload = (empty_method || data->set.filesize == 0);
+  /* The POST payload is in memory */
+  bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields);
+  CURLcode ret = CURLE_OUT_OF_MEMORY;
+
+  if(empty_payload || post_payload) {
+    /* Calculate a real hash when we know the request payload */
+    ret = calc_payload_hash(data, sha_hash, sha_hex);
+    if(ret)
+      goto fail;
+    ret = CURLE_OUT_OF_MEMORY;
+  }
+  else {
+    /* Fall back to s3's UNSIGNED-PAYLOAD */
+    size_t len = sizeof(S3_UNSIGNED_PAYLOAD) - 1;
+    DEBUGASSERT(len < SHA256_HEX_LENGTH); /* 16 < 65 */
+    memcpy(sha_hex, S3_UNSIGNED_PAYLOAD, len);
+    sha_hex[len] = 0;
+  }
+
+  /* format the required content-sha256 header */
+  msnprintf(header, CONTENT_SHA256_HDR_LEN,
+            "x-%s-content-sha256: %s", provider1, sha_hex);
+
+  ret = CURLE_OK;
+fail:
+  return ret;
+}
+
 CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
 {
   CURLcode ret = CURLE_OUT_OF_MEMORY;
@@ -310,6 +384,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   char provider1[MAX_SIGV4_LEN + 1]="";
   char region[MAX_SIGV4_LEN + 1]="";
   char service[MAX_SIGV4_LEN + 1]="";
+  bool sign_as_s3 = false;
   const char *hostname = conn->host.name;
   time_t clock;
   struct tm tm;
@@ -318,20 +393,21 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   struct dynbuf canonical_headers;
   struct dynbuf signed_headers;
   char *date_header = NULL;
+  Curl_HttpReq httpreq;
+  const char *method = NULL;
   char *payload_hash = NULL;
   size_t payload_hash_len = 0;
-  const char *post_data = data->set.postfields;
-  size_t post_data_len = 0;
-  unsigned char sha_hash[32];
-  char sha_hex[65];
+  unsigned char sha_hash[SHA256_DIGEST_LENGTH];
+  char sha_hex[SHA256_HEX_LENGTH];
+  char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
   char *canonical_request = NULL;
   char *request_type = NULL;
   char *credential_scope = NULL;
   char *str_to_sign = NULL;
   const char *user = data->state.aptr.user ? data->state.aptr.user : "";
   char *secret = NULL;
-  unsigned char sign0[32] = {0};
-  unsigned char sign1[32] = {0};
+  unsigned char sign0[SHA256_DIGEST_LENGTH] = {0};
+  unsigned char sign1[SHA256_DIGEST_LENGTH] = {0};
   char *auth_headers = NULL;
 
   DEBUGASSERT(!proxy);
@@ -408,6 +484,30 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     }
   }
 
+  Curl_http_method(data, conn, &method, &httpreq);
+
+  /* AWS S3 requires a x-amz-content-sha256 header, and supports special
+   * values like UNSIGNED-PAYLOAD */
+  sign_as_s3 = (strcasecompare(provider0, "aws") &&
+                strcasecompare(service, "s3"));
+
+  payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
+
+  if(!payload_hash) {
+    if(sign_as_s3)
+      ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
+                                 sha_hex, content_sha256_hdr);
+    else
+      ret = calc_payload_hash(data, sha_hash, sha_hex);
+    if(ret)
+      goto fail;
+    ret = CURLE_OUT_OF_MEMORY;
+
+    payload_hash = sha_hex;
+    /* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */
+    payload_hash_len = strlen(sha_hex);
+  }
+
 #ifdef DEBUGBUILD
   {
     char *force_timestamp = getenv("CURL_FORCETIME");
@@ -429,54 +529,37 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   }
 
   ret = make_headers(data, hostname, timestamp, provider1,
-                     &date_header, &canonical_headers, &signed_headers);
+                     &date_header, content_sha256_hdr,
+                     &canonical_headers, &signed_headers);
   if(ret)
     goto fail;
   ret = CURLE_OUT_OF_MEMORY;
 
+  if(*content_sha256_hdr) {
+    /* make_headers() needed this without the \r\n for canonicalization */
+    size_t hdrlen = strlen(content_sha256_hdr);
+    DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr));
+    memcpy(content_sha256_hdr + hdrlen, "\r\n", 3);
+  }
+
   memcpy(date, timestamp, sizeof(date));
   date[sizeof(date) - 1] = 0;
 
-  payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
-
-  if(!payload_hash) {
-    if(post_data) {
-      if(data->set.postfieldsize < 0)
-        post_data_len = strlen(post_data);
-      else
-        post_data_len = (size_t)data->set.postfieldsize;
-    }
-    if(Curl_sha256it(sha_hash, (const unsigned char *) post_data,
-                     post_data_len))
-      goto fail;
-
-    sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
-    payload_hash = sha_hex;
-    payload_hash_len = strlen(sha_hex);
-  }
-
-  {
-    Curl_HttpReq httpreq;
-    const char *method;
-
-    Curl_http_method(data, conn, &method, &httpreq);
-
-    canonical_request =
-      curl_maprintf("%s\n" /* HTTPRequestMethod */
-                    "%s\n" /* CanonicalURI */
-                    "%s\n" /* CanonicalQueryString */
-                    "%s\n" /* CanonicalHeaders */
-                    "%s\n" /* SignedHeaders */
-                    "%.*s",  /* HashedRequestPayload in hex */
-                    method,
-                    data->state.up.path,
-                    data->state.up.query ? data->state.up.query : "",
-                    Curl_dyn_ptr(&canonical_headers),
-                    Curl_dyn_ptr(&signed_headers),
-                    (int)payload_hash_len, payload_hash);
-    if(!canonical_request)
-      goto fail;
-  }
+  canonical_request =
+    curl_maprintf("%s\n" /* HTTPRequestMethod */
+                  "%s\n" /* CanonicalURI */
+                  "%s\n" /* CanonicalQueryString */
+                  "%s\n" /* CanonicalHeaders */
+                  "%s\n" /* SignedHeaders */
+                  "%.*s",  /* HashedRequestPayload in hex */
+                  method,
+                  data->state.up.path,
+                  data->state.up.query ? data->state.up.query : "",
+                  Curl_dyn_ptr(&canonical_headers),
+                  Curl_dyn_ptr(&signed_headers),
+                  (int)payload_hash_len, payload_hash);
+  if(!canonical_request)
+    goto fail;
 
   /* provider 0 lowercase */
   Curl_strntolower(provider0, provider0, strlen(provider0));
@@ -493,7 +576,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
                    strlen(canonical_request)))
     goto fail;
 
-  sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
+  sha256_to_hex(sha_hex, sha_hash);
 
   /* provider 0 uppercase */
   Curl_strntoupper(provider0, provider0, strlen(provider0));
@@ -527,20 +610,22 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
   HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
 
-  sha256_to_hex(sha_hex, sign0, sizeof(sha_hex));
+  sha256_to_hex(sha_hex, sign0);
 
   /* provider 0 uppercase */
   auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
                                "Credential=%s/%s, "
                                "SignedHeaders=%s, "
                                "Signature=%s\r\n"
-                               "%s\r\n",
+                               "%s\r\n"
+                               "%s", /* optional sha256 header includes \r\n */
                                provider0,
                                user,
                                credential_scope,
                                Curl_dyn_ptr(&signed_headers),
                                sha_hex,
-                               date_header);
+                               date_header,
+                               content_sha256_hdr);
   if(!auth_headers) {
     goto fail;
   }

+ 1 - 0
tests/data/Makefile.inc

@@ -226,6 +226,7 @@ test1916 test1917 test1918 test1919 \
 test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \
 test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \
 test1955 test1956 test1957 test1958 test1959 test1960 \
+test1970 test1971 test1972 test1973 test1974 test1975 \
 \
 test2000 test2001 test2002 test2003 test2004 \
 \

+ 74 - 0
tests/data/test1970

@@ -0,0 +1,74 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+CURLOPT_AWS_SIGV4
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 302 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+Location: /%TESTNUMBER0002
+
+</data>
+<data2>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data2>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+# this relies on the debug feature which allow to set the time
+<features>
+SSL
+debug
+crypto
+</features>
+<setenv>
+CURL_FORCEHOST=1
+</setenv>
+
+<name>
+HTTP AWS_SIGV4 for AWS S3: UPLOAD with INFILESIZE=0
+</name>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<command>
+http://exam.ple.com:9000/aws_sigv4/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+^Content-Type:.*
+^Accept:.*
+</strip>
+<protocol>
+PUT /aws_sigv4/testapi/test HTTP/1.1
+Host: exam.ple.com:9000
+Authorization: AWS4-HMAC-SHA256 Credential=xxx/19700101/us-east-1/s3/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=a028756f42a859122e9609c1f90cae4b272d6b03bf60d9fd354138176dfa2260
+X-Amz-Date: 19700101T000000Z
+x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+Content-Length: 0
+
+</protocol>
+</verify>
+</testcase>

+ 70 - 0
tests/data/test1971

@@ -0,0 +1,70 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+CURLOPT_AWS_SIGV4
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 100 Continue
+
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+# this relies on the debug feature which allow to set the time
+<features>
+SSL
+debug
+crypto
+</features>
+<setenv>
+CURL_FORCEHOST=1
+</setenv>
+
+<name>
+HTTP AWS_SIGV4 for AWS S3: UPLOAD with unknown INFILESIZE
+</name>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<command>
+http://exam.ple.com:9000/aws_sigv4/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+^Content-Type:.*
+^Accept:.*
+</strip>
+<protocol>
+PUT /aws_sigv4/testapi/test HTTP/1.1
+Host: exam.ple.com:9000
+Authorization: AWS4-HMAC-SHA256 Credential=xxx/19700101/us-east-1/s3/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=4a6e9b4af0542ffb83744c6852f8e1bfec14f2a67e6f6f037b39f172f79d62af
+X-Amz-Date: 19700101T000000Z
+x-amz-content-sha256: UNSIGNED-PAYLOAD
+Transfer-Encoding: chunked
+Expect: 100-continue
+
+0
+
+</protocol>
+</verify>
+</testcase>

+ 79 - 0
tests/data/test1972

@@ -0,0 +1,79 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+CURLOPT_AWS_SIGV4
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 302 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+Location: /%TESTNUMBER0002
+
+</data>
+<data2>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data2>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+# this relies on the debug feature which allow to set the time
+<features>
+SSL
+debug
+crypto
+</features>
+<setenv>
+CURL_FORCEHOST=1
+</setenv>
+
+<name>
+HTTP AWS_SIGV4 for AWS S3: MIMEPOST
+</name>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<command>
+http://exam.ple.com:9000/aws_sigv4/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+^Content-Type:.*
+^Accept:.*
+</strip>
+<protocol>
+POST /aws_sigv4/testapi/test HTTP/1.1
+Host: exam.ple.com:9000
+Authorization: AWS4-HMAC-SHA256 Credential=xxx/19700101/us-east-1/s3/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=eaee0f1c5984ad5d81c8bc7805f28c7b83b35322de654b2ace18cb8cf6d5a9cb
+X-Amz-Date: 19700101T000000Z
+x-amz-content-sha256: UNSIGNED-PAYLOAD
+Content-Length: 142
+
+--------------------------3433323135333231
+Content-Disposition: attachment; name="foo"
+
+bar
+--------------------------3433323135333231--
+</protocol>
+</verify>
+</testcase>

+ 75 - 0
tests/data/test1973

@@ -0,0 +1,75 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+CURLOPT_AWS_SIGV4
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 302 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+Location: /%TESTNUMBER0002
+
+</data>
+<data2>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data2>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+# this relies on the debug feature which allow to set the time
+<features>
+SSL
+debug
+crypto
+</features>
+<setenv>
+CURL_FORCEHOST=1
+</setenv>
+
+<name>
+HTTP AWS_SIGV4 for AWS S3: POSTFIELDS
+</name>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<command>
+http://exam.ple.com:9000/aws_sigv4/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+^Content-Type:.*
+^Accept:.*
+</strip>
+<protocol>
+POST /aws_sigv4/testapi/test HTTP/1.1
+Host: exam.ple.com:9000
+Authorization: AWS4-HMAC-SHA256 Credential=xxx/19700101/us-east-1/s3/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=7eb34202214384872221b99a9c671b7517891ac6af56b0aff24ec51adf62b10a
+X-Amz-Date: 19700101T000000Z
+x-amz-content-sha256: 4b02e333ccf7cf530ddee3e10ebe54e935500b5e570e68650d63d743e8bbc045
+Content-Length: 12
+
+post fields
+</protocol>
+</verify>
+</testcase>

+ 73 - 0
tests/data/test1974

@@ -0,0 +1,73 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+CURLOPT_AWS_SIGV4
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 302 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+Location: /%TESTNUMBER0002
+
+</data>
+<data2>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data2>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+# this relies on the debug feature which allow to set the time
+<features>
+SSL
+debug
+crypto
+</features>
+<setenv>
+CURL_FORCEHOST=1
+</setenv>
+
+<name>
+HTTP AWS_SIGV4 for AWS S3: GET
+</name>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<command>
+http://exam.ple.com:9000/aws_sigv4/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+^Content-Type:.*
+^Accept:.*
+</strip>
+<protocol>
+GET /aws_sigv4/testapi/test HTTP/1.1
+Host: exam.ple.com:9000
+Authorization: AWS4-HMAC-SHA256 Credential=xxx/19700101/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=e6270423932feafe9b00ca5d60c9ed566be649f9ca9676144288273945153021
+X-Amz-Date: 19700101T000000Z
+x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+
+</protocol>
+</verify>
+</testcase>

+ 70 - 0
tests/data/test1975

@@ -0,0 +1,70 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+CURLOPT_AWS_SIGV4
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 100 Continue
+
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Type: text/html
+Content-Length: 0
+
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+# this relies on the debug feature which allow to set the time
+<features>
+SSL
+debug
+crypto
+</features>
+<setenv>
+CURL_FORCEHOST=1
+</setenv>
+
+<name>
+HTTP AWS_SIGV4 for AWS S3: UPLOAD with given x-amz-content-sha256
+</name>
+<tool>
+lib%TESTNUMBER
+</tool>
+
+<command>
+http://exam.ple.com:9000/aws_sigv4/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPPORT
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+^Content-Type:.*
+^Accept:.*
+</strip>
+<protocol>
+PUT /aws_sigv4/testapi/test HTTP/1.1
+Host: exam.ple.com:9000
+Authorization: AWS4-HMAC-SHA256 Credential=xxx/19700101/us-east-1/s3/aws4_request, SignedHeaders=content-type;host;x-amz-content-sha256;x-amz-date, Signature=a028756f42a859122e9609c1f90cae4b272d6b03bf60d9fd354138176dfa2260
+X-Amz-Date: 19700101T000000Z
+Transfer-Encoding: chunked
+X-Amz-Content-Sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+Expect: 100-continue
+
+0
+
+</protocol>
+</verify>
+</testcase>

+ 19 - 0
tests/libtest/Makefile.inc

@@ -70,6 +70,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect                \
  lib1933 lib1934 lib1935 lib1936 lib1937 lib1938 lib1939 lib1940 \
  lib1945 lib1946 lib1947 lib1948 lib1955 lib1956 lib1957 lib1958 lib1959 \
  lib1960 \
+ lib1970 lib1971 lib1972 lib1973 lib1974 lib1975 \
  lib2301 lib2302 lib2304 lib2305 \
  lib2402 \
  lib2502 \
@@ -798,6 +799,24 @@ lib1959_CPPFLAGS = $(AM_CPPFLAGS)
 lib1960_SOURCES = lib1960.c $(SUPPORTFILES)
 lib1960_LDADD = $(TESTUTIL_LIBS)
 
+lib1970_SOURCES = lib1970.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1970_LDADD = $(TESTUTIL_LIBS)
+
+lib1971_SOURCES = lib1971.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1971_LDADD = $(TESTUTIL_LIBS)
+
+lib1972_SOURCES = lib1972.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1972_LDADD = $(TESTUTIL_LIBS)
+
+lib1973_SOURCES = lib1973.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1973_LDADD = $(TESTUTIL_LIBS)
+
+lib1974_SOURCES = lib1974.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1974_LDADD = $(TESTUTIL_LIBS)
+
+lib1975_SOURCES = lib1975.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1975_LDADD = $(TESTUTIL_LIBS)
+
 lib2301_SOURCES = lib2301.c $(SUPPORTFILES)
 lib2301_LDADD = $(TESTUTIL_LIBS)
 

+ 73 - 0
tests/libtest/lib1970.c

@@ -0,0 +1,73 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+int test(char *URL)
+{
+  CURL *curl;
+  CURLcode res = TEST_ERR_MAJOR_BAD;
+  struct curl_slist *list = NULL;
+  struct curl_slist *connect_to = NULL;
+
+  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl = curl_easy_init();
+  if(!curl) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  test_setopt(curl, CURLOPT_UPLOAD, 1L);
+  test_setopt(curl, CURLOPT_INFILESIZE, 0L);
+  test_setopt(curl, CURLOPT_VERBOSE, 1L);
+  test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3");
+  test_setopt(curl, CURLOPT_USERPWD, "xxx");
+  test_setopt(curl, CURLOPT_HEADER, 0L);
+  test_setopt(curl, CURLOPT_URL, URL);
+  list = curl_slist_append(list, "Content-Type: application/json");
+  if(!list)
+    goto test_cleanup;
+  test_setopt(curl, CURLOPT_HTTPHEADER, list);
+  if(libtest_arg2) {
+    connect_to = curl_slist_append(connect_to, libtest_arg2);
+  }
+  test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
+
+  res = curl_easy_perform(curl);
+
+test_cleanup:
+
+  curl_slist_free_all(connect_to);
+  curl_slist_free_all(list);
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return res;
+}

+ 83 - 0
tests/libtest/lib1971.c

@@ -0,0 +1,83 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+static size_t read_callback(char *buffer, size_t size, size_t nitems,
+                            void *userdata)
+{
+  (void)buffer; /* unused */
+  (void)size; /* unused */
+  (void)nitems; /* unused */
+  (void)userdata; /* unused */
+  return 0;
+}
+
+int test(char *URL)
+{
+  CURL *curl;
+  CURLcode res = TEST_ERR_MAJOR_BAD;
+  struct curl_slist *list = NULL;
+  struct curl_slist *connect_to = NULL;
+
+  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl = curl_easy_init();
+  if(!curl) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  test_setopt(curl, CURLOPT_UPLOAD, 1L);
+  test_setopt(curl, CURLOPT_READFUNCTION, read_callback);
+  test_setopt(curl, CURLOPT_VERBOSE, 1L);
+  test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3");
+  test_setopt(curl, CURLOPT_USERPWD, "xxx");
+  test_setopt(curl, CURLOPT_HEADER, 0L);
+  test_setopt(curl, CURLOPT_URL, URL);
+  list = curl_slist_append(list, "Content-Type: application/json");
+  if(!list)
+    goto test_cleanup;
+  test_setopt(curl, CURLOPT_HTTPHEADER, list);
+  if(libtest_arg2) {
+    connect_to = curl_slist_append(connect_to, libtest_arg2);
+  }
+  test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
+
+  res = curl_easy_perform(curl);
+
+test_cleanup:
+
+  curl_slist_free_all(connect_to);
+  curl_slist_free_all(list);
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return res;
+}

+ 84 - 0
tests/libtest/lib1972.c

@@ -0,0 +1,84 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+int test(char *URL)
+{
+  CURL *curl;
+  curl_mime *mime = NULL;
+  curl_mimepart *part = NULL;
+  CURLcode res = TEST_ERR_MAJOR_BAD;
+  struct curl_slist *list = NULL;
+  struct curl_slist *connect_to = NULL;
+
+  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl = curl_easy_init();
+  if(!curl) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  mime = curl_mime_init(curl);
+  if(!mime)
+    goto test_cleanup;
+  part = curl_mime_addpart(mime);
+  if(!part)
+    goto test_cleanup;
+  curl_mime_name(part, "foo");
+  curl_mime_data(part, "bar", CURL_ZERO_TERMINATED);
+
+  test_setopt(curl, CURLOPT_MIMEPOST, mime);
+  test_setopt(curl, CURLOPT_VERBOSE, 1L);
+  test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3");
+  test_setopt(curl, CURLOPT_USERPWD, "xxx");
+  test_setopt(curl, CURLOPT_HEADER, 0L);
+  test_setopt(curl, CURLOPT_URL, URL);
+  list = curl_slist_append(list, "Content-Type: application/json");
+  if(!list)
+    goto test_cleanup;
+  test_setopt(curl, CURLOPT_HTTPHEADER, list);
+  if(libtest_arg2) {
+    connect_to = curl_slist_append(connect_to, libtest_arg2);
+  }
+  test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
+
+  res = curl_easy_perform(curl);
+
+test_cleanup:
+
+  curl_slist_free_all(connect_to);
+  curl_slist_free_all(list);
+  curl_easy_cleanup(curl);
+  curl_mime_free(mime);
+  curl_global_cleanup();
+
+  return res;
+}

+ 72 - 0
tests/libtest/lib1973.c

@@ -0,0 +1,72 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+int test(char *URL)
+{
+  CURL *curl;
+  CURLcode res = TEST_ERR_MAJOR_BAD;
+  struct curl_slist *list = NULL;
+  struct curl_slist *connect_to = NULL;
+
+  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl = curl_easy_init();
+  if(!curl) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  test_setopt(curl, CURLOPT_POSTFIELDS, "post fields\n");
+  test_setopt(curl, CURLOPT_VERBOSE, 1L);
+  test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3");
+  test_setopt(curl, CURLOPT_USERPWD, "xxx");
+  test_setopt(curl, CURLOPT_HEADER, 0L);
+  test_setopt(curl, CURLOPT_URL, URL);
+  list = curl_slist_append(list, "Content-Type: application/json");
+  if(!list)
+    goto test_cleanup;
+  test_setopt(curl, CURLOPT_HTTPHEADER, list);
+  if(libtest_arg2) {
+    connect_to = curl_slist_append(connect_to, libtest_arg2);
+  }
+  test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
+
+  res = curl_easy_perform(curl);
+
+test_cleanup:
+
+  curl_slist_free_all(connect_to);
+  curl_slist_free_all(list);
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return res;
+}

+ 65 - 0
tests/libtest/lib1974.c

@@ -0,0 +1,65 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+int test(char *URL)
+{
+  CURL *curl;
+  CURLcode res = TEST_ERR_MAJOR_BAD;
+  struct curl_slist *connect_to = NULL;
+
+  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl = curl_easy_init();
+  if(!curl) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  test_setopt(curl, CURLOPT_VERBOSE, 1L);
+  test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3");
+  test_setopt(curl, CURLOPT_USERPWD, "xxx");
+  test_setopt(curl, CURLOPT_HEADER, 0L);
+  test_setopt(curl, CURLOPT_URL, URL);
+  if(libtest_arg2) {
+    connect_to = curl_slist_append(connect_to, libtest_arg2);
+  }
+  test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
+
+  res = curl_easy_perform(curl);
+
+test_cleanup:
+
+  curl_slist_free_all(connect_to);
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return res;
+}

+ 85 - 0
tests/libtest/lib1975.c

@@ -0,0 +1,85 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "test.h"
+
+#include "memdebug.h"
+
+static size_t read_callback(char *buffer, size_t size, size_t nitems,
+                            void *userdata)
+{
+  (void)buffer; /* unused */
+  (void)size; /* unused */
+  (void)nitems; /* unused */
+  (void)userdata; /* unused */
+  return 0;
+}
+
+int test(char *URL)
+{
+  CURL *curl;
+  CURLcode res = TEST_ERR_MAJOR_BAD;
+  struct curl_slist *list = NULL;
+  struct curl_slist *connect_to = NULL;
+
+  if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
+    fprintf(stderr, "curl_global_init() failed\n");
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  curl = curl_easy_init();
+  if(!curl) {
+    fprintf(stderr, "curl_easy_init() failed\n");
+    curl_global_cleanup();
+    return TEST_ERR_MAJOR_BAD;
+  }
+
+  test_setopt(curl, CURLOPT_UPLOAD, 1L);
+  test_setopt(curl, CURLOPT_READFUNCTION, read_callback);
+  test_setopt(curl, CURLOPT_VERBOSE, 1L);
+  test_setopt(curl, CURLOPT_AWS_SIGV4, "aws:amz:us-east-1:s3");
+  test_setopt(curl, CURLOPT_USERPWD, "xxx");
+  test_setopt(curl, CURLOPT_HEADER, 0L);
+  test_setopt(curl, CURLOPT_URL, URL);
+  list = curl_slist_append(list, "Content-Type: application/json");
+  if(!list)
+    goto test_cleanup;
+  curl_slist_append(list, "X-Amz-Content-Sha256: "
+    "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+  test_setopt(curl, CURLOPT_HTTPHEADER, list);
+  if(libtest_arg2) {
+    connect_to = curl_slist_append(connect_to, libtest_arg2);
+  }
+  test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
+
+  res = curl_easy_perform(curl);
+
+test_cleanup:
+
+  curl_slist_free_all(connect_to);
+  curl_slist_free_all(list);
+  curl_easy_cleanup(curl);
+  curl_global_cleanup();
+
+  return res;
+}