aws_sigv4: fix header computation

Handle canonical headers and signed headers creation as explained here:
https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html

The algo tells that signed and canonical must contain at last host and
x-amz-date.

So we check whatever thoses are present in the curl http headers list.
If they are, we use the one enter by curl user, otherwise we generate
them.  then we to lower, and remove space from each http headers plus
host and x-amz-date, then sort them all by alphabetical order.

This patch also fix a bug with host header, which was ignoring the port.

Closes #7966
This commit is contained in:
Matthias Gatto 2022-01-13 15:53:52 +01:00 committed by Daniel Stenberg
parent 3c0050d13e
commit 29c4aa00a1
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
17 changed files with 535 additions and 224 deletions

View File

@ -44,6 +44,8 @@
#include "curl_memory.h"
#include "memdebug.h"
#include "slist.h"
#define HMAC_SHA256(k, kl, d, dl, o) \
do { \
ret = Curl_hmacit(Curl_HMAC_SHA256, \
@ -51,49 +53,240 @@
(unsigned int)kl, \
(unsigned char *)d, \
(unsigned int)dl, o); \
if(ret != CURLE_OK) { \
if(ret) { \
goto fail; \
} \
} while(0)
#define TIMESTAMP_SIZE 17
static void sha256_to_hex(char *dst, unsigned char *sha, size_t dst_l)
{
int i;
DEBUGASSERT(dst_l >= 65);
for(i = 0; i < 32; ++i) {
curl_msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
msnprintf(dst + (i * 2), dst_l - (i * 2), "%02x", sha[i]);
}
}
static char *find_date_hdr(struct Curl_easy *data, const char *sig_hdr)
{
char *tmp = Curl_checkheaders(data, sig_hdr, strlen(sig_hdr));
if(tmp)
return tmp;
return Curl_checkheaders(data, STRCONST("Date"));
}
/* remove whitespace, and lowercase all headers */
static void trim_headers(struct curl_slist *head)
{
struct curl_slist *l;
for(l = head; l; l = l->next) {
char *value; /* to read from */
char *store;
size_t colon = strcspn(l->data, ":");
Curl_strntolower(l->data, l->data, colon);
value = &l->data[colon];
if(!*value)
continue;
++value;
store = value;
/* skip leading whitespace */
while(*value && ISBLANK(*value))
value++;
while(*value) {
int space = 0;
while(*value && ISBLANK(*value)) {
value++;
space++;
}
if(space) {
/* replace any number of consecutive whitespace with a single space,
unless at the end of the string, then nothing */
if(*value)
*store++ = ' ';
}
else
*store++ = *value++;
}
*store = 0; /* null terminate */
}
}
/* maximum lenth for the aws sivg4 parts */
#define MAX_SIGV4_LEN 64
#define MAX_SIGV4_LEN_TXT "64"
#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
/* FQDN + host: */
#define FULL_HOST_LEN (255 + sizeof("host:"))
/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
#define DATE_FULL_HDR_LEN (DATE_HDR_KEY_LEN + TIMESTAMP_SIZE + 1)
/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
static CURLcode make_headers(struct Curl_easy *data,
const char *hostname,
char *timestamp,
char *provider1,
char **date_header,
struct dynbuf *canonical_headers,
struct dynbuf *signed_headers)
{
char date_hdr_key[DATE_HDR_KEY_LEN];
char date_full_hdr[DATE_FULL_HDR_LEN];
struct curl_slist *head = NULL;
struct curl_slist *tmp_head = NULL;
CURLcode ret = CURLE_OUT_OF_MEMORY;
struct curl_slist *l;
int again = 1;
/* provider1 mid */
Curl_strntolower(provider1, provider1, strlen(provider1));
provider1[0] = Curl_raw_toupper(provider1[0]);
msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
/* provider1 lowercase */
Curl_strntolower(provider1, provider1, 1); /* first byte only */
msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
"x-%s-date:%s", provider1, timestamp);
if(Curl_checkheaders(data, STRCONST("Host"))) {
head = NULL;
}
else {
char full_host[FULL_HOST_LEN];
if(data->state.aptr.host) {
size_t pos;
if(strlen(data->state.aptr.host) > FULL_HOST_LEN) {
ret = CURLE_URL_MALFORMAT;
goto fail;
}
strcpy(full_host, data->state.aptr.host);
/* remove /r/n as the separator for canonical request must be '\n' */
pos = strcspn(full_host, "\n\r");
full_host[pos] = 0;
}
else {
if(strlen(hostname) > FULL_HOST_LEN) {
ret = CURLE_URL_MALFORMAT;
goto fail;
}
msnprintf(full_host, FULL_HOST_LEN, "host:%s", hostname);
}
head = curl_slist_append(NULL, full_host);
if(!head)
goto fail;
}
for(l = data->set.headers; l; l = l->next) {
tmp_head = curl_slist_append(head, l->data);
if(!tmp_head)
goto fail;
head = tmp_head;
}
trim_headers(head);
*date_header = find_date_hdr(data, date_hdr_key);
if(!*date_header) {
tmp_head = curl_slist_append(head, date_full_hdr);
if(!tmp_head)
goto fail;
head = tmp_head;
*date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp);
}
else {
char *value;
*date_header = strdup(*date_header);
if(!*date_header)
goto fail;
value = strchr(*date_header, ':');
if(!value)
goto fail;
++value;
while(ISBLANK(*value))
++value;
strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
timestamp[TIMESTAMP_SIZE - 1] = 0;
}
/* alpha-sort in a case sensitive manner */
do {
again = 0;
for(l = head; l; l = l->next) {
struct curl_slist *next = l->next;
if(next && strcmp(l->data, next->data) > 0) {
char *tmp = l->data;
l->data = next->data;
next->data = tmp;
again = 1;
}
}
} while(again);
for(l = head; l; l = l->next) {
char *tmp;
if(Curl_dyn_add(canonical_headers, l->data))
goto fail;
if(Curl_dyn_add(canonical_headers, "\n"))
goto fail;
tmp = strchr(l->data, ':');
if(tmp)
*tmp = 0;
if(l != head) {
if(Curl_dyn_add(signed_headers, ";"))
goto fail;
}
if(Curl_dyn_add(signed_headers, l->data))
goto fail;
}
ret = CURLE_OK;
fail:
curl_slist_free_all(head);
return ret;
}
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
{
CURLcode ret = CURLE_OUT_OF_MEMORY;
struct connectdata *conn = data->conn;
size_t len;
const char *tmp0;
const char *tmp1;
char *provider0_low = NULL;
char *provider0_up = NULL;
char *provider1_low = NULL;
char *provider1_mid = NULL;
char *region = NULL;
char *service = NULL;
const char *arg;
char provider0[MAX_SIGV4_LEN + 1]="";
char provider1[MAX_SIGV4_LEN + 1]="";
char region[MAX_SIGV4_LEN + 1]="";
char service[MAX_SIGV4_LEN + 1]="";
const char *hostname = conn->host.name;
#ifdef DEBUGBUILD
char *force_timestamp;
#endif
time_t clock;
struct tm tm;
char timestamp[17];
char timestamp[TIMESTAMP_SIZE];
char date[9];
const char *content_type = Curl_checkheaders(data, STRCONST("Content-Type"));
char *canonical_headers = NULL;
char *signed_headers = NULL;
Curl_HttpReq httpreq;
const char *method;
size_t post_data_len;
const char *post_data = data->set.postfields ? data->set.postfields : "";
struct dynbuf canonical_headers;
struct dynbuf signed_headers;
char *date_header = NULL;
const char *post_data = data->set.postfields;
size_t post_data_len = 0;
unsigned char sha_hash[32];
char sha_hex[65];
char *canonical_request = NULL;
@ -101,10 +294,9 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
char *credential_scope = NULL;
char *str_to_sign = NULL;
const char *user = data->state.aptr.user ? data->state.aptr.user : "";
const char *passwd = data->state.aptr.passwd ? data->state.aptr.passwd : "";
char *secret = NULL;
unsigned char tmp_sign0[32] = {0};
unsigned char tmp_sign1[32] = {0};
unsigned char sign0[32] = {0};
unsigned char sign1[32] = {0};
char *auth_headers = NULL;
DEBUGASSERT(!proxy);
@ -115,6 +307,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
return CURLE_OK;
}
/* we init thoses buffers here, so goto fail will free initialized dynbuf */
Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
/*
* Parameters parsing
* Google and Outscale use the same OSC or GOOG,
@ -122,223 +318,154 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
* AWS is the default because most of non-amazon providers
* are still using aws:amz as a prefix.
*/
tmp0 = data->set.str[STRING_AWS_SIGV4] ?
arg = data->set.str[STRING_AWS_SIGV4] ?
data->set.str[STRING_AWS_SIGV4] : "aws:amz";
tmp1 = strchr(tmp0, ':');
len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
if(len < 1) {
infof(data, "first provider can't be empty");
/* provider1[:provider2[:region[:service]]]
No string can be longer than N bytes of non-whitespace
*/
(void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
":%" MAX_SIGV4_LEN_TXT "[^:]"
":%" MAX_SIGV4_LEN_TXT "[^:]"
":%" MAX_SIGV4_LEN_TXT "s",
provider0, provider1, region, service);
if(!provider0[0]) {
failf(data, "first provider can't be empty");
ret = CURLE_BAD_FUNCTION_ARGUMENT;
goto fail;
}
provider0_low = malloc(len + 1);
provider0_up = malloc(len + 1);
if(!provider0_low || !provider0_up) {
goto fail;
}
Curl_strntolower(provider0_low, tmp0, len);
provider0_low[len] = '\0';
Curl_strntoupper(provider0_up, tmp0, len);
provider0_up[len] = '\0';
else if(!provider1[0])
strcpy(provider1, provider0);
if(tmp1) {
tmp0 = tmp1 + 1;
tmp1 = strchr(tmp0, ':');
len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
if(len < 1) {
infof(data, "second provider can't be empty");
ret = CURLE_BAD_FUNCTION_ARGUMENT;
goto fail;
}
provider1_low = malloc(len + 1);
provider1_mid = malloc(len + 1);
if(!provider1_low || !provider1_mid) {
goto fail;
}
Curl_strntolower(provider1_low, tmp0, len);
provider1_low[len] = '\0';
Curl_strntolower(provider1_mid, tmp0, len);
provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
provider1_mid[len] = '\0';
if(tmp1) {
tmp0 = tmp1 + 1;
tmp1 = strchr(tmp0, ':');
len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
if(len < 1) {
infof(data, "region can't be empty");
ret = CURLE_BAD_FUNCTION_ARGUMENT;
goto fail;
}
region = Curl_memdup(tmp0, len + 1);
if(!region) {
goto fail;
}
region[len] = '\0';
if(tmp1) {
tmp0 = tmp1 + 1;
service = strdup(tmp0);
if(!service) {
goto fail;
}
if(strlen(service) < 1) {
infof(data, "service can't be empty");
ret = CURLE_BAD_FUNCTION_ARGUMENT;
goto fail;
}
}
}
}
else {
provider1_low = Curl_memdup(provider0_low, len + 1);
provider1_mid = Curl_memdup(provider0_low, len + 1);
if(!provider1_low || !provider1_mid) {
goto fail;
}
provider1_mid[0] = Curl_raw_toupper(provider1_mid[0]);
}
if(!service) {
tmp0 = hostname;
tmp1 = strchr(tmp0, '.');
if(!tmp1) {
infof(data, "service missing in parameters or hostname");
if(!service[0]) {
char *hostdot = strchr(hostname, '.');
if(!hostdot) {
failf(data, "service missing in parameters and hostname");
ret = CURLE_URL_MALFORMAT;
goto fail;
}
len = tmp1 - tmp0;
service = Curl_memdup(tmp0, len + 1);
if(!service) {
len = hostdot - hostname;
if(len > MAX_SIGV4_LEN) {
failf(data, "service too long in hostname");
ret = CURLE_URL_MALFORMAT;
goto fail;
}
strncpy(service, hostname, len);
service[len] = '\0';
if(!region) {
tmp0 = tmp1 + 1;
tmp1 = strchr(tmp0, '.');
if(!tmp1) {
infof(data, "region missing in parameters or hostname");
if(!region[0]) {
const char *reg = hostdot + 1;
const char *hostreg = strchr(reg, '.');
if(!hostreg) {
failf(data, "region missing in parameters and hostname");
ret = CURLE_URL_MALFORMAT;
goto fail;
}
len = tmp1 - tmp0;
region = Curl_memdup(tmp0, len + 1);
if(!region) {
len = hostreg - reg;
if(len > MAX_SIGV4_LEN) {
failf(data, "region too long in hostname");
ret = CURLE_URL_MALFORMAT;
goto fail;
}
strncpy(region, reg, len);
region[len] = '\0';
}
}
#ifdef DEBUGBUILD
force_timestamp = getenv("CURL_FORCETIME");
if(force_timestamp)
clock = 0;
else
time(&clock);
{
char *force_timestamp = getenv("CURL_FORCETIME");
if(force_timestamp)
clock = 0;
else
time(&clock);
}
#else
time(&clock);
#endif
ret = Curl_gmtime(clock, &tm);
if(ret != CURLE_OK) {
if(ret) {
goto fail;
}
if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
ret = CURLE_OUT_OF_MEMORY;
goto fail;
}
ret = make_headers(data, hostname, timestamp, provider1,
&date_header, &canonical_headers, &signed_headers);
if(ret)
goto fail;
ret = CURLE_OUT_OF_MEMORY;
memcpy(date, timestamp, sizeof(date));
date[sizeof(date) - 1] = 0;
if(content_type) {
content_type = strchr(content_type, ':');
if(!content_type) {
ret = CURLE_FAILED_INIT;
goto fail;
}
content_type++;
/* Skip whitespace now */
while(*content_type == ' ' || *content_type == '\t')
++content_type;
canonical_headers = curl_maprintf("content-type:%s\n"
"host:%s\n"
"x-%s-date:%s\n",
content_type,
hostname,
provider1_low, timestamp);
signed_headers = curl_maprintf("content-type;host;x-%s-date",
provider1_low);
if(post_data) {
if(data->set.postfieldsize < 0)
post_data_len = strlen(post_data);
else
post_data_len = (size_t)data->set.postfieldsize;
}
else {
canonical_headers = curl_maprintf("host:%s\n"
"x-%s-date:%s\n",
hostname,
provider1_low, timestamp);
signed_headers = curl_maprintf("host;x-%s-date", provider1_low);
}
if(!canonical_headers || !signed_headers) {
goto fail;
}
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)) {
post_data_len))
goto fail;
}
sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
Curl_http_method(data, conn, &method, &httpreq);
{
Curl_HttpReq httpreq;
const char *method;
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 : "",
canonical_headers,
signed_headers,
sha_hex);
if(!canonical_request) {
goto fail;
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),
sha_hex);
if(!canonical_request)
goto fail;
}
request_type = curl_maprintf("%s4_request", provider0_low);
if(!request_type) {
/* provider 0 lowercase */
Curl_strntolower(provider0, provider0, strlen(provider0));
request_type = curl_maprintf("%s4_request", provider0);
if(!request_type)
goto fail;
}
credential_scope = curl_maprintf("%s/%s/%s/%s",
date, region, service, request_type);
if(!credential_scope) {
if(!credential_scope)
goto fail;
}
if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
strlen(canonical_request))) {
strlen(canonical_request)))
goto fail;
}
sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
/* provider 0 uppercase */
Curl_strntoupper(provider0, provider0, strlen(provider0));
/*
* Google allow to use rsa key instead of HMAC, so this code might change
* In the future, but for now we support only HMAC version
* Google allows using RSA key instead of HMAC, so this code might change
* in the future. For now we ony support HMAC.
*/
str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
"%s\n" /* RequestDateTime */
"%s\n" /* CredentialScope */
"%s", /* HashedCanonicalRequest in hex */
provider0_up,
provider0,
timestamp,
credential_scope,
sha_hex);
@ -346,36 +473,33 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
goto fail;
}
secret = curl_maprintf("%s4%s", provider0_up, passwd);
if(!secret) {
/* provider 0 uppercase */
secret = curl_maprintf("%s4%s", provider0,
data->state.aptr.passwd ?
data->state.aptr.passwd : "");
if(!secret)
goto fail;
}
HMAC_SHA256(secret, strlen(secret),
date, strlen(date), tmp_sign0);
HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
region, strlen(region), tmp_sign1);
HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
service, strlen(service), tmp_sign0);
HMAC_SHA256(tmp_sign0, sizeof(tmp_sign0),
request_type, strlen(request_type), tmp_sign1);
HMAC_SHA256(tmp_sign1, sizeof(tmp_sign1),
str_to_sign, strlen(str_to_sign), tmp_sign0);
HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
HMAC_SHA256(sign1, sizeof(sign1), service, 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, tmp_sign0, sizeof(sha_hex));
sha256_to_hex(sha_hex, sign0, sizeof(sha_hex));
/* provider 0 uppercase */
auth_headers = curl_maprintf("Authorization: %s4-HMAC-SHA256 "
"Credential=%s/%s, "
"SignedHeaders=%s, "
"Signature=%s\r\n"
"X-%s-Date: %s\r\n",
provider0_up,
"%s\r\n",
provider0,
user,
credential_scope,
signed_headers,
Curl_dyn_ptr(&signed_headers),
sha_hex,
provider1_mid,
timestamp);
date_header);
if(!auth_headers) {
goto fail;
}
@ -386,19 +510,14 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
ret = CURLE_OK;
fail:
free(provider0_low);
free(provider0_up);
free(provider1_low);
free(provider1_mid);
free(region);
free(service);
free(canonical_headers);
free(signed_headers);
Curl_dyn_free(&canonical_headers);
Curl_dyn_free(&signed_headers);
free(canonical_request);
free(request_type);
free(credential_scope);
free(str_to_sign);
free(secret);
free(date_header);
return ret;
}

