From 6268b094ebc288645bbaf0922070b31b52854cf5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 16:06:28 +0900 Subject: [PATCH 01/23] resolve: fix segfault when built with OPENSSL_NO_DEPRECATED_3_0 In that case, deprecated funcdions are not loaded from libcrypto.so, and calling them causes segfault. --- src/resolve/resolved-dns-dnssec.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 36eb3595d5..6e5b4a0e02 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -79,9 +79,11 @@ static int dnssec_rsa_verify_raw( const void *data, size_t data_size, const void *exponent, size_t exponent_size, const void *modulus, size_t modulus_size) { + +#if !defined(OPENSSL_NO_DEPRECATED_3_0) + DISABLE_WARNING_DEPRECATED_DECLARATIONS; int r; - DISABLE_WARNING_DEPRECATED_DECLARATIONS; _cleanup_(RSA_freep) RSA *rpubkey = NULL; _cleanup_(EVP_PKEY_freep) EVP_PKEY *epubkey = NULL; _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL; @@ -134,6 +136,9 @@ static int dnssec_rsa_verify_raw( REENABLE_WARNING; return r; +#else + return -EOPNOTSUPP; +#endif } static int dnssec_rsa_verify( @@ -204,9 +209,11 @@ static int dnssec_ecdsa_verify_raw( const void *signature_s, size_t signature_s_size, const void *data, size_t data_size, const void *key, size_t key_size) { + +#if !defined(OPENSSL_NO_DEPRECATED_3_0) + DISABLE_WARNING_DEPRECATED_DECLARATIONS; int k; - DISABLE_WARNING_DEPRECATED_DECLARATIONS; _cleanup_(EC_GROUP_freep) EC_GROUP *ec_group = NULL; _cleanup_(EC_POINT_freep) EC_POINT *p = NULL; _cleanup_(EC_KEY_freep) EC_KEY *eckey = NULL; @@ -268,6 +275,9 @@ static int dnssec_ecdsa_verify_raw( REENABLE_WARNING; return k; +#else + return -EOPNOTSUPP; +#endif } static int dnssec_ecdsa_verify( From 5fec7096cfa27760d0a7de7fd5587c33549e8ca9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 16:09:37 +0900 Subject: [PATCH 02/23] crypto-util: load several more symbols They will be used in later commits. Note, ECDSA_SIG_new() and ECDSA_SIG_set0() are not deprecated. They are moved to the section for non-deprecated symbols. --- src/shared/crypto-util.c | 36 ++++++++++++++++++++++++++++++------ src/shared/crypto-util.h | 18 ++++++++++++++++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 57da78b003..2810856888 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -100,6 +100,10 @@ DLSYM_PROTOTYPE(BN_set_word) = NULL; DLSYM_PROTOTYPE(BN_sub_word) = NULL; DLSYM_PROTOTYPE(CRYPTO_free) = NULL; DLSYM_PROTOTYPE(ECDSA_SIG_free) = NULL; +DLSYM_PROTOTYPE(ECDSA_SIG_get0_r) = NULL; +DLSYM_PROTOTYPE(ECDSA_SIG_get0_s) = NULL; +DLSYM_PROTOTYPE(ECDSA_SIG_new) = NULL; +DLSYM_PROTOTYPE(ECDSA_SIG_set0) = NULL; DLSYM_PROTOTYPE(EC_GROUP_free) = NULL; DLSYM_PROTOTYPE(EC_GROUP_get0_generator) = NULL; DLSYM_PROTOTYPE(EC_GROUP_get0_order) = NULL; @@ -170,7 +174,8 @@ DLSYM_PROTOTYPE(EVP_PKEY_CTX_new) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_from_name) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_id) = NULL; static DLSYM_PROTOTYPE(EVP_PKEY_CTX_set0_rsa_oaep_label) = NULL; -static DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_rsa_keygen_bits) = NULL; static DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_rsa_oaep_md) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_rsa_padding) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_signature_md) = NULL; @@ -183,17 +188,23 @@ DLSYM_PROTOTYPE(EVP_PKEY_eq) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_free) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_fromdata) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_generate) = NULL; static DLSYM_PROTOTYPE(EVP_PKEY_get1_encoded_public_key) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_get_base_id) = NULL; static DLSYM_PROTOTYPE(EVP_PKEY_get_bits) = NULL; -static DLSYM_PROTOTYPE(EVP_PKEY_get_bn_param) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_get_bn_param) = NULL; static DLSYM_PROTOTYPE(EVP_PKEY_get_group_name) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_get_id) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_get_octet_string_param) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_get_size) = NULL; static DLSYM_PROTOTYPE(EVP_PKEY_get_utf8_string_param) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_keygen) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_keygen_init) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_new) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_new_raw_public_key) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_public_check) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_sign) = NULL; +DLSYM_PROTOTYPE(EVP_PKEY_sign_init) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_verify) = NULL; DLSYM_PROTOTYPE(EVP_PKEY_verify_init) = NULL; DLSYM_PROTOTYPE(EVP_aes_256_ctr) = NULL; @@ -217,6 +228,7 @@ DLSYM_PROTOTYPE(OPENSSL_sk_value) = NULL; DLSYM_PROTOTYPE(OSSL_EC_curve_nid2name) = NULL; DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free) = NULL; DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new) = NULL; +DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_BN) = NULL; static DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_octet_string) = NULL; DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string) = NULL; DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param) = NULL; @@ -284,11 +296,13 @@ static DLSYM_PROTOTYPE(X509_get_signature_info) = NULL; DLSYM_PROTOTYPE(X509_get_subject_name) = NULL; DLSYM_PROTOTYPE(X509_gmtime_adj) = NULL; DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING) = NULL; +DLSYM_PROTOTYPE(d2i_ECDSA_SIG) = NULL; DLSYM_PROTOTYPE(d2i_ECPKParameters) = NULL; DLSYM_PROTOTYPE(d2i_PKCS7) = NULL; DLSYM_PROTOTYPE(d2i_PUBKEY) = NULL; DLSYM_PROTOTYPE(d2i_X509) = NULL; DLSYM_PROTOTYPE(i2d_ASN1_INTEGER) = NULL; +DLSYM_PROTOTYPE(i2d_ECDSA_SIG) = NULL; DLSYM_PROTOTYPE(i2d_PKCS7) = NULL; DLSYM_PROTOTYPE(i2d_PKCS7_fp) = NULL; DLSYM_PROTOTYPE(i2d_PUBKEY) = NULL; @@ -315,8 +329,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ENGINE*, sym_ENGINE_free, ENGINE_freep, #if !defined(OPENSSL_NO_DEPRECATED_3_0) DISABLE_WARNING_DEPRECATED_DECLARATIONS; -DLSYM_PROTOTYPE(ECDSA_SIG_new) = NULL; -DLSYM_PROTOTYPE(ECDSA_SIG_set0) = NULL; DLSYM_PROTOTYPE(ECDSA_do_verify) = NULL; DLSYM_PROTOTYPE(EC_KEY_check_key) = NULL; DLSYM_PROTOTYPE(EC_KEY_free) = NULL; @@ -428,6 +440,10 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(BN_sub_word), DLSYM_ARG(CRYPTO_free), DLSYM_ARG(ECDSA_SIG_free), + DLSYM_ARG(ECDSA_SIG_get0_r), + DLSYM_ARG(ECDSA_SIG_get0_s), + DLSYM_ARG(ECDSA_SIG_new), + DLSYM_ARG(ECDSA_SIG_set0), DLSYM_ARG(EC_GROUP_free), DLSYM_ARG(EC_GROUP_get0_generator), DLSYM_ARG(EC_GROUP_get0_order), @@ -499,6 +515,7 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(EVP_PKEY_CTX_new_id), DLSYM_ARG(EVP_PKEY_CTX_set0_rsa_oaep_label), DLSYM_ARG(EVP_PKEY_CTX_set_ec_paramgen_curve_nid), + DLSYM_ARG(EVP_PKEY_CTX_set_rsa_keygen_bits), DLSYM_ARG(EVP_PKEY_CTX_set_rsa_oaep_md), DLSYM_ARG(EVP_PKEY_CTX_set_rsa_padding), DLSYM_ARG(EVP_PKEY_CTX_set_signature_md), @@ -511,17 +528,23 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(EVP_PKEY_free), DLSYM_ARG(EVP_PKEY_fromdata), DLSYM_ARG(EVP_PKEY_fromdata_init), + DLSYM_ARG(EVP_PKEY_generate), DLSYM_ARG(EVP_PKEY_get1_encoded_public_key), DLSYM_ARG(EVP_PKEY_get_base_id), DLSYM_ARG(EVP_PKEY_get_bits), DLSYM_ARG(EVP_PKEY_get_bn_param), DLSYM_ARG(EVP_PKEY_get_group_name), DLSYM_ARG(EVP_PKEY_get_id), + DLSYM_ARG(EVP_PKEY_get_octet_string_param), + DLSYM_ARG(EVP_PKEY_get_size), DLSYM_ARG(EVP_PKEY_get_utf8_string_param), DLSYM_ARG(EVP_PKEY_keygen), DLSYM_ARG(EVP_PKEY_keygen_init), DLSYM_ARG(EVP_PKEY_new), DLSYM_ARG(EVP_PKEY_new_raw_public_key), + DLSYM_ARG(EVP_PKEY_public_check), + DLSYM_ARG(EVP_PKEY_sign), + DLSYM_ARG(EVP_PKEY_sign_init), DLSYM_ARG(EVP_PKEY_verify), DLSYM_ARG(EVP_PKEY_verify_init), DLSYM_ARG(EVP_aes_256_ctr), @@ -545,6 +568,7 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(OSSL_EC_curve_nid2name), DLSYM_ARG(OSSL_PARAM_BLD_free), DLSYM_ARG(OSSL_PARAM_BLD_new), + DLSYM_ARG(OSSL_PARAM_BLD_push_BN), DLSYM_ARG(OSSL_PARAM_BLD_push_octet_string), DLSYM_ARG(OSSL_PARAM_BLD_push_utf8_string), DLSYM_ARG(OSSL_PARAM_BLD_to_param), @@ -612,11 +636,13 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(X509_get_subject_name), DLSYM_ARG(X509_gmtime_adj), DLSYM_ARG(d2i_ASN1_OCTET_STRING), + DLSYM_ARG(d2i_ECDSA_SIG), DLSYM_ARG(d2i_ECPKParameters), DLSYM_ARG(d2i_PKCS7), DLSYM_ARG(d2i_PUBKEY), DLSYM_ARG(d2i_X509), DLSYM_ARG(i2d_ASN1_INTEGER), + DLSYM_ARG(i2d_ECDSA_SIG), DLSYM_ARG(i2d_PKCS7), DLSYM_ARG(i2d_PKCS7_fp), DLSYM_ARG(i2d_PUBKEY), @@ -631,8 +657,6 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG_FORCE(ENGINE_load_private_key), #endif #if !defined(OPENSSL_NO_DEPRECATED_3_0) - DLSYM_ARG_FORCE(ECDSA_SIG_new), - DLSYM_ARG_FORCE(ECDSA_SIG_set0), DLSYM_ARG_FORCE(ECDSA_do_verify), DLSYM_ARG_FORCE(EC_KEY_check_key), DLSYM_ARG_FORCE(EC_KEY_free), diff --git a/src/shared/crypto-util.h b/src/shared/crypto-util.h index 53da4cabe6..6ecf66bbf6 100644 --- a/src/shared/crypto-util.h +++ b/src/shared/crypto-util.h @@ -122,6 +122,10 @@ extern DLSYM_PROTOTYPE(BN_set_word); extern DLSYM_PROTOTYPE(BN_sub_word); extern DLSYM_PROTOTYPE(CRYPTO_free); extern DLSYM_PROTOTYPE(ECDSA_SIG_free); +extern DLSYM_PROTOTYPE(ECDSA_SIG_get0_r); +extern DLSYM_PROTOTYPE(ECDSA_SIG_get0_s); +extern DLSYM_PROTOTYPE(ECDSA_SIG_new); +extern DLSYM_PROTOTYPE(ECDSA_SIG_set0); extern DLSYM_PROTOTYPE(EC_GROUP_free); extern DLSYM_PROTOTYPE(EC_GROUP_get0_generator); extern DLSYM_PROTOTYPE(EC_GROUP_get0_order); @@ -175,18 +179,27 @@ extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_free); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_from_name); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_new_id); +extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_ec_paramgen_curve_nid); +extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_rsa_keygen_bits); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_rsa_padding); extern DLSYM_PROTOTYPE(EVP_PKEY_CTX_set_signature_md); extern DLSYM_PROTOTYPE(EVP_PKEY_eq); extern DLSYM_PROTOTYPE(EVP_PKEY_free); extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata); extern DLSYM_PROTOTYPE(EVP_PKEY_fromdata_init); +extern DLSYM_PROTOTYPE(EVP_PKEY_generate); extern DLSYM_PROTOTYPE(EVP_PKEY_get_base_id); +extern DLSYM_PROTOTYPE(EVP_PKEY_get_bn_param); extern DLSYM_PROTOTYPE(EVP_PKEY_get_id); +extern DLSYM_PROTOTYPE(EVP_PKEY_get_octet_string_param); +extern DLSYM_PROTOTYPE(EVP_PKEY_get_size); extern DLSYM_PROTOTYPE(EVP_PKEY_keygen); extern DLSYM_PROTOTYPE(EVP_PKEY_keygen_init); extern DLSYM_PROTOTYPE(EVP_PKEY_new); extern DLSYM_PROTOTYPE(EVP_PKEY_new_raw_public_key); +extern DLSYM_PROTOTYPE(EVP_PKEY_public_check); +extern DLSYM_PROTOTYPE(EVP_PKEY_sign); +extern DLSYM_PROTOTYPE(EVP_PKEY_sign_init); extern DLSYM_PROTOTYPE(EVP_PKEY_verify); extern DLSYM_PROTOTYPE(EVP_PKEY_verify_init); extern DLSYM_PROTOTYPE(EVP_aes_256_ctr); @@ -210,6 +223,7 @@ extern DLSYM_PROTOTYPE(OPENSSL_sk_value); extern DLSYM_PROTOTYPE(OSSL_EC_curve_nid2name); extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_free); extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_new); +extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_BN); extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_push_utf8_string); extern DLSYM_PROTOTYPE(OSSL_PARAM_BLD_to_param); extern DLSYM_PROTOTYPE(OSSL_PARAM_construct_BN); @@ -256,11 +270,13 @@ extern DLSYM_PROTOTYPE(X509_get_pubkey); extern DLSYM_PROTOTYPE(X509_get_subject_name); extern DLSYM_PROTOTYPE(X509_gmtime_adj); extern DLSYM_PROTOTYPE(d2i_ASN1_OCTET_STRING); +extern DLSYM_PROTOTYPE(d2i_ECDSA_SIG); extern DLSYM_PROTOTYPE(d2i_ECPKParameters); extern DLSYM_PROTOTYPE(d2i_PKCS7); extern DLSYM_PROTOTYPE(d2i_PUBKEY); extern DLSYM_PROTOTYPE(d2i_X509); extern DLSYM_PROTOTYPE(i2d_ASN1_INTEGER); +extern DLSYM_PROTOTYPE(i2d_ECDSA_SIG); extern DLSYM_PROTOTYPE(i2d_PKCS7); extern DLSYM_PROTOTYPE(i2d_PKCS7_fp); extern DLSYM_PROTOTYPE(i2d_PUBKEY); @@ -269,8 +285,6 @@ extern DLSYM_PROTOTYPE(i2d_X509_NAME); #if !defined(OPENSSL_NO_DEPRECATED_3_0) DISABLE_WARNING_DEPRECATED_DECLARATIONS; -extern DLSYM_PROTOTYPE(ECDSA_SIG_new); -extern DLSYM_PROTOTYPE(ECDSA_SIG_set0); extern DLSYM_PROTOTYPE(ECDSA_do_verify); extern DLSYM_PROTOTYPE(EC_KEY_check_key); extern DLSYM_PROTOTYPE(EC_KEY_free); From 6bf0540e936b469109dde304f05fd3bfb5e28c3c Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 04:34:56 +0900 Subject: [PATCH 03/23] resolve: add unit test for dnssec_rsa_verify_raw() --- src/resolve/meson.build | 4 + src/resolve/resolved-dns-dnssec-crypto.h | 16 ++ src/resolve/resolved-dns-dnssec.c | 4 +- src/resolve/test-dnssec-crypto.c | 260 +++++++++++++++++++++++ 4 files changed, 282 insertions(+), 2 deletions(-) create mode 100644 src/resolve/resolved-dns-dnssec-crypto.h create mode 100644 src/resolve/test-dnssec-crypto.c diff --git a/src/resolve/meson.build b/src/resolve/meson.build index f024b02047..019c4b7bb0 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -131,6 +131,10 @@ executables += [ 'sources' : files('test-dnssec-complex.c'), 'type' : 'manual', }, + resolve_test_template + { + 'sources' : files('test-dnssec-crypto.c'), + 'conditions' : ['HAVE_OPENSSL'], + }, resolve_test_template + { 'sources' : files('test-dns-search-domain.c'), }, diff --git a/src/resolve/resolved-dns-dnssec-crypto.h b/src/resolve/resolved-dns-dnssec-crypto.h new file mode 100644 index 0000000000..018bab7ba5 --- /dev/null +++ b/src/resolve/resolved-dns-dnssec-crypto.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "crypto-util.h" +#include "shared-forward.h" + +#if HAVE_OPENSSL + +int dnssec_rsa_verify_raw( + const EVP_MD *hash_algorithm, + const void *signature, size_t signature_size, + const void *data, size_t data_size, + const void *exponent, size_t exponent_size, + const void *modulus, size_t modulus_size); + +#endif diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 6e5b4a0e02..3e370d8d71 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -12,12 +12,12 @@ #include "memory-util.h" #include "memstream-util.h" #include "resolved-dns-dnssec.h" +#include "resolved-dns-dnssec-crypto.h" #include "sort-util.h" #include "string-table.h" #include "string-util.h" #include "time-util.h" - #define VERIFY_RRS_MAX 256 #define MAX_KEY_SIZE (32*1024) @@ -73,7 +73,7 @@ static int dnssec_verify_errno(int r) { return r == -EOPNOTSUPP ? -EIO : r; } -static int dnssec_rsa_verify_raw( +int dnssec_rsa_verify_raw( const EVP_MD *hash_algorithm, const void *signature, size_t signature_size, const void *data, size_t data_size, diff --git a/src/resolve/test-dnssec-crypto.c b/src/resolve/test-dnssec-crypto.c new file mode 100644 index 0000000000..66744795e0 --- /dev/null +++ b/src/resolve/test-dnssec-crypto.c @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#if HAVE_VALGRIND_VALGRIND_H +# include +#endif + +#include "hexdecoct.h" +#include "resolved-dns-dnssec-crypto.h" +#include "tests.h" + +static void iovec_dump_c(const char *name, const struct iovec *iov) { + assert(iovec_is_set(iov)); + + printf("static const uint8_t %s[] = {", name); + const uint8_t *buf = iov->iov_base; + for (size_t i = 0; i < iov->iov_len; i++) { + if (i % 8 == 0) + fputs("\n ", stdout); + else + putchar(' '); + + fputs("0x", stdout); + putchar(hexchar((buf[i] & 0xf0) >> 4)); + putchar(hexchar((buf[i] & 0x0f) >> 0)); + putchar(','); + } + puts("\n};\n"); +} + +static void export_bn(const BIGNUM *x, struct iovec *ret) { + assert(x); + assert(ret); + + _cleanup_(iovec_done) struct iovec iov = {}; + ASSERT_OK(iovec_alloc(sym_BN_num_bytes(x), &iov)); + ASSERT_EQ(sym_BN_bn2bin(x, iov.iov_base), (int) iov.iov_len); + + *ret = TAKE_STRUCT(iov); +} + +static const uint8_t test_signature_buf[] = { + 0xa6, 0x6c, 0x60, 0xec, 0x89, 0x90, 0x74, 0xba, + 0x9a, 0x6b, 0x7b, 0xbe, 0xd5, 0x46, 0xfb, 0x5c, + 0xa1, 0x80, 0x15, 0x4c, 0x98, 0x1e, 0xae, 0x13, + 0x41, 0xf1, 0xd3, 0x9e, 0xe6, 0x4f, 0x57, 0x47, + 0x4b, 0xef, 0x3e, 0xe3, 0xdf, 0xef, 0x5e, 0x2f, + 0x76, 0xb2, 0x4a, 0x65, 0x72, 0x81, 0x19, 0xd7, + 0x6e, 0x1c, 0xe7, 0xed, 0x4b, 0x36, 0xd1, 0x06, + 0xaa, 0x86, 0x05, 0xbe, 0x9c, 0x8b, 0x69, 0x80, + 0x30, 0x7f, 0x13, 0xb0, 0x6f, 0xc8, 0x53, 0x42, + 0x00, 0xe2, 0x9e, 0xc6, 0x64, 0xcf, 0x83, 0xac, + 0x83, 0x38, 0x9a, 0xe7, 0xdf, 0x4a, 0x80, 0x08, + 0x22, 0x74, 0x6c, 0x14, 0x20, 0xfe, 0x7d, 0x92, + 0x5f, 0x53, 0xa8, 0xb6, 0x59, 0xd5, 0xae, 0x08, + 0x11, 0x30, 0x0f, 0xf2, 0x5d, 0x3d, 0x69, 0xa6, + 0x94, 0xf5, 0xfa, 0x58, 0xe3, 0x70, 0x75, 0x5b, + 0x68, 0xb1, 0x47, 0x10, 0x72, 0xbf, 0x97, 0xc5, + 0x96, 0x97, 0x05, 0x9a, 0xd5, 0x36, 0xa5, 0x84, + 0x99, 0x4d, 0x0d, 0xe9, 0x47, 0xeb, 0xa8, 0xac, + 0x63, 0xad, 0x70, 0x0d, 0xc1, 0x45, 0xf2, 0x84, + 0x5f, 0x58, 0xcc, 0xcc, 0x77, 0xb4, 0x46, 0xc2, + 0x7e, 0x30, 0xb6, 0x35, 0x76, 0x8c, 0xe8, 0x27, + 0xa3, 0x86, 0x32, 0x29, 0xe7, 0x72, 0x5e, 0x41, + 0x1f, 0x5d, 0x81, 0x35, 0xe8, 0x46, 0x17, 0x55, + 0x51, 0xc8, 0x88, 0x9e, 0x72, 0x9a, 0x78, 0xc2, + 0xed, 0x3c, 0x32, 0x54, 0xd6, 0x46, 0x94, 0xe1, + 0x9c, 0xce, 0xc1, 0x49, 0x2c, 0x43, 0x53, 0x7b, + 0x7d, 0xe3, 0xc5, 0x7e, 0x76, 0x5b, 0x4b, 0xe0, + 0xac, 0x3a, 0xaf, 0xea, 0xfd, 0xc6, 0x62, 0x64, + 0x0c, 0x17, 0xb7, 0x37, 0x6c, 0x1a, 0x7d, 0x69, + 0xea, 0x84, 0x54, 0x1b, 0xd5, 0x0e, 0x9e, 0x76, + 0x0a, 0x12, 0xb7, 0x29, 0xb4, 0xd2, 0x61, 0xc4, + 0x38, 0xb6, 0xfc, 0x34, 0xd2, 0x20, 0xb0, 0x90, +}; + +static const uint8_t test_digest_buf[] = { + 0x64, 0xec, 0x88, 0xca, 0x00, 0xb2, 0x68, 0xe5, + 0xba, 0x1a, 0x35, 0x67, 0x8a, 0x1b, 0x53, 0x16, + 0xd2, 0x12, 0xf4, 0xf3, 0x66, 0xb2, 0x47, 0x72, + 0x32, 0x53, 0x4a, 0x8a, 0xec, 0xa3, 0x7f, 0x3c, +}; + +static const uint8_t test_exponent_buf[] = { + 0x01, 0x00, 0x01, +}; + +static const uint8_t test_modulus_buf[] = { + 0xa6, 0xf9, 0xcc, 0xed, 0x81, 0x49, 0xf0, 0x83, + 0xa7, 0xe5, 0x15, 0x93, 0xdd, 0x64, 0xa1, 0x66, + 0x40, 0xf9, 0x46, 0xd2, 0x3d, 0x16, 0xfb, 0x84, + 0x50, 0x53, 0x55, 0xba, 0x87, 0xcb, 0x15, 0xb8, + 0x98, 0xa4, 0xd2, 0xb4, 0xa6, 0xb4, 0x41, 0x2b, + 0xb4, 0x32, 0x0a, 0xc7, 0x8a, 0xe0, 0xa0, 0x2f, + 0x4e, 0xf7, 0x57, 0x44, 0xd3, 0x27, 0x94, 0x8a, + 0x10, 0x71, 0xc5, 0x3d, 0xa7, 0x25, 0xc2, 0x3f, + 0xdd, 0x1a, 0xa3, 0x05, 0x73, 0x41, 0xd8, 0x1c, + 0x99, 0xfd, 0x7f, 0x84, 0xe5, 0x09, 0xd2, 0x89, + 0x93, 0x61, 0xc6, 0xd6, 0xac, 0x6e, 0x9d, 0xe0, + 0xfb, 0x86, 0x81, 0x4a, 0x2f, 0x0a, 0xe8, 0x11, + 0xc8, 0x7f, 0x2b, 0x8b, 0x1b, 0xa3, 0x00, 0x15, + 0x25, 0xf0, 0x44, 0x39, 0xcd, 0x43, 0xbc, 0x19, + 0xad, 0xfb, 0xd3, 0xa7, 0x3f, 0x63, 0xc1, 0x78, + 0x0b, 0x69, 0x46, 0xc8, 0xe5, 0x6e, 0x15, 0x75, + 0x08, 0x8e, 0xc5, 0x67, 0xb7, 0x51, 0x61, 0x8a, + 0xb1, 0xff, 0x0a, 0xc5, 0x11, 0x30, 0x71, 0xca, + 0x88, 0x02, 0x3b, 0xfc, 0x40, 0x06, 0x11, 0x8a, + 0x0c, 0x0f, 0xb2, 0x7c, 0xf4, 0xd7, 0x07, 0xf4, + 0x85, 0xcd, 0xb7, 0x70, 0xcc, 0x5e, 0x21, 0xa0, + 0xcf, 0xb3, 0xe7, 0x47, 0x3e, 0xab, 0x20, 0x72, + 0xc3, 0xea, 0xcd, 0xfd, 0xb8, 0x50, 0x7c, 0xaa, + 0x2f, 0xd3, 0xf2, 0xc1, 0x99, 0xbc, 0x9c, 0x34, + 0xfd, 0x53, 0x26, 0x87, 0x35, 0x37, 0x78, 0xac, + 0x19, 0x13, 0x43, 0xb3, 0x80, 0x92, 0x7d, 0xeb, + 0xe2, 0x05, 0x62, 0xbf, 0x50, 0xde, 0x18, 0x17, + 0x74, 0xff, 0xa2, 0x5b, 0xfd, 0x14, 0xbf, 0xdd, + 0x21, 0xa6, 0xcb, 0xb9, 0x67, 0xce, 0x50, 0x06, + 0x91, 0x26, 0x71, 0x97, 0x5c, 0xf1, 0x81, 0x3b, + 0x68, 0xe8, 0xd9, 0xdf, 0xd3, 0xe3, 0xcc, 0x61, + 0xae, 0x73, 0x6c, 0xc8, 0x6c, 0x7b, 0x67, 0xd3, +}; + +static const struct iovec test_signature = IOVEC_MAKE(test_signature_buf, sizeof(test_signature_buf)); +static const struct iovec test_digest = IOVEC_MAKE(test_digest_buf, sizeof(test_digest_buf)); +static const struct iovec test_exponent = IOVEC_MAKE(test_exponent_buf, sizeof(test_exponent_buf)); +static const struct iovec test_modulus = IOVEC_MAKE(test_modulus_buf, sizeof(test_modulus_buf)); + +TEST(generate_rsa_test_vectors) { + /* This does not test anything but generates test vectors for dnssec_rsa_verify_raw(). + * This is skipped when we are running on valgrind or sanitizers, as it is extremely slow. */ +#if HAVE_VALGRIND_VALGRIND_H + if (RUNNING_ON_VALGRIND) + return (void) log_tests_skipped("Running on valgrind"); +#endif +#if HAS_FEATURE_ADDRESS_SANITIZER + return (void) log_tests_skipped("Running on sanitizers"); +#endif + + const struct iovec test_message = CONST_IOVEC_MAKE_STRING("Hello world"); + + /* Generate a 2048-bit RSA key pair. */ + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *kctx = + ASSERT_NOT_NULL(sym_EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_keygen_init(kctx)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_CTX_set_rsa_keygen_bits(kctx, 2048)); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + ASSERT_OK_POSITIVE(sym_EVP_PKEY_generate(kctx, &pkey)); + + /* Export public key parameters. */ + _cleanup_(BN_freep) BIGNUM *n = NULL, *e = NULL; + ASSERT_OK_POSITIVE(sym_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_E, &e)); + _cleanup_(iovec_done) struct iovec modulus = {}, exponent = {}; + export_bn(n, &modulus); + export_bn(e, &exponent); + + /* Calculate SHA-256 digest. */ + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mctx = ASSERT_NOT_NULL(sym_EVP_MD_CTX_new()); + ASSERT_OK_POSITIVE(sym_EVP_DigestInit_ex(mctx, sym_EVP_sha256(), NULL)); + ASSERT_OK_POSITIVE(sym_EVP_DigestUpdate(mctx, test_message.iov_base, test_message.iov_len)); + struct iovec digest = IOVEC_ALLOCA(EVP_MAX_MD_SIZE); + unsigned len; + ASSERT_OK_POSITIVE(sym_EVP_DigestFinal_ex(mctx, digest.iov_base, &len)); + digest.iov_len = len; + + /* Sign the digest with RSA PKCS#1 v1.5. */ + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *sctx = ASSERT_NOT_NULL(sym_EVP_PKEY_CTX_new(pkey, NULL)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_sign_init(sctx)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_CTX_set_rsa_padding(sctx, RSA_PKCS1_PADDING)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_CTX_set_signature_md(sctx, sym_EVP_sha256())); + size_t sz; + ASSERT_OK_POSITIVE(sym_EVP_PKEY_sign(sctx, NULL, &sz, digest.iov_base, digest.iov_len)); + struct iovec signature = IOVEC_ALLOCA(sz); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_sign(sctx, signature.iov_base, &signature.iov_len, digest.iov_base, digest.iov_len)); + + iovec_dump_c("test_signature_buf", &signature); + iovec_dump_c("test_digest_buf", &digest); + iovec_dump_c("test_exponent_buf", &exponent); + iovec_dump_c("test_modulus_buf", &modulus); +} + +#define TEST_RSA_VERIFY(signature, digest, exponent, modulus, expected) \ + if (expected >= 0) \ + ASSERT_OK_EQ(dnssec_rsa_verify_raw( \ + sym_EVP_sha256(), \ + signature.iov_base, signature.iov_len, \ + digest.iov_base, digest.iov_len, \ + exponent.iov_base, exponent.iov_len, \ + modulus.iov_base, modulus.iov_len), \ + expected); \ + else \ + ASSERT_ERROR(dnssec_rsa_verify_raw( \ + sym_EVP_sha256(), \ + signature.iov_base, signature.iov_len, \ + digest.iov_base, digest.iov_len, \ + exponent.iov_base, exponent.iov_len, \ + modulus.iov_base, modulus.iov_len), \ + -expected); + +TEST(dnssec_rsa_verify_raw) { +#if !defined(OPENSSL_NO_DEPRECATED_3_0) + uint8_t *p; + + TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, test_modulus, 1); + + _cleanup_(iovec_done) struct iovec bad_signature = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_signature, &bad_signature)); + p = bad_signature.iov_base; + p[0] ^= 0x01; + TEST_RSA_VERIFY(bad_signature, test_digest, test_exponent, test_modulus, 0); + + p[0] ^= 0x01; + bad_signature.iov_len -= 1; + TEST_RSA_VERIFY(bad_signature, test_digest, test_exponent, test_modulus, -EINVAL); + + _cleanup_(iovec_done) struct iovec bad_digest = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_digest, &bad_digest)); + p = bad_digest.iov_base; + p[0] ^= 0x01; + TEST_RSA_VERIFY(test_signature, bad_digest, test_exponent, test_modulus, 0); + + p[0] ^= 0x01; + bad_digest.iov_len -= 1; + TEST_RSA_VERIFY(test_signature, bad_digest, test_exponent, test_modulus, 0); + + _cleanup_(iovec_done) struct iovec bad_exponent = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_exponent, &bad_exponent)); + p = bad_exponent.iov_base; + p[0] ^= 0x01; + TEST_RSA_VERIFY(test_signature, test_digest, bad_exponent, test_modulus, 0); + + p[0] ^= 0x01; + bad_exponent.iov_len -= 1; + TEST_RSA_VERIFY(test_signature, test_digest, bad_exponent, test_modulus, 0); + + _cleanup_(iovec_done) struct iovec bad_modulus = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_modulus, &bad_modulus)); + p = bad_modulus.iov_base; + p[0] ^= 0x01; + TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, bad_modulus, 0); + + p[0] ^= 0x01; + p[bad_modulus.iov_len - 1] ^= 0x01; + TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, bad_modulus, 0); + + p[bad_modulus.iov_len - 1] ^= 0x01; + bad_modulus.iov_len -= 1; + TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, bad_modulus, -EINVAL); +#else + TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, test_modulus, -EOPNOTSUPP); +#endif +} + +static int intro(void) { + if (DLOPEN_LIBCRYPTO(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0) + return EXIT_TEST_SKIP; + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); From 918193b438111f9b26bfec974a62dd49ba113bb9 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 16:21:34 +0900 Subject: [PATCH 04/23] resolve: make dnssec_rsa_verify_raw() take struct iovec This also - adds missing assertions, - moves variable declarations. No functional change, just refactoring. --- src/resolve/resolved-dns-dnssec-crypto.h | 8 ++--- src/resolve/resolved-dns-dnssec.c | 46 ++++++++++++------------ src/resolve/test-dnssec-crypto.c | 16 ++++----- 3 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec-crypto.h b/src/resolve/resolved-dns-dnssec-crypto.h index 018bab7ba5..c0b00e6ae2 100644 --- a/src/resolve/resolved-dns-dnssec-crypto.h +++ b/src/resolve/resolved-dns-dnssec-crypto.h @@ -8,9 +8,9 @@ int dnssec_rsa_verify_raw( const EVP_MD *hash_algorithm, - const void *signature, size_t signature_size, - const void *data, size_t data_size, - const void *exponent, size_t exponent_size, - const void *modulus, size_t modulus_size); + const struct iovec *signature, + const struct iovec *hash, + const struct iovec *exponent, + const struct iovec *modulus); #endif diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 3e370d8d71..7d07105250 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -75,31 +75,30 @@ static int dnssec_verify_errno(int r) { int dnssec_rsa_verify_raw( const EVP_MD *hash_algorithm, - const void *signature, size_t signature_size, - const void *data, size_t data_size, - const void *exponent, size_t exponent_size, - const void *modulus, size_t modulus_size) { + const struct iovec *signature, + const struct iovec *hash, + const struct iovec *exponent, + const struct iovec *modulus) { #if !defined(OPENSSL_NO_DEPRECATED_3_0) DISABLE_WARNING_DEPRECATED_DECLARATIONS; int r; - _cleanup_(RSA_freep) RSA *rpubkey = NULL; - _cleanup_(EVP_PKEY_freep) EVP_PKEY *epubkey = NULL; - _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = NULL; - _cleanup_(BN_freep) BIGNUM *e = NULL, *m = NULL; - assert(hash_algorithm); + assert(iovec_is_set(signature)); + assert(iovec_is_set(hash)); + assert(iovec_is_set(exponent)); + assert(iovec_is_set(modulus)); - e = sym_BN_bin2bn(exponent, exponent_size, NULL); + _cleanup_(BN_freep) BIGNUM *e = sym_BN_bin2bn(exponent->iov_base, exponent->iov_len, NULL); if (!e) return log_openssl_errors(LOG_DEBUG, "Failed to convert RSA exponent to BIGNUM"); - m = sym_BN_bin2bn(modulus, modulus_size, NULL); + _cleanup_(BN_freep) BIGNUM *m = sym_BN_bin2bn(modulus->iov_base, modulus->iov_len, NULL); if (!m) return log_openssl_errors(LOG_DEBUG, "Failed to convert RSA modulus to BIGNUM"); - rpubkey = sym_RSA_new(); + _cleanup_(RSA_freep) RSA *rpubkey = sym_RSA_new(); if (!rpubkey) return -ENOMEM; @@ -107,17 +106,17 @@ int dnssec_rsa_verify_raw( return log_openssl_errors(LOG_DEBUG, "Failed to set RSA public key"); e = m = NULL; - if ((size_t) sym_RSA_size(rpubkey) != signature_size) + if ((size_t) sym_RSA_size(rpubkey) != signature->iov_len) return -EINVAL; - epubkey = sym_EVP_PKEY_new(); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *epubkey = sym_EVP_PKEY_new(); if (!epubkey) return -ENOMEM; if (sym_EVP_PKEY_assign_RSA(epubkey, sym_RSAPublicKey_dup(rpubkey)) <= 0) return log_openssl_errors(LOG_DEBUG, "Failed to assign RSA public key"); - ctx = sym_EVP_PKEY_CTX_new(epubkey, NULL); + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = sym_EVP_PKEY_CTX_new(epubkey, NULL); if (!ctx) return -ENOMEM; @@ -130,7 +129,7 @@ int dnssec_rsa_verify_raw( if (sym_EVP_PKEY_CTX_set_signature_md(ctx, hash_algorithm) <= 0) return log_openssl_errors(LOG_DEBUG, "Failed to set RSA signature digest"); - r = sym_EVP_PKEY_verify(ctx, signature, signature_size, data, data_size); + r = sym_EVP_PKEY_verify(ctx, signature->iov_base, signature->iov_len, hash->iov_base, hash->iov_len); if (r < 0) return log_openssl_errors(LOG_DEBUG, "Signature verification failed"); @@ -143,7 +142,7 @@ int dnssec_rsa_verify_raw( static int dnssec_rsa_verify( const EVP_MD *hash_algorithm, - const void *hash, size_t hash_size, + const struct iovec *hash, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { @@ -151,8 +150,7 @@ static int dnssec_rsa_verify( void *exponent, *modulus; assert(hash_algorithm); - assert(hash); - assert(hash_size > 0); + assert(iovec_is_set(hash)); assert(rrsig); assert(dnskey); @@ -196,10 +194,10 @@ static int dnssec_rsa_verify( return dnssec_rsa_verify_raw( hash_algorithm, - rrsig->rrsig.signature, rrsig->rrsig.signature_size, - hash, hash_size, - exponent, exponent_size, - modulus, modulus_size); + &IOVEC_MAKE(rrsig->rrsig.signature, rrsig->rrsig.signature_size), + hash, + &IOVEC_MAKE(exponent, exponent_size), + &IOVEC_MAKE(modulus, modulus_size)); } static int dnssec_ecdsa_verify_raw( @@ -687,7 +685,7 @@ static int dnssec_rrset_verify_sig( case DNSSEC_ALGORITHM_RSASHA512: return dnssec_verify_errno(dnssec_rsa_verify( md_algorithm, - hash, hash_size, + &IOVEC_MAKE(hash, hash_size), rrsig, dnskey)); diff --git a/src/resolve/test-dnssec-crypto.c b/src/resolve/test-dnssec-crypto.c index 66744795e0..c8b91bb1ba 100644 --- a/src/resolve/test-dnssec-crypto.c +++ b/src/resolve/test-dnssec-crypto.c @@ -182,18 +182,18 @@ TEST(generate_rsa_test_vectors) { if (expected >= 0) \ ASSERT_OK_EQ(dnssec_rsa_verify_raw( \ sym_EVP_sha256(), \ - signature.iov_base, signature.iov_len, \ - digest.iov_base, digest.iov_len, \ - exponent.iov_base, exponent.iov_len, \ - modulus.iov_base, modulus.iov_len), \ + &signature, \ + &digest, \ + &exponent, \ + &modulus), \ expected); \ else \ ASSERT_ERROR(dnssec_rsa_verify_raw( \ sym_EVP_sha256(), \ - signature.iov_base, signature.iov_len, \ - digest.iov_base, digest.iov_len, \ - exponent.iov_base, exponent.iov_len, \ - modulus.iov_base, modulus.iov_len), \ + &signature, \ + &digest, \ + &exponent, \ + &modulus), \ -expected); TEST(dnssec_rsa_verify_raw) { From cd3c0859b23f5b64e7f42d7bd68ae4195daf9784 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 17:00:27 +0900 Subject: [PATCH 05/23] resolved: migrate RSA key construction to OpenSSL 3 EVP API OpenSSL 3.0 deprecated low-level key manipulation functions and direct access to RSA structures (such as RSA_new(), RSA_set0_key(), and RSA_size()). This commit modernizes dnssec_rsa_verify_raw() by replacing these deprecated functions with the provider-aware EVP API: * Uses OSSL_PARAM_BLD and EVP_PKEY_fromdata() to construct the RSA public key directly from the modulus and exponent BIGNUMs. * Replaces RSA_size() with EVP_PKEY_get_size(). Consequently, the workaround macros suppressing deprecated warnings (DISABLE_WARNING_DEPRECATED_DECLARATIONS) and the conditional fallback blocks (#if !defined(OPENSSL_NO_DEPRECATED_3_0)) are no longer needed and have been dropped. Unit tests are also updated to run unconditionally. --- src/resolve/resolved-dns-dnssec.c | 42 +++++++++++++++++-------------- src/resolve/test-dnssec-crypto.c | 4 --- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 7d07105250..b9afdac9ff 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -80,8 +80,6 @@ int dnssec_rsa_verify_raw( const struct iovec *exponent, const struct iovec *modulus) { -#if !defined(OPENSSL_NO_DEPRECATED_3_0) - DISABLE_WARNING_DEPRECATED_DECLARATIONS; int r; assert(hash_algorithm); @@ -98,24 +96,34 @@ int dnssec_rsa_verify_raw( if (!m) return log_openssl_errors(LOG_DEBUG, "Failed to convert RSA modulus to BIGNUM"); - _cleanup_(RSA_freep) RSA *rpubkey = sym_RSA_new(); - if (!rpubkey) + _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = sym_OSSL_PARAM_BLD_new(); + if (!bld) return -ENOMEM; - if (sym_RSA_set0_key(rpubkey, m, e, NULL) <= 0) - return log_openssl_errors(LOG_DEBUG, "Failed to set RSA public key"); - e = m = NULL; + if (sym_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to push RSA exponent to OSSL_PARAM_BLD"); - if ((size_t) sym_RSA_size(rpubkey) != signature->iov_len) + if (sym_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, m) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to push RSA modulus to OSSL_PARAM_BLD"); + + _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = sym_OSSL_PARAM_BLD_to_param(bld); + if (!params) + return log_openssl_errors(LOG_DEBUG, "Failed to generate OSSL param"); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *kctx = sym_EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!kctx) + return -ENOMEM; + + if (sym_EVP_PKEY_fromdata_init(kctx) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to initialize key creation"); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *epubkey = NULL; + if (sym_EVP_PKEY_fromdata(kctx, &epubkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to load RSA public key from raw data"); + + if ((size_t) sym_EVP_PKEY_get_size(epubkey) != signature->iov_len) return -EINVAL; - _cleanup_(EVP_PKEY_freep) EVP_PKEY *epubkey = sym_EVP_PKEY_new(); - if (!epubkey) - return -ENOMEM; - - if (sym_EVP_PKEY_assign_RSA(epubkey, sym_RSAPublicKey_dup(rpubkey)) <= 0) - return log_openssl_errors(LOG_DEBUG, "Failed to assign RSA public key"); - _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *ctx = sym_EVP_PKEY_CTX_new(epubkey, NULL); if (!ctx) return -ENOMEM; @@ -133,11 +141,7 @@ int dnssec_rsa_verify_raw( if (r < 0) return log_openssl_errors(LOG_DEBUG, "Signature verification failed"); - REENABLE_WARNING; return r; -#else - return -EOPNOTSUPP; -#endif } static int dnssec_rsa_verify( diff --git a/src/resolve/test-dnssec-crypto.c b/src/resolve/test-dnssec-crypto.c index c8b91bb1ba..034320b348 100644 --- a/src/resolve/test-dnssec-crypto.c +++ b/src/resolve/test-dnssec-crypto.c @@ -197,7 +197,6 @@ TEST(generate_rsa_test_vectors) { -expected); TEST(dnssec_rsa_verify_raw) { -#if !defined(OPENSSL_NO_DEPRECATED_3_0) uint8_t *p; TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, test_modulus, 1); @@ -245,9 +244,6 @@ TEST(dnssec_rsa_verify_raw) { p[bad_modulus.iov_len - 1] ^= 0x01; bad_modulus.iov_len -= 1; TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, bad_modulus, -EINVAL); -#else - TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, test_modulus, -EOPNOTSUPP); -#endif } static int intro(void) { From 59fa4dd142fbc8a8c3165a17faca633e4e83bad8 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 20:15:31 +0900 Subject: [PATCH 06/23] resolve: add unit test for dnssec_ecdsa_verify_raw() --- src/resolve/resolved-dns-dnssec-crypto.h | 8 ++ src/resolve/resolved-dns-dnssec.c | 2 +- src/resolve/test-dnssec-crypto.c | 156 +++++++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) diff --git a/src/resolve/resolved-dns-dnssec-crypto.h b/src/resolve/resolved-dns-dnssec-crypto.h index c0b00e6ae2..568e511d18 100644 --- a/src/resolve/resolved-dns-dnssec-crypto.h +++ b/src/resolve/resolved-dns-dnssec-crypto.h @@ -13,4 +13,12 @@ int dnssec_rsa_verify_raw( const struct iovec *exponent, const struct iovec *modulus); +int dnssec_ecdsa_verify_raw( + const EVP_MD *hash_algorithm, + int curve, + const void *signature_r, size_t signature_r_size, + const void *signature_s, size_t signature_s_size, + const void *data, size_t data_size, + const void *key, size_t key_size); + #endif diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index b9afdac9ff..b7748272f3 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -204,7 +204,7 @@ static int dnssec_rsa_verify( &IOVEC_MAKE(modulus, modulus_size)); } -static int dnssec_ecdsa_verify_raw( +int dnssec_ecdsa_verify_raw( const EVP_MD *hash_algorithm, int curve, const void *signature_r, size_t signature_r_size, diff --git a/src/resolve/test-dnssec-crypto.c b/src/resolve/test-dnssec-crypto.c index 034320b348..ff22f5ddd9 100644 --- a/src/resolve/test-dnssec-crypto.c +++ b/src/resolve/test-dnssec-crypto.c @@ -246,6 +246,162 @@ TEST(dnssec_rsa_verify_raw) { TEST_RSA_VERIFY(test_signature, test_digest, test_exponent, bad_modulus, -EINVAL); } +static const uint8_t test_ecdsa_r_buf[] = { + 0x0b, 0x81, 0x39, 0x45, 0xa8, 0xcd, 0x1b, 0x12, + 0xd6, 0x9f, 0xa9, 0x88, 0xf2, 0x28, 0x39, 0x13, + 0x59, 0x1d, 0xe7, 0xee, 0xea, 0x92, 0x87, 0x0a, + 0x2c, 0x7a, 0xf2, 0x3b, 0xfb, 0x3e, 0xcb, 0x3b, +}; + +static const uint8_t test_ecdsa_s_buf[] = { + 0x74, 0xcd, 0x7c, 0x0b, 0x52, 0xa5, 0x7b, 0xf5, + 0x63, 0x84, 0xd5, 0xd9, 0xd2, 0xcb, 0xe3, 0xce, + 0x55, 0x05, 0x11, 0x7c, 0xe1, 0xfe, 0x55, 0xb4, + 0x54, 0x2e, 0x6e, 0x1c, 0xff, 0x50, 0xe5, 0x92, +}; + +static const uint8_t test_ecdsa_key_buf[] = { + 0x04, 0x23, 0x1f, 0x7d, 0x08, 0xec, 0x20, 0xcd, + 0xde, 0x03, 0x93, 0xa7, 0xb2, 0x47, 0x73, 0xeb, + 0xde, 0xd3, 0x5a, 0xbe, 0x35, 0x01, 0xda, 0x31, + 0x2b, 0x7b, 0x61, 0xcf, 0xd2, 0x30, 0xbf, 0xbf, + 0xb7, 0x6f, 0x0d, 0x86, 0x0a, 0x32, 0x46, 0xb6, + 0xdc, 0xb0, 0xd0, 0xa7, 0x3f, 0x5d, 0xdd, 0xdd, + 0xb9, 0xbb, 0x6b, 0x01, 0x61, 0x93, 0x2c, 0x1a, + 0x26, 0x65, 0x5f, 0x46, 0xb1, 0xe1, 0xc7, 0x1d, + 0x21, +}; + +static const struct iovec test_ecdsa_r = IOVEC_MAKE(test_ecdsa_r_buf, sizeof(test_ecdsa_r_buf)); +static const struct iovec test_ecdsa_s = IOVEC_MAKE(test_ecdsa_s_buf, sizeof(test_ecdsa_s_buf)); +static const struct iovec test_ecdsa_key = IOVEC_MAKE(test_ecdsa_key_buf, sizeof(test_ecdsa_key_buf)); + +TEST(generate_ecdsa_test_vectors) { + /* This does not test anything but generates test vectors for dnssec_ecdsa_verify_raw(). + * This is skipped when we are running on valgrind or sanitizers, as it is extremely slow. */ +#if HAVE_VALGRIND_VALGRIND_H + if (RUNNING_ON_VALGRIND) + return (void) log_tests_skipped("Running on valgrind"); +#endif +#if HAS_FEATURE_ADDRESS_SANITIZER + return (void) log_tests_skipped("Running on sanitizers"); +#endif + + /* Generate a P-256 (prime256v1) EC key pair. */ + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *kctx = + ASSERT_NOT_NULL(sym_EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_keygen_init(kctx)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_CTX_set_ec_paramgen_curve_nid(kctx, NID_X9_62_prime256v1)); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; + ASSERT_OK_POSITIVE(sym_EVP_PKEY_generate(kctx, &pkey)); + + /* Export uncompressed public key point (0x04 || X || Y) */ + size_t key_sz = 0; + ASSERT_OK_POSITIVE(sym_EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, NULL, 0, &key_sz)); + struct iovec pubkey = IOVEC_ALLOCA(key_sz); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY, pubkey.iov_base, key_sz, &key_sz)); + pubkey.iov_len = key_sz; + + /* Sign the existing test_digest with ECDSA */ + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *sctx = ASSERT_NOT_NULL(sym_EVP_PKEY_CTX_new(pkey, NULL)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_sign_init(sctx)); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_CTX_set_signature_md(sctx, sym_EVP_sha256())); + size_t sig_sz; + ASSERT_OK_POSITIVE(sym_EVP_PKEY_sign(sctx, NULL, &sig_sz, test_digest.iov_base, test_digest.iov_len)); + struct iovec signature_der = IOVEC_ALLOCA(sig_sz); + ASSERT_OK_POSITIVE(sym_EVP_PKEY_sign(sctx, signature_der.iov_base, &signature_der.iov_len, test_digest.iov_base, test_digest.iov_len)); + + /* ECDSA signature is DER encoded. Extract r and s components. */ + const uint8_t *p = signature_der.iov_base; + _cleanup_(ECDSA_SIG_freep) ECDSA_SIG *esig = ASSERT_NOT_NULL(sym_d2i_ECDSA_SIG(NULL, &p, signature_der.iov_len)); + + const BIGNUM *r_bn = sym_ECDSA_SIG_get0_r(esig); + const BIGNUM *s_bn = sym_ECDSA_SIG_get0_s(esig); + + _cleanup_(iovec_done) struct iovec r_iov = {}, s_iov = {}; + export_bn(r_bn, &r_iov); + export_bn(s_bn, &s_iov); + + iovec_dump_c("test_ecdsa_r_buf", &r_iov); + iovec_dump_c("test_ecdsa_s_buf", &s_iov); + iovec_dump_c("test_ecdsa_key_buf", &pubkey); +} + +#define TEST_ECDSA_VERIFY(r, s, digest, key, expected) \ + if (expected >= 0) \ + ASSERT_OK_EQ(dnssec_ecdsa_verify_raw( \ + sym_EVP_sha256(), \ + NID_X9_62_prime256v1, \ + (r).iov_base, (r).iov_len, \ + (s).iov_base, (s).iov_len, \ + (digest).iov_base, (digest).iov_len, \ + (key).iov_base, (key).iov_len), \ + expected); \ + else \ + ASSERT_ERROR(dnssec_ecdsa_verify_raw( \ + sym_EVP_sha256(), \ + NID_X9_62_prime256v1, \ + (r).iov_base, (r).iov_len, \ + (s).iov_base, (s).iov_len, \ + (digest).iov_base, (digest).iov_len, \ + (key).iov_base, (key).iov_len), \ + -expected); + +TEST(dnssec_ecdsa_verify_raw) { +#if !defined(OPENSSL_NO_DEPRECATED_3_0) + uint8_t *p; + + /* Normal verification */ + TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, test_digest, test_ecdsa_key, 1); + + /* Fuzzing R component */ + _cleanup_(iovec_done) struct iovec bad_r = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_ecdsa_r, &bad_r)); + p = bad_r.iov_base; + p[0] ^= 0x01; + TEST_ECDSA_VERIFY(bad_r, test_ecdsa_s, test_digest, test_ecdsa_key, 0); + + p[0] ^= 0x01; + bad_r.iov_len -= 1; + TEST_ECDSA_VERIFY(bad_r, test_ecdsa_s, test_digest, test_ecdsa_key, 0); + + /* Fuzzing S component */ + _cleanup_(iovec_done) struct iovec bad_s = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_ecdsa_s, &bad_s)); + p = bad_s.iov_base; + p[0] ^= 0x01; + TEST_ECDSA_VERIFY(test_ecdsa_r, bad_s, test_digest, test_ecdsa_key, 0); + + p[0] ^= 0x01; + bad_s.iov_len -= 1; + TEST_ECDSA_VERIFY(test_ecdsa_r, bad_s, test_digest, test_ecdsa_key, 0); + + /* Fuzzing Digest */ + _cleanup_(iovec_done) struct iovec bad_digest = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_digest, &bad_digest)); + p = bad_digest.iov_base; + p[0] ^= 0x01; + TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, bad_digest, test_ecdsa_key, 0); + + p[0] ^= 0x01; + bad_digest.iov_len -= 1; + TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, bad_digest, test_ecdsa_key, 0); + + /* Fuzzing Public Key Point */ + _cleanup_(iovec_done) struct iovec bad_key = {}; + ASSERT_NOT_NULL(iovec_memdup(&test_ecdsa_key, &bad_key)); + p = bad_key.iov_base; + p[bad_key.iov_len - 1] ^= 0x01; + TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, test_digest, bad_key, -ENOTRECOVERABLE); + + p[bad_key.iov_len - 1] ^= 0x01; + bad_key.iov_len -= 1; + TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, test_digest, bad_key, -ENOTRECOVERABLE); +#else + TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, test_digest, test_ecdsa_key, -EOPNOTSUPP); +#endif +} + static int intro(void) { if (DLOPEN_LIBCRYPTO(LOG_DEBUG, SD_ELF_NOTE_DLOPEN_PRIORITY_RECOMMENDED) < 0) return EXIT_TEST_SKIP; From 13be55350746554a1b5a0f26c5c5c21e7e538087 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 20:16:00 +0900 Subject: [PATCH 07/23] resolve: make dnssec_ecdsa_verify_raw() take struct iovec This also - adds missing assertions, - moves variable declarations, - rename variables. No functional change, just refactoring. --- src/resolve/resolved-dns-dnssec-crypto.h | 8 +-- src/resolve/resolved-dns-dnssec.c | 70 ++++++++++++------------ src/resolve/test-dnssec-crypto.c | 10 +--- 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec-crypto.h b/src/resolve/resolved-dns-dnssec-crypto.h index 568e511d18..87dd6c8db5 100644 --- a/src/resolve/resolved-dns-dnssec-crypto.h +++ b/src/resolve/resolved-dns-dnssec-crypto.h @@ -16,9 +16,9 @@ int dnssec_rsa_verify_raw( int dnssec_ecdsa_verify_raw( const EVP_MD *hash_algorithm, int curve, - const void *signature_r, size_t signature_r_size, - const void *signature_s, size_t signature_s_size, - const void *data, size_t data_size, - const void *key, size_t key_size); + const struct iovec *signature_r, + const struct iovec *signature_s, + const struct iovec *hash, + const struct iovec *key); #endif diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index b7748272f3..b2e04b3928 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -207,40 +207,37 @@ static int dnssec_rsa_verify( int dnssec_ecdsa_verify_raw( const EVP_MD *hash_algorithm, int curve, - const void *signature_r, size_t signature_r_size, - const void *signature_s, size_t signature_s_size, - const void *data, size_t data_size, - const void *key, size_t key_size) { + const struct iovec *signature_r, + const struct iovec *signature_s, + const struct iovec *hash, + const struct iovec *key) { #if !defined(OPENSSL_NO_DEPRECATED_3_0) DISABLE_WARNING_DEPRECATED_DECLARATIONS; - int k; - - _cleanup_(EC_GROUP_freep) EC_GROUP *ec_group = NULL; - _cleanup_(EC_POINT_freep) EC_POINT *p = NULL; - _cleanup_(EC_KEY_freep) EC_KEY *eckey = NULL; - _cleanup_(BN_CTX_freep) BN_CTX *bctx = NULL; - _cleanup_(BN_freep) BIGNUM *r = NULL, *s = NULL; - _cleanup_(ECDSA_SIG_freep) ECDSA_SIG *sig = NULL; + int r; assert(hash_algorithm); + assert(iovec_is_set(signature_r)); + assert(iovec_is_set(signature_s)); + assert(iovec_is_set(hash)); + assert(iovec_is_set(key)); - ec_group = sym_EC_GROUP_new_by_curve_name(curve); + _cleanup_(EC_GROUP_freep) EC_GROUP *ec_group = sym_EC_GROUP_new_by_curve_name(curve); if (!ec_group) return -ENOMEM; - p = sym_EC_POINT_new(ec_group); + _cleanup_(EC_POINT_freep) EC_POINT *p = sym_EC_POINT_new(ec_group); if (!p) return -ENOMEM; - bctx = sym_BN_CTX_new(); + _cleanup_(BN_CTX_freep) BN_CTX *bctx = sym_BN_CTX_new(); if (!bctx) return -ENOMEM; - if (sym_EC_POINT_oct2point(ec_group, p, key, key_size, bctx) <= 0) + if (sym_EC_POINT_oct2point(ec_group, p, key->iov_base, key->iov_len, bctx) <= 0) return log_openssl_errors(LOG_DEBUG, "Failed to parse EC public key point"); - eckey = sym_EC_KEY_new(); + _cleanup_(EC_KEY_freep) EC_KEY *eckey = sym_EC_KEY_new(); if (!eckey) return -ENOMEM; @@ -250,33 +247,34 @@ int dnssec_ecdsa_verify_raw( if (sym_EC_KEY_set_public_key(eckey, p) <= 0) return log_openssl_errors(LOG_DEBUG, "EC_KEY_set_public_key failed"); - if (sym_EC_KEY_check_key(eckey) != 1) + if (sym_EC_KEY_check_key(eckey) <= 0) return log_openssl_errors(LOG_DEBUG, "EC_KEY_check_key failed"); - r = sym_BN_bin2bn(signature_r, signature_r_size, NULL); - if (!r) + _cleanup_(BN_freep) BIGNUM *bn_r = sym_BN_bin2bn(signature_r->iov_base, signature_r->iov_len, NULL); + if (!bn_r) return log_openssl_errors(LOG_DEBUG, "Failed to convert ECDSA signature r to BIGNUM"); - s = sym_BN_bin2bn(signature_s, signature_s_size, NULL); - if (!s) + _cleanup_(BN_freep) BIGNUM *bn_s = sym_BN_bin2bn(signature_s->iov_base, signature_s->iov_len, NULL); + if (!bn_s) return log_openssl_errors(LOG_DEBUG, "Failed to convert ECDSA signature s to BIGNUM"); /* TODO: We should eventually use the EVP API once it supports ECDSA signature verification */ - sig = sym_ECDSA_SIG_new(); + _cleanup_(ECDSA_SIG_freep) ECDSA_SIG *sig = sym_ECDSA_SIG_new(); if (!sig) return -ENOMEM; - if (sym_ECDSA_SIG_set0(sig, r, s) <= 0) + if (sym_ECDSA_SIG_set0(sig, bn_r, bn_s) <= 0) return log_openssl_errors(LOG_DEBUG, "Failed to set ECDSA signature"); - r = s = NULL; + TAKE_PTR(bn_r); + TAKE_PTR(bn_s); - k = sym_ECDSA_do_verify(data, data_size, sig, eckey); - if (k < 0) + r = sym_ECDSA_do_verify(hash->iov_base, hash->iov_len, sig, eckey); + if (r < 0) return log_openssl_errors(LOG_DEBUG, "Signature verification failed"); REENABLE_WARNING; - return k; + return r; #else return -EOPNOTSUPP; #endif @@ -285,7 +283,7 @@ int dnssec_ecdsa_verify_raw( static int dnssec_ecdsa_verify( const EVP_MD *hash_algorithm, int algorithm, - const void *hash, size_t hash_size, + const struct iovec *hash, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { @@ -293,8 +291,8 @@ static int dnssec_ecdsa_verify( size_t key_size; uint8_t *q; - assert(hash); - assert(hash_size); + assert(hash_algorithm); + assert(iovec_is_set(hash)); assert(rrsig); assert(dnskey); @@ -320,10 +318,10 @@ static int dnssec_ecdsa_verify( return dnssec_ecdsa_verify_raw( hash_algorithm, curve, - rrsig->rrsig.signature, key_size, - (uint8_t*) rrsig->rrsig.signature + key_size, key_size, - hash, hash_size, - q, key_size*2+1); + &IOVEC_MAKE(rrsig->rrsig.signature, key_size), + &IOVEC_MAKE((uint8_t*) rrsig->rrsig.signature + key_size, key_size), + hash, + &IOVEC_MAKE(q, key_size * 2 + 1)); } static int dnssec_eddsa_verify_raw( @@ -698,7 +696,7 @@ static int dnssec_rrset_verify_sig( return dnssec_verify_errno(dnssec_ecdsa_verify( md_algorithm, rrsig->rrsig.algorithm, - hash, hash_size, + &IOVEC_MAKE(hash, hash_size), rrsig, dnskey)); diff --git a/src/resolve/test-dnssec-crypto.c b/src/resolve/test-dnssec-crypto.c index ff22f5ddd9..47dff4af69 100644 --- a/src/resolve/test-dnssec-crypto.c +++ b/src/resolve/test-dnssec-crypto.c @@ -332,19 +332,13 @@ TEST(generate_ecdsa_test_vectors) { ASSERT_OK_EQ(dnssec_ecdsa_verify_raw( \ sym_EVP_sha256(), \ NID_X9_62_prime256v1, \ - (r).iov_base, (r).iov_len, \ - (s).iov_base, (s).iov_len, \ - (digest).iov_base, (digest).iov_len, \ - (key).iov_base, (key).iov_len), \ + &r, &s, &digest, &key), \ expected); \ else \ ASSERT_ERROR(dnssec_ecdsa_verify_raw( \ sym_EVP_sha256(), \ NID_X9_62_prime256v1, \ - (r).iov_base, (r).iov_len, \ - (s).iov_base, (s).iov_len, \ - (digest).iov_base, (digest).iov_len, \ - (key).iov_base, (key).iov_len), \ + &r, &s, &digest, &key), \ -expected); TEST(dnssec_ecdsa_verify_raw) { From c18ee628a502b5694caade7482be85d0e7cc0284 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 21:05:50 +0900 Subject: [PATCH 08/23] resolved: migrate ECDSA verification to OpenSSL 3 EVP API OpenSSL 3.0 deprecated low-level Elliptic Curve (EC) key manipulation functions (EC_KEY, EC_POINT, EC_GROUP) and direct signature verification functions like ECDSA_do_verify(). This commit modernizes dnssec_ecdsa_verify_raw() by transitioning to the provider-aware EVP API: * Uses OSSL_PARAM arrays and EVP_PKEY_fromdata() to construct the EC public key directly from the raw octet string and curve name, avoiding deprecated EC_POINT parsing. * Converts the raw R and S signature components into a DER-encoded ASN.1 signature using i2d_ECDSA_SIG(), as required by the modern EVP API. * Uses EVP_PKEY_verify() for the actual signature validation. Additionally, this drops an outdated TODO comment waiting for raw ECDSA support in the EVP API, as well as the deprecated warning suppression macros and fallback code blocks. Unit tests for ECDSA are now run unconditionally. --- src/resolve/resolved-dns-dnssec.c | 70 ++++++++++++++++--------------- src/resolve/test-dnssec-crypto.c | 4 -- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index b2e04b3928..67cf991e70 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -212,8 +212,6 @@ int dnssec_ecdsa_verify_raw( const struct iovec *hash, const struct iovec *key) { -#if !defined(OPENSSL_NO_DEPRECATED_3_0) - DISABLE_WARNING_DEPRECATED_DECLARATIONS; int r; assert(hash_algorithm); @@ -222,33 +220,26 @@ int dnssec_ecdsa_verify_raw( assert(iovec_is_set(hash)); assert(iovec_is_set(key)); - _cleanup_(EC_GROUP_freep) EC_GROUP *ec_group = sym_EC_GROUP_new_by_curve_name(curve); - if (!ec_group) + const char *curve_name = sym_OBJ_nid2sn(curve); + if (!curve_name) + return log_openssl_errors(LOG_DEBUG, "Unknown curve NID"); + + OSSL_PARAM params[] = { + sym_OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, (char*) curve_name, 0), + sym_OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY, key->iov_base, key->iov_len), + sym_OSSL_PARAM_construct_end(), + }; + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *kctx = sym_EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (!kctx) return -ENOMEM; - _cleanup_(EC_POINT_freep) EC_POINT *p = sym_EC_POINT_new(ec_group); - if (!p) - return -ENOMEM; + if (sym_EVP_PKEY_fromdata_init(kctx) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to initialize EC key creation"); - _cleanup_(BN_CTX_freep) BN_CTX *bctx = sym_BN_CTX_new(); - if (!bctx) - return -ENOMEM; - - if (sym_EC_POINT_oct2point(ec_group, p, key->iov_base, key->iov_len, bctx) <= 0) - return log_openssl_errors(LOG_DEBUG, "Failed to parse EC public key point"); - - _cleanup_(EC_KEY_freep) EC_KEY *eckey = sym_EC_KEY_new(); - if (!eckey) - return -ENOMEM; - - if (sym_EC_KEY_set_group(eckey, ec_group) <= 0) - return log_openssl_errors(LOG_DEBUG, "Failed to set EC group"); - - if (sym_EC_KEY_set_public_key(eckey, p) <= 0) - return log_openssl_errors(LOG_DEBUG, "EC_KEY_set_public_key failed"); - - if (sym_EC_KEY_check_key(eckey) <= 0) - return log_openssl_errors(LOG_DEBUG, "EC_KEY_check_key failed"); + _cleanup_(EVP_PKEY_freep) EVP_PKEY *epubkey = NULL; + if (sym_EVP_PKEY_fromdata(kctx, &epubkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to load EC public key from raw data"); _cleanup_(BN_freep) BIGNUM *bn_r = sym_BN_bin2bn(signature_r->iov_base, signature_r->iov_len, NULL); if (!bn_r) @@ -258,8 +249,6 @@ int dnssec_ecdsa_verify_raw( if (!bn_s) return log_openssl_errors(LOG_DEBUG, "Failed to convert ECDSA signature s to BIGNUM"); - /* TODO: We should eventually use the EVP API once it supports ECDSA signature verification */ - _cleanup_(ECDSA_SIG_freep) ECDSA_SIG *sig = sym_ECDSA_SIG_new(); if (!sig) return -ENOMEM; @@ -269,15 +258,30 @@ int dnssec_ecdsa_verify_raw( TAKE_PTR(bn_r); TAKE_PTR(bn_s); - r = sym_ECDSA_do_verify(hash->iov_base, hash->iov_len, sig, eckey); + _cleanup_(OPENSSL_freep) void *buf = NULL; + r = sym_i2d_ECDSA_SIG(sig, (unsigned char**) &buf); + if (r <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to DER encode ECDSA signature"); + struct iovec der_sig = IOVEC_MAKE(buf, r); + + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *vctx = sym_EVP_PKEY_CTX_new(epubkey, NULL); + if (!vctx) + return -ENOMEM; + + if (sym_EVP_PKEY_public_check(vctx) <= 0) + return log_openssl_errors(LOG_DEBUG, "EC public key validation failed"); + + if (sym_EVP_PKEY_verify_init(vctx) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to initialize ECDSA signature verification"); + + if (sym_EVP_PKEY_CTX_set_signature_md(vctx, hash_algorithm) <= 0) + return log_openssl_errors(LOG_DEBUG, "Failed to set ECDSA signature digest"); + + r = sym_EVP_PKEY_verify(vctx, der_sig.iov_base, der_sig.iov_len, hash->iov_base, hash->iov_len); if (r < 0) return log_openssl_errors(LOG_DEBUG, "Signature verification failed"); - REENABLE_WARNING; return r; -#else - return -EOPNOTSUPP; -#endif } static int dnssec_ecdsa_verify( diff --git a/src/resolve/test-dnssec-crypto.c b/src/resolve/test-dnssec-crypto.c index 47dff4af69..82a91a6386 100644 --- a/src/resolve/test-dnssec-crypto.c +++ b/src/resolve/test-dnssec-crypto.c @@ -342,7 +342,6 @@ TEST(generate_ecdsa_test_vectors) { -expected); TEST(dnssec_ecdsa_verify_raw) { -#if !defined(OPENSSL_NO_DEPRECATED_3_0) uint8_t *p; /* Normal verification */ @@ -391,9 +390,6 @@ TEST(dnssec_ecdsa_verify_raw) { p[bad_key.iov_len - 1] ^= 0x01; bad_key.iov_len -= 1; TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, test_digest, bad_key, -ENOTRECOVERABLE); -#else - TEST_ECDSA_VERIFY(test_ecdsa_r, test_ecdsa_s, test_digest, test_ecdsa_key, -EOPNOTSUPP); -#endif } static int intro(void) { From ec35f207b6939a3035a70cf30bb0442d2bec258b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 21:14:47 +0900 Subject: [PATCH 09/23] crypto-util: drop unused deprecated symbols --- src/shared/crypto-util.c | 31 ------------------------------- src/shared/crypto-util.h | 21 --------------------- 2 files changed, 52 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 2810856888..68455abc64 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -327,23 +327,6 @@ REENABLE_WARNING; DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ENGINE*, sym_ENGINE_free, ENGINE_freep, NULL); #endif -#if !defined(OPENSSL_NO_DEPRECATED_3_0) -DISABLE_WARNING_DEPRECATED_DECLARATIONS; -DLSYM_PROTOTYPE(ECDSA_do_verify) = NULL; -DLSYM_PROTOTYPE(EC_KEY_check_key) = NULL; -DLSYM_PROTOTYPE(EC_KEY_free) = NULL; -DLSYM_PROTOTYPE(EC_KEY_new) = NULL; -DLSYM_PROTOTYPE(EC_KEY_set_group) = NULL; -DLSYM_PROTOTYPE(EC_KEY_set_public_key) = NULL; -DLSYM_PROTOTYPE(EVP_PKEY_assign) = NULL; -DLSYM_PROTOTYPE(RSAPublicKey_dup) = NULL; -DLSYM_PROTOTYPE(RSA_free) = NULL; -DLSYM_PROTOTYPE(RSA_new) = NULL; -DLSYM_PROTOTYPE(RSA_set0_key) = NULL; -DLSYM_PROTOTYPE(RSA_size) = NULL; -REENABLE_WARNING; -#endif - #ifndef OPENSSL_NO_UI_CONSOLE static DLSYM_PROTOTYPE(UI_OpenSSL) = NULL; static DLSYM_PROTOTYPE(UI_create_method) = NULL; @@ -656,20 +639,6 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG_FORCE(ENGINE_init), DLSYM_ARG_FORCE(ENGINE_load_private_key), #endif -#if !defined(OPENSSL_NO_DEPRECATED_3_0) - DLSYM_ARG_FORCE(ECDSA_do_verify), - DLSYM_ARG_FORCE(EC_KEY_check_key), - DLSYM_ARG_FORCE(EC_KEY_free), - DLSYM_ARG_FORCE(EC_KEY_new), - DLSYM_ARG_FORCE(EC_KEY_set_group), - DLSYM_ARG_FORCE(EC_KEY_set_public_key), - DLSYM_ARG_FORCE(EVP_PKEY_assign), - DLSYM_ARG_FORCE(RSAPublicKey_dup), - DLSYM_ARG_FORCE(RSA_free), - DLSYM_ARG_FORCE(RSA_new), - DLSYM_ARG_FORCE(RSA_set0_key), - DLSYM_ARG_FORCE(RSA_size), -#endif #ifndef OPENSSL_NO_UI_CONSOLE DLSYM_ARG(UI_OpenSSL), DLSYM_ARG(UI_create_method), diff --git a/src/shared/crypto-util.h b/src/shared/crypto-util.h index 6ecf66bbf6..b0358090a7 100644 --- a/src/shared/crypto-util.h +++ b/src/shared/crypto-util.h @@ -283,26 +283,6 @@ extern DLSYM_PROTOTYPE(i2d_PUBKEY); extern DLSYM_PROTOTYPE(i2d_X509); extern DLSYM_PROTOTYPE(i2d_X509_NAME); -#if !defined(OPENSSL_NO_DEPRECATED_3_0) -DISABLE_WARNING_DEPRECATED_DECLARATIONS; -extern DLSYM_PROTOTYPE(ECDSA_do_verify); -extern DLSYM_PROTOTYPE(EC_KEY_check_key); -extern DLSYM_PROTOTYPE(EC_KEY_free); -extern DLSYM_PROTOTYPE(EC_KEY_new); -extern DLSYM_PROTOTYPE(EC_KEY_set_group); -extern DLSYM_PROTOTYPE(EC_KEY_set_public_key); -extern DLSYM_PROTOTYPE(EVP_PKEY_assign); -extern DLSYM_PROTOTYPE(RSAPublicKey_dup); -extern DLSYM_PROTOTYPE(RSA_free); -extern DLSYM_PROTOTYPE(RSA_new); -extern DLSYM_PROTOTYPE(RSA_set0_key); -extern DLSYM_PROTOTYPE(RSA_size); -REENABLE_WARNING; - -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(EC_KEY*, sym_EC_KEY_free, EC_KEY_freep, NULL); -DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(RSA*, sym_RSA_free, RSA_freep, NULL); -#endif - /* Mirrors of OpenSSL macros that go through our dlopen'd sym_* variants, so we don't end up linking against * libcrypto just for these. */ #define sym_BIO_get_md_ctx(b, mdcp) sym_BIO_ctrl((b), BIO_C_GET_MD_CTX, 0, (char*) (mdcp)) @@ -312,7 +292,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(RSA*, sym_RSA_free, RSA_freep, NULL); #define sym_BN_one(a) sym_BN_set_word(a, 1) #define sym_EVP_MD_CTX_get_size(ctx) sym_EVP_MD_get_size(sym_EVP_MD_CTX_get0_md(ctx)) #define sym_EVP_MD_CTX_get0_name(ctx) sym_EVP_MD_get0_name(sym_EVP_MD_CTX_get0_md(ctx)) -#define sym_EVP_PKEY_assign_RSA(pkey, rsa) sym_EVP_PKEY_assign((pkey), EVP_PKEY_RSA, (rsa)) #define sym_OPENSSL_free(addr) sym_CRYPTO_free((addr), OPENSSL_FILE, OPENSSL_LINE) #define sym_PKCS7_set_detached(p, v) sym_PKCS7_ctrl((p), PKCS7_OP_SET_DETACHED_SIGNATURE, (v), NULL) From c0c6a9e8e477c79370afd2415785f5c33f05ff43 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 30 Jun 2026 23:56:43 +0900 Subject: [PATCH 10/23] crypto-util: simplify pubkey_fingerprint() There is no need to call i2d_PublicKey() twice. Passing a pointer to NULL allows OpenSSL to automatically allocate the necessary buffer. --- src/shared/crypto-util.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 68455abc64..f99147d7bb 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -1635,10 +1635,9 @@ int ecc_ecdh(const EVP_PKEY *private_pkey, int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_size) { _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* m = NULL; - _cleanup_free_ void *d = NULL, *h = NULL; - int sz, lsz, msz; + _cleanup_free_ void *h = NULL; + int lsz, msz; unsigned umsz; - unsigned char *dd; int r; /* Calculates a message digest of the DER encoded public key */ @@ -1652,15 +1651,8 @@ int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_s if (r < 0) return r; - sz = sym_i2d_PublicKey(pk, NULL); - if (sz < 0) - return log_openssl_errors(LOG_DEBUG, "Unable to convert public key to DER format"); - - dd = d = malloc(sz); - if (!d) - return log_oom_debug(); - - lsz = sym_i2d_PublicKey(pk, &dd); + _cleanup_(OPENSSL_freep) void *d = NULL; + lsz = sym_i2d_PublicKey(pk, (unsigned char**) &d); if (lsz < 0) return log_openssl_errors(LOG_DEBUG, "Unable to convert public key to DER format"); From 94e8d7209ef86892e6b6d045e25d875528ea1d0d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 1 Jul 2026 00:00:11 +0900 Subject: [PATCH 11/23] crypto-util: use correct cleanup function for OpenSSL buffers Buffers allocated by OpenSSL must be freed with OPENSSL_free(). Fortunately, we do not enable the secure heap, so OPENSSL_free() is currently equivalent to free(), but let's fix this for correctness. --- src/shared/crypto-util.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index f99147d7bb..006edbe866 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -1860,7 +1860,6 @@ static int ecc_pkey_generate_volume_keys( _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey_new = NULL; _cleanup_(erase_and_freep) void *decrypted_key = NULL; - _cleanup_free_ unsigned char *saved_key = NULL; size_t decrypted_key_size, saved_key_size; int r; @@ -1892,10 +1891,17 @@ static int ecc_pkey_generate_volume_keys( /* EVP_PKEY_get1_encoded_public_key() always returns uncompressed format of EC points. See https://github.com/openssl/openssl/discussions/22835 */ - saved_key_size = sym_EVP_PKEY_get1_encoded_public_key(pkey_new, &saved_key); + _cleanup_(OPENSSL_freep) void *buf = NULL; + saved_key_size = sym_EVP_PKEY_get1_encoded_public_key(pkey_new, (unsigned char**) &buf); if (saved_key_size == 0) return log_openssl_errors(LOG_DEBUG, "Failed to convert the generated public key to SEC1 format"); + /* 'buf' is allocated by OpenSSL and must be freed via OPENSSL_free(). We duplicate it here so the + * caller can safely use standard free(). */ + _cleanup_free_ void *saved_key = memdup(buf, saved_key_size); + if (!saved_key) + return log_oom_debug(); + *ret_decrypted_key = TAKE_PTR(decrypted_key); *ret_decrypted_key_size = decrypted_key_size; *ret_saved_key = TAKE_PTR(saved_key); @@ -2278,7 +2284,7 @@ OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { } int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { - _cleanup_free_ uint8_t *der = NULL; + _cleanup_(OPENSSL_freep) void *der = NULL; int dersz, r; assert(cert); @@ -2287,7 +2293,7 @@ int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { if (r < 0) return r; - dersz = sym_i2d_X509(cert, &der); + dersz = sym_i2d_X509(cert, (unsigned char**) &der); if (dersz < 0) return log_openssl_errors(LOG_DEBUG, "Unable to convert PEM certificate to DER format"); From fc54be2f666ef09a8b85edc41c094ff23adf8558 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 1 Jul 2026 00:33:27 +0900 Subject: [PATCH 12/23] crypto-util: simplify openssl_extract_public_key() Drop memstream and i2d_PUBKEY_fp(). We can simply use i2d_PUBKEY() which automatically allocates the necessary buffer for us. Note that dropping the secure erase (erase_and_freep()) in favor of OPENSSL_free() is intentional and safe, as the buffer only holds public key material which does not need to be securely wiped. --- src/shared/crypto-util.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 006edbe866..ac4737afc0 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -306,7 +306,6 @@ DLSYM_PROTOTYPE(i2d_ECDSA_SIG) = NULL; DLSYM_PROTOTYPE(i2d_PKCS7) = NULL; DLSYM_PROTOTYPE(i2d_PKCS7_fp) = NULL; DLSYM_PROTOTYPE(i2d_PUBKEY) = NULL; -static DLSYM_PROTOTYPE(i2d_PUBKEY_fp) = NULL; static DLSYM_PROTOTYPE(i2d_PublicKey) = NULL; DLSYM_PROTOTYPE(i2d_X509) = NULL; DLSYM_PROTOTYPE(i2d_X509_NAME) = NULL; @@ -629,7 +628,6 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(i2d_PKCS7), DLSYM_ARG(i2d_PKCS7_fp), DLSYM_ARG(i2d_PUBKEY), - DLSYM_ARG(i2d_PUBKEY_fp), DLSYM_ARG(i2d_PublicKey), DLSYM_ARG(i2d_X509), DLSYM_ARG(i2d_X509_NAME), @@ -2401,21 +2399,12 @@ int openssl_extract_public_key(EVP_PKEY *private_key, EVP_PKEY **ret) { if (r < 0) return r; - _cleanup_(memstream_done) MemStream m = {}; - FILE *tf = memstream_init(&m); - if (!tf) - return -ENOMEM; - - if (sym_i2d_PUBKEY_fp(tf, private_key) != 1) + _cleanup_(OPENSSL_freep) void *buf = NULL; + int len = sym_i2d_PUBKEY(private_key, (unsigned char**) &buf); + if (len < 0) return log_openssl_errors(LOG_DEBUG, "Failed to extract public key in DER format"); - _cleanup_(erase_and_freep) char *buf = NULL; - size_t len; - r = memstream_finalize(&m, &buf, &len); - if (r < 0) - return r; - - const unsigned char *t = (const unsigned char*) buf; + const unsigned char *t = buf; if (!sym_d2i_PUBKEY(ret, &t, len)) return log_openssl_errors(LOG_DEBUG, "Failed to parse public key in DER format"); From b5e75134f380d66c3d4d173b05fca82b770a1454 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 1 Jul 2026 00:39:53 +0900 Subject: [PATCH 13/23] crypto-util: drop manual endianness handling in rsa_pkey_from_n_e() Currently, rsa_pkey_from_n_e() uses architecture-specific `#if` branches and memdup_reverse() to handle big-endian RSA components (n and e) before passing them directly to OSSL_PARAM_construct_BN(). We can simplify this by parsing the raw big-endian bytes into BIGNUMs first using BN_bin2bn(), which natively expects big-endian data. We can then push these BIGNUMs into OSSL_PARAM_BLD. This delegates the data format handling entirely to OpenSSL and successfully removes the platform-specific code. --- src/shared/crypto-util.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index ac4737afc0..8e6c5a9d44 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -1350,30 +1350,32 @@ int rsa_pkey_from_n_e(const void *n, size_t n_size, const void *e, size_t e_size if (sym_EVP_PKEY_fromdata_init(ctx) <= 0) return log_openssl_errors(LOG_DEBUG, "Failed to initialize EVP_PKEY_CTX"); - OSSL_PARAM params[3]; + _cleanup_(BN_freep) BIGNUM *bn_n = sym_BN_bin2bn(n, n_size, NULL); + if (!bn_n) + return log_openssl_errors(LOG_DEBUG, "Failed to create BIGNUM n"); -#if __BYTE_ORDER == __BIG_ENDIAN - params[0] = sym_OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, (void*)n, n_size); - params[1] = sym_OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, (void*)e, e_size); -#else - _cleanup_free_ void *native_n = memdup_reverse(n, n_size); - if (!native_n) - return log_oom_debug(); + _cleanup_(BN_freep) BIGNUM *bn_e = sym_BN_bin2bn(e, e_size, NULL); + if (!bn_e) + return log_openssl_errors(LOG_DEBUG, "Failed to create BIGNUM e"); - _cleanup_free_ void *native_e = memdup_reverse(e, e_size); - if (!native_e) - return log_oom_debug(); + _cleanup_(OSSL_PARAM_BLD_freep) OSSL_PARAM_BLD *bld = sym_OSSL_PARAM_BLD_new(); + if (!bld) + return log_openssl_errors(LOG_DEBUG, "Failed to create new OSSL_PARAM_BLD"); - params[0] = sym_OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_N, native_n, n_size); - params[1] = sym_OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_RSA_E, native_e, e_size); -#endif - params[2] = sym_OSSL_PARAM_construct_end(); + if (!sym_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, bn_n)) + return log_openssl_errors(LOG_DEBUG, "Failed to push n to RSA params"); + + if (!sym_OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, bn_e)) + return log_openssl_errors(LOG_DEBUG, "Failed to push e to RSA params"); + + _cleanup_(OSSL_PARAM_freep) OSSL_PARAM *params = sym_OSSL_PARAM_BLD_to_param(bld); + if (!params) + return log_openssl_errors(LOG_DEBUG, "Failed to build RSA OSSL_PARAM"); if (sym_EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) return log_openssl_errors(LOG_DEBUG, "Failed to create RSA EVP_PKEY"); *ret = TAKE_PTR(pkey); - return 0; } From 75bc1060950d1762eeec3a2f9049274d58ee0110 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 1 Jul 2026 01:55:39 +0900 Subject: [PATCH 14/23] memory-util: drop unused memdup_reverse() With the previous commit, now the function is unused anymore. Let's drop it. --- src/basic/memory-util.c | 16 ---------------- src/basic/memory-util.h | 3 --- 2 files changed, 19 deletions(-) diff --git a/src/basic/memory-util.c b/src/basic/memory-util.c index f03ed7adbb..10a5157ca6 100644 --- a/src/basic/memory-util.c +++ b/src/basic/memory-util.c @@ -19,22 +19,6 @@ size_t page_size(void) { return pgsz; } -void* memdup_reverse(const void *mem, size_t size) { - assert(mem); - assert(size != 0); - - void *p = malloc(size); - if (!p) - return NULL; - - uint8_t *p_dst = p; - const uint8_t *p_src = mem; - for (size_t i = 0, k = size; i < size; i++, k--) - p_dst[i] = p_src[k-1]; - - return p; -} - void* erase_and_free(void *p) { size_t l; diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h index 4caf58585a..39dff8271a 100644 --- a/src/basic/memory-util.h +++ b/src/basic/memory-util.h @@ -98,6 +98,3 @@ static inline void erase_and_freep(void *p) { static inline void erase_char(char *p) { explicit_bzero_safe(p, sizeof(char)); } - -/* Makes a copy of the buffer with reversed order of bytes */ -void* memdup_reverse(const void *mem, size_t size); From 3aaffa43f0a46cb6ba3bf880d4910a12c17fd0ed Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 1 Jul 2026 14:32:24 +0900 Subject: [PATCH 15/23] crypto-util: drop dlopen_libcrypto() from static functions --- src/shared/crypto-util.c | 48 +++++++--------------------------------- 1 file changed, 8 insertions(+), 40 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 8e6c5a9d44..5f1041d64c 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -1866,10 +1866,6 @@ static int ecc_pkey_generate_volume_keys( _cleanup_free_ char *curve_name = NULL; size_t len = 0; - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - if (sym_EVP_PKEY_get_group_name(pkey, NULL, 0, &len) != 1 || len == 0) return log_openssl_errors(LOG_DEBUG, "Failed to determine PKEY group name length"); @@ -2001,16 +1997,10 @@ static int load_key_from_provider( UI_METHOD *ui_method, EVP_PKEY **ret) { - int r; - assert(provider); assert(private_key_uri); assert(ret); - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - /* Load the provider so that this can work without any custom written configuration in /etc/. * Also load the 'default' as that seems to be the recommendation. */ if (!sym_OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) @@ -2045,18 +2035,10 @@ static int load_key_from_provider( static int load_key_from_engine(const char *engine, const char *private_key_uri, UI_METHOD *ui_method, EVP_PKEY **ret) { #if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) - int r; -#endif - assert(engine); assert(private_key_uri); assert(ret); -#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - DISABLE_WARNING_DEPRECATED_DECLARATIONS; _cleanup_(ENGINE_freep) ENGINE *e = sym_ENGINE_by_id(engine); if (!e) @@ -2126,10 +2108,6 @@ static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) assert(path); assert(ret); - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - r = read_full_file_full( AT_FDCWD, path, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, @@ -2153,17 +2131,9 @@ static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSSLAskPasswordUI **ret) { #ifndef OPENSSL_NO_UI_CONSOLE - int r; -#endif - assert(request); assert(ret); -#ifndef OPENSSL_NO_UI_CONSOLE - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - _cleanup_(UI_destroy_methodp) UI_METHOD *method = sym_UI_create_method("systemd-ask-password"); if (!method) return log_openssl_errors(LOG_DEBUG, "Failed to initialize openssl user interface"); @@ -2202,10 +2172,6 @@ static int load_x509_certificate_from_file(const char *path, X509 **ret) { assert(path); assert(ret); - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - r = read_full_file_full( AT_FDCWD, path, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, @@ -2229,16 +2195,10 @@ static int load_x509_certificate_from_file(const char *path, X509 **ret) { } static int load_x509_certificate_from_provider(const char *provider, const char *certificate_uri, X509 **ret) { - int r; - assert(provider); assert(certificate_uri); assert(ret); - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - /* Load the provider so that this can work without any custom written configuration in /etc/. * Also load the 'default' as that seems to be the recommendation. */ if (!sym_OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) @@ -2311,6 +2271,10 @@ int openssl_load_x509_certificate( assert(certificate); + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + switch (certificate_source_type) { case OPENSSL_CERTIFICATE_SOURCE_FILE: @@ -2350,6 +2314,10 @@ int openssl_load_private_key( assert(ret_private_key); assert(ret_user_interface); + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + if (private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { r = openssl_load_private_key_from_file(private_key, ret_private_key); if (r < 0) From 7c21061f091d9063f256a9cc4984a8e45d302730 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 1 Jul 2026 14:44:39 +0900 Subject: [PATCH 16/23] crypto-util: make OpenSSL ENGINE API symbols optional during dlopen If systemd is compiled with OpenSSL 3 headers but executed in an environment where OpenSSL 4 (libcrypto.so.4) is loaded, dlopen_many_sym_or_warn() will fail because OpenSSL 4 completely removes the deprecated ENGINE API. This breaks the ability to dynamically fallback and seamlessly upgrade OpenSSL without recompiling systemd. To fix this, drop the ENGINE API symbols from the mandatory DLSYM_ARG() list. Instead, try to load them via DLSYM_OPTIONAL() after the library is opened. load_key_from_engine() is updated to check for their presence and return -EOPNOTSUPP if the loaded OpenSSL version does not provide them. --- src/shared/crypto-util.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 5f1041d64c..fddb2ba206 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -631,12 +631,6 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(i2d_PublicKey), DLSYM_ARG(i2d_X509), DLSYM_ARG(i2d_X509_NAME), -#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) - DLSYM_ARG_FORCE(ENGINE_by_id), - DLSYM_ARG_FORCE(ENGINE_free), - DLSYM_ARG_FORCE(ENGINE_init), - DLSYM_ARG_FORCE(ENGINE_load_private_key), -#endif #ifndef OPENSSL_NO_UI_CONSOLE DLSYM_ARG(UI_OpenSSL), DLSYM_ARG(UI_create_method), @@ -661,6 +655,15 @@ int dlopen_libcrypto(int log_level) { return -EOPNOTSUPP; /* turn into recognizable error */ } +#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) + /* Load ENGINE API optionally so we don't fail when loading libcrypto.so.4 even if systemd is built + * with openssl-3 headers. */ + DLSYM_OPTIONAL(libcrypto_dl, ENGINE_by_id); + DLSYM_OPTIONAL(libcrypto_dl, ENGINE_init); + DLSYM_OPTIONAL(libcrypto_dl, ENGINE_free); + DLSYM_OPTIONAL(libcrypto_dl, ENGINE_load_private_key); +#endif + return r; #else return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP), @@ -2040,6 +2043,13 @@ static int load_key_from_engine(const char *engine, const char *private_key_uri, assert(ret); DISABLE_WARNING_DEPRECATED_DECLARATIONS; + if (!sym_ENGINE_by_id || + !sym_ENGINE_free || + !sym_ENGINE_init || + !sym_ENGINE_load_private_key) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "ENGINE API is not available in the loaded OpenSSL library."); + _cleanup_(ENGINE_freep) ENGINE *e = sym_ENGINE_by_id(engine); if (!e) return log_openssl_errors(LOG_DEBUG, "Failed to load signing engine '%s'", engine); From 7301e2602daed8330a84ac5a38639b143819692a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 2 Jul 2026 03:29:29 +0900 Subject: [PATCH 17/23] crypto-util: move functions Implementations for loading private/public keys and X.509 certificates were scattered. Group them together to improve readability. --- src/shared/crypto-util.c | 330 +++++++++++++++++++-------------------- 1 file changed, 165 insertions(+), 165 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index fddb2ba206..60e2730bc7 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -2070,6 +2070,49 @@ static int load_key_from_engine(const char *engine, const char *private_key_uri, #endif } +static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) { + _cleanup_(erase_and_freep) char *rawkey = NULL; + _cleanup_(BIO_freep) BIO *kb = NULL; + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL; + size_t rawkeysz; + int r; + + assert(path); + assert(ret); + + r = read_full_file_full( + AT_FDCWD, path, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &rawkey, &rawkeysz); + if (r < 0) + return log_debug_errno(r, "Failed to read key file '%s': %m", path); + + kb = sym_BIO_new_mem_buf(rawkey, rawkeysz); + if (!kb) + return log_oom_debug(); + + pk = sym_PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL); + if (!pk) + return log_openssl_errors(LOG_DEBUG, "Failed to parse PEM private key"); + + *ret = TAKE_PTR(pk); + + return 0; +} + +OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { + if (!ui) + return NULL; + +#ifndef OPENSSL_NO_UI_CONSOLE + assert(sym_UI_get_default_method() == ui->method); + sym_UI_set_default_method(sym_UI_OpenSSL()); + sym_UI_destroy_method(ui->method); +#endif + return mfree(ui); +} + #ifndef OPENSSL_NO_UI_CONSOLE static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) { int r; @@ -2108,37 +2151,6 @@ static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) { } #endif -static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) { - _cleanup_(erase_and_freep) char *rawkey = NULL; - _cleanup_(BIO_freep) BIO *kb = NULL; - _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL; - size_t rawkeysz; - int r; - - assert(path); - assert(ret); - - r = read_full_file_full( - AT_FDCWD, path, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &rawkey, &rawkeysz); - if (r < 0) - return log_debug_errno(r, "Failed to read key file '%s': %m", path); - - kb = sym_BIO_new_mem_buf(rawkey, rawkeysz); - if (!kb) - return log_oom_debug(); - - pk = sym_PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL); - if (!pk) - return log_openssl_errors(LOG_DEBUG, "Failed to parse PEM private key"); - - *ret = TAKE_PTR(pk); - - return 0; -} - static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSSLAskPasswordUI **ret) { #ifndef OPENSSL_NO_UI_CONSOLE assert(request); @@ -2172,140 +2184,6 @@ static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSS #endif } -static int load_x509_certificate_from_file(const char *path, X509 **ret) { - _cleanup_free_ char *rawcert = NULL; - _cleanup_(X509_freep) X509 *cert = NULL; - _cleanup_(BIO_freep) BIO *cb = NULL; - size_t rawcertsz; - int r; - - assert(path); - assert(ret); - - r = read_full_file_full( - AT_FDCWD, path, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &rawcert, &rawcertsz); - if (r < 0) - return log_debug_errno(r, "Failed to read certificate file '%s': %m", path); - - cb = sym_BIO_new_mem_buf(rawcert, rawcertsz); - if (!cb) - return log_oom_debug(); - - cert = sym_PEM_read_bio_X509(cb, NULL, NULL, NULL); - if (!cert) - return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", - sym_ERR_error_string(sym_ERR_get_error(), NULL)); - - *ret = TAKE_PTR(cert); - - return 0; -} - -static int load_x509_certificate_from_provider(const char *provider, const char *certificate_uri, X509 **ret) { - assert(provider); - assert(certificate_uri); - assert(ret); - - /* Load the provider so that this can work without any custom written configuration in /etc/. - * Also load the 'default' as that seems to be the recommendation. */ - if (!sym_OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) - return log_openssl_errors(LOG_DEBUG, "Failed to load OpenSSL provider '%s'", provider); - if (!sym_OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true)) - return log_openssl_errors(LOG_DEBUG, "Failed to load OpenSSL provider 'default'"); - - _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = sym_OSSL_STORE_open( - certificate_uri, - /* ui_method= */ NULL, - /* ui_method= */ NULL, - /* post_process= */ NULL, - /* post_process_data= */ NULL); - if (!store) - return log_openssl_errors(LOG_DEBUG, "Failed to open OpenSSL store via '%s'", certificate_uri); - - if (sym_OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) == 0) - return log_openssl_errors(LOG_DEBUG, "Failed to filter store by X.509 certificates"); - - _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = sym_OSSL_STORE_load(store); - if (!info) - return log_openssl_errors(LOG_DEBUG, "Failed to load OpenSSL store via '%s'", certificate_uri); - - _cleanup_(X509_freep) X509 *cert = sym_OSSL_STORE_INFO_get1_CERT(info); - if (!cert) - return log_openssl_errors(LOG_DEBUG, "Failed to load certificate via '%s'", certificate_uri); - - *ret = TAKE_PTR(cert); - - return 0; -} - -OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { - if (!ui) - return NULL; - -#ifndef OPENSSL_NO_UI_CONSOLE - assert(sym_UI_get_default_method() == ui->method); - sym_UI_set_default_method(sym_UI_OpenSSL()); - sym_UI_destroy_method(ui->method); -#endif - return mfree(ui); -} - -int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { - _cleanup_(OPENSSL_freep) void *der = NULL; - int dersz, r; - - assert(cert); - - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - - dersz = sym_i2d_X509(cert, (unsigned char**) &der); - if (dersz < 0) - return log_openssl_errors(LOG_DEBUG, "Unable to convert PEM certificate to DER format"); - - sha256_direct(der, dersz, buffer); - return 0; -} - -int openssl_load_x509_certificate( - CertificateSourceType certificate_source_type, - const char *certificate_source, - const char *certificate, - X509 **ret) { - - int r; - - assert(certificate); - - r = dlopen_libcrypto(LOG_DEBUG); - if (r < 0) - return r; - - switch (certificate_source_type) { - - case OPENSSL_CERTIFICATE_SOURCE_FILE: - r = load_x509_certificate_from_file(certificate, ret); - break; - case OPENSSL_CERTIFICATE_SOURCE_PROVIDER: - r = load_x509_certificate_from_provider(certificate_source, certificate, ret); - break; - default: - assert_not_reached(); - } - if (r < 0) - return log_debug_errno( - r, - "Failed to load certificate '%s' from OpenSSL certificate source %s: %m", - certificate, - certificate_source); - - return 0; -} - int openssl_load_private_key( KeySourceType private_key_source_type, const char *private_key_source, @@ -2390,6 +2268,128 @@ int openssl_extract_public_key(EVP_PKEY *private_key, EVP_PKEY **ret) { return 0; } + +static int load_x509_certificate_from_file(const char *path, X509 **ret) { + _cleanup_free_ char *rawcert = NULL; + _cleanup_(X509_freep) X509 *cert = NULL; + _cleanup_(BIO_freep) BIO *cb = NULL; + size_t rawcertsz; + int r; + + assert(path); + assert(ret); + + r = read_full_file_full( + AT_FDCWD, path, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &rawcert, &rawcertsz); + if (r < 0) + return log_debug_errno(r, "Failed to read certificate file '%s': %m", path); + + cb = sym_BIO_new_mem_buf(rawcert, rawcertsz); + if (!cb) + return log_oom_debug(); + + cert = sym_PEM_read_bio_X509(cb, NULL, NULL, NULL); + if (!cert) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", + sym_ERR_error_string(sym_ERR_get_error(), NULL)); + + *ret = TAKE_PTR(cert); + + return 0; +} + +static int load_x509_certificate_from_provider(const char *provider, const char *certificate_uri, X509 **ret) { + assert(provider); + assert(certificate_uri); + assert(ret); + + /* Load the provider so that this can work without any custom written configuration in /etc/. + * Also load the 'default' as that seems to be the recommendation. */ + if (!sym_OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true)) + return log_openssl_errors(LOG_DEBUG, "Failed to load OpenSSL provider '%s'", provider); + if (!sym_OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true)) + return log_openssl_errors(LOG_DEBUG, "Failed to load OpenSSL provider 'default'"); + + _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = sym_OSSL_STORE_open( + certificate_uri, + /* ui_method= */ NULL, + /* ui_method= */ NULL, + /* post_process= */ NULL, + /* post_process_data= */ NULL); + if (!store) + return log_openssl_errors(LOG_DEBUG, "Failed to open OpenSSL store via '%s'", certificate_uri); + + if (sym_OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) == 0) + return log_openssl_errors(LOG_DEBUG, "Failed to filter store by X.509 certificates"); + + _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = sym_OSSL_STORE_load(store); + if (!info) + return log_openssl_errors(LOG_DEBUG, "Failed to load OpenSSL store via '%s'", certificate_uri); + + _cleanup_(X509_freep) X509 *cert = sym_OSSL_STORE_INFO_get1_CERT(info); + if (!cert) + return log_openssl_errors(LOG_DEBUG, "Failed to load certificate via '%s'", certificate_uri); + + *ret = TAKE_PTR(cert); + + return 0; +} + +int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { + _cleanup_(OPENSSL_freep) void *der = NULL; + int dersz, r; + + assert(cert); + + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + + dersz = sym_i2d_X509(cert, (unsigned char**) &der); + if (dersz < 0) + return log_openssl_errors(LOG_DEBUG, "Unable to convert PEM certificate to DER format"); + + sha256_direct(der, dersz, buffer); + return 0; +} + +int openssl_load_x509_certificate( + CertificateSourceType certificate_source_type, + const char *certificate_source, + const char *certificate, + X509 **ret) { + + int r; + + assert(certificate); + + r = dlopen_libcrypto(LOG_DEBUG); + if (r < 0) + return r; + + switch (certificate_source_type) { + + case OPENSSL_CERTIFICATE_SOURCE_FILE: + r = load_x509_certificate_from_file(certificate, ret); + break; + case OPENSSL_CERTIFICATE_SOURCE_PROVIDER: + r = load_x509_certificate_from_provider(certificate_source, certificate, ret); + break; + default: + assert_not_reached(); + } + if (r < 0) + return log_debug_errno( + r, + "Failed to load certificate '%s' from OpenSSL certificate source %s: %m", + certificate, + certificate_source); + + return 0; +} #endif int parse_openssl_certificate_source_argument( From f63cc9af18dc10440b79d3b093c5c10b89fb36af Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 2 Jul 2026 03:38:03 +0900 Subject: [PATCH 18/23] crypto-util: drop redundant logs The called functions already log errors internally. --- src/shared/crypto-util.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index 60e2730bc7..b3f714acbc 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -2216,7 +2216,7 @@ int openssl_load_private_key( _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; r = openssl_ask_password_ui_new(request, &ui); if (r < 0) - return log_debug_errno(r, "Failed to allocate ask-password user interface: %m"); + return r; UI_METHOD *ui_method = NULL; #ifndef OPENSSL_NO_UI_CONSOLE @@ -2235,11 +2235,7 @@ int openssl_load_private_key( assert_not_reached(); } if (r < 0) - return log_debug_errno( - r, - "Failed to load key '%s' from OpenSSL private key source %s: %m", - private_key, - private_key_source); + return r; *ret_user_interface = TAKE_PTR(ui); } From 805f8c6503a79138c17031314c1a746784effe3a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 2 Jul 2026 03:33:04 +0900 Subject: [PATCH 19/23] crypto-util: allow loading private keys from engine/provider without UI support OpenSSL UI is not a mandatory feature to load private keys from an engine or a provider. Let's allow loading private keys even if OpenSSL UI is not supported. Note that even if OPENSSL_NO_UI_CONSOLE is set, the type UI_METHOD is always defined. Hence, the `#ifndef` condition in the definition of struct OpenSSLAskPasswordUI is unnecessary and can be dropped. --- src/shared/crypto-util.c | 57 +++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index b3f714acbc..f9e0c7f628 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -31,9 +31,7 @@ struct OpenSSLAskPasswordUI { AskPasswordRequest request; -#ifndef OPENSSL_NO_UI_CONSOLE UI_METHOD *method; -#endif }; DLSYM_PROTOTYPE(ASN1_ANY_it) = NULL; @@ -1997,7 +1995,7 @@ int pkey_generate_volume_keys( static int load_key_from_provider( const char *provider, const char *private_key_uri, - UI_METHOD *ui_method, + UI_METHOD *ui_method, /* can be NULL */ EVP_PKEY **ret) { assert(provider); @@ -2036,7 +2034,12 @@ static int load_key_from_provider( return 0; } -static int load_key_from_engine(const char *engine, const char *private_key_uri, UI_METHOD *ui_method, EVP_PKEY **ret) { +static int load_key_from_engine( + const char *engine, + const char *private_key_uri, + UI_METHOD *ui_method, /* can be NULL */ + EVP_PKEY **ret) { + #if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) assert(engine); assert(private_key_uri); @@ -2152,10 +2155,10 @@ static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) { #endif static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSSLAskPasswordUI **ret) { -#ifndef OPENSSL_NO_UI_CONSOLE assert(request); assert(ret); +#ifndef OPENSSL_NO_UI_CONSOLE _cleanup_(UI_destroy_methodp) UI_METHOD *method = sym_UI_create_method("systemd-ask-password"); if (!method) return log_openssl_errors(LOG_DEBUG, "Failed to initialize openssl user interface"); @@ -2180,7 +2183,8 @@ static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSS *ret = TAKE_PTR(ui); return 0; #else - return -EOPNOTSUPP; + *ret = NULL; + return 0; #endif } @@ -2206,40 +2210,33 @@ int openssl_load_private_key( if (r < 0) return r; - if (private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { - r = openssl_load_private_key_from_file(private_key, ret_private_key); - if (r < 0) - return r; + _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; - *ret_user_interface = NULL; - } else { - _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; + switch (private_key_source_type) { + case OPENSSL_KEY_SOURCE_FILE: + r = openssl_load_private_key_from_file(private_key, ret_private_key); + break; + case OPENSSL_KEY_SOURCE_ENGINE: r = openssl_ask_password_ui_new(request, &ui); if (r < 0) return r; - UI_METHOD *ui_method = NULL; -#ifndef OPENSSL_NO_UI_CONSOLE - ui_method = ui->method; -#endif - - switch (private_key_source_type) { - - case OPENSSL_KEY_SOURCE_ENGINE: - r = load_key_from_engine(private_key_source, private_key, ui_method, ret_private_key); - break; - case OPENSSL_KEY_SOURCE_PROVIDER: - r = load_key_from_provider(private_key_source, private_key, ui_method, ret_private_key); - break; - default: - assert_not_reached(); - } + r = load_key_from_engine(private_key_source, private_key, ui ? ui->method : NULL, ret_private_key); + break; + case OPENSSL_KEY_SOURCE_PROVIDER: + r = openssl_ask_password_ui_new(request, &ui); if (r < 0) return r; - *ret_user_interface = TAKE_PTR(ui); + r = load_key_from_provider(private_key_source, private_key, ui ? ui->method : NULL, ret_private_key); + break; + default: + assert_not_reached(); } + if (r < 0) + return r; + *ret_user_interface = TAKE_PTR(ui); return 0; } From d26da0a7057cd0db563c12d2297a57e20528253d Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 2 Jul 2026 03:47:06 +0900 Subject: [PATCH 20/23] crypto-util: make OpenSSL UI API symbols optional during dlopen Previously, if systemd was built with OpenSSL UI support, it would fail to load libcrypto at runtime if the library lacked UI support, requiring a recompilation of systemd to fix. Let's relax this strict requirement by making the UI methods optional during dlopen(). openssl_ui_supported() is added to dynamically check if all required UI symbols were successfully loaded. --- src/shared/crypto-util.c | 72 +++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 20 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index f9e0c7f628..a6aa020bdc 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -628,23 +628,7 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(i2d_PUBKEY), DLSYM_ARG(i2d_PublicKey), DLSYM_ARG(i2d_X509), - DLSYM_ARG(i2d_X509_NAME), -#ifndef OPENSSL_NO_UI_CONSOLE - DLSYM_ARG(UI_OpenSSL), - DLSYM_ARG(UI_create_method), - DLSYM_ARG(UI_destroy_method), - DLSYM_ARG(UI_get0_output_string), - DLSYM_ARG(UI_get_default_method), - DLSYM_ARG(UI_get_method), - DLSYM_ARG(UI_get_string_type), - DLSYM_ARG(UI_method_get_ex_data), - DLSYM_ARG(UI_method_get_reader), - DLSYM_ARG(UI_method_set_ex_data), - DLSYM_ARG(UI_method_set_reader), - DLSYM_ARG(UI_set_default_method), - DLSYM_ARG(UI_set_result), -#endif - NULL); + DLSYM_ARG(i2d_X509_NAME)); if (r >= 0) break; } @@ -653,6 +637,24 @@ int dlopen_libcrypto(int log_level) { return -EOPNOTSUPP; /* turn into recognizable error */ } +#ifndef OPENSSL_NO_UI_CONSOLE + /* Load UI API optionally so we don't fail to load libcrypto.so lacking UI support, + * even if systemd is built with UI support enabled in the headers. */ + DLSYM_OPTIONAL(libcrypto_dl, UI_OpenSSL); + DLSYM_OPTIONAL(libcrypto_dl, UI_create_method); + DLSYM_OPTIONAL(libcrypto_dl, UI_destroy_method); + DLSYM_OPTIONAL(libcrypto_dl, UI_get0_output_string); + DLSYM_OPTIONAL(libcrypto_dl, UI_get_default_method); + DLSYM_OPTIONAL(libcrypto_dl, UI_get_method); + DLSYM_OPTIONAL(libcrypto_dl, UI_get_string_type); + DLSYM_OPTIONAL(libcrypto_dl, UI_method_get_ex_data); + DLSYM_OPTIONAL(libcrypto_dl, UI_method_get_reader); + DLSYM_OPTIONAL(libcrypto_dl, UI_method_set_ex_data); + DLSYM_OPTIONAL(libcrypto_dl, UI_method_set_reader); + DLSYM_OPTIONAL(libcrypto_dl, UI_set_default_method); + DLSYM_OPTIONAL(libcrypto_dl, UI_set_result); +#endif + #if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) /* Load ENGINE API optionally so we don't fail when loading libcrypto.so.4 even if systemd is built * with openssl-3 headers. */ @@ -2104,10 +2106,33 @@ static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) return 0; } +static bool openssl_ui_supported(void) { +#ifndef OPENSSL_NO_UI_CONSOLE + return + sym_UI_OpenSSL && + sym_UI_create_method && + sym_UI_destroy_method && + sym_UI_get0_output_string && + sym_UI_get_default_method && + sym_UI_get_method && + sym_UI_get_string_type && + sym_UI_method_get_ex_data && + sym_UI_method_get_reader && + sym_UI_method_set_ex_data && + sym_UI_method_set_reader && + sym_UI_set_default_method && + sym_UI_set_result; +#else + return false; +#endif +} + OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { if (!ui) return NULL; + assert(openssl_ui_supported()); + #ifndef OPENSSL_NO_UI_CONSOLE assert(sym_UI_get_default_method() == ui->method); sym_UI_set_default_method(sym_UI_OpenSSL()); @@ -2120,7 +2145,9 @@ OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) { int r; - switch(sym_UI_get_string_type(uis)) { + assert(openssl_ui_supported()); + + switch (sym_UI_get_string_type(uis)) { case UIT_PROMPT: { /* If no ask password request was configured use the default openssl UI. */ AskPasswordRequest *req = (AskPasswordRequest*) sym_UI_method_get_ex_data(sym_UI_get_method(ui), 0); @@ -2158,6 +2185,12 @@ static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSS assert(request); assert(ret); + if (!openssl_ui_supported()) { + log_debug("OpenSSL UI API is not supported."); + *ret = NULL; + return 0; + } + #ifndef OPENSSL_NO_UI_CONSOLE _cleanup_(UI_destroy_methodp) UI_METHOD *method = sym_UI_create_method("systemd-ask-password"); if (!method) @@ -2183,8 +2216,7 @@ static int openssl_ask_password_ui_new(const AskPasswordRequest *request, OpenSS *ret = TAKE_PTR(ui); return 0; #else - *ret = NULL; - return 0; + assert_not_reached(); #endif } From 71a94be08c54a8f50564f546fd31cf9965e9333f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 2 Jul 2026 04:46:51 +0900 Subject: [PATCH 21/23] crypto-util: drop unused symbol --- src/shared/crypto-util.c | 2 -- src/shared/crypto-util.h | 1 - 2 files changed, 3 deletions(-) diff --git a/src/shared/crypto-util.c b/src/shared/crypto-util.c index a6aa020bdc..d791996efc 100644 --- a/src/shared/crypto-util.c +++ b/src/shared/crypto-util.c @@ -274,7 +274,6 @@ DLSYM_PROTOTYPE(PKCS7_set_content) = NULL; static DLSYM_PROTOTYPE(PKCS7_set_type) = NULL; DLSYM_PROTOTYPE(PKCS7_sign) = NULL; DLSYM_PROTOTYPE(PKCS7_verify) = NULL; -DLSYM_PROTOTYPE(SHA1) = NULL; DLSYM_PROTOTYPE(SHA512) = NULL; DLSYM_PROTOTYPE(X509_ALGOR_free) = NULL; static DLSYM_PROTOTYPE(X509_ALGOR_set0) = NULL; @@ -596,7 +595,6 @@ int dlopen_libcrypto(int log_level) { DLSYM_ARG(PKCS7_set_type), DLSYM_ARG(PKCS7_sign), DLSYM_ARG(PKCS7_verify), - DLSYM_ARG(SHA1), DLSYM_ARG(SHA512), DLSYM_ARG(X509_ALGOR_free), DLSYM_ARG(X509_ALGOR_set0), diff --git a/src/shared/crypto-util.h b/src/shared/crypto-util.h index b0358090a7..42a3f2f8c0 100644 --- a/src/shared/crypto-util.h +++ b/src/shared/crypto-util.h @@ -255,7 +255,6 @@ extern DLSYM_PROTOTYPE(PKCS7_new); extern DLSYM_PROTOTYPE(PKCS7_set_content); extern DLSYM_PROTOTYPE(PKCS7_sign); extern DLSYM_PROTOTYPE(PKCS7_verify); -extern DLSYM_PROTOTYPE(SHA1); extern DLSYM_PROTOTYPE(SHA512); extern DLSYM_PROTOTYPE(X509_ALGOR_free); extern DLSYM_PROTOTYPE(X509_ATTRIBUTE_free); From 4bf6e751984db9bf35a516a4177bb5b3493989dc Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 2 Jul 2026 04:47:32 +0900 Subject: [PATCH 22/23] ci/build-test: try to build with OPENSSL_NO_DEPRECATED --- .github/workflows/build-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-test.sh b/.github/workflows/build-test.sh index 9c3d2e7338..084e2c4f1a 100755 --- a/.github/workflows/build-test.sh +++ b/.github/workflows/build-test.sh @@ -12,7 +12,7 @@ success() { echo >&2 -e "\033[32;1m$1\033[0m"; } ARGS=( "--optimization=0 -Dopenssl=disabled -Dtpm=true -Dtpm2=enabled" "--optimization=s -Dutmp=false -Dc_args='-DOPENSSL_NO_UI_CONSOLE=1'" - "--optimization=2 -Ddns-over-tls=openssl" + "--optimization=2 -Ddns-over-tls=openssl -Dc_args='-DOPENSSL_NO_DEPRECATED'" "--optimization=3 -Db_lto=true -Ddns-over-tls=false" "--optimization=3 -Db_lto=false -Dtpm2=disabled -Dlibfido2=disabled -Dp11kit=disabled -Defi=false -Dbootloader=disabled" "--optimization=3 -Dfexecve=true -Dstandalone-binaries=true -Dstatic-libsystemd=true -Dstatic-libudev=true" From d04423c870a7b62f376e26ce7a4bc48d2370f423 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 2 Jul 2026 17:31:38 +0900 Subject: [PATCH 23/23] bless-boot: avoid false maybe-uninitialized warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Obserbed with GCC-11 on Ubuntu. ``` In file included from ../src/shared/format-table.h:7, from ../src/bless-boot/bless-boot.c:11: ../src/bless-boot/bless-boot.c: In function ‘verb_set’: ../src/basic/log.h:187:27: error: ‘source2’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 187 | ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ | ^~~~~~~~~~~~ ../src/bless-boot/bless-boot.c:458:40: note: ‘source2’ was declared here 458 | const char *target, *source1, *source2; | ^~~~~~~ In file included from ../src/shared/format-table.h:7, from ../src/bless-boot/bless-boot.c:11: ../src/basic/log.h:187:27: error: ‘source1’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 187 | ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ | ^~~~~~~~~~~~ ../src/bless-boot/bless-boot.c:458:30: note: ‘source1’ was declared here 458 | const char *target, *source1, *source2; | ^~~~~~~ In file included from ../src/shared/format-table.h:7, from ../src/bless-boot/bless-boot.c:11: ../src/basic/log.h:187:27: error: ‘target’ may be used uninitialized in this function [-Werror=maybe-uninitialized] 187 | ? log_internal(_level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \ | ^~~~~~~~~~~~ ../src/bless-boot/bless-boot.c:458:21: note: ‘target’ was declared here 458 | const char *target, *source1, *source2; | ^~~~~~ cc1: all warnings being treated as errors ``` --- src/bless-boot/bless-boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bless-boot/bless-boot.c b/src/bless-boot/bless-boot.c index 43fb72cddb..4242182ac1 100644 --- a/src/bless-boot/bless-boot.c +++ b/src/bless-boot/bless-boot.c @@ -455,7 +455,7 @@ VERB_FULL(verb_set, "indeterminate", NULL, VERB_ANY, 1, 0, STATUS_INDETERMINATE, "Undo any marking as good or bad"); static int verb_set(int argc, char *argv[], uintptr_t data, void *userdata) { _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL; - const char *target, *source1, *source2; + const char *target = NULL, *source1 = NULL, *source2 = NULL; /* avoid false maybe-uninitialized warning */ uint64_t left, done; Status status = data; int r;