diff --git a/0069-curl-7.61.1-aws-sigv4.patch b/0069-curl-7.61.1-aws-sigv4.patch new file mode 100644 index 0000000..874c2d7 --- /dev/null +++ b/0069-curl-7.61.1-aws-sigv4.patch @@ -0,0 +1,988 @@ +diff -Naur curl-7.61.1.orig/lib/http_aws_sigv4.c curl-7.61.1/lib/http_aws_sigv4.c +--- curl-7.61.1.orig/lib/http_aws_sigv4.c 1970-01-01 01:00:00.000000000 +0100 ++++ curl-7.61.1/lib/http_aws_sigv4.c 2025-12-02 13:31:38.991776478 +0100 +@@ -0,0 +1,511 @@ ++/*************************************************************************** ++ * _ _ ____ _ ++ * Project ___| | | | _ \| | ++ * / __| | | | |_) | | ++ * | (__| |_| | _ <| |___ ++ * \___|\___/|_| \_\_____| ++ * ++ * Copyright (C) 1998 - 2021, Daniel Stenberg, , 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. ++ * ++ ***************************************************************************/ ++ ++#include "curl_setup.h" ++ ++#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) ++ ++#define USE_AWS_SIGV4 ++ ++#include "urldata.h" ++#include "strcase.h" ++#include "strdup.h" ++#include "vauth/vauth.h" ++#include "vauth/digest.h" ++#include "http_aws_sigv4.h" ++#include "curl_sha256.h" ++#include "curl_hmac.h" ++#include "warnless.h" ++#include "transfer.h" ++ ++#include "strcase.h" ++#include "parsedate.h" ++#include "sendf.h" ++ ++#include ++ ++/* The last 3 #include files should be in this order */ ++#include "curl_printf.h" ++#include "curl_memory.h" ++#include "memdebug.h" ++ ++/* SHA256 Init/Update/Final function pointers for HMAC */ ++#if defined(USE_OPENSSL) ++#include ++#include ++#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) ++#define USE_OPENSSL_SHA256 ++#endif ++#endif ++ ++#ifdef USE_OPENSSL_SHA256 ++static void SHA256_Init_Wrapper(void *context) ++{ ++ SHA256_Init((SHA256_CTX *)context); ++} ++ ++static void SHA256_Update_Wrapper(void *context, ++ const unsigned char *data, ++ unsigned int len) ++{ ++ SHA256_Update((SHA256_CTX *)context, data, len); ++} ++ ++static void SHA256_Final_Wrapper(unsigned char *result, void *context) ++{ ++ SHA256_Final(result, (SHA256_CTX *)context); ++} ++#else ++/* Use internal SHA256 implementation */ ++typedef struct sha256_state SHA256_CTX; ++ ++static void SHA256_Init_Wrapper(void *context); ++static int SHA256_Update_Wrapper_Internal(void *context, ++ const unsigned char *data, ++ unsigned int len); ++static int SHA256_Final_Wrapper_Internal(unsigned char *result, ++ void *context); ++ ++static void SHA256_Update_Wrapper(void *context, ++ const unsigned char *data, ++ unsigned int len) ++{ ++ SHA256_Update_Wrapper_Internal(context, data, len); ++} ++ ++static void SHA256_Final_Wrapper(unsigned char *result, void *context) ++{ ++ SHA256_Final_Wrapper_Internal(result, context); ++} ++ ++/* Forward declarations for internal SHA256 functions */ ++extern void SHA256_Init(SHA256_CTX *ctx); ++extern int SHA256_Update(SHA256_CTX *ctx, const unsigned char *data, ++ unsigned int len); ++extern int SHA256_Final(unsigned char *out, SHA256_CTX *ctx); ++ ++static void SHA256_Init_Wrapper(void *context) ++{ ++ SHA256_Init((SHA256_CTX *)context); ++} ++ ++static int SHA256_Update_Wrapper_Internal(void *context, ++ const unsigned char *data, ++ unsigned int len) ++{ ++ return SHA256_Update((SHA256_CTX *)context, data, len); ++} ++ ++static int SHA256_Final_Wrapper_Internal(unsigned char *result, ++ void *context) ++{ ++ return SHA256_Final(result, (SHA256_CTX *)context); ++} ++#endif ++ ++/* HMAC-SHA256 parameters for curl 7.61.1 */ ++static const HMAC_params Curl_HMAC_SHA256[] = { ++ { ++ CURLX_FUNCTION_CAST(HMAC_hinit_func, SHA256_Init_Wrapper), ++ CURLX_FUNCTION_CAST(HMAC_hupdate_func, SHA256_Update_Wrapper), ++ CURLX_FUNCTION_CAST(HMAC_hfinal_func, SHA256_Final_Wrapper), ++ sizeof(SHA256_CTX), ++ 64, /* Maximum key length (block size) */ ++ 32 /* Result length (SHA256 produces 32 bytes) */ ++ } ++}; ++ ++#define HMAC_SHA256(k, kl, d, dl, o) \ ++ do { \ ++ HMAC_context *hctx = Curl_HMAC_init(Curl_HMAC_SHA256, \ ++ (unsigned char *)k, \ ++ (unsigned int)kl); \ ++ if(!hctx) { \ ++ ret = CURLE_OUT_OF_MEMORY; \ ++ goto fail; \ ++ } \ ++ Curl_HMAC_update(hctx, \ ++ (unsigned char *)d, \ ++ (unsigned int)dl); \ ++ Curl_HMAC_final(hctx, o); \ ++ } while(0) ++ ++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]); ++ } ++} ++ ++CURLcode Curl_output_aws_sigv4(struct connectdata *conn, bool proxy) ++{ ++ CURLcode ret = CURLE_OUT_OF_MEMORY; ++ struct Curl_easy *data = conn->data; ++ 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 *hostname = conn->host.name; ++#ifdef DEBUGBUILD ++ char *force_timestamp; ++#endif ++ time_t clock; ++ struct tm tm; ++ char timestamp[17]; ++ char date[9]; ++ const char *content_type = Curl_checkheaders(conn, "Content-Type"); ++ char *canonical_headers = NULL; ++ char *signed_headers = NULL; ++ Curl_HttpReq httpreq; ++ const char *method = NULL; ++ const char *post_data = data->set.postfields ? data->set.postfields : ""; ++ unsigned char sha_hash[32]; ++ char sha_hex[65]; ++ char *canonical_request = NULL; ++ char *request_type = NULL; ++ 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}; ++ char *auth_headers = NULL; ++ ++ DEBUGASSERT(!proxy); ++ (void)proxy; ++ ++ if(Curl_checkheaders(conn, "Authorization")) { ++ /* Authorization already present, Bailing out */ ++ return CURLE_OK; ++ } ++ ++ /* ++ * Parameters parsing ++ * Google and Outscale use the same OSC or GOOG, ++ * but Amazon uses AWS and AMZ for header arguments. ++ * 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] ? ++ 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\n"); ++ 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'; ++ ++ 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\n"); ++ 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\n"); ++ 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\n"); ++ 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, '.'); ++ len = tmp1 - tmp0; ++ if(!tmp1 || len < 1) { ++ infof(data, "service missing in parameters or hostname\n"); ++ ret = CURLE_URL_MALFORMAT; ++ goto fail; ++ } ++ service = Curl_memdup(tmp0, len + 1); ++ if(!service) { ++ goto fail; ++ } ++ service[len] = '\0'; ++ ++ if(!region) { ++ tmp0 = tmp1 + 1; ++ tmp1 = strchr(tmp0, '.'); ++ len = tmp1 - tmp0; ++ if(!tmp1 || len < 1) { ++ infof(data, "region missing in parameters or hostname\n"); ++ ret = CURLE_URL_MALFORMAT; ++ goto fail; ++ } ++ region = Curl_memdup(tmp0, len + 1); ++ if(!region) { ++ goto fail; ++ } ++ region[len] = '\0'; ++ } ++ } ++ ++#ifdef DEBUGBUILD ++ 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) { ++ goto fail; ++ } ++ if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) { ++ goto fail; ++ } ++ 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); ++ } ++ 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; ++ } ++ ++ Curl_sha256it(sha_hash, ++ (const unsigned char *) post_data, strlen(post_data)); ++ sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); ++ ++ /* Determine HTTP method - curl 7.61.1 style */ ++ httpreq = data->set.httpreq; ++ switch(httpreq) { ++ case HTTPREQ_GET: ++ method = "GET"; ++ break; ++ case HTTPREQ_POST: ++ case HTTPREQ_POST_FORM: ++ case HTTPREQ_POST_MIME: ++ method = "POST"; ++ break; ++ case HTTPREQ_PUT: ++ method = "PUT"; ++ break; ++ case HTTPREQ_HEAD: ++ method = "HEAD"; ++ break; ++ case HTTPREQ_OPTIONS: ++ method = "OPTIONS"; ++ break; ++ case HTTPREQ_CUSTOM: ++ method = data->set.customrequest; ++ break; ++ default: ++ method = "GET"; ++ break; ++ } ++ ++ 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; ++ } ++ ++ request_type = curl_maprintf("%s4_request", provider0_low); ++ if(!request_type) { ++ goto fail; ++ } ++ ++ credential_scope = curl_maprintf("%s/%s/%s/%s", ++ date, region, service, request_type); ++ if(!credential_scope) { ++ goto fail; ++ } ++ ++ Curl_sha256it(sha_hash, (unsigned char *) canonical_request, ++ strlen(canonical_request)); ++ sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex)); ++ ++ /* ++ * Google allow to use rsa key instead of HMAC, so this code might change ++ * In the furure, but for now we support only HMAC version ++ */ ++ str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */ ++ "%s\n" /* RequestDateTime */ ++ "%s\n" /* CredentialScope */ ++ "%s", /* HashedCanonicalRequest in hex */ ++ provider0_up, ++ timestamp, ++ credential_scope, ++ sha_hex); ++ if(!str_to_sign) { ++ goto fail; ++ } ++ ++ secret = curl_maprintf("%s4%s", provider0_up, 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); ++ ++ sha256_to_hex(sha_hex, tmp_sign0, sizeof(sha_hex)); ++ ++ 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, ++ user, ++ credential_scope, ++ signed_headers, ++ sha_hex, ++ provider1_mid, ++ timestamp); ++ if(!auth_headers) { ++ goto fail; ++ } ++ ++ Curl_safefree(data->state.aptr.userpwd); ++ data->state.aptr.userpwd = auth_headers; ++ data->state.authhost.done = TRUE; ++ 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); ++ free(canonical_request); ++ free(request_type); ++ free(credential_scope); ++ free(str_to_sign); ++ free(secret); ++ return ret; ++} ++ ++#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */ +diff -Naur curl-7.61.1.orig/lib/http_aws_sigv4.h curl-7.61.1/lib/http_aws_sigv4.h +--- curl-7.61.1.orig/lib/http_aws_sigv4.h 1970-01-01 01:00:00.000000000 +0100 ++++ curl-7.61.1/lib/http_aws_sigv4.h 2025-12-02 13:31:38.992776500 +0100 +@@ -0,0 +1,29 @@ ++#ifndef HEADER_CURL_HTTP_AWS_SIGV4_H ++#define HEADER_CURL_HTTP_AWS_SIGV4_H ++/*************************************************************************** ++ * _ _ ____ _ ++ * Project ___| | | | _ \| | ++ * / __| | | | |_) | | ++ * | (__| |_| | _ <| |___ ++ * \___|\___/|_| \_\_____| ++ * ++ * Copyright (C) 1998 - 2024, Daniel Stenberg, , et al. ++ * ++ * AWS SigV4 Support - Backport to RHEL 8 ++ * ++ ***************************************************************************/ ++ ++#include "curl_setup.h" ++ ++#ifdef USE_AWS_SIGV4 ++ ++/* AWS SigV4 authentication function */ ++CURLcode Curl_output_aws_sigv4(struct connectdata *conn, bool proxy); ++ ++#else ++ ++#define Curl_output_aws_sigv4(x,y) CURLE_NOT_BUILT_IN ++ ++#endif /* USE_AWS_SIGV4 */ ++ ++#endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */ +diff -Naur curl-7.61.1.orig/lib/Makefile.inc curl-7.61.1/lib/Makefile.inc +--- curl-7.61.1.orig/lib/Makefile.inc 2025-12-02 13:31:37.675747831 +0100 ++++ curl-7.61.1/lib/Makefile.inc 2025-12-02 13:31:40.198802751 +0100 +@@ -54,7 +54,7 @@ + curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c \ + x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \ + mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \ +- urlapi.c ++ urlapi.c http_aws_sigv4.c + + LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ + formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \ +@@ -74,7 +74,8 @@ + curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h \ + x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \ + curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \ +- curl_path.h curl_ctype.h curl_range.h psl.h urlapi-int.h ++ curl_path.h curl_ctype.h curl_range.h psl.h urlapi-int.h \ ++ http_aws_sigv4.h + + LIB_RCFILES = libcurl.rc + +diff -Naur curl-7.61.1.orig/lib/openldap.c curl-7.61.1/lib/openldap.c +--- curl-7.61.1.orig/lib/openldap.c 2025-12-02 13:31:37.681747962 +0100 ++++ curl-7.61.1/lib/openldap.c 2025-12-02 13:31:41.175824017 +0100 +@@ -79,8 +79,8 @@ + static CURLcode ldap_setup_connection(struct connectdata *conn); + static CURLcode ldap_do(struct connectdata *conn, bool *done); + static CURLcode ldap_done(struct connectdata *conn, CURLcode, bool); +-static CURLcode ldap_connect(struct connectdata *conn, bool *done); +-static CURLcode ldap_connecting(struct connectdata *conn, bool *done); ++static CURLcode oldap_connect(struct connectdata *conn, bool *done); ++static CURLcode ooldap_connecting(struct connectdata *conn, bool *done); + static CURLcode ldap_disconnect(struct connectdata *conn, bool dead); + + static Curl_recv ldap_recv; +@@ -95,8 +95,8 @@ + ldap_do, /* do_it */ + ldap_done, /* done */ + ZERO_NULL, /* do_more */ +- ldap_connect, /* connect_it */ +- ldap_connecting, /* connecting */ ++ oldap_connect, /* connect_it */ ++ ooldap_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ +@@ -121,8 +121,8 @@ + ldap_do, /* do_it */ + ldap_done, /* done */ + ZERO_NULL, /* do_more */ +- ldap_connect, /* connect_it */ +- ldap_connecting, /* connecting */ ++ oldap_connect, /* connect_it */ ++ ooldap_connecting, /* connecting */ + ZERO_NULL, /* doing */ + ZERO_NULL, /* proto_getsock */ + ZERO_NULL, /* doing_getsock */ +@@ -206,7 +206,7 @@ + static Sockbuf_IO ldapsb_tls; + #endif + +-static CURLcode ldap_connect(struct connectdata *conn, bool *done) ++static CURLcode oldap_connect(struct connectdata *conn, bool *done) + { + ldapconninfo *li = conn->proto.generic; + struct Curl_easy *data = conn->data; +@@ -253,7 +253,7 @@ + return CURLE_OK; + } + +-static CURLcode ldap_connecting(struct connectdata *conn, bool *done) ++static CURLcode ooldap_connecting(struct connectdata *conn, bool *done) + { + ldapconninfo *li = conn->proto.generic; + struct Curl_easy *data = conn->data; +diff -Naur curl-7.61.1.orig/lib/http.c curl-7.61.1/lib/http.c +--- curl-7.61.1.orig/lib/http.c 2025-12-05 12:00:00.000000000 +0100 ++++ curl-7.61.1/lib/http.c 2025-12-05 12:30:00.000000000 +0100 +@@ -60,6 +60,7 @@ + #include "http_ntlm.h" + #include "curl_ntlm_wb.h" + #include "http_negotiate.h" ++#include "http_aws_sigv4.h" + #include "url.h" + #include "share.h" + #include "hostip.h" +@@ -362,6 +363,8 @@ + pick->picked = CURLAUTH_NTLM_WB; + else if(avail & CURLAUTH_BASIC) + pick->picked = CURLAUTH_BASIC; ++ else if(avail & CURLAUTH_AWS_SIGV4) ++ pick->picked = CURLAUTH_AWS_SIGV4; + else { + pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */ + picked = FALSE; +@@ -682,6 +685,21 @@ + functions work that way */ + authstatus->done = TRUE; + } ++ if(authstatus->picked == CURLAUTH_AWS_SIGV4) { ++ /* AWS SigV4 */ ++ if((!proxy && data->set.str[STRING_AWS_SIGV4] && ++ !Curl_checkheaders(conn, "Authorization:"))) { ++ auth = "AWS_SIGV4"; ++ result = Curl_output_aws_sigv4(conn, FALSE); ++ if(result) ++ return result; ++ } ++ ++ /* NOTE: this function should set 'done' TRUE, as the other auth ++ functions work that way */ ++ authstatus->done = TRUE; ++ } ++ + + if(auth) { + infof(data, "%s auth using %s with user '%s'\n", +diff -Naur curl-7.61.1.orig/include/curl/curl.h curl-7.61.1/include/curl/curl.h +--- curl-7.61.1.orig/include/curl/curl.h 2018-07-11 07:17:00.000000000 +0200 ++++ curl-7.61.1/include/curl/curl.h 2025-12-10 14:00:00.000000000 +0100 +@@ -710,6 +710,7 @@ + #define CURLAUTH_NTLM (((unsigned long)1)<<3) + #define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) + #define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) ++#define CURLAUTH_AWS_SIGV4 (((unsigned long)1)<<7) + #define CURLAUTH_BEARER (((unsigned long)1)<<6) + #define CURLAUTH_ONLY (((unsigned long)1)<<31) + #define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) +@@ -1856,6 +1857,9 @@ + /* Disallow specifying username/login in URL. */ + CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278), + ++ /* AWS HTTP V4 Signature */ ++ CINIT(AWS_SIGV4, STRINGPOINT, 279), ++ + CURLOPT_LASTENTRY /* the last unused */ + } CURLoption; + +--- curl-7.61.1.orig/lib/urldata.h 2018-07-11 07:17:00.000000000 +0200 ++++ curl-7.61.1/lib/urldata.h 2025-12-09 00:00:00.000000000 +0100 +@@ -1414,6 +1414,7 @@ + STRING_TLSAUTH_PASSWORD_PROXY, /* TLS auth */ + #endif + STRING_BEARER, /* , if used */ ++ STRING_AWS_SIGV4, /* , if used */ + #ifdef USE_UNIX_SOCKETS + STRING_UNIX_SOCKET_PATH, /* path to Unix socket, if used */ + #endif +diff -Naur curl-7.61.1.orig/lib/http_aws_sigv4.c curl-7.61.1/lib/http_aws_sigv4.c +--- curl-7.61.1.orig/lib/http_aws_sigv4.c 2025-12-09 07:00:00.000000000 +0100 ++++ curl-7.61.1/lib/http_aws_sigv4.c 2025-12-09 14:30:00.000000000 +0100 +@@ -192,8 +192,8 @@ + char *request_type = NULL; + 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 : ""; ++ const char *user = conn->user ? conn->user : ""; ++ const char *passwd = conn->passwd ? conn->passwd : ""; + char *secret = NULL; + unsigned char tmp_sign0[32] = {0}; + unsigned char tmp_sign1[32] = {0}; +@@ -399,7 +399,7 @@ + method = "OPTIONS"; + break; + case HTTPREQ_CUSTOM: +- method = data->set.customrequest; ++ method = data->set.str[STRING_CUSTOMREQUEST]; + break; + default: + method = "GET"; +@@ -406,6 +406,16 @@ + break; + } + ++ /* Extract query string from path if present */ ++ const char *query_str = NULL; ++ char *question_mark = strchr(data->state.path, '?'); ++ if(question_mark) { ++ query_str = question_mark + 1; ++ } ++ else { ++ query_str = ""; ++ } ++ + canonical_request = + curl_maprintf("%s\n" /* HTTPRequestMethod */ + "%s\n" /* CanonicalURI */ +@@ -414,8 +424,8 @@ + "%s\n" /* SignedHeaders */ + "%s", /* HashedRequestPayload in hex */ + method, +- data->state.up.path, +- data->state.up.query ? data->state.up.query : "", ++ data->state.path, ++ query_str, + canonical_headers, + signed_headers, + sha_hex); +@@ -488,8 +498,8 @@ + goto fail; + } + +- Curl_safefree(data->state.aptr.userpwd); +- data->state.aptr.userpwd = auth_headers; ++ Curl_safefree(conn->allocptr.userpwd); ++ conn->allocptr.userpwd = auth_headers; + data->state.authhost.done = TRUE; + ret = CURLE_OK; + +@@ -509,3 +519,5 @@ + free(secret); + return ret; + } ++ ++#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) */ +diff -Naur a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions +--- a/docs/libcurl/symbols-in-versions 2018-09-04 22:48:37.000000000 +0200 ++++ b/docs/libcurl/symbols-in-versions 2025-12-10 14:23:34.178991689 +0100 +@@ -15,6 +15,7 @@ + CURLAUTH_ANY 7.10.6 + CURLAUTH_ANYSAFE 7.10.6 + CURLAUTH_BASIC 7.10.6 ++CURLAUTH_AWS_SIGV4 7.61.1 + CURLAUTH_BEARER 7.61.0 + CURLAUTH_DIGEST 7.10.6 + CURLAUTH_DIGEST_IE 7.19.3 +@@ -344,6 +345,7 @@ + CURLOPT_ACCEPT_ENCODING 7.21.6 + CURLOPT_ADDRESS_SCOPE 7.19.0 + CURLOPT_APPEND 7.17.0 ++CURLOPT_AWS_SIGV4 7.61.1 + CURLOPT_AUTOREFERER 7.1 + CURLOPT_BUFFERSIZE 7.10 + CURLOPT_CAINFO 7.4.2 +diff -Naur a/lib/setopt.c b/lib/setopt.c +--- a/lib/setopt.c 2025-12-02 13:31:37.682747984 +0100 ++++ b/lib/setopt.c 2025-12-10 12:46:02.532544111 +0100 +@@ -618,6 +618,20 @@ + data->set.httpreq = HTTPREQ_POST_FORM; + data->set.opt_no_body = FALSE; /* this is implied */ + break; ++ ++ case CURLOPT_AWS_SIGV4: ++ /* ++ * String that is merged to some authentication ++ * parameters are used by the algorithm. ++ */ ++ result = Curl_setstropt(&data->set.str[STRING_AWS_SIGV4], ++ va_arg(param, char *)); ++ /* ++ * Basic been set by default it need to be unset here ++ */ ++ if(data->set.str[STRING_AWS_SIGV4]) ++ data->set.httpauth = CURLAUTH_AWS_SIGV4; ++ break; + #endif /* CURL_DISABLE_HTTP */ + + case CURLOPT_MIMEPOST: +diff -Naur a/src/tool_cfgable.c b/src/tool_cfgable.c +--- a/src/tool_cfgable.c 2025-12-02 13:31:37.707011250 +0100 ++++ b/src/tool_cfgable.c 2025-12-10 12:33:54.247212425 +0100 +@@ -130,6 +130,7 @@ + Curl_safefree(config->krblevel); + + Curl_safefree(config->oauth_bearer); ++ Curl_safefree(config->aws_sigv4); + + Curl_safefree(config->unix_socket_path); + Curl_safefree(config->writeout); +diff -Naur a/src/tool_cfgable.h b/src/tool_cfgable.h +--- a/src/tool_cfgable.h 2025-12-02 13:31:37.707011250 +0100 ++++ b/src/tool_cfgable.h 2025-12-10 12:33:38.542315604 +0100 +@@ -240,6 +240,7 @@ + bool test_event_based; + #endif + char *oauth_bearer; /* OAuth 2.0 bearer token */ ++ char *aws_sigv4; /* AWS Signature Version 4 */ + bool nonpn; /* enable/disable TLS NPN extension */ + bool noalpn; /* enable/disable TLS ALPN extension */ + char *unix_socket_path; /* path to Unix domain socket */ +diff -Naur a/src/tool_getparam.c b/src/tool_getparam.c +--- a/src/tool_getparam.c 2025-12-02 13:31:37.711748615 +0100 ++++ b/src/tool_getparam.c 2025-12-10 12:36:24.374195746 +0100 +@@ -79,6 +79,7 @@ + {"*a", "random-file", ARG_FILENAME}, + {"*b", "egd-file", ARG_STRING}, + {"*B", "oauth2-bearer", ARG_STRING}, ++ {"*V", "aws-sigv4", ARG_STRING}, + {"*c", "connect-timeout", ARG_STRING}, + {"*d", "ciphers", ARG_STRING}, + {"*D", "dns-interface", ARG_STRING}, +@@ -813,6 +814,10 @@ + config->disable_eprt = toggle; + break; + case 'Z': /* --eprt */ ++ case 'V': /* --aws-sigv4 */ ++ GetStr(&config->aws_sigv4, nextarg); ++ config->authtype |= CURLAUTH_AWS_SIGV4; ++ break; + config->disable_eprt = (!toggle)?TRUE:FALSE; + break; + case '~': /* --xattr */ +diff -Naur a/src/tool_help.c b/src/tool_help.c +--- a/src/tool_help.c 2025-12-02 13:31:37.709748572 +0100 ++++ b/src/tool_help.c 2025-12-10 12:39:33.200888878 +0100 +@@ -52,6 +52,8 @@ + "Pick any authentication method"}, + {"-a, --append", + "Append to target file when uploading"}, ++ {" --aws-sigv4 ", ++ "Use AWS V4 signature authentication"}, + {" --basic", + "Use HTTP Basic Authentication"}, + {" --cacert ", +diff -Naur a/src/tool_operate.c b/src/tool_operate.c +--- a/src/tool_operate.c 2025-12-02 13:31:37.711748615 +0100 ++++ b/src/tool_operate.c 2025-12-10 12:40:10.466140413 +0100 +@@ -1136,6 +1136,8 @@ + my_setopt_str(curl, CURLOPT_SSLKEYTYPE, config->key_type); + my_setopt_str(curl, CURLOPT_PROXY_SSLKEYTYPE, + config->proxy_key_type); ++ my_setopt_str(curl, CURLOPT_AWS_SIGV4, ++ config->aws_sigv4); + + if(config->insecure_ok) { + my_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); +diff -Naur a/docs/curl.1 b/docs/curl.1 +--- a/docs/curl.1 2018-09-05 08:15:48.000000000 +0200 ++++ b/docs/curl.1 2025-12-10 12:00:00.000000000 +0100 +@@ -163,6 +163,12 @@ + See also \fI--proxy-anyauth\fP and \fI--basic\fP and \fI--digest\fP. + .IP "-a, --append" + (FTP SFTP) When used in an upload, this makes curl append to the target file instead of ++.IP "--aws-sigv4 " ++(HTTP) Use AWS Signature Version 4 authentication for the specified service and region. ++The service is the AWS service name (such as s3) and the region is the geographical ++AWS region (such as us-east-1). The credentials must be provided via \fI-u, --user\fP. ++ ++If this option is used several times, the last one will be used. + overwriting it. If the remote file doesn't exist, it will be created. Note + that this flag is ignored by some SFTP servers (including OpenSSH). + .IP "--basic" +diff -Naur a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 +--- a/docs/libcurl/curl_easy_setopt.3 2018-09-05 08:15:48.000000000 +0200 ++++ b/docs/libcurl/curl_easy_setopt.3 2025-12-10 12:00:00.000000000 +0100 +@@ -262,6 +262,8 @@ + .IP CURLOPT_DISALLOW_USERNAME_IN_URL + Don't allow username in URL. See \fICURLOPT_DISALLOW_USERNAME_IN_URL(3)\fP + .SH HTTP OPTIONS ++.IP CURLOPT_AWS_SIGV4 ++Set AWS Signature Version 4 authentication. See \fICURLOPT_AWS_SIGV4(3)\fP + .IP CURLOPT_AUTOREFERER + Automatically set Referer: header. See \fICURLOPT_AUTOREFERER(3)\fP + .IP CURLOPT_ACCEPT_ENCODING +diff -Naur a/docs/libcurl/opts/CURLOPT_AWS_SIGV4.3 b/docs/libcurl/opts/CURLOPT_AWS_SIGV4.3 +--- a/docs/libcurl/opts/CURLOPT_AWS_SIGV4.3 1970-01-01 00:00:00.000000000 +0000 ++++ b/docs/libcurl/opts/CURLOPT_AWS_SIGV4.3 2025-12-10 12:00:00.000000000 +0100 +@@ -0,0 +1,78 @@ ++.\" ************************************************************************** ++.\" * _ _ ____ _ ++.\" * Project ___| | | | _ \| | ++.\" * / __| | | | |_) | | ++.\" * | (__| |_| | _ <| |___ ++.\" * \___\|\___/|_| \_\_____| ++.\" * ++.\" * Copyright (C) 1998 - 2018, Daniel Stenberg, , 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. ++.\" * ++.\" ************************************************************************** ++.\" ++.TH CURLOPT_AWS_SIGV4 3 "December 10, 2025" "libcurl 7.61.1" "curl_easy_setopt options" ++ ++.SH NAME ++CURLOPT_AWS_SIGV4 \- set AWS Signature Version 4 authentication ++.SH SYNOPSIS ++.nf ++#include ++ ++CURLcode curl_easy_setopt(CURL *handle, CURLOPT_AWS_SIGV4, char *param); ++.SH DESCRIPTION ++Pass a char * that is the collection of service and region parameters that ++are used to authorize and sign requests. ++ ++The format is: ++.B service:region ++where the ++.B service ++is the AWS service (for example, ++.B s3 ++) and the ++.B region ++is the geographical AWS region (for example, ++.B us-east-1 ++). ++ ++When this option is set, the HTTP request will be signed using AWS Signature ++Version 4. The request is signed using the credentials provided in the ++\fICURLOPT_USERNAME(3)\fP and \fICURLOPT_PASSWORD(3)\fP options, which ++correspond to the AWS Access Key ID and Secret Access Key, respectively. ++ ++The application does not have to keep the string around after setting this ++option. ++.SH DEFAULT ++NULL ++.SH PROTOCOLS ++HTTP ++.SH EXAMPLE ++.nf ++CURL *curl = curl_easy_init(); ++if(curl) { ++ curl_easy_setopt(curl, CURLOPT_URL, ++ "https://s3.us-east-1.amazonaws.com/bucket/file"); ++ curl_easy_setopt(curl, CURLOPT_AWS_SIGV4, "s3:us-east-1"); ++ curl_easy_setopt(curl, CURLOPT_USERNAME, "AKIAIOSFODNN7EXAMPLE"); ++ curl_easy_setopt(curl, CURLOPT_PASSWORD, ++ "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"); ++ curl_easy_perform(curl); ++} ++.SH AVAILABILITY ++Added in 7.61.1 ++.SH RETURN VALUE ++Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not. ++.SH "SEE ALSO" ++.BR CURLOPT_HTTPAUTH "(3), " ++.BR CURLOPT_USERNAME "(3), " ++.BR CURLOPT_PASSWORD "(3), " diff --git a/curl.spec b/curl.spec index 5939ad0..7d8ddda 100644 --- a/curl.spec +++ b/curl.spec @@ -1,7 +1,7 @@ Summary: A utility for getting files from remote servers (FTP, HTTP, and others) Name: curl Version: 7.61.1 -Release: 34%{?dist}.9 +Release: 34%{?dist}.10 License: MIT Source: https://curl.haxx.se/download/%{name}-%{version}.tar.xz @@ -199,6 +199,9 @@ Patch67: 0067-curl-7.61.1-ntlm-force-http-1-1.patch # cookie: don't treat the leading slash as trailing (CVE-2025-9086) Patch68: 0068-curl-7.61.1-CVE-2025-9086.patch +# AWS Signature Version 4 authentication support +Patch69: 0069-curl-7.61.1-aws-sigv4.patch + # patch making libcurl multilib ready Patch101: 0101-curl-7.32.0-multilib.patch @@ -440,6 +443,7 @@ git apply %{PATCH52} %patch -P 66 -p1 %patch -P 67 -p1 %patch -P 68 -p1 +%patch -P 69 -p1 # make tests/*.py use Python 3 sed -e '1 s|^#!/.*python|#!%{__python3}|' -i tests/*.py @@ -602,6 +606,9 @@ rm -f ${RPM_BUILD_ROOT}%{_libdir}/libcurl.la %{_libdir}/libcurl.so.4.[0-9].[0-9].minimal %changelog +* Wed Dec 03 2025 Jacek Migacz - 7.61.1-34.el8_10.10 +- AWS Signature Version 4 authentication support (RHEL-116183) + * Fri Oct 24 2025 Jacek Migacz - 7.61.1-34.el8_10.9 - cookie: don't treat the leading slash as trailing (CVE-2025-9086) Resolves: RHEL-121655