Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-15-SP7:Update
curl.36282
curl-aws_sigv4-url-encode-the-canonical-path.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File curl-aws_sigv4-url-encode-the-canonical-path.patch of Package curl.36282
From 768909d89cb3089f96fb495b13e636ecf0742e3d Mon Sep 17 00:00:00 2001 From: Matthias Gatto <matthias.gatto@outscale.com> Date: Mon, 27 May 2024 14:58:11 +0200 Subject: [PATCH] aws-sigv4: url encode the canonical path Refactors canon_query, so it could use the encoding part of the function to use it in the path. As the path doesn't encode '/', but encode '=', I had to add some conditions to know If I was doing the query or path encoding. Also, instead of adding a `bool in_path` variable, I use `bool *found_equals` to know if the function was called for the query or path, as found_equals is used only in query_encoding. Test 472 verifies. Reported-by: Alexander Shtuchkin Fixes #13754 Closes #13814 Signed-off-by: Matthias Gatto <matthias.gatto@outscale.com> Index: curl-8.6.0/lib/http_aws_sigv4.c =================================================================== --- curl-8.6.0.orig/lib/http_aws_sigv4.c +++ curl-8.6.0/lib/http_aws_sigv4.c @@ -426,6 +426,76 @@ static int compare_func(const void *a, c #define MAX_QUERYPAIRS 64 +/** + * 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) +{ + 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; + } + } + } + } + return result; +} + + static CURLcode canon_query(struct Curl_easy *data, const char *query, struct dynbuf *dq) { @@ -463,54 +533,11 @@ static CURLcode canon_query(struct Curl_ ap = &array[0]; for(i = 0; !result && (i < entry); i++, ap++) { - size_t len; const char *q = ap->p; bool found_equals = false; 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 '~': - /* allowed as-is */ - result = Curl_dyn_addn(dq, q, 1); - break; - case '=': - /* allowed as-is */ - result = Curl_dyn_addn(dq, q, 1); - found_equals = true; - 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; - } - } - } - } + 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); @@ -543,6 +570,7 @@ CURLcode Curl_output_aws_sigv4(struct Cu 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; @@ -573,6 +601,7 @@ CURLcode Curl_output_aws_sigv4(struct Cu 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); /* * Parameters parsing @@ -701,6 +730,11 @@ CURLcode Curl_output_aws_sigv4(struct Cu result = canon_query(data, 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); + if(result) + goto fail; result = CURLE_OUT_OF_MEMORY; canonical_request = @@ -711,7 +745,7 @@ CURLcode Curl_output_aws_sigv4(struct Cu "%s\n" /* SignedHeaders */ "%.*s", /* HashedRequestPayload in hex */ method, - data->state.up.path, + Curl_dyn_ptr(&canonical_path), Curl_dyn_ptr(&canonical_query) ? Curl_dyn_ptr(&canonical_query) : "", Curl_dyn_ptr(&canonical_headers), @@ -803,6 +837,7 @@ CURLcode Curl_output_aws_sigv4(struct Cu fail: Curl_dyn_free(&canonical_query); + Curl_dyn_free(&canonical_path); Curl_dyn_free(&canonical_headers); Curl_dyn_free(&signed_headers); free(canonical_request); Index: curl-8.6.0/tests/data/Makefile.inc =================================================================== --- curl-8.6.0.orig/tests/data/Makefile.inc +++ curl-8.6.0/tests/data/Makefile.inc @@ -73,7 +73,7 @@ test426 test427 test428 test429 test430 test435 test436 test437 test438 test439 test440 test441 test442 test443 \ test444 test445 test446 test447 test448 test449 test450 test451 test452 \ test453 test454 test455 test456 test457 test458 test459 test460 test461 \ -\ +test472 \ test490 test491 test492 test493 test494 test495 test496 test497 test498 \ \ test500 test501 test502 test503 test504 test505 test506 test507 test508 \ Index: curl-8.6.0/tests/data/test472 =================================================================== --- /dev/null +++ curl-8.6.0/tests/data/test472 @@ -0,0 +1,59 @@ +<testcase> +<info> +<keywords> +HTTP +aws-sigv4 +</keywords> +</info> + +# +# Server-side +<reply> +<data crlf="yes"> +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- +</data> +</reply> + +# +# Client-side +<client> +<server> +http +</server> +<features> +debug +Unicode +</features> +<name> +aws-sigv4 with query +</name> +<command> +"http://fake.fake.fake:8000/%TESTNUMBER/a=あ" -u user:secret --aws-sigv4 "aws:amz:us-east-2:es" --connect-to fake.fake.fake:8000:%HOSTIP:%HTTPPORT +</command> +</client> + +# +# Verify data after the test has been "shot" +<verify> +<protocol crlf="yes"> +GET /472/a=%e3%81%82 HTTP/1.1 +Host: fake.fake.fake:8000 +Authorization: AWS4-HMAC-SHA256 Credential=user/19700101/us-east-2/es/aws4_request, SignedHeaders=host;x-amz-date, Signature=c63315c199922f7ee00141869a250389405d19e205057249fb74726d940b1fc3 +X-Amz-Date: 19700101T000000Z +User-Agent: curl/%VERSION +Accept: */* + +</protocol> +</verify> +</testcase> Index: curl-8.6.0/tests/data/Makefile.in =================================================================== --- curl-8.6.0.orig/tests/data/Makefile.in +++ curl-8.6.0/tests/data/Makefile.in @@ -445,7 +445,7 @@ test426 test427 test428 test429 test430 test435 test436 test437 test438 test439 test440 test441 test442 test443 \ test444 test445 test446 test447 test448 test449 test450 test451 test452 \ test453 test454 test455 test456 test457 test458 test459 test460 test461 \ -\ +test472 \ test490 test491 test492 test493 test494 test495 test496 test497 test498 \ \ test500 test501 test502 test503 test504 test505 test506 test507 test508 \
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor