diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c
index 308970bb89..7a61bfb729 100644
--- a/lib/http_aws_sigv4.c
+++ b/lib/http_aws_sigv4.c
@@ -817,67 +817,14 @@ fail:
return result;
}
-CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
+static CURLcode parse_sigv4_params(struct Curl_easy *data,
+ const char *hostname,
+ struct Curl_str *provider0,
+ struct Curl_str *provider1,
+ struct Curl_str *region,
+ struct Curl_str *service)
{
- CURLcode result = CURLE_OUT_OF_MEMORY;
- struct connectdata *conn = data->conn;
- const char *line;
- struct Curl_str provider0;
- struct Curl_str provider1;
- struct Curl_str region = { NULL, 0 };
- struct Curl_str service = { NULL, 0 };
- const char *hostname = conn->origin->hostname;
- time_t clock;
- struct tm tm;
- char timestamp[TIMESTAMP_SIZE];
- char date[9];
- struct dynbuf canonical_headers;
- struct dynbuf signed_headers;
- struct dynbuf canonical_query;
- struct dynbuf canonical_path;
- char *date_header = NULL;
- Curl_HttpReq httpreq;
- const char *method = NULL;
- const char *payload_hash = NULL;
- size_t payload_hash_len = 0;
- unsigned char sha_hash[CURL_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 = Curl_creds_user(data->state.creds);
- const char *passwd = Curl_creds_passwd(data->state.creds);
- char *secret = NULL;
- unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 };
- unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 };
- char *auth_headers = NULL;
-
- if(data->set.path_as_is) {
- failf(data, "Cannot use sigv4 authentication with path-as-is flag");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- if(Curl_checkheaders(data, STRCONST("Authorization"))) {
- /* Authorization already present, Bailing out */
- return CURLE_OK;
- }
-
- /* we init those buffers here, so goto fail will free initialized dynbuf */
- curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
- curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
- curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
- curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
-
- /*
- * Parameters parsing
- * Google and Outscale use the same OSC or GOOG,
- * but Amazon uses AWS and AMZ for header arguments.
- * AWS is the default because most of non-amazon providers
- * are still using aws:amz as a prefix.
- */
- line = data->set.str[STRING_AWS_SIGV4];
+ const char *line = data->set.str[STRING_AWS_SIGV4];
if(!line || !*line)
line = "aws:amz";
@@ -885,71 +832,89 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
No string can be longer than N bytes of non-whitespace
*/
- if(curlx_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) {
+ if(curlx_str_until(&line, provider0, MAX_SIGV4_LEN, ':')) {
failf(data, "first aws-sigv4 provider cannot be empty");
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto fail;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
}
if(curlx_str_single(&line, ':') ||
- curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
- provider1 = provider0;
+ curlx_str_until(&line, provider1, MAX_SIGV4_LEN, ':')) {
+ *provider1 = *provider0;
}
else if(curlx_str_single(&line, ':') ||
- curlx_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') ||
+ curlx_str_until(&line, region, MAX_SIGV4_LEN, ':') ||
curlx_str_single(&line, ':') ||
- curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
+ curlx_str_until(&line, service, MAX_SIGV4_LEN, ':')) {
/* nothing to do */
}
- if(!curlx_strlen(&service)) {
+ if(!curlx_strlen(service)) {
const char *p = hostname;
- if(curlx_str_until(&p, &service, MAX_SIGV4_LEN, '.') ||
+ if(curlx_str_until(&p, service, MAX_SIGV4_LEN, '.') ||
curlx_str_single(&p, '.')) {
failf(data, "aws-sigv4: service missing in parameters and hostname");
- result = CURLE_URL_MALFORMAT;
- goto fail;
+ return CURLE_URL_MALFORMAT;
}
infof(data, "aws_sigv4: picked service %.*s from host",
- (int)curlx_strlen(&service), curlx_str(&service));
+ (int)curlx_strlen(service), curlx_str(service));
- if(!curlx_strlen(®ion)) {
- if(curlx_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') ||
+ if(!curlx_strlen(region)) {
+ if(curlx_str_until(&p, region, MAX_SIGV4_LEN, '.') ||
curlx_str_single(&p, '.')) {
failf(data, "aws-sigv4: region missing in parameters and hostname");
- result = CURLE_URL_MALFORMAT;
- goto fail;
+ return CURLE_URL_MALFORMAT;
}
infof(data, "aws_sigv4: picked region %.*s from host",
- (int)curlx_strlen(®ion), curlx_str(®ion));
+ (int)curlx_strlen(region), curlx_str(region));
}
}
- Curl_http_method(data, &method, &httpreq);
+ return CURLE_OK;
+}
- payload_hash =
- parse_content_sha_hdr(data, curlx_str(&provider1),
- curlx_strlen(&provider1), &payload_hash_len);
+static CURLcode get_payload_hash(struct Curl_easy *data,
+ Curl_HttpReq httpreq,
+ struct Curl_str *provider0,
+ struct Curl_str *provider1,
+ struct Curl_str *service,
+ unsigned char *sha_hash,
+ char *sha_hex,
+ char *content_sha256_hdr,
+ const char **payload_hash_out,
+ size_t *payload_hash_len_out)
+{
+ *payload_hash_out =
+ parse_content_sha_hdr(data, curlx_str(provider1),
+ curlx_strlen(provider1), payload_hash_len_out);
- if(!payload_hash) {
+ if(!*payload_hash_out) {
+ CURLcode result;
/* AWS S3 requires a x-amz-content-sha256 header, and supports special
* values like UNSIGNED-PAYLOAD */
- bool sign_as_s3 = curlx_str_casecompare(&provider0, "aws") &&
- curlx_str_casecompare(&service, "s3");
+ bool sign_as_s3 = curlx_str_casecompare(provider0, "aws") &&
+ curlx_str_casecompare(service, "s3");
if(sign_as_s3)
- result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1),
- curlx_strlen(&provider1), sha_hash,
+ result = calc_s3_payload_hash(data, httpreq, curlx_str(provider1),
+ curlx_strlen(provider1), sha_hash,
sha_hex, content_sha256_hdr);
else
result = calc_payload_hash(data, sha_hash, sha_hex);
if(result)
- goto fail;
+ return result;
- payload_hash = sha_hex;
+ *payload_hash_out = sha_hex;
/* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */
- payload_hash_len = strlen(sha_hex);
+ *payload_hash_len_out = strlen(sha_hex);
}
+ return CURLE_OK;
+}
+
+static CURLcode get_timestamp(char *timestamp, size_t stampsize)
+{
+ time_t clock;
+ struct tm tm;
+ CURLcode result;
#ifdef DEBUGBUILD
{
@@ -963,43 +928,54 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
clock = time(NULL);
#endif
result = curlx_gmtime(clock, &tm);
- if(result) {
- goto fail;
- }
- if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
- }
+ if(result)
+ return result;
+
+ if(!strftime(timestamp, stampsize, "%Y%m%dT%H%M%SZ", &tm))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+static CURLcode make_canonical_request(struct Curl_easy *data,
+ const char *hostname,
+ char *timestamp,
+ struct Curl_str *provider1,
+ struct Curl_str *service,
+ const char *method,
+ const char *payload_hash,
+ size_t payload_hash_len,
+ char **date_header_out,
+ char *content_sha256_hdr,
+ struct dynbuf *canonical_headers,
+ struct dynbuf *signed_headers,
+ char **canonical_request_out)
+{
+ struct dynbuf canonical_query;
+ struct dynbuf canonical_path;
+ CURLcode result;
+
+ curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
+ curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
result = make_headers(data, hostname, timestamp,
- curlx_str(&provider1), curlx_strlen(&provider1),
- &date_header, content_sha256_hdr,
- &canonical_headers, &signed_headers);
+ curlx_str(provider1), curlx_strlen(provider1),
+ date_header_out, content_sha256_hdr,
+ canonical_headers, signed_headers);
if(result)
goto fail;
- 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;
-
result = canon_query(data->state.up.query, &canonical_query);
if(result)
goto fail;
result = canon_path(data->state.up.path, strlen(data->state.up.path),
&canonical_path,
- should_urlencode(&service));
+ should_urlencode(service));
if(result)
goto fail;
- result = CURLE_OUT_OF_MEMORY;
- canonical_request =
+ *canonical_request_out =
curl_maprintf("%s\n" /* HTTPRequestMethod */
"%s\n" /* CanonicalURI */
"%s\n" /* CanonicalQueryString */
@@ -1010,37 +986,65 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
curlx_dyn_ptr(&canonical_path),
curlx_dyn_ptr(&canonical_query) ?
curlx_dyn_ptr(&canonical_query) : "",
- curlx_dyn_ptr(&canonical_headers),
- curlx_dyn_ptr(&signed_headers),
+ curlx_dyn_ptr(canonical_headers),
+ curlx_dyn_ptr(signed_headers),
(int)payload_hash_len, payload_hash);
- if(!canonical_request)
+ if(!*canonical_request_out) {
+ result = CURLE_OUT_OF_MEMORY;
goto fail;
+ }
- infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]",
- canonical_request);
+ result = CURLE_OK;
+fail:
+ curlx_dyn_free(&canonical_query);
+ curlx_dyn_free(&canonical_path);
+ return result;
+}
+
+static CURLcode make_string_to_sign(struct Curl_easy *data,
+ struct Curl_str *provider0,
+ struct Curl_str *region,
+ struct Curl_str *service,
+ const char *date,
+ const char *timestamp,
+ const char *canonical_request,
+ char **request_type_out,
+ char **credential_scope_out,
+ char **str_to_sign_out)
+{
+ char *request_type;
+ char *credential_scope;
+ char *str_to_sign;
+ unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
+ char sha_hex[SHA256_HEX_LENGTH];
request_type = curl_maprintf("%.*s4_request",
- (int)curlx_strlen(&provider0),
- curlx_str(&provider0));
+ (int)curlx_strlen(provider0),
+ curlx_str(provider0));
if(!request_type)
- goto fail;
+ return CURLE_OUT_OF_MEMORY;
/* provider0 is lowercased *after* curl_maprintf() so that the buffer
can be written to */
- Curl_strntolower(request_type, request_type, curlx_strlen(&provider0));
+ Curl_strntolower(request_type, request_type, curlx_strlen(provider0));
credential_scope = curl_maprintf("%s/%.*s/%.*s/%s", date,
- (int)curlx_strlen(®ion),
- curlx_str(®ion),
- (int)curlx_strlen(&service),
- curlx_str(&service),
+ (int)curlx_strlen(region),
+ curlx_str(region),
+ (int)curlx_strlen(service),
+ curlx_str(service),
request_type);
- if(!credential_scope)
- goto fail;
+ if(!credential_scope) {
+ curlx_free(request_type);
+ return CURLE_OUT_OF_MEMORY;
+ }
- if(Curl_sha256it(sha_hash, (unsigned char *)canonical_request,
- strlen(canonical_request)))
- goto fail;
+ if(Curl_sha256it(sha_hash, (const unsigned char *)canonical_request,
+ strlen(canonical_request))) {
+ curlx_free(request_type);
+ curlx_free(credential_scope);
+ return CURLE_OUT_OF_MEMORY;
+ }
sha256_to_hex(sha_hex, sha_hash);
@@ -1052,35 +1056,69 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
"%s\n" /* RequestDateTime */
"%s\n" /* CredentialScope */
"%s", /* HashedCanonicalRequest in hex */
- (int)curlx_strlen(&provider0),
- curlx_str(&provider0),
+ (int)curlx_strlen(provider0),
+ curlx_str(provider0),
timestamp,
credential_scope,
sha_hex);
- if(!str_to_sign)
- goto fail;
+ if(!str_to_sign) {
+ curlx_free(request_type);
+ curlx_free(credential_scope);
+ return CURLE_OUT_OF_MEMORY;
+ }
/* make provider0 part done uppercase */
- Curl_strntoupper(str_to_sign, curlx_str(&provider0),
- curlx_strlen(&provider0));
+ Curl_strntoupper(str_to_sign, curlx_str(provider0),
+ curlx_strlen(provider0));
infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]",
str_to_sign);
- secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0),
- curlx_str(&provider0), passwd);
+ *request_type_out = request_type;
+ *credential_scope_out = credential_scope;
+ *str_to_sign_out = str_to_sign;
+ return CURLE_OK;
+}
+
+static CURLcode sign_and_set_auth_headers(struct Curl_easy *data,
+ struct Curl_str *provider0,
+ struct Curl_str *region,
+ struct Curl_str *service,
+ const char *request_type,
+ const char *credential_scope,
+ const char *date,
+ const char *str_to_sign,
+ const char *date_header,
+ const char *content_sha256_hdr,
+ struct dynbuf *signed_headers)
+{
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ const char *passwd = Curl_creds_passwd(data->state.creds);
+ char *secret = NULL;
+ unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 };
+ unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 };
+ char sha_hex[SHA256_HEX_LENGTH];
+ char *auth_headers = NULL;
+ char *user = curl_escape(Curl_creds_user(data->state.creds), 0);
+ if(!user)
+ return CURLE_OUT_OF_MEMORY;
+
+ secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(provider0),
+ curlx_str(provider0), passwd);
if(!secret)
goto fail;
/* make provider0 part done uppercase */
- Curl_strntoupper(secret, curlx_str(&provider0), curlx_strlen(&provider0));
+ Curl_strntoupper(secret, curlx_str(provider0), curlx_strlen(provider0));
HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
HMAC_SHA256(sign0, sizeof(sign0),
- curlx_str(®ion), curlx_strlen(®ion), sign1);
+ curlx_str(region), curlx_strlen(region), sign1);
HMAC_SHA256(sign1, sizeof(sign1),
- curlx_str(&service), curlx_strlen(&service), sign0);
- HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
- HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
+ curlx_str(service), curlx_strlen(service), sign0);
+ 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);
@@ -1090,27 +1128,28 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
"Credential=%s/%s, "
"SignedHeaders=%s, "
"Signature=%s\r\n"
+ "%s"
+ "%s%s",
+ (int)curlx_strlen(provider0),
+ curlx_str(provider0),
+ user,
+ credential_scope,
+ curlx_dyn_ptr(signed_headers),
+ sha_hex,
/*
* date_header is added here, only if it was not
* user-specified (using CURLOPT_HTTPHEADER).
* date_header includes \r\n
*/
- "%s"
- "%s", /* optional sha256 header includes \r\n */
- (int)curlx_strlen(&provider0),
- curlx_str(&provider0),
- user,
- credential_scope,
- curlx_dyn_ptr(&signed_headers),
- sha_hex,
date_header ? date_header : "",
- content_sha256_hdr);
- if(!auth_headers) {
+ content_sha256_hdr,
+ content_sha256_hdr[0] ? "\r\n": "");
+ if(!auth_headers)
goto fail;
- }
+
/* provider 0 uppercase */
Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
- curlx_str(&provider0), curlx_strlen(&provider0));
+ curlx_str(provider0), curlx_strlen(provider0));
curlx_free(data->req.hd_auth);
data->req.hd_auth = auth_headers;
@@ -1118,15 +1157,91 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
result = CURLE_OK;
fail:
- curlx_dyn_free(&canonical_query);
- curlx_dyn_free(&canonical_path);
+ curlx_free(user);
+ curlx_free(secret);
+ return result;
+}
+
+CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ struct connectdata *conn = data->conn;
+ struct Curl_str provider0 = { NULL, 0 };
+ struct Curl_str provider1 = { NULL, 0 };
+ struct Curl_str region = { NULL, 0 };
+ struct Curl_str service = { NULL, 0 };
+ const char *hostname = conn->origin->hostname;
+ char timestamp[TIMESTAMP_SIZE];
+ char date[9];
+ struct dynbuf canonical_headers;
+ struct dynbuf signed_headers;
+ char *date_header = NULL;
+ Curl_HttpReq httpreq;
+ const char *method = NULL;
+ const char *payload_hash = NULL;
+ size_t payload_hash_len = 0;
+ unsigned char sha_hash[CURL_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;
+
+ if(data->set.path_as_is) {
+ failf(data, "Cannot use sigv4 authentication with path-as-is flag");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ if(Curl_checkheaders(data, STRCONST("Authorization")))
+ /* Authorization already present, Bailing out */
+ return CURLE_OK;
+
+ /* we init those buffers here, so goto fail will free initialized dynbuf */
+ curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
+ curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
+
+ result = parse_sigv4_params(data, hostname, &provider0, &provider1,
+ ®ion, &service);
+ if(!result) {
+ Curl_http_method(data, &method, &httpreq);
+ result = get_payload_hash(data, httpreq, &provider0, &provider1, &service,
+ sha_hash, sha_hex, content_sha256_hdr,
+ &payload_hash, &payload_hash_len);
+ }
+
+ if(!result)
+ result = get_timestamp(timestamp, sizeof(timestamp));
+
+ if(!result)
+ result = make_canonical_request(data, hostname, timestamp,
+ &provider1, &service,
+ method, payload_hash, payload_hash_len,
+ &date_header, content_sha256_hdr,
+ &canonical_headers, &signed_headers,
+ &canonical_request);
+ if(!result) {
+ /* the timestamp might have been updated in make_canonical_request */
+ memcpy(date, timestamp, sizeof(date) - 1);
+ date[sizeof(date) - 1] = 0;
+
+ result = make_string_to_sign(data, &provider0, ®ion, &service,
+ date, timestamp, canonical_request,
+ &request_type, &credential_scope,
+ &str_to_sign);
+ }
+ if(!result)
+ result = sign_and_set_auth_headers(data, &provider0, ®ion, &service,
+ request_type, credential_scope,
+ date, str_to_sign, date_header,
+ content_sha256_hdr, &signed_headers);
+
curlx_dyn_free(&canonical_headers);
curlx_dyn_free(&signed_headers);
curlx_free(canonical_request);
curlx_free(request_type);
curlx_free(credential_scope);
curlx_free(str_to_sign);
- curlx_free(secret);
curlx_free(date_header);
return result;
}
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 393f7531bd..413f7a1143 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -281,7 +281,7 @@ test3100 test3101 test3102 test3103 test3104 test3105 test3106 \
\
test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \
test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \
-test3216 test3217 test3218 test3219 test3220 test3221 \
+test3216 test3217 test3218 test3219 test3220 test3221 test3222 \
\
test3300 test3301 test3302 test3303 test3304 test3305 \
\
diff --git a/tests/data/test1933 b/tests/data/test1933
index e1ef8b95ba..cda12d19a1 100644
--- a/tests/data/test1933
+++ b/tests/data/test1933
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1934 b/tests/data/test1934
index 2128b299c2..9c52a0d0bd 100644
--- a/tests/data/test1934
+++ b/tests/data/test1934
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1935 b/tests/data/test1935
index 158620a3bd..f92c7e8ea2 100644
--- a/tests/data/test1935
+++ b/tests/data/test1935
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1936 b/tests/data/test1936
index 330fccc756..e43eb0be69 100644
--- a/tests/data/test1936
+++ b/tests/data/test1936
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1937 b/tests/data/test1937
index 19049410be..2043053e59 100644
--- a/tests/data/test1937
+++ b/tests/data/test1937
@@ -5,6 +5,7 @@
HTTP
HTTP POST
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1938 b/tests/data/test1938
index 91e27f8768..a2ea20fc96 100644
--- a/tests/data/test1938
+++ b/tests/data/test1938
@@ -5,6 +5,7 @@
HTTP
HTTP POST
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1955 b/tests/data/test1955
index 7b84f746bd..82349c2539 100644
--- a/tests/data/test1955
+++ b/tests/data/test1955
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1956 b/tests/data/test1956
index 3e8a9f9ce4..f3ad8b859a 100644
--- a/tests/data/test1956
+++ b/tests/data/test1956
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1957 b/tests/data/test1957
index 181eabb9f5..77b48b2388 100644
--- a/tests/data/test1957
+++ b/tests/data/test1957
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1959 b/tests/data/test1959
index 7f0357da8f..27a778c305 100644
--- a/tests/data/test1959
+++ b/tests/data/test1959
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1970 b/tests/data/test1970
index e697cabfee..8dae28c753 100644
--- a/tests/data/test1970
+++ b/tests/data/test1970
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1971 b/tests/data/test1971
index bc44cd3eaa..c303018c72 100644
--- a/tests/data/test1971
+++ b/tests/data/test1971
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1972 b/tests/data/test1972
index 7de801da7e..d128dccf45 100644
--- a/tests/data/test1972
+++ b/tests/data/test1972
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1973 b/tests/data/test1973
index 896631f151..c2527df686 100644
--- a/tests/data/test1973
+++ b/tests/data/test1973
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1974 b/tests/data/test1974
index 6a99684d88..20a10a8705 100644
--- a/tests/data/test1974
+++ b/tests/data/test1974
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1975 b/tests/data/test1975
index a4d1a7f0f7..e5d6272eda 100644
--- a/tests/data/test1975
+++ b/tests/data/test1975
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1976 b/tests/data/test1976
index 5a0ff39dc0..3a09cc2ca3 100644
--- a/tests/data/test1976
+++ b/tests/data/test1976
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test1978 b/tests/data/test1978
index 3ad4c670e2..b2a4285b96 100644
--- a/tests/data/test1978
+++ b/tests/data/test1978
@@ -4,6 +4,7 @@
HTTP
CURLOPT_AWS_SIGV4
+aws-sigv4
diff --git a/tests/data/test3222 b/tests/data/test3222
new file mode 100644
index 0000000000..cf6caa157f
--- /dev/null
+++ b/tests/data/test3222
@@ -0,0 +1,57 @@
+
+
+
+
+HTTP
+aws-sigv4
+
+
+
+# Server-side
+
+
+HTTP/1.1 200 OK
+Date: Tue, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
+ETag: "21025-dc7-39462498"
+Accept-Ranges: bytes
+Content-Length: 6
+Connection: close
+Content-Type: text/html
+Funny-head: yesyes
+
+-foo-
+
+
+
+# Client-side
+
+
+http
+
+
+Debug
+aws
+
+
+aws-sigv4 with CRLF in username
+
+
+"http://user%0d%0a:secret@fake.fake.fake:8000/" --aws-sigv4 "aws:amz:us-east-2:es" --connect-to fake.fake.fake:8000:%HOSTIP:%HTTPPORT
+
+
+
+# Verify data after the test has been "shot"
+
+
+GET / HTTP/1.1
+Host: fake.fake.fake:8000
+Authorization: AWS4-HMAC-SHA256 Credential=user%0D%0A/19700101/us-east-2/es/aws4_request, SignedHeaders=host;x-amz-date, Signature=e5747e9555c0e96f1067cc4bf9f6055e72a185178e5dd0c2909279ec1d66360b
+X-Amz-Date: 19700101T000000Z
+User-Agent: curl/%VERSION
+Accept: */*
+
+
+
+