View File

@ -225,6 +225,7 @@ test1916 test1917 test1918 test1919 \
\
test1933 test1934 test1935 test1936 test1937 test1938 test1939 test1940 \
test1941 test1942 test1943 test1944 test1945 test1946 test1947 test1948 \
test1955 \
\
test2000 test2001 test2002 test2003 test2004 \
\

View File

@ -47,7 +47,7 @@ lib%TESTNUMBER
</tool>
<command>
http://xxx:yyy@%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
http://xxx:yyy@127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
</command>
</client>
@ -60,8 +60,8 @@ http://xxx:yyy@%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
</strip>
<protocol>
GET /%TESTNUMBER/testapi/test HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-xxx-date, Signature=d2c2dff48c59ec49dc31ef94f18c5dc1ac3eae2a70d51633a4342dadc0683664
Host: 127.0.0.1:9000
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-xxx-date, Signature=3d8e00a02e437211a596143dcd590fcc805b731365c68f7f48951ea6eda39c4f
X-Xxx-Date: 19700101T000000Z
</protocol>

View File

@ -47,7 +47,7 @@ lib%TESTNUMBER
</tool>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
</command>
</client>
@ -60,8 +60,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
</strip>
<protocol>
GET /%TESTNUMBER/testapi/test HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=938937ca7da6bb3dbf15e30928265ec6f061532d035d2afda92fa7cb10feb196
Host: 127.0.0.1:9000
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/0/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=cf8dc9a4af903a1a9bb1385d8e2366d780afb501e266436598438395e502d58c
X-Yyy-Date: 19700101T000000Z
</protocol>

View File

@ -47,7 +47,7 @@ lib%TESTNUMBER
</tool>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
</command>
</client>
@ -60,8 +60,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
</strip>
<protocol>
GET /%TESTNUMBER/testapi/test HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=240750deb9263d4c8ece71c016f3919b56e518249390ef075740f94ef8df846f
Host: 127.0.0.1:9000
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/127/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=a0b11b97b54689428d4188b788ed32865d607822d85d3e91cf06141f479dac0b
X-Yyy-Date: 19700101T000000Z
</protocol>

View File

@ -47,7 +47,7 @@ lib%TESTNUMBER
</tool>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
</command>
</client>
@ -60,8 +60,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
</strip>
<protocol>
GET /%TESTNUMBER/testapi/test HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/sss/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=f32cf87977cea5d3274b524b53e5d28f4aac54c372f710ae0cc3a9ececaf169f
Host: 127.0.0.1:9000
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/rrr/sss/xxx4_request, SignedHeaders=content-type;host;x-yyy-date, Signature=026b713d76b0789bd224c5e41322f74eed088f8a22fd15183ca68376c575c5b0
X-Yyy-Date: 19700101T000000Z
</protocol>

View File

@ -48,7 +48,7 @@ lib%TESTNUMBER
</tool>
<command>
http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
http://127.0.0.1:9000/%TESTNUMBER/testapi/test 127.0.0.1:9000:%HOSTIP:%HTTPPORT
</command>
</client>
@ -61,8 +61,8 @@ http://%HOSTIP:%HTTPPORT/%TESTNUMBER/testapi/test
</strip>
<protocol nonewline="yes">
POST /%TESTNUMBER/testapi/test HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Authorization: PROVIDER14-HMAC-SHA256 Credential=keyId/19700101/region/service/provider14_request, SignedHeaders=content-type;host;x-provider2-date, Signature=391e410177d0e9ee80728082446ef69d6b29157fe71f8b4805fce7c186fd956d
Host: 127.0.0.1:9000
Authorization: PROVIDER14-HMAC-SHA256 Credential=keyId/19700101/region/service/provider14_request, SignedHeaders=content-type;host;x-provider2-date, Signature=4928ccf97a9e71fe27f91db5a3b3c943b6080d25e6f4df8593d4c38e7d1e849b
X-Provider2-Date: 19700101T000000Z
Content-Length: 8

