From: Andrei Florea Date: Wed, 2 Apr 2025 07:41:54 +0000 (+0200) Subject: TLS: add CURLOPT_SSL_SIGNATURE_ALGORITHMS and --sigalgs X-Git-Tag: curl-8_14_0~167 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=a638828c887df840cdc8028c198d50dfb81860a8;p=thirdparty%2Fcurl.git TLS: add CURLOPT_SSL_SIGNATURE_ALGORITHMS and --sigalgs Fixes #12982 Closes #16964 --- diff --git a/docs/TODO b/docs/TODO index 90d13665c8..09ad092e9d 100644 --- a/docs/TODO +++ b/docs/TODO @@ -122,7 +122,6 @@ 13.11 Some TLS options are not offered for HTTPS proxies 13.13 Make sure we forbid TLS 1.3 post-handshake authentication 13.14 Support the clienthello extension - 13.15 Select signature algorithms 13.16 Share the CA cache 13.17 Add missing features to TLS backends @@ -900,14 +899,6 @@ https://datatracker.ietf.org/doc/html/rfc7685 https://github.com/curl/curl/issues/2299 -13.15 Select signature algorithms - - Consider adding an option or a way for users to select TLS signature - algorithm. The signature algorithms set by a client are used directly in the - supported signature algorithm in the client hello message. - - https://github.com/curl/curl/issues/12982 - 13.16 Share the CA cache For TLS backends that supports CA caching, it makes sense to allow the share diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc index 3cc44c9896..82e64daa88 100644 --- a/docs/cmdline-opts/Makefile.inc +++ b/docs/cmdline-opts/Makefile.inc @@ -253,6 +253,7 @@ DPAGES = \ show-error.md \ show-headers.md \ silent.md \ + sigalgs.md \ skip-existing.md \ socks4.md \ socks4a.md \ diff --git a/docs/cmdline-opts/sigalgs.md b/docs/cmdline-opts/sigalgs.md new file mode 100644 index 0000000000..537b34f3ad --- /dev/null +++ b/docs/cmdline-opts/sigalgs.md @@ -0,0 +1,33 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Long: sigalgs +Arg: +Help: TLS signature algorithms to use +Protocols: TLS +Added: 8.14.0 +Category: tls +Multi: single +See-also: + - ciphers +Example: + - --sigalgs ecdsa_secp256r1_sha256 $URL +--- + +# `--sigalgs` + +Set specific signature algorithms to use during SSL session establishment according to RFC +5246, 7.4.1.4.1. + +An algorithm can use either a signature algorithm and a hash algorithm pair separated by a +`+` (e.g. `ECDSA+SHA224`), or its TLS 1.3 signature scheme name (e.g. `ed25519`). + +Multiple algorithms can be provided by separating them with `:` +(e.g. `DSA+SHA256:rsa_pss_pss_sha256`). The parameter is available as `-sigalgs` in the +OpenSSL `s_client` and `s_server` utilities. + +`--sigalgs` allows a OpenSSL powered curl to make SSL-connections with exactly +the signature algorithms requested by the client, avoiding nontransparent client/server +negotiations. + +If this option is set, the default signature algorithm list built into OpenSSL are ignored. diff --git a/docs/libcurl/curl_easy_setopt.md b/docs/libcurl/curl_easy_setopt.md index bf5f90e25f..8a5390f9f0 100644 --- a/docs/libcurl/curl_easy_setopt.md +++ b/docs/libcurl/curl_easy_setopt.md @@ -1128,6 +1128,10 @@ Control SSL behavior. See CURLOPT_SSL_OPTIONS(3) Disable SSL session-id cache. See CURLOPT_SSL_SESSIONID_CACHE(3) +## CURLOPT_SSL_SIGNATURE_ALGORITHMS + +TLS signature algorithms to use. See CURLOPT_SSL_SIGNATURE_ALGORITHMS(3) + ## CURLOPT_SSL_VERIFYHOST Verify the hostname in the SSL certificate. See CURLOPT_SSL_VERIFYHOST(3) diff --git a/docs/libcurl/opts/CURLOPT_SSL_SIGNATURE_ALGORITHMS.md b/docs/libcurl/opts/CURLOPT_SSL_SIGNATURE_ALGORITHMS.md new file mode 100644 index 0000000000..4fce470bf8 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_SSL_SIGNATURE_ALGORITHMS.md @@ -0,0 +1,84 @@ +--- +c: Copyright (C) Daniel Stenberg, , et al. +SPDX-License-Identifier: curl +Title: CURLOPT_SSL_SIGNATURE_ALGORITHMS +Section: 3 +Source: libcurl +See-also: + - CURLOPT_SSL_CIPHER_LIST (3) + - CURLOPT_SSL_EC_CURVES (3) + - CURLOPT_SSLVERSION (3) + - CURLOPT_USE_SSL (3) +Protocol: + - TLS +TLS-backend: + - OpenSSL +Added-in: 8.14.0 +--- + +# NAME + +CURLOPT_SSL_SIGNATURE_ALGORITHMS - signature algorithms to use for TLS + +# SYNOPSIS + +~~~c +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SSL_SIGNATURE_ALGORITHMS, char *list); +~~~ + +# DESCRIPTION + +Pass a char pointer, pointing to a null-terminated string holding the list of +signature algorithms to use for the TLS connection. The list must be syntactically +correct, it consists of one or more signature algorithm strings separated by colons. + +A valid example of a signature algorithms list with OpenSSL is: +~~~ +"DSA+SHA256:rsa_pss_pss_sha256" +~~~ + +The application does not have to keep the string around after setting this +option. + +Using this option multiple times makes the last set string override the +previous ones. Set it to NULL to disable its use again. + +Works with OpenSSL and its BoringSSL fork (added in 8.14.0). + +# DEFAULT + +NULL, use built-in list + +# %PROTOCOLS% + +# EXAMPLE + +~~~c +int main(void) +{ + CURL *curl = curl_easy_init(); + if(curl) { + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); + curl_easy_setopt(curl, CURLOPT_SSL_SIGNATURE_ALGORITHMS, + "DSA+SHA256:rsa_pss_pss_sha256"); + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + } +} +~~~ + +# HISTORY + +OpenSSL support added in 8.14.0. + +# %AVAILABILITY% + +# RETURN VALUE + +curl_easy_setopt(3) returns a CURLcode indicating success or error. + +CURLE_OK (0) means everything was OK, non-zero means an error occurred, see +libcurl-errors(3). diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 24f668cb07..794f47b9f3 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -367,6 +367,7 @@ man_MANS = \ CURLOPT_SSL_FALSESTART.3 \ CURLOPT_SSL_OPTIONS.3 \ CURLOPT_SSL_SESSIONID_CACHE.3 \ + CURLOPT_SSL_SIGNATURE_ALGORITHMS.3 \ CURLOPT_SSL_VERIFYHOST.3 \ CURLOPT_SSL_VERIFYPEER.3 \ CURLOPT_SSL_VERIFYSTATUS.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 66b0b7c27f..e757219a73 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -849,6 +849,7 @@ CURLOPT_SSL_ENABLE_NPN 7.36.0 7.86.0 CURLOPT_SSL_FALSESTART 7.42.0 CURLOPT_SSL_OPTIONS 7.25.0 CURLOPT_SSL_SESSIONID_CACHE 7.16.0 +CURLOPT_SSL_SIGNATURE_ALGORITHMS 8.14.0 CURLOPT_SSL_VERIFYHOST 7.8.1 CURLOPT_SSL_VERIFYPEER 7.4.2 CURLOPT_SSL_VERIFYSTATUS 7.41.0 diff --git a/docs/options-in-versions b/docs/options-in-versions index a4ce20093a..a9ef8db047 100644 --- a/docs/options-in-versions +++ b/docs/options-in-versions @@ -218,6 +218,7 @@ --show-error (-S) 5.9 --show-headers (-i) 4.8 --silent (-s) 4.0 +--sigalgs 8.14.0 --skip-existing 8.10.0 --socks4 7.15.2 --socks4a 7.18.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index 84966e81e4..4f103d94f6 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -2258,6 +2258,9 @@ typedef enum { CURLOPT(CURLOPT_UPLOAD_FLAGS, CURLOPTTYPE_LONG, 327), + /* set TLS supported signature algorithms */ + CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h index 27e22b6984..6415269646 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -402,6 +402,8 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_SSLKEY || \ (option) == CURLOPT_SSLKEYTYPE || \ (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_SSL_EC_CURVES || \ + (option) == CURLOPT_SSL_SIGNATURE_ALGORITHMS || \ (option) == CURLOPT_TLS13_CIPHERS || \ (option) == CURLOPT_TLSAUTH_PASSWORD || \ (option) == CURLOPT_TLSAUTH_TYPE || \ @@ -413,7 +415,6 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_AWS_SIGV4 || \ (option) == CURLOPT_USERPWD || \ (option) == CURLOPT_XOAUTH2_BEARER || \ - (option) == CURLOPT_SSL_EC_CURVES || \ 0) /* evaluates to true if option takes a curl_write_callback argument */ diff --git a/lib/easyoptions.c b/lib/easyoptions.c index 860aa4c2ba..03d676df0e 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -317,6 +317,8 @@ const struct curl_easyoption Curl_easyopts[] = { {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0}, {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0}, {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0}, + {"SSL_SIGNATURE_ALGORITHMS", CURLOPT_SSL_SIGNATURE_ALGORITHMS, + CURLOT_STRING, 0}, {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0}, {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0}, {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0}, @@ -378,6 +380,6 @@ const struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return (CURLOPT_LASTENTRY % 10000) != (327 + 1); + return (CURLOPT_LASTENTRY % 10000) != (328 + 1); } #endif diff --git a/lib/setopt.c b/lib/setopt.c index ee3fff8a1f..394dee1a4f 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2393,6 +2393,15 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, */ return Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], ptr); + case CURLOPT_SSL_SIGNATURE_ALGORITHMS: + /* + * Set accepted signature algorithms. + * Specify colon-delimited list of signature scheme names. + */ + if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS)) + return Curl_setstropt(&data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS], + ptr); + return CURLE_NOT_BUILT_IN; #endif #ifdef USE_SSH case CURLOPT_SSH_PUBLIC_KEYFILE: diff --git a/lib/urldata.h b/lib/urldata.h index e3224ebd16..7a8997d3fd 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -265,6 +265,7 @@ struct ssl_primary_config { char *clientcert; char *cipher_list; /* list of ciphers to use */ char *cipher_list13; /* list of TLS 1.3 cipher suites to use */ + char *signature_algorithms; /* list of signature algorithms to use */ char *pinned_key; char *CRLfile; /* CRL to check certificate revocation */ struct curl_blob *cert_blob; @@ -1474,6 +1475,7 @@ enum dupstring { #endif STRING_ECH_CONFIG, /* CURLOPT_ECH_CONFIG */ STRING_ECH_PUBLIC, /* CURLOPT_ECH_PUBLIC */ + STRING_SSL_SIGNATURE_ALGORITHMS, /* CURLOPT_SSL_SIGNATURE_ALGORITHMS */ /* -- end of null-terminated strings -- */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 78aacb05fc..90f1f463cd 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -197,6 +197,16 @@ static void ossl_provider_cleanup(struct Curl_easy *data); #endif #endif +/* Whether SSL_CTX_set1_sigalgs_list is available + * OpenSSL: supported since 1.0.2 (commit 0b362de5f575) + * BoringSSL: supported since 0.20240913.0 (commit 826ce15) + * LibreSSL: no + */ +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L && \ + !defined(LIBRESSL_VERSION_NUMBER)) + #define HAVE_SSL_CTX_SET1_SIGALGS +#endif + #ifdef LIBRESSL_VERSION_NUMBER #define OSSL_PACKAGE "LibreSSL" #elif defined(OPENSSL_IS_BORINGSSL) @@ -3870,6 +3880,21 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } } +#ifdef HAVE_SSL_CTX_SET1_SIGALGS +#define OSSL_SIGALG_CAST(x) OSSL_CURVE_CAST(x) + { + const char *signature_algorithms = conn_config->signature_algorithms; + if(signature_algorithms) { + if(!SSL_CTX_set1_sigalgs_list(octx->ssl_ctx, + OSSL_SIGALG_CAST(signature_algorithms))) { + failf(data, "failed setting signature algorithms: '%s'", + signature_algorithms); + return CURLE_SSL_CIPHER; + } + } + } +#endif + #ifdef USE_OPENSSL_SRP if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { char * const ssl_username = ssl_config->primary.username; @@ -5524,6 +5549,9 @@ const struct Curl_ssl Curl_ssl_openssl = { #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES SSLSUPP_TLS13_CIPHERSUITES | #endif +#ifdef HAVE_SSL_CTX_SET1_SIGALGS + SSLSUPP_SIGNATURE_ALGORITHMS | +#endif #ifdef USE_ECH_OPENSSL SSLSUPP_ECH | #endif diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 8893b1a0af..80b3759e43 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -215,6 +215,7 @@ match_ssl_primary_config(struct Curl_easy *data, strcasecompare(c1->cipher_list, c2->cipher_list) && strcasecompare(c1->cipher_list13, c2->cipher_list13) && strcasecompare(c1->curves, c2->curves) && + strcasecompare(c1->signature_algorithms, c2->signature_algorithms) && strcasecompare(c1->CRLfile, c2->CRLfile) && strcasecompare(c1->pinned_key, c2->pinned_key)) return TRUE; @@ -259,6 +260,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, CLONE_STRING(cipher_list13); CLONE_STRING(pinned_key); CLONE_STRING(curves); + CLONE_STRING(signature_algorithms); CLONE_STRING(CRLfile); #ifdef USE_TLS_SRP CLONE_STRING(username); @@ -281,6 +283,7 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc) Curl_safefree(sslc->ca_info_blob); Curl_safefree(sslc->issuercert_blob); Curl_safefree(sslc->curves); + Curl_safefree(sslc->signature_algorithms); Curl_safefree(sslc->CRLfile); #ifdef USE_TLS_SRP Curl_safefree(sslc->username); @@ -299,6 +302,8 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) data->set.str[STRING_SSL_CIPHER_LIST]; data->set.ssl.primary.cipher_list13 = data->set.str[STRING_SSL_CIPHER13_LIST]; + data->set.ssl.primary.signature_algorithms = + data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS]; data->set.ssl.primary.pinned_key = data->set.str[STRING_SSL_PINNEDPUBLICKEY]; data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT]; diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 0302151bb5..a52d96fffa 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -42,6 +42,7 @@ struct dynbuf; #define SSLSUPP_ECH (1<<7) #define SSLSUPP_CA_CACHE (1<<8) #define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */ +#define SSLSUPP_SIGNATURE_ALGORITHMS (1<<10) /* supports TLS sigalgs */ #ifdef USE_ECH # include "../curl_base64.h" diff --git a/packages/OS400/README.OS400 b/packages/OS400/README.OS400 index f3202ae905..51bd26fb10 100644 --- a/packages/OS400/README.OS400 +++ b/packages/OS400/README.OS400 @@ -127,6 +127,7 @@ options: CURLOPT_SSLKEYTYPE CURLOPT_SSL_CIPHER_LIST CURLOPT_SSL_EC_CURVES + CURLOPT_SSL_SIGNATURE_ALGORIHMS CURLOPT_TLS13_CIPHERS CURLOPT_TLSAUTH_PASSWORD CURLOPT_TLSAUTH_TYPE diff --git a/packages/OS400/ccsidcurl.c b/packages/OS400/ccsidcurl.c index ccd397f0e6..a9c04d5a42 100644 --- a/packages/OS400/ccsidcurl.c +++ b/packages/OS400/ccsidcurl.c @@ -1164,6 +1164,7 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...) case CURLOPT_SSLKEYTYPE: case CURLOPT_SSL_CIPHER_LIST: case CURLOPT_SSL_EC_CURVES: + case CURLOPT_SSL_SIGNATURE_ALGORITHMS: case CURLOPT_TLS13_CIPHERS: case CURLOPT_TLSAUTH_PASSWORD: case CURLOPT_TLSAUTH_TYPE: diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h index 4067490fab..ed49f759ed 100644 --- a/src/tool_cfgable.h +++ b/src/tool_cfgable.h @@ -135,6 +135,7 @@ struct OperationConfig { char *etag_compare_file; char *customrequest; char *ssl_ec_curves; + char *ssl_signature_algorithms; char *krblevel; char *request_target; char *writeout; /* %-styled format string to output */ diff --git a/src/tool_getparam.c b/src/tool_getparam.c index e1d2864975..47e404c08e 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -305,6 +305,8 @@ static const struct LongShort aliases[]= { {"sessionid", ARG_BOOL|ARG_NO, ' ', C_SESSIONID}, {"show-error", ARG_BOOL, 'S', C_SHOW_ERROR}, {"show-headers", ARG_BOOL, 'i', C_SHOW_HEADERS}, + {"sigalgs", ARG_STRG|ARG_TLS, ' ', + C_SIGNATURE_ALGORITHMS}, {"silent", ARG_BOOL, 's', C_SILENT}, {"skip-existing", ARG_BOOL, ' ', C_SKIP_EXISTING}, {"socks4", ARG_STRG, ' ', C_SOCKS4}, @@ -2692,6 +2694,9 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */ case C_CURVES: /* --curves */ err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK); break; + case C_SIGNATURE_ALGORITHMS: /* --sigalgs */ + err = getstr(&config->ssl_signature_algorithms, nextarg, DENY_BLANK); + break; case C_FAIL_EARLY: /* --fail-early */ global->fail_early = toggle; break; diff --git a/src/tool_getparam.h b/src/tool_getparam.h index d770e2695b..b0f924816c 100644 --- a/src/tool_getparam.h +++ b/src/tool_getparam.h @@ -242,6 +242,7 @@ typedef enum { C_SHOW_ERROR, C_SHOW_HEADERS, C_SILENT, + C_SIGNATURE_ALGORITHMS, C_SKIP_EXISTING, C_SOCKS4, C_SOCKS4A, diff --git a/src/tool_listhelp.c b/src/tool_listhelp.c index b69cd89e19..785f4cd936 100644 --- a/src/tool_listhelp.c +++ b/src/tool_listhelp.c @@ -659,6 +659,9 @@ const struct helptxt helptext[] = { {"-i, --show-headers", "Show response headers in output", CURLHELP_IMPORTANT | CURLHELP_VERBOSE | CURLHELP_OUTPUT}, + {" --sigalgs ", + "TLS signature algorithms to use", + CURLHELP_TLS}, {"-s, --silent", "Silent mode", CURLHELP_IMPORTANT | CURLHELP_VERBOSE}, diff --git a/src/tool_operate.c b/src/tool_operate.c index 426d1de347..b50a68545b 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1263,6 +1263,10 @@ static CURLcode config2setopts(struct GlobalConfig *global, if(config->ssl_ec_curves) my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves); + if(config->ssl_signature_algorithms) + my_setopt_str(curl, CURLOPT_SSL_SIGNATURE_ALGORITHMS, + config->ssl_signature_algorithms); + if(config->writeout) my_setopt_long(curl, CURLOPT_CERTINFO, 1);