diff options
Diffstat (limited to 'libs/libcurl/src/http_aws_sigv4.c')
-rw-r--r-- | libs/libcurl/src/http_aws_sigv4.c | 604 |
1 files changed, 417 insertions, 187 deletions
diff --git a/libs/libcurl/src/http_aws_sigv4.c b/libs/libcurl/src/http_aws_sigv4.c index 0c796566f2..754e3777a8 100644 --- a/libs/libcurl/src/http_aws_sigv4.c +++ b/libs/libcurl/src/http_aws_sigv4.c @@ -35,7 +35,7 @@ #include "parsedate.h"
#include "sendf.h"
#include "escape.h"
-#include "strparse.h"
+#include "curlx/strparse.h"
#include <time.h>
@@ -63,6 +63,27 @@ /* hex-encoded with trailing null */
#define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1)
+#define MAX_QUERY_COMPONENTS 128
+
+struct pair {
+ struct dynbuf key;
+ struct dynbuf value;
+};
+
+static void dyn_array_free(struct dynbuf *db, size_t num_elements);
+static void pair_array_free(struct pair *pair_array, size_t num_elements);
+static CURLcode split_to_dyn_array(const char *source,
+ struct dynbuf db[MAX_QUERY_COMPONENTS],
+ size_t *num_splits);
+static bool is_reserved_char(const char c);
+static CURLcode uri_encode_path(struct Curl_str *original_path,
+ struct dynbuf *new_path);
+static CURLcode encode_query_component(char *component, size_t len,
+ struct dynbuf *db);
+static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
+ struct dynbuf *out);
+static bool should_urlencode(struct Curl_str *service_name);
+
static void sha256_to_hex(char *dst, unsigned char *sha)
{
Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH,
@@ -95,7 +116,7 @@ static void trim_headers(struct curl_slist *head) store = (char *)CURL_UNCONST(value);
/* skip leading whitespace */
- Curl_str_passblanks(&value);
+ curlx_str_passblanks(&value);
while(*value) {
int space = 0;
@@ -172,9 +193,9 @@ static CURLcode merge_duplicate_headers(struct curl_slist *head) char *colon_next;
char *val_next;
- Curl_dyn_init(&buf, CURL_MAX_HTTP_HEADER);
+ curlx_dyn_init(&buf, CURL_MAX_HTTP_HEADER);
- result = Curl_dyn_add(&buf, curr->data);
+ result = curlx_dyn_add(&buf, curr->data);
if(result)
return result;
@@ -182,16 +203,16 @@ static CURLcode merge_duplicate_headers(struct curl_slist *head) DEBUGASSERT(colon_next);
val_next = colon_next + 1;
- result = Curl_dyn_addn(&buf, ",", 1);
+ result = curlx_dyn_addn(&buf, ",", 1);
if(result)
return result;
- result = Curl_dyn_add(&buf, val_next);
+ result = curlx_dyn_add(&buf, val_next);
if(result)
return result;
free(curr->data);
- curr->data = Curl_dyn_ptr(&buf);
+ curr->data = curlx_dyn_ptr(&buf);
curr->next = next->next;
free(next->data);
@@ -319,7 +340,7 @@ static CURLcode make_headers(struct Curl_easy *data, goto fail;
}
++value;
- Curl_str_passblanks(&value);
+ curlx_str_passblanks(&value);
endp = value;
while(*endp && ISALNUM(*endp))
++endp;
@@ -357,9 +378,9 @@ static CURLcode make_headers(struct Curl_easy *data, for(l = head; l; l = l->next) {
char *tmp;
- if(Curl_dyn_add(canonical_headers, l->data))
+ if(curlx_dyn_add(canonical_headers, l->data))
goto fail;
- if(Curl_dyn_add(canonical_headers, "\n"))
+ if(curlx_dyn_add(canonical_headers, "\n"))
goto fail;
tmp = strchr(l->data, ':');
@@ -367,10 +388,10 @@ static CURLcode make_headers(struct Curl_easy *data, *tmp = 0;
if(l != head) {
- if(Curl_dyn_add(signed_headers, ";"))
+ if(curlx_dyn_add(signed_headers, ";"))
goto fail;
}
- if(Curl_dyn_add(signed_headers, l->data))
+ if(curlx_dyn_add(signed_headers, l->data))
goto fail;
}
@@ -390,8 +411,7 @@ fail: static const char *parse_content_sha_hdr(struct Curl_easy *data,
const char *provider1,
size_t plen,
- size_t *value_len)
-{
+ size_t *value_len) {
char key[CONTENT_SHA256_KEY_LEN];
size_t key_len;
const char *value;
@@ -409,7 +429,7 @@ static const char *parse_content_sha_hdr(struct Curl_easy *data, return NULL;
++value;
- Curl_str_passblanks(&value);
+ curlx_str_passblanks(&value);
len = strlen(value);
while(len > 0 && ISBLANK(value[len-1]))
@@ -478,152 +498,176 @@ fail: return ret;
}
-struct pair {
- const char *p;
- size_t len;
-};
-
static int compare_func(const void *a, const void *b)
{
+
const struct pair *aa = a;
const struct pair *bb = b;
+ const size_t aa_key_len = curlx_dyn_len(&aa->key);
+ const size_t bb_key_len = curlx_dyn_len(&bb->key);
+ const size_t aa_value_len = curlx_dyn_len(&aa->value);
+ const size_t bb_value_len = curlx_dyn_len(&bb->value);
+ int compare;
+
/* If one element is empty, the other is always sorted higher */
- if(aa->len == 0 && bb->len == 0)
+
+ /* Compare keys */
+ if((aa_key_len == 0) && (bb_key_len == 0))
return 0;
- if(aa->len == 0)
+ if(aa_key_len == 0)
return -1;
- if(bb->len == 0)
+ if(bb_key_len == 0)
return 1;
- return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len);
-}
+ compare = strcmp(curlx_dyn_ptr(&aa->key), curlx_dyn_ptr(&bb->key));
+ if(compare) {
+ return compare;
+ }
+
+ /* Compare values */
+ if((aa_value_len == 0) && (bb_value_len == 0))
+ return 0;
+ if(aa_value_len == 0)
+ return -1;
+ if(bb_value_len == 0)
+ return 1;
+ compare = strcmp(curlx_dyn_ptr(&aa->value), curlx_dyn_ptr(&bb->value));
-#define MAX_QUERYPAIRS 64
+ return compare;
-/**
- * found_equals have a double meaning,
- * detect if an equal have been found when called from canon_query,
- * and mark that this function is called to compute the path,
- * if found_equals is NULL.
- */
-static CURLcode canon_string(const char *q, size_t len,
- struct dynbuf *dq, bool *found_equals)
+}
+
+UNITTEST CURLcode canon_path(const char *q, size_t len,
+ struct dynbuf *new_path,
+ bool do_uri_encode)
{
CURLcode result = CURLE_OK;
- for(; len && !result; q++, len--) {
- if(ISALNUM(*q))
- result = Curl_dyn_addn(dq, q, 1);
- else {
- switch(*q) {
- case '-':
- case '.':
- case '_':
- case '~':
- /* allowed as-is */
- result = Curl_dyn_addn(dq, q, 1);
- break;
- case '%':
- /* uppercase the following if hexadecimal */
- if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) {
- char tmp[3]="%";
- tmp[1] = Curl_raw_toupper(q[1]);
- tmp[2] = Curl_raw_toupper(q[2]);
- result = Curl_dyn_addn(dq, tmp, 3);
- q += 2;
- len -= 2;
- }
- else
- /* '%' without a following two-digit hex, encode it */
- result = Curl_dyn_addn(dq, "%25", 3);
- break;
- default: {
- const char hex[] = "0123456789ABCDEF";
- char out[3]={'%'};
-
- if(!found_equals) {
- /* if found_equals is NULL assuming, been in path */
- if(*q == '/') {
- /* allowed as if */
- result = Curl_dyn_addn(dq, q, 1);
- break;
- }
- }
- else {
- /* allowed as-is */
- if(*q == '=') {
- result = Curl_dyn_addn(dq, q, 1);
- *found_equals = TRUE;
- break;
- }
- }
- /* URL encode */
- out[1] = hex[((unsigned char)*q) >> 4];
- out[2] = hex[*q & 0xf];
- result = Curl_dyn_addn(dq, out, 3);
- break;
- }
- }
- }
+ struct Curl_str original_path;
+
+ curlx_str_assign(&original_path, q, len);
+
+ /* Normalized path will be either the same or shorter than the original
+ * path, plus trailing slash */
+
+ if(do_uri_encode)
+ result = uri_encode_path(&original_path, new_path);
+ else
+ result = curlx_dyn_addn(new_path, q, len);
+
+ if(!result) {
+ if(curlx_dyn_len(new_path) == 0)
+ result = curlx_dyn_add(new_path, "/");
}
+
return result;
}
-
-static CURLcode canon_query(struct Curl_easy *data,
- const char *query, struct dynbuf *dq)
+UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq)
{
CURLcode result = CURLE_OK;
- int entry = 0;
- int i;
- const char *p = query;
- struct pair array[MAX_QUERYPAIRS];
- struct pair *ap = &array[0];
+
+ struct dynbuf query_array[MAX_QUERY_COMPONENTS];
+ struct pair encoded_query_array[MAX_QUERY_COMPONENTS];
+ size_t num_query_components;
+ size_t counted_query_components = 0;
+ size_t index;
+
if(!query)
return result;
- /* sort the name=value pairs first */
- do {
- char *amp;
- entry++;
- ap->p = p;
- amp = strchr(p, '&');
- if(amp)
- ap->len = amp - p; /* excluding the ampersand */
+ result = split_to_dyn_array(query, &query_array[0],
+ &num_query_components);
+ if(result) {
+ goto fail;
+ }
+
+ /* Create list of pairs, each pair containing an encoded query
+ * component */
+
+ for(index = 0; index < num_query_components; index++) {
+ const char *in_key;
+ size_t in_key_len;
+ char *offset;
+ size_t query_part_len = curlx_dyn_len(&query_array[index]);
+ char *query_part = curlx_dyn_ptr(&query_array[index]);
+
+ in_key = query_part;
+
+ offset = strchr(query_part, '=');
+ /* If there is no equals, this key has no value */
+ if(!offset) {
+ in_key_len = strlen(in_key);
+ }
else {
- ap->len = strlen(p);
- break;
+ in_key_len = offset - in_key;
}
- ap++;
- p = amp + 1;
- } while(entry < MAX_QUERYPAIRS);
- if(entry == MAX_QUERYPAIRS) {
- /* too many query pairs for us */
- failf(data, "aws-sigv4: too many query pairs in URL");
- return CURLE_URL_MALFORMAT;
- }
-
- qsort(&array[0], entry, sizeof(struct pair), compare_func);
-
- ap = &array[0];
- for(i = 0; !result && (i < entry); i++, ap++) {
- const char *q = ap->p;
- bool found_equals = FALSE;
- if(!ap->len)
- continue;
- result = canon_string(q, ap->len, dq, &found_equals);
- if(!result && !found_equals) {
- /* queries without value still need an equals */
- result = Curl_dyn_addn(dq, "=", 1);
+
+ curlx_dyn_init(&encoded_query_array[index].key, query_part_len*3 + 1);
+ curlx_dyn_init(&encoded_query_array[index].value, query_part_len*3 + 1);
+ counted_query_components++;
+
+ /* Decode/encode the key */
+ result = http_aws_decode_encode(in_key, in_key_len,
+ &encoded_query_array[index].key);
+ if(result) {
+ goto fail;
+ }
+
+ /* Decode/encode the value if it exists */
+ if(offset && offset != (query_part + query_part_len - 1)) {
+ size_t in_value_len;
+ const char *in_value = offset + 1;
+ in_value_len = query_part + query_part_len - (offset + 1);
+ result = http_aws_decode_encode(in_value, in_value_len,
+ &encoded_query_array[index].value);
+ if(result) {
+ goto fail;
+ }
}
- if(!result && i < entry - 1) {
- /* insert ampersands between query pairs */
- result = Curl_dyn_addn(dq, "&", 1);
+ else {
+ /* If there is no value, the value is an empty string */
+ curlx_dyn_init(&encoded_query_array[index].value, 2);
+ result = curlx_dyn_addn(&encoded_query_array[index].value, "", 1);
+ }
+
+ if(result) {
+ goto fail;
}
}
+
+ /* Sort the encoded query components by key and value */
+ qsort(&encoded_query_array, num_query_components,
+ sizeof(struct pair), compare_func);
+
+ /* Append the query components together to make a full query string */
+ for(index = 0; index < num_query_components; index++) {
+
+ if(index)
+ result = curlx_dyn_addn(dq, "&", 1);
+ if(!result) {
+ char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key);
+ char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value);
+ size_t vlen = curlx_dyn_len(&encoded_query_array[index].value);
+ if(value_ptr && vlen) {
+ result = curlx_dyn_addf(dq, "%s=%s", key_ptr, value_ptr);
+ }
+ else {
+ /* Empty value is always encoded to key= */
+ result = curlx_dyn_addf(dq, "%s=", key_ptr);
+ }
+ }
+ if(result)
+ break;
+ }
+
+fail:
+ if(counted_query_components)
+ /* the encoded_query_array might not be initialized yet */
+ pair_array_free(&encoded_query_array[0], counted_query_components);
+ dyn_array_free(&query_array[0], num_query_components);
return result;
}
-
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
{
CURLcode result = CURLE_OUT_OF_MEMORY;
@@ -660,16 +704,21 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) 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 */
- Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
- Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
- Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
- Curl_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
+ 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
@@ -678,69 +727,70 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) * 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] ?
- data->set.str[STRING_AWS_SIGV4] : "aws:amz";
+ line = data->set.str[STRING_AWS_SIGV4];
+ if(!line || !*line)
+ line = "aws:amz";
/* provider0[:provider1[:region[:service]]]
No string can be longer than N bytes of non-whitespace
*/
- if(Curl_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;
}
- if(Curl_str_single(&line, ':') ||
- Curl_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
+ if(curlx_str_single(&line, ':') ||
+ curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
provider1 = provider0;
}
- else if(Curl_str_single(&line, ':') ||
- Curl_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') ||
- Curl_str_single(&line, ':') ||
- Curl_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
+ else if(curlx_str_single(&line, ':') ||
+ curlx_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') ||
+ curlx_str_single(&line, ':') ||
+ curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
/* nothing to do */
}
- if(!Curl_strlen(&service)) {
+ if(!curlx_strlen(&service)) {
const char *p = hostname;
- if(Curl_str_until(&p, &service, MAX_SIGV4_LEN, '.') ||
- Curl_str_single(&p, '.')) {
+ 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;
}
infof(data, "aws_sigv4: picked service %.*s from host",
- (int)Curl_strlen(&service), Curl_str(&service));
+ (int)curlx_strlen(&service), curlx_str(&service));
- if(!Curl_strlen(®ion)) {
- if(Curl_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') ||
- Curl_str_single(&p, '.')) {
+ if(!curlx_strlen(®ion)) {
+ if(curlx_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') ||
+ curlx_str_single(&p, '.')) {
failf(data, "aws-sigv4: region missing in parameters and hostname");
result = CURLE_URL_MALFORMAT;
goto fail;
}
infof(data, "aws_sigv4: picked region %.*s from host",
- (int)Curl_strlen(®ion), Curl_str(®ion));
+ (int)curlx_strlen(®ion), curlx_str(®ion));
}
}
Curl_http_method(data, conn, &method, &httpreq);
payload_hash =
- parse_content_sha_hdr(data, Curl_str(&provider1), Curl_strlen(&provider1),
- &payload_hash_len);
+ parse_content_sha_hdr(data, curlx_str(&provider1),
+ curlx_strlen(&provider1), &payload_hash_len);
if(!payload_hash) {
/* AWS S3 requires a x-amz-content-sha256 header, and supports special
* values like UNSIGNED-PAYLOAD */
- bool sign_as_s3 = Curl_str_casecompare(&provider0, "aws") &&
- Curl_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, Curl_str(&provider1),
- Curl_strlen(&provider1), sha_hash, sha_hex,
- content_sha256_hdr);
+ 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)
@@ -772,7 +822,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) }
result = make_headers(data, hostname, timestamp,
- Curl_str(&provider1), Curl_strlen(&provider1),
+ curlx_str(&provider1), curlx_strlen(&provider1),
&date_header, content_sha256_hdr,
&canonical_headers, &signed_headers);
if(result)
@@ -788,12 +838,13 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) memcpy(date, timestamp, sizeof(date));
date[sizeof(date) - 1] = 0;
- result = canon_query(data, data->state.up.query, &canonical_query);
+ result = canon_query(data->state.up.query, &canonical_query);
if(result)
goto fail;
- result = canon_string(data->state.up.path, strlen(data->state.up.path),
- &canonical_path, NULL);
+ result = canon_path(data->state.up.path, strlen(data->state.up.path),
+ &canonical_path,
+ should_urlencode(&service));
if(result)
goto fail;
result = CURLE_OUT_OF_MEMORY;
@@ -806,29 +857,30 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "%s\n" /* SignedHeaders */
"%.*s", /* HashedRequestPayload in hex */
method,
- Curl_dyn_ptr(&canonical_path),
- Curl_dyn_ptr(&canonical_query) ?
- Curl_dyn_ptr(&canonical_query) : "",
- Curl_dyn_ptr(&canonical_headers),
- Curl_dyn_ptr(&signed_headers),
+ 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),
(int)payload_hash_len, payload_hash);
if(!canonical_request)
goto fail;
- DEBUGF(infof(data, "Canonical request: %s", canonical_request));
+ infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]",
+ canonical_request);
request_type = aprintf("%.*s4_request",
- (int)Curl_strlen(&provider0), Curl_str(&provider0));
+ (int)curlx_strlen(&provider0), curlx_str(&provider0));
if(!request_type)
goto fail;
/* provider0 is lowercased *after* aprintf() so that the buffer can be
written to */
- Curl_strntolower(request_type, request_type, Curl_strlen(&provider0));
+ Curl_strntolower(request_type, request_type, curlx_strlen(&provider0));
credential_scope = aprintf("%s/%.*s/%.*s/%s", date,
- (int)Curl_strlen(®ion), Curl_str(®ion),
- (int)Curl_strlen(&service), Curl_str(&service),
+ (int)curlx_strlen(®ion), curlx_str(®ion),
+ (int)curlx_strlen(&service), curlx_str(&service),
request_type);
if(!credential_scope)
goto fail;
@@ -847,7 +899,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) "%s\n" /* RequestDateTime */
"%s\n" /* CredentialScope */
"%s", /* HashedCanonicalRequest in hex */
- (int)Curl_strlen(&provider0), Curl_str(&provider0),
+ (int)curlx_strlen(&provider0), curlx_str(&provider0),
timestamp,
credential_scope,
sha_hex);
@@ -855,26 +907,32 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) goto fail;
/* make provider0 part done uppercase */
- Curl_strntoupper(str_to_sign, Curl_str(&provider0), Curl_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 = aprintf("%.*s4%s", (int)Curl_strlen(&provider0),
- Curl_str(&provider0), data->state.aptr.passwd ?
+ secret = aprintf("%.*s4%s", (int)curlx_strlen(&provider0),
+ curlx_str(&provider0), data->state.aptr.passwd ?
data->state.aptr.passwd : "");
if(!secret)
goto fail;
/* make provider0 part done uppercase */
- Curl_strntoupper(secret, Curl_str(&provider0), Curl_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),
- Curl_str(®ion), Curl_strlen(®ion), sign1);
+ curlx_str(®ion), curlx_strlen(®ion), sign1);
HMAC_SHA256(sign1, sizeof(sign1),
- Curl_str(&service), Curl_strlen(&service), 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);
+ infof(data, "aws_sigv4: Signature - %s", sha_hex);
+
auth_headers = aprintf("Authorization: %.*s4-HMAC-SHA256 "
"Credential=%s/%s, "
"SignedHeaders=%s, "
@@ -886,10 +944,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) */
"%s"
"%s", /* optional sha256 header includes \r\n */
- (int)Curl_strlen(&provider0), Curl_str(&provider0),
+ (int)curlx_strlen(&provider0), curlx_str(&provider0),
user,
credential_scope,
- Curl_dyn_ptr(&signed_headers),
+ curlx_dyn_ptr(&signed_headers),
sha_hex,
date_header ? date_header : "",
content_sha256_hdr);
@@ -898,7 +956,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) }
/* provider 0 uppercase */
Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
- Curl_str(&provider0), Curl_strlen(&provider0));
+ curlx_str(&provider0), curlx_strlen(&provider0));
free(data->state.aptr.userpwd);
data->state.aptr.userpwd = auth_headers;
@@ -906,10 +964,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data) result = CURLE_OK;
fail:
- Curl_dyn_free(&canonical_query);
- Curl_dyn_free(&canonical_path);
- Curl_dyn_free(&canonical_headers);
- Curl_dyn_free(&signed_headers);
+ curlx_dyn_free(&canonical_query);
+ curlx_dyn_free(&canonical_path);
+ curlx_dyn_free(&canonical_headers);
+ curlx_dyn_free(&signed_headers);
free(canonical_request);
free(request_type);
free(credential_scope);
@@ -919,4 +977,176 @@ fail: return result;
}
+/*
+* Frees all allocated strings in a dynbuf pair array, and the dynbuf itself
+*/
+
+static void pair_array_free(struct pair *pair_array, size_t num_elements)
+{
+ size_t index;
+
+ for(index = 0; index != num_elements; index++) {
+ curlx_dyn_free(&pair_array[index].key);
+ curlx_dyn_free(&pair_array[index].value);
+ }
+
+}
+
+/*
+* Frees all allocated strings in a split dynbuf, and the dynbuf itself
+*/
+
+static void dyn_array_free(struct dynbuf *db, size_t num_elements)
+{
+ size_t index;
+
+ for(index = 0; index < num_elements; index++)
+ curlx_dyn_free((&db[index]));
+}
+
+/*
+* Splits source string by SPLIT_BY, and creates an array of dynbuf in db.
+* db is initialized by this function.
+* Caller is responsible for freeing the array elements with dyn_array_free
+*/
+
+#define SPLIT_BY '&'
+
+static CURLcode split_to_dyn_array(const char *source,
+ struct dynbuf db[MAX_QUERY_COMPONENTS],
+ size_t *num_splits_out)
+{
+ CURLcode result = CURLE_OK;
+ size_t len = strlen(source);
+ size_t pos; /* Position in result buffer */
+ size_t start = 0; /* Start of current segment */
+ size_t segment_length = 0;
+ size_t index = 0;
+ size_t num_splits = 0;
+
+ /* Split source_ptr on SPLIT_BY and store the segment offsets and length in
+ * array */
+ for(pos = 0; pos < len; pos++) {
+ if(source[pos] == SPLIT_BY) {
+ if(segment_length) {
+ curlx_dyn_init(&db[index], segment_length + 1);
+ result = curlx_dyn_addn(&db[index], &source[start],
+ segment_length);
+ if(result)
+ goto fail;
+
+ segment_length = 0;
+ index++;
+ if(++num_splits == MAX_QUERY_COMPONENTS) {
+ result = CURLE_TOO_LARGE;
+ goto fail;
+ }
+ }
+ start = pos + 1;
+ }
+ else {
+ segment_length++;
+ }
+ }
+
+ if(segment_length) {
+ curlx_dyn_init(&db[index], segment_length + 1);
+ result = curlx_dyn_addn(&db[index], &source[start], segment_length);
+ if(!result) {
+ if(++num_splits == MAX_QUERY_COMPONENTS)
+ result = CURLE_TOO_LARGE;
+ }
+ }
+fail:
+ *num_splits_out = num_splits;
+ return result;
+}
+
+
+static bool is_reserved_char(const char c)
+{
+ return (ISALNUM(c) || ISURLPUNTCS(c));
+}
+
+static CURLcode uri_encode_path(struct Curl_str *original_path,
+ struct dynbuf *new_path)
+{
+ const char *p = curlx_str(original_path);
+ size_t i;
+
+ for(i = 0; i < curlx_strlen(original_path); i++) {
+ /* Do not encode slashes or unreserved chars from RFC 3986 */
+ CURLcode result = CURLE_OK;
+ unsigned char c = p[i];
+ if(is_reserved_char(c) || c == '/')
+ result = curlx_dyn_addn(new_path, &c, 1);
+ else
+ result = curlx_dyn_addf(new_path, "%%%02X", c);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+
+
+static CURLcode encode_query_component(char *component, size_t len,
+ struct dynbuf *db)
+{
+ size_t i;
+ for(i = 0; i < len; i++) {
+ CURLcode result = CURLE_OK;
+ unsigned char this_char = component[i];
+
+ if(is_reserved_char(this_char))
+ /* Escape unreserved chars from RFC 3986 */
+ result = curlx_dyn_addn(db, &this_char, 1);
+ else if(this_char == '+')
+ /* Encode '+' as space */
+ result = curlx_dyn_add(db, "%20");
+ else
+ result = curlx_dyn_addf(db, "%%%02X", this_char);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+* Populates a dynbuf containing url_encode(url_decode(in))
+*/
+
+static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
+ struct dynbuf *out)
+{
+ char *out_s;
+ size_t out_s_len;
+ CURLcode result =
+ Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA);
+
+ if(!result) {
+ result = encode_query_component(out_s, out_s_len, out);
+ Curl_safefree(out_s);
+ }
+ return result;
+}
+
+static bool should_urlencode(struct Curl_str *service_name)
+{
+ /*
+ * These services require unmodified (not additionally url encoded) URL
+ * paths.
+ * should_urlencode == true is equivalent to should_urlencode_uri_path
+ * from the AWS SDK. Urls are already normalized by the curl url parser
+ */
+
+ if(curlx_str_cmp(service_name, "s3") ||
+ curlx_str_cmp(service_name, "s3-express") ||
+ curlx_str_cmp(service_name, "s3-outposts")) {
+ return false;
+ }
+ return true;
+}
+
#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */
|