Binary file not shown.

75
tests/data/test1955 Normal file
View File

@ -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 with X-Xxx-Content-Sha256
</name>
<tool>
lib%TESTNUMBER
</tool>
<command>
http://exam.ple.com:9000/%TESTNUMBER/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 /%TESTNUMBER/testapi/test HTTP/1.1
Host: exam.ple.com:9000
Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/ple/exam/xxx4_request, SignedHeaders=content-type;host;tesmixcase;test0;test1;test2;test_space;x-xxx-date, Signature=819251feec8de52dfaa992320241f23d27cefa979c93e039ae7df03ac486ed16
X-Xxx-Date: 19700101T000000Z
test2: 1234
test_space: t s m end
tesMixCase: MixCase
</protocol>
</verify>
</testcase>

View File

@ -64,7 +64,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1905 lib1906 lib1907 lib1908 lib1910 lib1911 lib1912 lib1913 \
lib1915 lib1916 lib1917 lib1918 lib1919 \
lib1933 lib1934 lib1935 lib1936 lib1937 lib1938 lib1939 lib1940 \
lib1945 lib1946 lib1947 lib1948 \
lib1945 lib1946 lib1947 lib1948 lib1955 \
lib2301 lib2302 \
lib3010 lib3025 lib3026 lib3027
@ -757,6 +757,10 @@ lib1948_SOURCES = lib1948.c $(SUPPORTFILES)
lib1948_LDADD = $(TESTUTIL_LIBS)
lib1948_CPPFLAGS = $(AM_CPPFLAGS)
lib1955_SOURCES = lib1955.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1955_LDADD = $(TESTUTIL_LIBS)
lib1955_CPPFLAGS = $(AM_CPPFLAGS)
lib2301_SOURCES = lib2301.c $(SUPPORTFILES)
lib2301_LDADD = $(TESTUTIL_LIBS)

View File

@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@ -47,6 +48,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_AWS_SIGV4, "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);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@ -54,6 +59,7 @@ int test(char *URL)
test_cleanup:
curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();

View File

@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@ -48,6 +49,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy");
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);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@ -55,6 +60,7 @@ int test(char *URL)
test_cleanup:
curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();

View File

@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@ -48,6 +49,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy");
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);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@ -55,6 +60,7 @@ int test(char *URL)
test_cleanup:
curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();

View File

@ -29,6 +29,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@ -48,6 +49,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "xxx:yyy");
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);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
@ -55,6 +60,7 @@ int test(char *URL)
test_cleanup:
curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();

View File

@ -30,6 +30,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
@ -50,6 +51,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey");
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);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
test_setopt(curl, CURLOPT_POSTFIELDS, "postData");
@ -58,6 +63,7 @@ int test(char *URL)
test_cleanup:
curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();

View File

@ -30,6 +30,7 @@ int test(char *URL)
{
CURL *curl;
CURLcode res = TEST_ERR_MAJOR_BAD;
struct curl_slist *connect_to = NULL;
struct curl_slist *list = NULL;
unsigned char data[] = {0x70, 0x6f, 0x73, 0x74, 0, 0x44, 0x61, 0x74, 0x61};
@ -51,6 +52,10 @@ int test(char *URL)
test_setopt(curl, CURLOPT_USERPWD, "keyId:SecretKey");
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);
list = curl_slist_append(list, "Content-Type: application/json");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
test_setopt(curl, CURLOPT_POSTFIELDS, data);
@ -60,6 +65,7 @@ int test(char *URL)
test_cleanup:
curl_slist_free_all(connect_to);
curl_slist_free_all(list);
curl_easy_cleanup(curl);
curl_global_cleanup();

76
tests/libtest/lib1955.c Normal file
View File

@ -0,0 +1,76 @@
/***************************************************************************
* _ _ ____ _
* 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_VERBOSE, 1L);
test_setopt(curl, CURLOPT_AWS_SIGV4, "xxx");
test_setopt(curl, CURLOPT_USERPWD, "xxx");
test_setopt(curl, CURLOPT_HEADER, 0L);
test_setopt(curl, CURLOPT_URL, URL);
list = curl_slist_append(list, "test2: 1234");
if(!list)
goto test_cleanup;
if(libtest_arg2) {
connect_to = curl_slist_append(connect_to, libtest_arg2);
}
test_setopt(curl, CURLOPT_CONNECT_TO, connect_to);
curl_slist_append(list, "Content-Type: application/json");
curl_slist_append(list, "test1:");
curl_slist_append(list, "test0");
curl_slist_append(list, "test_space: t\ts m\t end ");
curl_slist_append(list, "tesMixCase: MixCase");
test_setopt(curl, CURLOPT_HTTPHEADER, list);
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;
}