From b5c65f8b7be71d5abd9f317275e4a4d0b20ccf93 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Sat, 12 Aug 2023 15:06:08 -0400 Subject: [PATCH] http_aws_sigv4: handle no-value user header entries - Handle user headers in format 'name:' and 'name;' with no value. The former is used when the user wants to remove an internal libcurl header and the latter is used when the user actually wants to send a no-value header in the format 'name:' (note the semi-colon is converted by libcurl to a colon). Prior to this change the AWS header import code did not special case either of those and the generated AWS SignedHeaders would be incorrect. Reported-by: apparentorder@users.noreply.github.com Ref: https://curl.se/docs/manpage.html#-H Fixes https://github.com/curl/curl/issues/11664 Closes https://github.com/curl/curl/pull/11668 --- lib/http_aws_sigv4.c | 35 +++++++++++++++++++++++++++++++++-- tests/data/test1955 | 5 +++-- tests/libtest/lib1955.c | 16 +++++++++++++++- 3 files changed, 51 insertions(+), 5 deletions(-) diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index d934f17148..f39d02cced 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -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; } diff --git a/tests/data/test1955 b/tests/data/test1955 index 342b7b703c..27bb466c75 100644 --- a/tests/data/test1955 +++ b/tests/data/test1955 @@ -64,9 +64,10 @@ http://exam.ple.com:9000/aws_sigv4/testapi/test exam.ple.com:9000:%HOSTIP:%HTTPP GET /aws_sigv4/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=779a8ff876528aece8bf03b1296702af0644a4745aa5feabb6ebb1a7bb0d907e +Authorization: XXX4-HMAC-SHA256 Credential=xxx/19700101/ple/exam/xxx4_request, SignedHeaders=content-type;host;tesmixcase;test2;test3;test_space;x-xxx-date, Signature=dd39202e9fb7b836ebf2abb83b114cae11ff3b6a169f0c64b290a774a873db9d X-Xxx-Date: 19700101T000000Z -test2: 1234 +test3: 1234 +test2: test_space: t s m end tesMixCase: MixCase diff --git a/tests/libtest/lib1955.c b/tests/libtest/lib1955.c index d92582bd5e..3328d7ef1a 100644 --- a/tests/libtest/lib1955.c +++ b/tests/libtest/lib1955.c @@ -49,7 +49,7 @@ int test(char *URL) 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"); + list = curl_slist_append(list, "test3: 1234"); if(!list) goto test_cleanup; if(libtest_arg2) { @@ -57,8 +57,22 @@ int test(char *URL) } test_setopt(curl, CURLOPT_CONNECT_TO, connect_to); curl_slist_append(list, "Content-Type: application/json"); + + /* 'name;' user headers with no value are used to send an empty header in the + format 'name:' (note the semi-colon becomes a colon). this entry should + show in SignedHeaders without an additional semi-colon, as any other + header would. eg 'foo;test2;test3' and not 'foo;test2;;test3'. */ + curl_slist_append(list, "test2;"); + + /* 'name:' user headers with no value are used to signal an internal header + of that name should be removed and are not sent as a header. this entry + should not show in SignedHeaders. */ curl_slist_append(list, "test1:"); + + /* 'name' user headers with no separator or value are invalid and ignored. + this entry should not show in SignedHeaders. */ 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);