summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/http_aws_sigv4.c
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2023-09-20 17:09:59 (GMT)
committerBrad King <brad.king@kitware.com>2023-09-22 14:55:37 (GMT)
commitc1f76e6c211e9e562328f1c0571e8877599f880e (patch)
treef4c879a444b512b178f89ccd5b1c0f4a0db4ee99 /Utilities/cmcurl/lib/http_aws_sigv4.c
parent1fb19cbdad18011756945f84d19951a199a399bc (diff)
parent017637e40f954e791a895a04855d0411bda61c10 (diff)
downloadCMake-c1f76e6c211e9e562328f1c0571e8877599f880e.zip
CMake-c1f76e6c211e9e562328f1c0571e8877599f880e.tar.gz
CMake-c1f76e6c211e9e562328f1c0571e8877599f880e.tar.bz2
Merge branch 'upstream-curl' into update-curl
* upstream-curl: curl 2023-09-13 (6fa1d817) Upstream significantly refactored `lib/CMakeLists.txt`, so take the upstream version of everything except the code added by commit 54cb23c657 (curl: Restore installation of OpenSSL DLLs, 2014-11-03, v3.2.0-rc1~418^2~4). We will apply our customizations again in a follow-up commit.
Diffstat (limited to 'Utilities/cmcurl/lib/http_aws_sigv4.c')
-rw-r--r--Utilities/cmcurl/lib/http_aws_sigv4.c249
1 files changed, 200 insertions, 49 deletions
diff --git a/Utilities/cmcurl/lib/http_aws_sigv4.c b/Utilities/cmcurl/lib/http_aws_sigv4.c
index 8060162..f39d02c 100644
--- a/Utilities/cmcurl/lib/http_aws_sigv4.c
+++ b/Utilities/cmcurl/lib/http_aws_sigv4.c
@@ -24,7 +24,7 @@
#include "curl_setup.h"
-#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS)
#include "urldata.h"
#include "strcase.h"
@@ -44,16 +44,16 @@
#include "slist.h"
-#define HMAC_SHA256(k, kl, d, dl, o) \
- do { \
- ret = Curl_hmacit(Curl_HMAC_SHA256, \
- (unsigned char *)k, \
- kl, \
- (unsigned char *)d, \
- dl, o); \
- if(ret) { \
- goto fail; \
- } \
+#define HMAC_SHA256(k, kl, d, dl, o) \
+ do { \
+ result = Curl_hmacit(Curl_HMAC_SHA256, \
+ (unsigned char *)k, \
+ kl, \
+ (unsigned char *)d, \
+ dl, o); \
+ if(result) { \
+ goto fail; \
+ } \
} while(0)
#define TIMESTAMP_SIZE 17
@@ -199,10 +199,41 @@ static CURLcode make_headers(struct Curl_easy *data,
head = tmp_head;
}
+ /* copy user headers to our header list. the logic is based on how http.c
+ handles user headers.
+
+ user headers in format 'name:' with no value are used to signal that an
+ internal header of that name should be removed. those user headers are not
+ added to this list.
+
+ user headers in format 'name;' with no value are used to signal that a
+ header of that name with no value should be sent. those user headers are
+ added to this list but in the format that they will be sent, ie the
+ semi-colon is changed to a colon for format 'name:'.
+
+ user headers with a value of whitespace only, or without a colon or
+ semi-colon, are not added to this list.
+ */
for(l = data->set.headers; l; l = l->next) {
- tmp_head = curl_slist_append(head, l->data);
- if(!tmp_head)
+ char *dupdata, *ptr;
+ char *sep = strchr(l->data, ':');
+ if(!sep)
+ sep = strchr(l->data, ';');
+ if(!sep || (*sep == ':' && !*(sep + 1)))
+ continue;
+ for(ptr = sep + 1; ISSPACE(*ptr); ++ptr)
+ ;
+ if(!*ptr && ptr != sep + 1) /* a value of whitespace only */
+ continue;
+ dupdata = strdup(l->data);
+ if(!dupdata)
goto fail;
+ dupdata[sep - l->data] = ':';
+ tmp_head = Curl_slist_append_nodup(head, dupdata);
+ if(!tmp_head) {
+ free(dupdata);
+ goto fail;
+ }
head = tmp_head;
}
@@ -214,23 +245,22 @@ static CURLcode make_headers(struct Curl_easy *data,
if(!tmp_head)
goto fail;
head = tmp_head;
- *date_header = curl_maprintf("%s: %s", date_hdr_key, timestamp);
+ *date_header = curl_maprintf("%s: %s\r\n", date_hdr_key, timestamp);
}
else {
char *value;
- *date_header = strdup(*date_header);
- if(!*date_header)
- goto fail;
-
value = strchr(*date_header, ':');
- if(!value)
+ if(!value) {
+ *date_header = NULL;
goto fail;
+ }
++value;
while(ISBLANK(*value))
++value;
strncpy(timestamp, value, TIMESTAMP_SIZE - 1);
timestamp[TIMESTAMP_SIZE - 1] = 0;
+ *date_header = NULL;
}
/* alpha-sort in a case sensitive manner */
@@ -370,9 +400,112 @@ 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;
+ return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len);
+}
+
+#define MAX_QUERYPAIRS 64
+
+static CURLcode canon_query(struct Curl_easy *data,
+ 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];
+ 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 */
+ else {
+ ap->len = strlen(p);
+ break;
+ }
+ 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++) {
+ size_t len;
+ const char *q = ap->p;
+ if(!ap->len)
+ continue;
+ for(len = ap->len; len && !result; q++, len--) {
+ if(ISALNUM(*q))
+ result = Curl_dyn_addn(dq, q, 1);
+ else {
+ switch(*q) {
+ case '-':
+ 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: {
+ /* URL encode */
+ const char hex[] = "0123456789ABCDEF";
+ char out[3]={'%'};
+ out[1] = hex[((unsigned char)*q)>>4];
+ out[2] = hex[*q & 0xf];
+ result = Curl_dyn_addn(dq, out, 3);
+ break;
+ }
+ }
+ }
+ }
+ if(i < entry - 1) {
+ /* insert ampersands between query pairs */
+ result = Curl_dyn_addn(dq, "&", 1);
+ }
+ }
+ return result;
+}
+
+
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
{
- CURLcode ret = CURLE_OUT_OF_MEMORY;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
struct connectdata *conn = data->conn;
size_t len;
const char *arg;
@@ -388,6 +521,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
char date[9];
struct dynbuf canonical_headers;
struct dynbuf signed_headers;
+ struct dynbuf canonical_query;
char *date_header = NULL;
Curl_HttpReq httpreq;
const char *method = NULL;
@@ -416,6 +550,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
/* 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);
/*
@@ -431,15 +566,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
/* 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;
+ failf(data, "first aws-sigv4 provider can't be empty");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
goto fail;
}
else if(!provider1[0])
@@ -448,35 +583,38 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
if(!service[0]) {
char *hostdot = strchr(hostname, '.');
if(!hostdot) {
- failf(data, "service missing in parameters and hostname");
- ret = CURLE_URL_MALFORMAT;
+ failf(data, "aws-sigv4: service missing in parameters and hostname");
+ result = CURLE_URL_MALFORMAT;
goto fail;
}
len = hostdot - hostname;
if(len > MAX_SIGV4_LEN) {
- failf(data, "service too long in hostname");
- ret = CURLE_URL_MALFORMAT;
+ failf(data, "aws-sigv4: service too long in hostname");
+ result = CURLE_URL_MALFORMAT;
goto fail;
}
strncpy(service, hostname, len);
service[len] = '\0';
+ infof(data, "aws_sigv4: picked service %s from host", service);
+
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;
+ failf(data, "aws-sigv4: region missing in parameters and hostname");
+ result = CURLE_URL_MALFORMAT;
goto fail;
}
len = hostreg - reg;
if(len > MAX_SIGV4_LEN) {
- failf(data, "region too long in hostname");
- ret = CURLE_URL_MALFORMAT;
+ failf(data, "aws-sigv4: region too long in hostname");
+ result = CURLE_URL_MALFORMAT;
goto fail;
}
strncpy(region, reg, len);
region[len] = '\0';
+ infof(data, "aws_sigv4: picked region %s from host", region);
}
}
@@ -491,11 +629,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
if(!payload_hash) {
if(sign_as_s3)
- ret = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
- sha_hex, content_sha256_hdr);
+ result = 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)
+ result = calc_payload_hash(data, sha_hash, sha_hex);
+ if(result)
goto fail;
payload_hash = sha_hex;
@@ -514,21 +652,20 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
#else
time(&clock);
#endif
- ret = Curl_gmtime(clock, &tm);
- if(ret) {
+ result = Curl_gmtime(clock, &tm);
+ if(result) {
goto fail;
}
if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
- ret = CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
goto fail;
}
- ret = make_headers(data, hostname, timestamp, provider1,
- &date_header, content_sha256_hdr,
- &canonical_headers, &signed_headers);
- if(ret)
+ result = make_headers(data, hostname, timestamp, provider1,
+ &date_header, content_sha256_hdr,
+ &canonical_headers, &signed_headers);
+ if(result)
goto fail;
- ret = CURLE_OUT_OF_MEMORY;
if(*content_sha256_hdr) {
/* make_headers() needed this without the \r\n for canonicalization */
@@ -540,6 +677,11 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
memcpy(date, timestamp, sizeof(date));
date[sizeof(date) - 1] = 0;
+ result = canon_query(data, data->state.up.query, &canonical_query);
+ if(result)
+ goto fail;
+ result = CURLE_OUT_OF_MEMORY;
+
canonical_request =
curl_maprintf("%s\n" /* HTTPRequestMethod */
"%s\n" /* CanonicalURI */
@@ -549,13 +691,16 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
"%.*s", /* HashedRequestPayload in hex */
method,
data->state.up.path,
- data->state.up.query ? data->state.up.query : "",
+ Curl_dyn_ptr(&canonical_query) ?
+ Curl_dyn_ptr(&canonical_query) : "",
Curl_dyn_ptr(&canonical_headers),
Curl_dyn_ptr(&signed_headers),
(int)payload_hash_len, payload_hash);
if(!canonical_request)
goto fail;
+ DEBUGF(infof(data, "Canonical request: %s", canonical_request));
+
/* provider 0 lowercase */
Curl_strntolower(provider0, provider0, strlen(provider0));
request_type = curl_maprintf("%s4_request", provider0);
@@ -612,14 +757,19 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
"Credential=%s/%s, "
"SignedHeaders=%s, "
"Signature=%s\r\n"
- "%s\r\n"
+ /*
+ * date_header is added here, only if it wasn't
+ * user-specified (using CURLOPT_HTTPHEADER).
+ * date_header includes \r\n
+ */
+ "%s"
"%s", /* optional sha256 header includes \r\n */
provider0,
user,
credential_scope,
Curl_dyn_ptr(&signed_headers),
sha_hex,
- date_header,
+ date_header ? date_header : "",
content_sha256_hdr);
if(!auth_headers) {
goto fail;
@@ -628,9 +778,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
Curl_safefree(data->state.aptr.userpwd);
data->state.aptr.userpwd = auth_headers;
data->state.authhost.done = TRUE;
- ret = CURLE_OK;
+ result = CURLE_OK;
fail:
+ Curl_dyn_free(&canonical_query);
Curl_dyn_free(&canonical_headers);
Curl_dyn_free(&signed_headers);
free(canonical_request);
@@ -639,7 +790,7 @@ fail:
free(str_to_sign);
free(secret);
free(date_header);
- return ret;
+ return result;
}
-#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */
+#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */