From ade99252e2cdd9eeff78566789008996d27e4dc0 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Wed, 20 Oct 2021 12:21:18 +0200 Subject: [PATCH 01/15] repart: port to our home-grown hmac_sha256 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces dependencies. The speed of the code here is uimportant, because we hash only a tiny amount of input data. Debian and Ubuntu currently build without repart, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=976959 > repart requires openssl and so far I tried to avoid linking against > both gnutls and openssl. Co-authored-by: Zbigniew Jędrzejewski-Szmek --- src/partition/repart.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/partition/repart.c b/src/partition/repart.c index f1af5bb0ee..895c0665d8 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -12,9 +12,6 @@ #include #include -#include -#include - #include "sd-id128.h" #include "alloc-util.h" @@ -38,6 +35,7 @@ #include "glyph-util.h" #include "gpt.h" #include "hexdecoct.h" +#include "hmac.h" #include "id128-util.h" #include "json.h" #include "list.h" @@ -1519,7 +1517,7 @@ static int fdisk_set_disklabel_id_by_uuid(struct fdisk_context *c, sd_id128_t id static int derive_uuid(sd_id128_t base, const char *token, sd_id128_t *ret) { union { - unsigned char md[SHA256_DIGEST_LENGTH]; + uint8_t md[SHA256_DIGEST_SIZE]; sd_id128_t id; } result; @@ -1531,11 +1529,7 @@ static int derive_uuid(sd_id128_t base, const char *token, sd_id128_t *ret) { * machine ID). We use the machine ID as key (and not as cleartext!) of the HMAC operation since it's * the machine ID we don't want to leak. */ - if (!HMAC(EVP_sha256(), - &base, sizeof(base), - (const unsigned char*) token, strlen(token), - result.md, NULL)) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "HMAC-SHA256 calculation failed."); + hmac_sha256(base.bytes, sizeof(base.bytes), token, strlen(token), result.md); /* Take the first half, mark it as v4 UUID */ assert_cc(sizeof(result.md) == sizeof(result.id) * 2); @@ -3067,7 +3061,7 @@ static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *re uint64_t counter; } _packed_ plaintext = {}; union { - unsigned char md[SHA256_DIGEST_LENGTH]; + uint8_t md[SHA256_DIGEST_SIZE]; sd_id128_t id; } result; @@ -3111,11 +3105,10 @@ static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *re plaintext.type_uuid = p->type_uuid; plaintext.counter = htole64(k); - if (!HMAC(EVP_sha256(), - &context->seed, sizeof(context->seed), - (const unsigned char*) &plaintext, k == 0 ? sizeof(sd_id128_t) : sizeof(plaintext), - result.md, NULL)) - return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "SHA256 calculation failed."); + hmac_sha256(context->seed.bytes, sizeof(context->seed.bytes), + &plaintext, + k == 0 ? sizeof(sd_id128_t) : sizeof(plaintext), + result.md); /* Take the first half, mark it as v4 UUID */ assert_cc(sizeof(result.md) == sizeof(result.id) * 2); From 57633d2376233629c73cf580650902ba6d1ada8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 2 Nov 2021 09:44:12 +0100 Subject: [PATCH 02/15] meson: add config setting to select between openssl and gcrypt This is not pretty, but it is supposed to be only a temporary measure. --- meson.build | 20 ++++++++++++++++++++ meson_options.txt | 2 ++ src/shared/openssl-util.h | 31 ++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index b0054b6667..dee05b6017 100644 --- a/meson.build +++ b/meson.build @@ -1523,6 +1523,18 @@ else endif conf.set10('ENABLE_REPART', have) +# We support one or the other. If gcrypt is available, we assume it's there to +# be used, and use it in preference. +opt = get_option('cryptolib') +if opt == 'openssl' and conf.get('HAVE_OPENSSL') == 0 + error('openssl requested as the default cryptolib, but not available') +endif +conf.set10('PREFER_OPENSSL', + opt == 'openssl' or (opt == 'auto' and conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_GCRYPT') == 0)) +conf.set10('HAVE_OPENSSL_OR_GCRYPT', + conf.get('HAVE_OPENSSL') == 1 or conf.get('HAVE_GCRYPT') == 1) +lib_openssl_or_gcrypt = conf.get('PREFER_OPENSSL') == 1 ? libopenssl : libgcrypt + want_importd = get_option('importd') if want_importd != 'false' have = (conf.get('HAVE_LIBCURL') == 1 and @@ -4023,6 +4035,14 @@ else found += 'static-libudev(@0@)'.format(static_libudev) endif +if conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 and conf.get('PREFER_OPENSSL') == 1 + found += 'cryptolib(openssl)' +elif conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 + found += 'cryptolib(gcrypt)' +else + missing += 'cryptolib' +endif + if conf.get('DNS_OVER_TLS_USE_GNUTLS') == 1 found += 'DNS-over-TLS(gnutls)' elif conf.get('DNS_OVER_TLS_USE_OPENSSL') == 1 diff --git a/meson_options.txt b/meson_options.txt index 1e91bf1fd2..0d3491a56c 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -380,6 +380,8 @@ option('gnutls', type : 'combo', choices : ['auto', 'true', 'false'], description : 'gnutls support') option('openssl', type : 'combo', choices : ['auto', 'true', 'false'], description : 'openssl support') +option('cryptolib', type : 'combo', choices : ['auto', 'openssl', 'gcrypt'], + description : 'whether to use openssl or gcrypt where both are supported') option('p11kit', type : 'combo', choices : ['auto', 'true', 'false'], description : 'p11kit support') option('libfido2', type : 'combo', choices : ['auto', 'true', 'false'], diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 5840d57d16..eca56d1729 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -5,6 +5,8 @@ #if HAVE_OPENSSL # include +# include +# include # include # include # include @@ -13,7 +15,15 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509_NAME*, X509_NAME_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY_CTX*, EVP_PKEY_CTX_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY*, EVP_PKEY_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER_CTX*, EVP_CIPHER_CTX_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(RSA*, RSA_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_POINT*, EC_POINT_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_GROUP*, EC_GROUP_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIGNUM*, BN_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BN_CTX*, BN_CTX_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ECDSA_SIG*, ECDSA_SIG_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(PKCS7*, PKCS7_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL); @@ -29,5 +39,24 @@ static inline void sk_X509_free_allp(STACK_OF(X509) **sk) { int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size); int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size); - +#endif + +#if PREFER_OPENSSL +/* The openssl definition */ +typedef const EVP_MD* hash_md_t; +typedef const EVP_MD* hash_algorithm_t; +typedef int elliptic_curve_t; +typedef EVP_MD_CTX* hash_context_t; +# define OPENSSL_OR_GCRYPT(a, b) (a) + +#elif HAVE_GCRYPT + +# include + +/* The gcrypt definition */ +typedef int hash_md_t; +typedef const char* hash_algorithm_t; +typedef const char* elliptic_curve_t; +typedef gcry_md_hd_t hash_context_t; +# define OPENSSL_OR_GCRYPT(a, b) (b) #endif From 6214d42bd26657e6f406b6f7039fbbac14d52369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 2 Nov 2021 09:58:04 +0100 Subject: [PATCH 03/15] import: port importd from libgcrypt to openssl^gcrypt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is heavily based on Kevin Kuehler's work, but the logic is also significantly changed: instead of a straighforward port to openssl, both versions of the code are kept, and at compile time we pick one or the other. The code is purposefully kept "dumb" — the idea is that the libgcrypt codepaths are only temporary and will be removed after everybody upgrades to openssl 3. Thus, a separate abstraction layer is not introduced. Instead, very simple ifdefs are used to select one or the other. If we added an abstraction layer, we'd have to remove it again afterwards, and it don't think it makes sense to do that for a temporary solution. Co-authored-by: Zbigniew Jędrzejewski-Szmek # Conflicts: # meson.build --- TODO | 1 - meson.build | 8 +++--- src/import/pull-job.c | 67 ++++++++++++++++++++++++++++++++++--------- src/import/pull-job.h | 4 +-- 4 files changed, 60 insertions(+), 20 deletions(-) diff --git a/TODO b/TODO index 0047829c51..e710f2c587 100644 --- a/TODO +++ b/TODO @@ -437,7 +437,6 @@ Features: confusion is gone) - port resolved over from libgcrypt (DNSSEC code) - port journald + fsprg over from libgcrypt - - port importd over from libgcrypt - when that's done: kill gnutls support in resolved * add growvol and makevol options for /etc/crypttab, similar to diff --git a/meson.build b/meson.build index dee05b6017..5748853121 100644 --- a/meson.build +++ b/meson.build @@ -1538,9 +1538,9 @@ lib_openssl_or_gcrypt = conf.get('PREFER_OPENSSL') == 1 ? libopenssl : libgcrypt want_importd = get_option('importd') if want_importd != 'false' have = (conf.get('HAVE_LIBCURL') == 1 and + conf.get('HAVE_OPENSSL_OR_GCRYPT') == 1 and conf.get('HAVE_ZLIB') == 1 and - conf.get('HAVE_XZ') == 1 and - conf.get('HAVE_GCRYPT') == 1) + conf.get('HAVE_XZ') == 1) if want_importd == 'true' and not have error('importd support was requested, but dependencies are not available') endif @@ -2729,10 +2729,10 @@ if conf.get('ENABLE_IMPORTD') == 1 link_with : [libshared], dependencies : [versiondep, libcurl, + lib_openssl_or_gcrypt, libz, libbzip2, - libxz, - libgcrypt], + libxz], install_rpath : rootlibexecdir, install : true, install_dir : rootlibexecdir) diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 34b116a8f8..f5eb82131e 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -41,8 +41,12 @@ PullJob* pull_job_unref(PullJob *j) { import_compress_free(&j->compress); - if (j->checksum_context) - gcry_md_close(j->checksum_context); + if (j->checksum_ctx) +#if PREFER_OPENSSL + EVP_MD_CTX_free(j->checksum_ctx); +#else + gcry_md_close(j->checksum_ctx); +#endif free(j->url); free(j->etag); @@ -102,9 +106,13 @@ static int pull_job_restart(PullJob *j, const char *new_url) { import_compress_free(&j->compress); - if (j->checksum_context) { - gcry_md_close(j->checksum_context); - j->checksum_context = NULL; + if (j->checksum_ctx) { +#if PREFER_OPENSSL + EVP_MD_CTX_free(j->checksum_ctx); +#else + gcry_md_close(j->checksum_ctx); +#endif + j->checksum_ctx = NULL; } r = pull_job_begin(j); @@ -200,16 +208,30 @@ void pull_job_curl_on_finished(CurlGlue *g, CURL *curl, CURLcode result) { goto finish; } - if (j->checksum_context) { - uint8_t *k; + if (j->checksum_ctx) { + unsigned checksum_len; +#if PREFER_OPENSSL + uint8_t k[EVP_MAX_MD_SIZE]; - k = gcry_md_read(j->checksum_context, GCRY_MD_SHA256); + r = EVP_DigestFinal_ex(j->checksum_ctx, k, &checksum_len); + if (r == 0) { + r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum."); + goto finish; + } + assert(checksum_len <= sizeof k); +#else + const uint8_t *k; + + k = gcry_md_read(j->checksum_ctx, GCRY_MD_SHA256); if (!k) { r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get checksum."); goto finish; } - j->checksum = hexmem(k, gcry_md_get_algo_dlen(GCRY_MD_SHA256)); + checksum_len = gcry_md_get_algo_dlen(GCRY_MD_SHA256); +#endif + + j->checksum = hexmem(k, checksum_len); if (!j->checksum) { r = log_oom(); goto finish; @@ -358,8 +380,16 @@ static int pull_job_write_compressed(PullJob *j, void *p, size_t sz) { return log_error_errno(SYNTHETIC_ERRNO(EFBIG), "Content length incorrect."); - if (j->checksum_context) - gcry_md_write(j->checksum_context, p, sz); + if (j->checksum_ctx) { +#if PREFER_OPENSSL + r = EVP_DigestUpdate(j->checksum_ctx, p, sz); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Could not hash chunk."); +#else + gcry_md_write(j->checksum_ctx, p, sz); +#endif + } r = import_uncompress(&j->compress, p, sz, pull_job_write_uncompressed, j); if (r < 0) @@ -392,11 +422,22 @@ static int pull_job_open_disk(PullJob *j) { } if (j->calc_checksum) { - initialize_libgcrypt(false); +#if PREFER_OPENSSL + j->checksum_ctx = EVP_MD_CTX_new(); + if (!j->checksum_ctx) + return log_oom(); - if (gcry_md_open(&j->checksum_context, GCRY_MD_SHA256, 0) != 0) + r = EVP_DigestInit_ex(j->checksum_ctx, EVP_sha256(), NULL); + if (r == 0) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to initialize hash context."); +#else + initialize_libgcrypt(false); + + if (gcry_md_open(&j->checksum_ctx, GCRY_MD_SHA256, 0) != 0) + return log_error_errno(SYNTHETIC_ERRNO(EIO), + "Failed to initialize hash context."); +#endif } return 0; diff --git a/src/import/pull-job.h b/src/import/pull-job.h index bc5258a693..7a98b0f2f6 100644 --- a/src/import/pull-job.h +++ b/src/import/pull-job.h @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include #include #include "curl-util.h" #include "import-compress.h" #include "macro.h" +#include "openssl-util.h" #include "pull-common.h" typedef struct PullJob PullJob; @@ -74,7 +74,7 @@ struct PullJob { usec_t last_status_usec; bool calc_checksum; - gcry_md_hd_t checksum_context; + hash_context_t checksum_ctx; char *checksum; bool sync; From bf4b1adf6f6fe74049dd9319a7f56349556139e5 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Thu, 10 Dec 2020 16:08:23 -0800 Subject: [PATCH 04/15] resolve: Add coverage for dnssec ecdsa (rfc6605) --- src/resolve/test-dnssec.c | 189 +++++++++++++++++++++++++++++++++++++- 1 file changed, 187 insertions(+), 2 deletions(-) diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index b0763694dc..ed46853eeb 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -173,7 +173,7 @@ static void test_dnssec_verify_rfc8080_ed25519_example1(void) { assert_se(dns_answer_add(answer, mx, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0); assert_se(dnssec_verify_rrset(answer, mx->key, rrsig, dnskey, - rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); + rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); #if GCRYPT_VERSION_NUMBER >= 0x010600 assert_se(result == DNSSEC_VALIDATED); #else @@ -265,13 +265,196 @@ static void test_dnssec_verify_rfc8080_ed25519_example2(void) { assert_se(dns_answer_add(answer, mx, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0); assert_se(dnssec_verify_rrset(answer, mx->key, rrsig, dnskey, - rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); + rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); #if GCRYPT_VERSION_NUMBER >= 0x010600 assert_se(result == DNSSEC_VALIDATED); #else assert_se(result == DNSSEC_UNSUPPORTED_ALGORITHM); #endif } + +static void test_dnssec_verify_rfc6605_example1(void) { + static const uint8_t signature_blob[] = { + 0xab, 0x1e, 0xb0, 0x2d, 0x8a, 0xa6, 0x87, 0xe9, 0x7d, 0xa0, 0x22, 0x93, 0x37, 0xaa, 0x88, 0x73, + 0xe6, 0xf0, 0xeb, 0x26, 0xbe, 0x28, 0x9f, 0x28, 0x33, 0x3d, 0x18, 0x3f, 0x5d, 0x3b, 0x7a, 0x95, + 0xc0, 0xc8, 0x69, 0xad, 0xfb, 0x74, 0x8d, 0xae, 0xe3, 0xc5, 0x28, 0x6e, 0xed, 0x66, 0x82, 0xc1, + 0x2e, 0x55, 0x33, 0x18, 0x6b, 0xac, 0xed, 0x9c, 0x26, 0xc1, 0x67, 0xa9, 0xeb, 0xae, 0x95, 0x0b, + }; + + static const uint8_t ds_fprint[] = { + 0x6f, 0x87, 0x3c, 0x73, 0x57, 0xde, 0xd9, 0xee, 0xf8, 0xef, 0xbd, 0x76, 0xed, 0xbd, 0xbb, 0xd7, + 0x5e, 0x7a, 0xe7, 0xa6, 0x9d, 0xeb, 0x6e, 0x7a, 0x7f, 0x8d, 0xb8, 0xeb, 0x6e, 0x5b, 0x7f, 0x97, + 0x35, 0x7b, 0x6e, 0xfb, 0xd1, 0xc7, 0xba, 0x77, 0xa7, 0xb7, 0xed, 0xd7, 0xfa, 0xd5, 0xdd, 0x7b, + }; + + static const uint8_t dnskey_blob[] = { + 0x1a, 0x88, 0xc8, 0x86, 0x15, 0xd4, 0x37, 0xfb, 0xb8, 0xbf, 0x9e, 0x19, 0x42, 0xa1, 0x92, 0x9f, + 0x28, 0x56, 0x27, 0x06, 0xae, 0x6c, 0x2b, 0xd3, 0x99, 0xe7, 0xb1, 0xbf, 0xb6, 0xd1, 0xe9, 0xe7, + 0x5b, 0x92, 0xb4, 0xaa, 0x42, 0x91, 0x7a, 0xe1, 0xc6, 0x1b, 0x70, 0x1e, 0xf0, 0x35, 0xc3, 0xfe, + 0x7b, 0xe3, 0x00, 0x9c, 0xba, 0xfe, 0x5a, 0x2f, 0x71, 0x31, 0x6c, 0x90, 0x2d, 0xcf, 0x0d, 0x00, + }; + + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds = NULL, *a = NULL, + *rrsig = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnssecResult result; + + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "example.net."); + assert_se(dnskey); + + dnskey->dnskey.flags = 257; + dnskey->dnskey.protocol = 3; + dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_ECDSAP256SHA256; + dnskey->dnskey.key_size = sizeof(dnskey_blob); + dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob)); + assert_se(dnskey->dnskey.key); + + log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); + + ds = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "example.net."); + assert_se(ds); + + ds->ds.key_tag = 55648; + ds->ds.algorithm = DNSSEC_ALGORITHM_ECDSAP256SHA256; + ds->ds.digest_type = DNSSEC_DIGEST_SHA256; + ds->ds.digest_size = sizeof(ds_fprint); + ds->ds.digest = memdup(ds_fprint, ds->ds.digest_size); + assert_se(ds->ds.digest); + + log_info("DS: %s", strna(dns_resource_record_to_string(ds))); + + a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.net"); + assert_se(a); + + a->a.in_addr.s_addr = inet_addr("192.0.2.1"); + + log_info("A: %s", strna(dns_resource_record_to_string(a))); + + rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "www.example.net."); + assert_se(rrsig); + + rrsig->rrsig.type_covered = DNS_TYPE_A; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_ECDSAP256SHA256; + rrsig->rrsig.labels = 3; + rrsig->rrsig.expiration = 1284026679; + rrsig->rrsig.inception = 1281607479; + rrsig->rrsig.key_tag = 55648; + rrsig->rrsig.original_ttl = 3600; + rrsig->rrsig.signer = strdup("example.net."); + assert_se(rrsig->rrsig.signer); + rrsig->rrsig.signature_size = sizeof(signature_blob); + rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); + assert_se(rrsig->rrsig.signature); + + log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + + assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); + + answer = dns_answer_new(1); + assert_se(answer); + assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0); + + assert_se(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, + rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); + assert_se(result == DNSSEC_VALIDATED); +} + +static void test_dnssec_verify_rfc6605_example2(void) { + static const uint8_t signature_blob[] = { + 0xfc, 0xbe, 0x61, 0x0c, 0xa2, 0x2f, 0x18, 0x3c, 0x88, 0xd5, 0xf7, 0x00, 0x45, 0x7d, 0xf3, 0xeb, + 0x9a, 0xab, 0x98, 0xfb, 0x15, 0xcf, 0xbd, 0xd0, 0x0f, 0x53, 0x2b, 0xe4, 0x21, 0x2a, 0x3a, 0x22, + 0xcf, 0xf7, 0x98, 0x71, 0x42, 0x8b, 0xae, 0xae, 0x81, 0x82, 0x79, 0x93, 0xaf, 0xcc, 0x56, 0xb1, + 0xb1, 0x3f, 0x06, 0x96, 0xbe, 0xf8, 0x85, 0xb6, 0xaf, 0x44, 0xa6, 0xb2, 0x24, 0xdb, 0xb2, 0x74, + 0x2b, 0xb3, 0x59, 0x34, 0x92, 0x3d, 0xdc, 0xfb, 0xc2, 0x7a, 0x97, 0x2f, 0x96, 0xdd, 0x70, 0x9c, + 0xee, 0xb1, 0xd9, 0xc8, 0xd1, 0x14, 0x8c, 0x44, 0xec, 0x71, 0xc0, 0x68, 0xa9, 0x59, 0xc2, 0x66, + + }; + + static const uint8_t ds_fprint[] = { + 0xef, 0x67, 0x7b, 0x6f, 0xad, 0xbd, 0xef, 0xa7, 0x1e, 0xd3, 0xae, 0x37, 0xf1, 0xef, 0x5c, 0xd1, + 0xb7, 0xf7, 0xd7, 0xdd, 0x35, 0xdd, 0xc7, 0xfc, 0xd3, 0x57, 0xf4, 0xf5, 0xe7, 0x1c, 0xf3, 0x86, + 0xfc, 0x77, 0xb7, 0xbd, 0xe3, 0xde, 0x5f, 0xdb, 0xb7, 0xb7, 0xd3, 0x97, 0x3a, 0x6b, 0xd6, 0xf4, + 0xe7, 0xad, 0xda, 0xf5, 0xbe, 0x5f, 0xe1, 0xdd, 0xbc, 0xf3, 0x8d, 0x39, 0x73, 0x7d, 0x34, 0xf1, + 0xaf, 0x78, 0xe9, 0xd7, 0xfd, 0xf3, 0x77, 0x7a, + }; + + static const uint8_t dnskey_blob[] = { + 0xc4, 0xa6, 0x1a, 0x36, 0x15, 0x9d, 0x18, 0xe7, 0xc9, 0xfa, 0x73, 0xeb, 0x2f, 0xcf, 0xda, 0xae, + 0x4c, 0x1f, 0xd8, 0x46, 0x37, 0x30, 0x32, 0x7e, 0x48, 0x4a, 0xca, 0x8a, 0xf0, 0x55, 0x4a, 0xe9, + 0xb5, 0xc3, 0xf7, 0xa0, 0xb1, 0x7b, 0xd2, 0x00, 0x3b, 0x4d, 0x26, 0x1c, 0x9e, 0x9b, 0x94, 0x42, + 0x3a, 0x98, 0x10, 0xe8, 0xaf, 0x17, 0xd4, 0x34, 0x52, 0x12, 0x4a, 0xdb, 0x61, 0x0f, 0x8e, 0x07, + 0xeb, 0xfc, 0xfe, 0xe5, 0xf8, 0xe4, 0xd0, 0x70, 0x63, 0xca, 0xe9, 0xeb, 0x91, 0x7a, 0x1a, 0x5b, + 0xab, 0xf0, 0x8f, 0xe6, 0x95, 0x53, 0x60, 0x17, 0xa5, 0xbf, 0xa9, 0x32, 0x37, 0xee, 0x6e, 0x34, + }; + + + _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *dnskey = NULL, *ds = NULL, *a = NULL, + *rrsig = NULL; + _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; + DnssecResult result; + + dnskey = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DNSKEY, "example.net."); + assert_se(dnskey); + + dnskey->dnskey.flags = 257; + dnskey->dnskey.protocol = 3; + dnskey->dnskey.algorithm = DNSSEC_ALGORITHM_ECDSAP384SHA384; + dnskey->dnskey.key_size = sizeof(dnskey_blob); + dnskey->dnskey.key = memdup(dnskey_blob, sizeof(dnskey_blob)); + assert_se(dnskey->dnskey.key); + + log_info("DNSKEY: %s", strna(dns_resource_record_to_string(dnskey))); + + ds = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_DS, "example.net."); + assert_se(ds); + + ds->ds.key_tag = 10771; + ds->ds.algorithm = DNSSEC_ALGORITHM_ECDSAP384SHA384; + ds->ds.digest_type = DNSSEC_DIGEST_SHA384; + ds->ds.digest_size = sizeof(ds_fprint); + ds->ds.digest = memdup(ds_fprint, ds->ds.digest_size); + assert_se(ds->ds.digest); + + log_info("DS: %s", strna(dns_resource_record_to_string(ds))); + + a = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_A, "www.example.net"); + assert_se(a); + + a->a.in_addr.s_addr = inet_addr("192.0.2.1"); + + log_info("A: %s", strna(dns_resource_record_to_string(a))); + + rrsig = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_RRSIG, "www.example.net."); + assert_se(rrsig); + + rrsig->rrsig.type_covered = DNS_TYPE_A; + rrsig->rrsig.algorithm = DNSSEC_ALGORITHM_ECDSAP384SHA384; + rrsig->rrsig.labels = 3; + rrsig->rrsig.expiration = 1284027625; + rrsig->rrsig.inception = 1281608425; + rrsig->rrsig.key_tag = 10771; + rrsig->rrsig.original_ttl = 3600; + rrsig->rrsig.signer = strdup("example.net."); + assert_se(rrsig->rrsig.signer); + rrsig->rrsig.signature_size = sizeof(signature_blob); + rrsig->rrsig.signature = memdup(signature_blob, rrsig->rrsig.signature_size); + assert_se(rrsig->rrsig.signature); + + log_info("RRSIG: %s", strna(dns_resource_record_to_string(rrsig))); + + assert_se(dnssec_key_match_rrsig(a->key, rrsig) > 0); + assert_se(dnssec_rrsig_match_dnskey(rrsig, dnskey, false) > 0); + + answer = dns_answer_new(1); + assert_se(answer); + assert_se(dns_answer_add(answer, a, 0, DNS_ANSWER_AUTHENTICATED, NULL) >= 0); + + assert_se(dnssec_verify_rrset(answer, a->key, rrsig, dnskey, + rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); + assert_se(result == DNSSEC_VALIDATED); +} + static void test_dnssec_verify_rrset(void) { static const uint8_t signature_blob[] = { @@ -613,6 +796,8 @@ int main(int argc, char *argv[]) { test_dnssec_verify_dns_key(); test_dnssec_verify_rfc8080_ed25519_example1(); test_dnssec_verify_rfc8080_ed25519_example2(); + test_dnssec_verify_rfc6605_example1(); + test_dnssec_verify_rfc6605_example2(); test_dnssec_verify_rrset(); test_dnssec_verify_rrset2(); test_dnssec_verify_rrset3(); From cc1ecbaaf311aaafe9077efbc0fcc8f2f20484d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 28 Oct 2021 14:03:44 +0200 Subject: [PATCH 05/15] resolved: split out function to generate signature dnssec_verify_rrset() is just too long. --- src/resolve/resolved-dns-dnssec.c | 125 +++++++++++++++++++----------- 1 file changed, 78 insertions(+), 47 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index fd7679f17d..ccb07aeb3c 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -625,6 +625,81 @@ static void dnssec_fix_rrset_ttl( rrsig->expiry = rrsig->rrsig.expiration * USEC_PER_SEC; } +static int dnssec_rrset_serialize_sig( + DnsResourceRecord *rrsig, + const char *source, + DnsResourceRecord **list, + size_t list_len, + bool wildcard, + char **ret_sig_data, + size_t *ret_sig_size) { + + _cleanup_free_ char *sig_data = NULL; + size_t sig_size = 0; + _cleanup_fclose_ FILE *f = NULL; + uint8_t wire_format_name[DNS_WIRE_FORMAT_HOSTNAME_MAX]; + DnsResourceRecord *rr; + int r; + + assert(rrsig); + assert(source); + assert(list || list_len == 0); + assert(ret_sig_data); + assert(ret_sig_size); + + f = open_memstream_unlocked(&sig_data, &sig_size); + if (!f) + return -ENOMEM; + + fwrite_uint16(f, rrsig->rrsig.type_covered); + fwrite_uint8(f, rrsig->rrsig.algorithm); + fwrite_uint8(f, rrsig->rrsig.labels); + fwrite_uint32(f, rrsig->rrsig.original_ttl); + fwrite_uint32(f, rrsig->rrsig.expiration); + fwrite_uint32(f, rrsig->rrsig.inception); + fwrite_uint16(f, rrsig->rrsig.key_tag); + + r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true); + if (r < 0) + return r; + fwrite(wire_format_name, 1, r, f); + + /* Convert the source of synthesis into wire format */ + r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true); + if (r < 0) + return r; + + for (size_t k = 0; k < list_len; k++) { + size_t l; + + rr = list[k]; + + /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */ + if (wildcard) + fwrite((uint8_t[]) { 1, '*'}, sizeof(uint8_t), 2, f); + fwrite(wire_format_name, 1, r, f); + + fwrite_uint16(f, rr->key->type); + fwrite_uint16(f, rr->key->class); + fwrite_uint32(f, rrsig->rrsig.original_ttl); + + l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr); + assert(l <= 0xFFFF); + + fwrite_uint16(f, (uint16_t) l); + fwrite(DNS_RESOURCE_RECORD_RDATA(rr), 1, l, f); + } + + r = fflush_and_check(f); + f = safe_fclose(f); /* sig_data may be reallocated when f is closed. */ + if (r < 0) + return r; + + *ret_sig_data = TAKE_PTR(sig_data); + *ret_sig_size = sig_size; + return 0; +} + int dnssec_verify_rrset( DnsAnswer *a, const DnsResourceKey *key, @@ -633,15 +708,13 @@ int dnssec_verify_rrset( usec_t realtime, DnssecResult *result) { - uint8_t wire_format_name[DNS_WIRE_FORMAT_HOSTNAME_MAX]; DnsResourceRecord **list, *rr; const char *source, *name; _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; int r, md_algorithm; size_t n = 0; - size_t sig_size = 0; _cleanup_free_ char *sig_data = NULL; - _cleanup_fclose_ FILE *f = NULL; + size_t sig_size = 0; size_t hash_size; void *hash; bool wildcard; @@ -746,50 +819,8 @@ int dnssec_verify_rrset( /* Bring the RRs into canonical order */ typesafe_qsort(list, n, rr_compare); - f = open_memstream_unlocked(&sig_data, &sig_size); - if (!f) - return -ENOMEM; - - fwrite_uint16(f, rrsig->rrsig.type_covered); - fwrite_uint8(f, rrsig->rrsig.algorithm); - fwrite_uint8(f, rrsig->rrsig.labels); - fwrite_uint32(f, rrsig->rrsig.original_ttl); - fwrite_uint32(f, rrsig->rrsig.expiration); - fwrite_uint32(f, rrsig->rrsig.inception); - fwrite_uint16(f, rrsig->rrsig.key_tag); - - r = dns_name_to_wire_format(rrsig->rrsig.signer, wire_format_name, sizeof(wire_format_name), true); - if (r < 0) - return r; - fwrite(wire_format_name, 1, r, f); - - /* Convert the source of synthesis into wire format */ - r = dns_name_to_wire_format(source, wire_format_name, sizeof(wire_format_name), true); - if (r < 0) - return r; - - for (size_t k = 0; k < n; k++) { - size_t l; - - rr = list[k]; - - /* Hash the source of synthesis. If this is a wildcard, then prefix it with the *. label */ - if (wildcard) - fwrite((uint8_t[]) { 1, '*'}, sizeof(uint8_t), 2, f); - fwrite(wire_format_name, 1, r, f); - - fwrite_uint16(f, rr->key->type); - fwrite_uint16(f, rr->key->class); - fwrite_uint32(f, rrsig->rrsig.original_ttl); - - l = DNS_RESOURCE_RECORD_RDATA_SIZE(rr); - assert(l <= 0xFFFF); - - fwrite_uint16(f, (uint16_t) l); - fwrite(DNS_RESOURCE_RECORD_RDATA(rr), 1, l, f); - } - - r = fflush_and_check(f); + r = dnssec_rrset_serialize_sig(rrsig, source, list, n, wildcard, + &sig_data, &sig_size); if (r < 0) return r; From 667dac6ed6674d4ab737580d0f9063e1f7359687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 21 Oct 2021 16:00:08 +0200 Subject: [PATCH 06/15] resolved: split out function to hash signature dnssec_verify_rrset() is just too long. --- src/resolve/resolved-dns-dnssec.c | 153 +++++++++++++++--------------- 1 file changed, 79 insertions(+), 74 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index ccb07aeb3c..38865bb467 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -700,6 +700,81 @@ static int dnssec_rrset_serialize_sig( return 0; } +static int dnssec_rrset_verify_sig( + DnsResourceRecord *rrsig, + DnsResourceRecord *dnskey, + const char *sig_data, + size_t sig_size) { + + assert(rrsig); + assert(dnskey); + assert(sig_data); + assert(sig_size > 0); + + _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; + void *hash; + size_t hash_size; + int md_algorithm; + + initialize_libgcrypt(false); + + switch (rrsig->rrsig.algorithm) { + case DNSSEC_ALGORITHM_ED25519: +#if GCRYPT_VERSION_NUMBER >= 0x010600 + return dnssec_eddsa_verify( + rrsig->rrsig.algorithm, + sig_data, sig_size, + rrsig, + dnskey); +#endif + case DNSSEC_ALGORITHM_ED448: + return -EOPNOTSUPP; + default: + /* OK, the RRs are now in canonical order. Let's calculate the digest */ + md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm); + if (md_algorithm < 0) + return md_algorithm; + + gcry_error_t err = gcry_md_open(&md, md_algorithm, 0); + if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md) + return -EIO; + + hash_size = gcry_md_get_algo_dlen(md_algorithm); + assert(hash_size > 0); + + gcry_md_write(md, sig_data, sig_size); + + hash = gcry_md_read(md, 0); + if (!hash) + return -EIO; + } + + switch (rrsig->rrsig.algorithm) { + + case DNSSEC_ALGORITHM_RSASHA1: + case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1: + case DNSSEC_ALGORITHM_RSASHA256: + case DNSSEC_ALGORITHM_RSASHA512: + return dnssec_rsa_verify( + gcry_md_algo_name(md_algorithm), + hash, hash_size, + rrsig, + dnskey); + + case DNSSEC_ALGORITHM_ECDSAP256SHA256: + case DNSSEC_ALGORITHM_ECDSAP384SHA384: + return dnssec_ecdsa_verify( + gcry_md_algo_name(md_algorithm), + rrsig->rrsig.algorithm, + hash, hash_size, + rrsig, + dnskey); + + default: + assert_not_reached(); + } +} + int dnssec_verify_rrset( DnsAnswer *a, const DnsResourceKey *key, @@ -710,14 +785,10 @@ int dnssec_verify_rrset( DnsResourceRecord **list, *rr; const char *source, *name; - _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; - int r, md_algorithm; - size_t n = 0; + size_t n = 0, sig_size; _cleanup_free_ char *sig_data = NULL; - size_t sig_size = 0; - size_t hash_size; - void *hash; bool wildcard; + int r; assert(key); assert(rrsig); @@ -824,76 +895,10 @@ int dnssec_verify_rrset( if (r < 0) return r; - initialize_libgcrypt(false); - - switch (rrsig->rrsig.algorithm) { -#if GCRYPT_VERSION_NUMBER >= 0x010600 - case DNSSEC_ALGORITHM_ED25519: - break; -#else - case DNSSEC_ALGORITHM_ED25519: -#endif - case DNSSEC_ALGORITHM_ED448: + r = dnssec_rrset_verify_sig(rrsig, dnskey, sig_data, sig_size); + if (r == -EOPNOTSUPP) { *result = DNSSEC_UNSUPPORTED_ALGORITHM; return 0; - default: { - gcry_error_t err; - - /* OK, the RRs are now in canonical order. Let's calculate the digest */ - md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm); - if (md_algorithm == -EOPNOTSUPP) { - *result = DNSSEC_UNSUPPORTED_ALGORITHM; - return 0; - } - if (md_algorithm < 0) - return md_algorithm; - - err = gcry_md_open(&md, md_algorithm, 0); - if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md) - return -EIO; - - hash_size = gcry_md_get_algo_dlen(md_algorithm); - assert(hash_size > 0); - - gcry_md_write(md, sig_data, sig_size); - - hash = gcry_md_read(md, 0); - if (!hash) - return -EIO; - } - } - - switch (rrsig->rrsig.algorithm) { - - case DNSSEC_ALGORITHM_RSASHA1: - case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1: - case DNSSEC_ALGORITHM_RSASHA256: - case DNSSEC_ALGORITHM_RSASHA512: - r = dnssec_rsa_verify( - gcry_md_algo_name(md_algorithm), - hash, hash_size, - rrsig, - dnskey); - break; - - case DNSSEC_ALGORITHM_ECDSAP256SHA256: - case DNSSEC_ALGORITHM_ECDSAP384SHA384: - r = dnssec_ecdsa_verify( - gcry_md_algo_name(md_algorithm), - rrsig->rrsig.algorithm, - hash, hash_size, - rrsig, - dnskey); - break; -#if GCRYPT_VERSION_NUMBER >= 0x010600 - case DNSSEC_ALGORITHM_ED25519: - r = dnssec_eddsa_verify( - rrsig->rrsig.algorithm, - sig_data, sig_size, - rrsig, - dnskey); - break; -#endif } if (r < 0) return r; From 0351cbb9e4812fcad694f506800fbadc5182b3f8 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Thu, 10 Dec 2020 16:08:26 -0800 Subject: [PATCH 07/15] resolve: Port dnssec verify from gcrypt to openssl^gcrypt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Zbigniew Jędrzejewski-Szmek --- meson.build | 1 + src/resolve/meson.build | 24 ++- src/resolve/resolved-dns-dnssec.c | 288 ++++++++++++++++++++++++------ src/resolve/test-dnssec.c | 18 +- 4 files changed, 260 insertions(+), 71 deletions(-) diff --git a/meson.build b/meson.build index 5748853121..b4a3b865c1 100644 --- a/meson.build +++ b/meson.build @@ -2151,6 +2151,7 @@ if conf.get('ENABLE_RESOLVE') == 1 libbasic_gcrypt, libsystemd_resolve_core], dependencies : [threads, + lib_openssl_or_gcrypt, libgpg_error, libm, libidn], diff --git a/src/resolve/meson.build b/src/resolve/meson.build index c7cb88ac04..d4534cb4a9 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -176,14 +176,16 @@ tests += [ [['src/resolve/test-resolve-tables.c'], [libsystemd_resolve_core, libshared], - [libgcrypt, + [lib_openssl_or_gcrypt, + libgcrypt, libgpg_error, libm]], [['src/resolve/test-dns-packet.c'], [libsystemd_resolve_core, libshared], - [libgcrypt, + [lib_openssl_or_gcrypt, + libgcrypt, libgpg_error, libm]], @@ -192,28 +194,33 @@ tests += [ 'src/resolve/resolved-etc-hosts.h'], [libsystemd_resolve_core, libshared], - [libgcrypt, + [lib_openssl_or_gcrypt, + libgcrypt, libgpg_error, libm]], [['src/resolve/test-resolved-packet.c'], [libsystemd_resolve_core, libshared], - [libgcrypt, + [lib_openssl_or_gcrypt, + libgcrypt, libgpg_error, libm]], [['src/resolve/test-dnssec.c'], [libsystemd_resolve_core, libshared], - [libgcrypt, + [lib_openssl_or_gcrypt, + libgcrypt, libgpg_error, - libm]], + libm], + [], 'HAVE_OPENSSL_OR_GCRYPT'], [['src/resolve/test-dnssec-complex.c'], [libsystemd_resolve_core, libshared], - [libgcrypt, + [lib_openssl_or_gcrypt, + libgcrypt, libgpg_error, libm], [], '', 'manual'], @@ -223,7 +230,8 @@ fuzzers += [ [['src/resolve/fuzz-dns-packet.c'], [libsystemd_resolve_core, libshared], - [libgcrypt, + [lib_openssl_or_gcrypt, + libgcrypt, libgpg_error, libm]], ] diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 38865bb467..20bb9c7db4 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -7,6 +7,7 @@ #include "gcrypt-util.h" #include "hexdecoct.h" #include "memory-util.h" +#include "openssl-util.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" #include "sort-util.h" @@ -58,7 +59,7 @@ uint16_t dnssec_keytag(DnsResourceRecord *dnskey, bool mask_revoke) { return sum & UINT32_C(0xFFFF); } -#if HAVE_GCRYPT +#if HAVE_OPENSSL_OR_GCRYPT static int rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b) { const DnsResourceRecord *x = *a, *y = *b; @@ -82,12 +83,66 @@ static int rr_compare(DnsResourceRecord * const *a, DnsResourceRecord * const *b } static int dnssec_rsa_verify_raw( - const char *hash_algorithm, + hash_algorithm_t 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) { +#if PREFER_OPENSSL + _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; + int r; + + assert(hash_algorithm); + + e = BN_bin2bn(exponent, exponent_size, NULL); + if (!e) + return -EIO; + + m = BN_bin2bn(modulus, modulus_size, NULL); + if (!m) + return -EIO; + + rpubkey = RSA_new(); + if (!rpubkey) + return -ENOMEM; + + if (RSA_set0_key(rpubkey, BN_dup(m), BN_dup(e), NULL) <= 0) + return -EIO; + + assert((size_t) RSA_size(rpubkey) == signature_size); + + epubkey = EVP_PKEY_new(); + if (!epubkey) + return -ENOMEM; + + if (EVP_PKEY_assign_RSA(epubkey, RSAPublicKey_dup(rpubkey)) <= 0) + return -EIO; + + ctx = EVP_PKEY_CTX_new(epubkey, NULL); + if (!ctx) + return -ENOMEM; + + if (EVP_PKEY_verify_init(ctx) <= 0) + return -EIO; + + if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0) + return -EIO; + + if (EVP_PKEY_CTX_set_signature_md(ctx, hash_algorithm) <= 0) + return -EIO; + + r = EVP_PKEY_verify(ctx, signature, signature_size, data, data_size); + if (r < 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Signature verification failed: 0x%lx", ERR_get_error()); + + return r; + +#else gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL; gcry_mpi_t n = NULL, e = NULL, s = NULL; gcry_error_t ge; @@ -147,10 +202,10 @@ static int dnssec_rsa_verify_raw( ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp); if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE) r = 0; - else if (ge != 0) { - log_debug("RSA signature check failed: %s", gpg_strerror(ge)); - r = -EIO; - } else + else if (ge != 0) + r = log_debug_errno(SYNTHETIC_ERRNO(EIO), + "RSA signature check failed: %s", gpg_strerror(ge)); + else r = 1; finish: @@ -169,10 +224,11 @@ finish: gcry_sexp_release(data_sexp); return r; +#endif } static int dnssec_rsa_verify( - const char *hash_algorithm, + hash_algorithm_t hash_algorithm, const void *hash, size_t hash_size, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { @@ -228,13 +284,77 @@ static int dnssec_rsa_verify( } static int dnssec_ecdsa_verify_raw( - const char *hash_algorithm, - const char *curve, + hash_algorithm_t hash_algorithm, + elliptic_curve_t 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) { +#if PREFER_OPENSSL + _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 k; + + assert(hash_algorithm); + + ec_group = EC_GROUP_new_by_curve_name(curve); + if (!ec_group) + return -ENOMEM; + + p = EC_POINT_new(ec_group); + if (!p) + return -ENOMEM; + + bctx = BN_CTX_new(); + if (!bctx) + return -ENOMEM; + + if (EC_POINT_oct2point(ec_group, p, key, key_size, bctx) <= 0) + return -EIO; + + eckey = EC_KEY_new(); + if (!eckey) + return -ENOMEM; + + if (EC_KEY_set_group(eckey, ec_group) <= 0) + return -EIO; + + if (EC_KEY_set_public_key(eckey, p) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "EC_POINT_bn2point failed: 0x%lx", ERR_get_error()); + + assert(EC_KEY_check_key(eckey) == 1); + + r = BN_bin2bn(signature_r, signature_r_size, NULL); + if (!r) + return -EIO; + + s = BN_bin2bn(signature_s, signature_s_size, NULL); + if (!s) + return -EIO; + + /* TODO: We should eventually use use the EVP API once it supports ECDSA signature verification */ + + sig = ECDSA_SIG_new(); + if (!sig) + return -ENOMEM; + + if (ECDSA_SIG_set0(sig, BN_dup(r), BN_dup(s)) <= 0) + return -EIO; + + k = ECDSA_do_verify(data, data_size, sig, eckey); + if (k < 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Signature verification failed: 0x%lx", ERR_get_error()); + + return k; + +#else gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL; gcry_mpi_t q = NULL, r = NULL, s = NULL; gcry_error_t ge; @@ -315,16 +435,17 @@ finish: gcry_sexp_release(data_sexp); return k; +#endif } static int dnssec_ecdsa_verify( - const char *hash_algorithm, + hash_algorithm_t hash_algorithm, int algorithm, const void *hash, size_t hash_size, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { - const char *curve; + elliptic_curve_t curve; size_t key_size; uint8_t *q; @@ -334,11 +455,11 @@ static int dnssec_ecdsa_verify( assert(dnskey); if (algorithm == DNSSEC_ALGORITHM_ECDSAP256SHA256) { + curve = OPENSSL_OR_GCRYPT(NID_X9_62_prime256v1, "NIST P-256"); /* NIST P-256 */ key_size = 32; - curve = "NIST P-256"; } else if (algorithm == DNSSEC_ALGORITHM_ECDSAP384SHA384) { + curve = OPENSSL_OR_GCRYPT(NID_secp384r1, "NIST P-384"); /* NIST P-384 */ key_size = 48; - curve = "NIST P-384"; } else return -EOPNOTSUPP; @@ -361,25 +482,66 @@ static int dnssec_ecdsa_verify( q, key_size*2+1); } -#if GCRYPT_VERSION_NUMBER >= 0x010600 static int dnssec_eddsa_verify_raw( - const char *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) { + elliptic_curve_t curve, + const uint8_t *signature, size_t signature_size, + const uint8_t *data, size_t data_size, + const uint8_t *key, size_t key_size) { +#if PREFER_OPENSSL + _cleanup_(EVP_PKEY_freep) EVP_PKEY *evkey = NULL; + _cleanup_(EVP_PKEY_CTX_freep) EVP_PKEY_CTX *pctx = NULL; + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = NULL; + int r; + + assert(curve == NID_ED25519); + assert(signature_size == key_size * 2); + + uint8_t *q = newa(uint8_t, signature_size + 1); + q[0] = 0x04; /* Prepend 0x04 to indicate an uncompressed key */ + memcpy(q+1, signature, signature_size); + + evkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, key, key_size); + if (!evkey) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "EVP_PKEY_new_raw_public_key failed: 0x%lx", ERR_get_error()); + + pctx = EVP_PKEY_CTX_new(evkey, NULL); + if (!pctx) + return -ENOMEM; + + ctx = EVP_MD_CTX_new(); + if (!ctx) + return -ENOMEM; + + /* This prevents EVP_DigestVerifyInit from managing pctx and complicating our free logic. */ + EVP_MD_CTX_set_pkey_ctx(ctx, pctx); + + /* One might be tempted to use EVP_PKEY_verify_init, but see Ed25519(7ssl). */ + if (EVP_DigestVerifyInit(ctx, &pctx, NULL, NULL, evkey) <= 0) + return -EIO; + + r = EVP_DigestVerify(ctx, signature, signature_size, data, data_size); + if (r < 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Signature verification failed: 0x%lx", ERR_get_error()); + + return r; + +#elif GCRYPT_VERSION_NUMBER >= 0x010600 gcry_sexp_t public_key_sexp = NULL, data_sexp = NULL, signature_sexp = NULL; gcry_error_t ge; int k; + assert(signature_size == key_size * 2); + ge = gcry_sexp_build(&signature_sexp, NULL, "(sig-val (eddsa (r %b) (s %b)))", - (int) signature_r_size, - signature_r, - (int) signature_s_size, - signature_s); + (int) key_size, + signature, + (int) key_size, + signature + key_size); if (ge != 0) { k = -EIO; goto finish; @@ -409,10 +571,10 @@ static int dnssec_eddsa_verify_raw( ge = gcry_pk_verify(signature_sexp, data_sexp, public_key_sexp); if (gpg_err_code(ge) == GPG_ERR_BAD_SIGNATURE) k = 0; - else if (ge != 0) { - log_debug("EdDSA signature check failed: %s", gpg_strerror(ge)); - k = -EIO; - } else + else if (ge != 0) + k = log_debug_errno(SYNTHETIC_ERRNO(EIO), + "EdDSA signature check failed: %s", gpg_strerror(ge)); + else k = 1; finish: if (public_key_sexp) @@ -423,6 +585,9 @@ finish: gcry_sexp_release(data_sexp); return k; +#else + return -EOPNOTSUPP; +#endif } static int dnssec_eddsa_verify( @@ -430,11 +595,11 @@ static int dnssec_eddsa_verify( const void *data, size_t data_size, DnsResourceRecord *rrsig, DnsResourceRecord *dnskey) { - const char *curve; + elliptic_curve_t curve; size_t key_size; if (algorithm == DNSSEC_ALGORITHM_ED25519) { - curve = "Ed25519"; + curve = OPENSSL_OR_GCRYPT(NID_ED25519, "Ed25519"); key_size = 32; } else return -EOPNOTSUPP; @@ -447,12 +612,10 @@ static int dnssec_eddsa_verify( return dnssec_eddsa_verify_raw( curve, - rrsig->rrsig.signature, key_size, - (uint8_t*) rrsig->rrsig.signature + key_size, key_size, + rrsig->rrsig.signature, rrsig->rrsig.signature_size, data, data_size, dnskey->dnskey.key, key_size); } -#endif static void md_add_uint8(gcry_md_hd_t md, uint8_t v) { gcry_md_write(md, &v, sizeof(v)); @@ -565,36 +728,32 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) { return realtime < inception || realtime > expiration; } -static int algorithm_to_gcrypt_md(uint8_t algorithm) { +static hash_md_t algorithm_to_implementation_id(uint8_t algorithm) { - /* Translates a DNSSEC signature algorithm into a gcrypt - * digest identifier. + /* Translates a DNSSEC signature algorithm into an openssl/gcrypt digest identifier. * - * Note that we implement all algorithms listed as "Must - * implement" and "Recommended to Implement" in RFC6944. We - * don't implement any algorithms that are listed as - * "Optional" or "Must Not Implement". Specifically, we do not - * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and - * GOST-ECC. */ + * Note that we implement all algorithms listed as "Must implement" and "Recommended to Implement" in + * RFC6944. We don't implement any algorithms that are listed as "Optional" or "Must Not Implement". + * Specifically, we do not implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and GOST-ECC. */ switch (algorithm) { case DNSSEC_ALGORITHM_RSASHA1: case DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1: - return GCRY_MD_SHA1; + return OPENSSL_OR_GCRYPT(EVP_sha1(), GCRY_MD_SHA1); case DNSSEC_ALGORITHM_RSASHA256: case DNSSEC_ALGORITHM_ECDSAP256SHA256: - return GCRY_MD_SHA256; + return OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256); case DNSSEC_ALGORITHM_ECDSAP384SHA384: - return GCRY_MD_SHA384; + return OPENSSL_OR_GCRYPT(EVP_sha384(), GCRY_MD_SHA384); case DNSSEC_ALGORITHM_RSASHA512: - return GCRY_MD_SHA512; + return OPENSSL_OR_GCRYPT(EVP_sha512(), GCRY_MD_SHA512); default: - return -EOPNOTSUPP; + return OPENSSL_OR_GCRYPT(NULL, -EOPNOTSUPP); } } @@ -711,16 +870,22 @@ static int dnssec_rrset_verify_sig( assert(sig_data); assert(sig_size > 0); + hash_md_t md_algorithm; + +#if PREFER_OPENSSL + uint8_t hash[EVP_MAX_MD_SIZE]; + unsigned hash_size; +#else _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; void *hash; size_t hash_size; - int md_algorithm; initialize_libgcrypt(false); +#endif switch (rrsig->rrsig.algorithm) { case DNSSEC_ALGORITHM_ED25519: -#if GCRYPT_VERSION_NUMBER >= 0x010600 +#if PREFER_OPENSSL || GCRYPT_VERSION_NUMBER >= 0x010600 return dnssec_eddsa_verify( rrsig->rrsig.algorithm, sig_data, sig_size, @@ -731,7 +896,27 @@ static int dnssec_rrset_verify_sig( return -EOPNOTSUPP; default: /* OK, the RRs are now in canonical order. Let's calculate the digest */ - md_algorithm = algorithm_to_gcrypt_md(rrsig->rrsig.algorithm); + md_algorithm = algorithm_to_implementation_id(rrsig->rrsig.algorithm); +#if PREFER_OPENSSL + if (!md_algorithm) + return -EOPNOTSUPP; + + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if (!ctx) + return -ENOMEM; + + if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0) + return -EIO; + + if (EVP_DigestUpdate(ctx, sig_data, sig_size) <= 0) + return -EIO; + + if (EVP_DigestFinal_ex(ctx, hash, &hash_size) <= 0) + return -EIO; + + assert(hash_size > 0); + +#else if (md_algorithm < 0) return md_algorithm; @@ -747,6 +932,7 @@ static int dnssec_rrset_verify_sig( hash = gcry_md_read(md, 0); if (!hash) return -EIO; +#endif } switch (rrsig->rrsig.algorithm) { @@ -756,7 +942,7 @@ static int dnssec_rrset_verify_sig( case DNSSEC_ALGORITHM_RSASHA256: case DNSSEC_ALGORITHM_RSASHA512: return dnssec_rsa_verify( - gcry_md_algo_name(md_algorithm), + OPENSSL_OR_GCRYPT(md_algorithm, gcry_md_algo_name(md_algorithm)), hash, hash_size, rrsig, dnskey); @@ -764,7 +950,7 @@ static int dnssec_rrset_verify_sig( case DNSSEC_ALGORITHM_ECDSAP256SHA256: case DNSSEC_ALGORITHM_ECDSAP384SHA384: return dnssec_ecdsa_verify( - gcry_md_algo_name(md_algorithm), + OPENSSL_OR_GCRYPT(md_algorithm, gcry_md_algo_name(md_algorithm)), rrsig->rrsig.algorithm, hash, hash_size, rrsig, diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index ed46853eeb..3263095360 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -1,20 +1,19 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include -#if HAVE_GCRYPT -#include -#endif #include #include +#if HAVE_GCRYPT +# include +#endif + #include "alloc-util.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-rr.h" #include "string-util.h" #include "hexdecoct.h" -#if HAVE_GCRYPT - static void test_dnssec_verify_dns_key(void) { static const uint8_t ds1_fprint[] = { @@ -174,7 +173,7 @@ static void test_dnssec_verify_rfc8080_ed25519_example1(void) { assert_se(dnssec_verify_rrset(answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); -#if GCRYPT_VERSION_NUMBER >= 0x010600 +#if PREFER_OPENSSL || GCRYPT_VERSION_NUMBER >= 0x010600 assert_se(result == DNSSEC_VALIDATED); #else assert_se(result == DNSSEC_UNSUPPORTED_ALGORITHM); @@ -266,7 +265,7 @@ static void test_dnssec_verify_rfc8080_ed25519_example2(void) { assert_se(dnssec_verify_rrset(answer, mx->key, rrsig, dnskey, rrsig->rrsig.inception * USEC_PER_SEC, &result) >= 0); -#if GCRYPT_VERSION_NUMBER >= 0x010600 +#if PREFER_OPENSSL || GCRYPT_VERSION_NUMBER >= 0x010600 assert_se(result == DNSSEC_VALIDATED); #else assert_se(result == DNSSEC_UNSUPPORTED_ALGORITHM); @@ -788,11 +787,7 @@ static void test_dnssec_nsec3_hash(void) { assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0); } -#endif - int main(int argc, char *argv[]) { - -#if HAVE_GCRYPT test_dnssec_verify_dns_key(); test_dnssec_verify_rfc8080_ed25519_example1(); test_dnssec_verify_rfc8080_ed25519_example2(); @@ -802,7 +797,6 @@ int main(int argc, char *argv[]) { test_dnssec_verify_rrset2(); test_dnssec_verify_rrset3(); test_dnssec_nsec3_hash(); -#endif return 0; } From 1cd7a2c172e82ffbf8d243b38475e494a2091822 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Thu, 10 Dec 2020 16:08:28 -0800 Subject: [PATCH 08/15] resolve: Port dnskey verification by ds to openssl^gcrypt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Zbigniew Jędrzejewski-Szmek --- src/resolve/resolved-dns-dnssec.c | 99 +++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 25 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 20bb9c7db4..9bcce83dcf 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -617,13 +617,23 @@ static int dnssec_eddsa_verify( dnskey->dnskey.key, key_size); } -static void md_add_uint8(gcry_md_hd_t md, uint8_t v) { - gcry_md_write(md, &v, sizeof(v)); +static int md_add_uint8(hash_context_t ctx, uint8_t v) { +#if PREFER_OPENSSL + return EVP_DigestUpdate(ctx, &v, sizeof(v)); +#else + gcry_md_write(ctx, &v, sizeof(v)); + return 0; +#endif } -static void md_add_uint16(gcry_md_hd_t md, uint16_t v) { +static int md_add_uint16(hash_context_t ctx, uint16_t v) { v = htobe16(v); - gcry_md_write(md, &v, sizeof(v)); +#if PREFER_OPENSSL + return EVP_DigestUpdate(ctx, &v, sizeof(v)); +#else + gcry_md_write(ctx, &v, sizeof(v)); + return 0; +#endif } static void fwrite_uint8(FILE *fp, uint8_t v) { @@ -1289,33 +1299,29 @@ int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) { return 0; } -static int digest_to_gcrypt_md(uint8_t algorithm) { +static hash_md_t digest_to_hash_md(uint8_t algorithm) { - /* Translates a DNSSEC digest algorithm into a gcrypt digest identifier */ + /* Translates a DNSSEC digest algorithm into an openssl/gcrypt digest identifier */ switch (algorithm) { case DNSSEC_DIGEST_SHA1: - return GCRY_MD_SHA1; + return OPENSSL_OR_GCRYPT(EVP_sha1(), GCRY_MD_SHA1); case DNSSEC_DIGEST_SHA256: - return GCRY_MD_SHA256; + return OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256); case DNSSEC_DIGEST_SHA384: - return GCRY_MD_SHA384; + return OPENSSL_OR_GCRYPT(EVP_sha384(), GCRY_MD_SHA384); default: - return -EOPNOTSUPP; + return OPENSSL_OR_GCRYPT(NULL, -EOPNOTSUPP); } } int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, bool mask_revoke) { uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX]; - _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; - gcry_error_t err; - size_t hash_size; - int md_algorithm, r; - void *result; + int r; assert(dnskey); assert(ds); @@ -1338,23 +1344,65 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, if (dnssec_keytag(dnskey, mask_revoke) != ds->ds.key_tag) return 0; - initialize_libgcrypt(false); + r = dns_name_to_wire_format(dns_resource_key_name(dnskey->key), wire_format, sizeof wire_format, true); + if (r < 0) + return r; - md_algorithm = digest_to_gcrypt_md(ds->ds.digest_type); - if (md_algorithm < 0) - return md_algorithm; + hash_md_t md_algorithm = digest_to_hash_md(ds->ds.digest_type); - hash_size = gcry_md_get_algo_dlen(md_algorithm); +#if PREFER_OPENSSL + if (!md_algorithm) + return -EOPNOTSUPP; + + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = NULL; + uint8_t result[EVP_MAX_MD_SIZE]; + + unsigned hash_size = EVP_MD_size(md_algorithm); assert(hash_size > 0); if (ds->ds.digest_size != hash_size) return 0; - r = dns_name_to_wire_format(dns_resource_key_name(dnskey->key), wire_format, sizeof(wire_format), true); - if (r < 0) - return r; + ctx = EVP_MD_CTX_new(); + if (!ctx) + return -ENOMEM; - err = gcry_md_open(&md, md_algorithm, 0); + if (EVP_DigestInit_ex(ctx, md_algorithm, NULL) <= 0) + return -EIO; + + if (EVP_DigestUpdate(ctx, wire_format, r) <= 0) + return -EIO; + + if (mask_revoke) + md_add_uint16(ctx, dnskey->dnskey.flags & ~DNSKEY_FLAG_REVOKE); + else + md_add_uint16(ctx, dnskey->dnskey.flags); + + r = md_add_uint8(ctx, dnskey->dnskey.protocol); + if (r <= 0) + return r; + r = md_add_uint8(ctx, dnskey->dnskey.algorithm); + if (r <= 0) + return r; + if (EVP_DigestUpdate(ctx, dnskey->dnskey.key, dnskey->dnskey.key_size) <= 0) + return -EIO; + + if (EVP_DigestFinal_ex(ctx, result, NULL) <= 0) + return -EIO; + +#else + if (md_algorithm < 0) + return -EOPNOTSUPP; + + _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; + + size_t hash_size = gcry_md_get_algo_dlen(md_algorithm); + assert(hash_size > 0); + + if (ds->ds.digest_size != hash_size) + return 0; + + gcry_error_t err = gcry_md_open(&md, md_algorithm, 0); if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md) return -EIO; @@ -1367,9 +1415,10 @@ int dnssec_verify_dnskey_by_ds(DnsResourceRecord *dnskey, DnsResourceRecord *ds, md_add_uint8(md, dnskey->dnskey.algorithm); gcry_md_write(md, dnskey->dnskey.key, dnskey->dnskey.key_size); - result = gcry_md_read(md, 0); + void *result = gcry_md_read(md, 0); if (!result) return -EIO; +#endif return memcmp(result, ds->ds.digest, ds->ds.digest_size) == 0; } From 1736344e9ed7be5de16501ad71cbcfb258afc866 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Thu, 10 Dec 2020 16:08:30 -0800 Subject: [PATCH 09/15] resolve: Port nsec3 code to openssl^gcrypt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Zbigniew Jędrzejewski-Szmek --- src/resolve/resolved-dns-dnssec.c | 73 +++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 9bcce83dcf..3b645ad3f6 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -1461,27 +1461,22 @@ int dnssec_verify_dnskey_by_ds_search(DnsResourceRecord *dnskey, DnsAnswer *vali return 0; } -static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) { +static hash_md_t nsec3_hash_to_hash_md(uint8_t algorithm) { - /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */ + /* Translates a DNSSEC NSEC3 hash algorithm into an openssl/gcrypt digest identifier */ switch (algorithm) { case NSEC3_ALGORITHM_SHA1: - return GCRY_MD_SHA1; + return OPENSSL_OR_GCRYPT(EVP_sha1(), GCRY_MD_SHA1); default: - return -EOPNOTSUPP; + return OPENSSL_OR_GCRYPT(NULL, -EOPNOTSUPP); } } int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { uint8_t wire_format[DNS_WIRE_FORMAT_HOSTNAME_MAX]; - _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; - gcry_error_t err; - size_t hash_size; - int algorithm; - void *result; int r; assert(nsec3); @@ -1496,13 +1491,55 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { "Ignoring NSEC3 RR %s with excessive number of iterations.", dns_resource_record_to_string(nsec3)); - algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm); + hash_md_t algorithm = nsec3_hash_to_hash_md(nsec3->nsec3.algorithm); +#if PREFER_OPENSSL + if (!algorithm) + return -EOPNOTSUPP; + + size_t hash_size = EVP_MD_size(algorithm); + assert(hash_size > 0); + + if (nsec3->nsec3.next_hashed_name_size != hash_size) + return -EINVAL; + + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if (!ctx) + return -ENOMEM; + + if (EVP_DigestInit_ex(ctx, algorithm, NULL) <= 0) + return -EIO; + + r = dns_name_to_wire_format(name, wire_format, sizeof(wire_format), true); + if (r < 0) + return r; + + if (EVP_DigestUpdate(ctx, wire_format, r) <= 0) + return -EIO; + if (EVP_DigestUpdate(ctx, nsec3->nsec3.salt, nsec3->nsec3.salt_size) <= 0) + return -EIO; + + uint8_t result[EVP_MAX_MD_SIZE]; + if (EVP_DigestFinal_ex(ctx, result, NULL) <= 0) + return -EIO; + + for (unsigned k = 0; k < nsec3->nsec3.iterations; k++) { + if (EVP_DigestInit_ex(ctx, algorithm, NULL) <= 0) + return -EIO; + if (EVP_DigestUpdate(ctx, result, hash_size) <= 0) + return -EIO; + if (EVP_DigestUpdate(ctx, nsec3->nsec3.salt, nsec3->nsec3.salt_size) <= 0) + return -EIO; + + if (EVP_DigestFinal_ex(ctx, result, NULL) <= 0) + return -EIO; + } +#else if (algorithm < 0) return algorithm; initialize_libgcrypt(false); - hash_size = gcry_md_get_algo_dlen(algorithm); + unsigned hash_size = gcry_md_get_algo_dlen(algorithm); assert(hash_size > 0); if (nsec3->nsec3.next_hashed_name_size != hash_size) @@ -1512,14 +1549,15 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { if (r < 0) return r; - err = gcry_md_open(&md, algorithm, 0); + _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; + gcry_error_t err = gcry_md_open(&md, algorithm, 0); if (gcry_err_code(err) != GPG_ERR_NO_ERROR || !md) return -EIO; gcry_md_write(md, wire_format, r); gcry_md_write(md, nsec3->nsec3.salt, nsec3->nsec3.salt_size); - result = gcry_md_read(md, 0); + void *result = gcry_md_read(md, 0); if (!result) return -EIO; @@ -1535,6 +1573,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) { if (!result) return -EIO; } +#endif memcpy(ret, result, hash_size); return (int) hash_size; @@ -1554,8 +1593,14 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsResourceRecord *nsec3) { return 0; /* Ignore NSEC3 RRs whose algorithm we don't know */ - if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0) +#if PREFER_OPENSSL + if (!nsec3_hash_to_hash_md(rr->nsec3.algorithm)) return 0; +#else + if (nsec3_hash_to_hash_md(rr->nsec3.algorithm) < 0) + return 0; +#endif + /* Ignore NSEC3 RRs with an excessive number of required iterations */ if (rr->nsec3.iterations > NSEC3_ITERATIONS_MAX) return 0; From fc169a6fb2e56d66fc2937e348e9858352ef4b39 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Thu, 10 Dec 2020 16:08:11 -0800 Subject: [PATCH 10/15] basic/openssl-util: Add sha256 hash wrapper --- src/shared/openssl-util.c | 37 +++++++++++++++++++++++++++++++++++++ src/shared/openssl-util.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index bd728e6c7c..75fed19f2e 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -4,6 +4,43 @@ #include "alloc-util.h" #if HAVE_OPENSSL +int openssl_hash(const EVP_MD *alg, + const void *msg, + size_t msg_len, + uint8_t *ret_hash, + size_t *ret_hash_len) { + + _cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *ctx = NULL; + unsigned len; + int r; + + ctx = EVP_MD_CTX_new(); + if (!ctx) + /* This function just calls OPENSSL_zalloc, so failure + * here is almost certainly a failed allocation. */ + return -ENOMEM; + + /* The documentation claims EVP_DigestInit behaves just like + * EVP_DigestInit_ex if passed NULL, except it also calls + * EVP_MD_CTX_reset, which deinitializes the context. */ + r = EVP_DigestInit_ex(ctx, alg, NULL); + if (r == 0) + return -EIO; + + r = EVP_DigestUpdate(ctx, msg, msg_len); + if (r == 0) + return -EIO; + + r = EVP_DigestFinal_ex(ctx, ret_hash, &len); + if (r == 0) + return -EIO; + + if (ret_hash_len) + *ret_hash_len = len; + + return 0; +} + int rsa_encrypt_bytes( EVP_PKEY *pkey, const void *decrypted_key, diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index eca56d1729..6eaf581195 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -36,6 +36,8 @@ static inline void sk_X509_free_allp(STACK_OF(X509) **sk) { sk_X509_pop_free(*sk, X509_free); } +int openssl_hash(const EVP_MD *alg, const void *msg, size_t msg_len, uint8_t *ret_hash, size_t *ret_hash_len); + int rsa_encrypt_bytes(EVP_PKEY *pkey, const void *decrypted_key, size_t decrypted_key_size, void **ret_encrypt_key, size_t *ret_encrypt_key_size); int rsa_pkey_to_suitable_key_size(EVP_PKEY *pkey, size_t *ret_suitable_key_size); From 7e8facb36b218720bfd9326c4b668acc5da56cd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 27 Oct 2021 15:39:48 +0200 Subject: [PATCH 11/15] port string_hashsum from libgcrypt to openssl^gcrypt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows resolved and importd to be built without libgcrypt. Note that we now say either 'cryptographic library' or 'cryptolib'. Co-authored-by: Zbigniew Jędrzejewski-Szmek --- TODO | 4 +-- meson.build | 24 +++++++-------- src/basic/build.c | 2 +- src/basic/gcrypt-util.c | 2 ++ src/basic/gcrypt-util.h | 19 +++++++----- src/resolve/resolvectl.c | 1 + src/resolve/resolved-conf.c | 6 ++-- src/resolve/resolved-dns-packet.c | 6 ++-- src/resolve/resolved-link.c | 4 +-- src/shared/openssl-util.c | 30 +++++++++++++++++++ src/shared/openssl-util.h | 12 ++++++++ src/test/meson.build | 6 ++-- .../{test-gcrypt-util.c => test-cryptolib.c} | 17 ++++++++--- 13 files changed, 96 insertions(+), 37 deletions(-) rename src/test/{test-gcrypt-util.c => test-cryptolib.c} (54%) diff --git a/TODO b/TODO index e710f2c587..6757836e68 100644 --- a/TODO +++ b/TODO @@ -433,9 +433,7 @@ Features: * socket units: allow creating a udev monitor socket with ListenDevices= or so, with matches, then activate app through that passing socket over -* unify on openssl (as soon as OpenSSL 3.0 is out, and the Debian license - confusion is gone) - - port resolved over from libgcrypt (DNSSEC code) +* unify on openssl: - port journald + fsprg over from libgcrypt - when that's done: kill gnutls support in resolved diff --git a/meson.build b/meson.build index b4a3b865c1..968d752fde 100644 --- a/meson.build +++ b/meson.build @@ -1448,18 +1448,6 @@ else endif conf.set10('HAVE_DBUS', have) -default_dnssec = get_option('default-dnssec') -if skip_deps - default_dnssec = 'no' -endif -if default_dnssec != 'no' and conf.get('HAVE_GCRYPT') == 0 - message('default-dnssec cannot be set to yes or allow-downgrade when gcrypt is disabled. Setting default-dnssec to no.') - default_dnssec = 'no' -endif -conf.set('DEFAULT_DNSSEC_MODE', - 'DNSSEC_' + default_dnssec.underscorify().to_upper()) -conf.set_quoted('DEFAULT_DNSSEC_MODE_STR', default_dnssec) - dns_over_tls = get_option('dns-over-tls') if dns_over_tls != 'false' if dns_over_tls == 'openssl' @@ -1535,6 +1523,18 @@ conf.set10('HAVE_OPENSSL_OR_GCRYPT', conf.get('HAVE_OPENSSL') == 1 or conf.get('HAVE_GCRYPT') == 1) lib_openssl_or_gcrypt = conf.get('PREFER_OPENSSL') == 1 ? libopenssl : libgcrypt +default_dnssec = get_option('default-dnssec') +if skip_deps + default_dnssec = 'no' +endif +if default_dnssec != 'no' and conf.get('HAVE_OPENSSL_OR_GCRYPT') == 0 + message('default-dnssec cannot be set to yes or allow-downgrade openssl and gcrypt are disabled. Setting default-dnssec to no.') + default_dnssec = 'no' +endif +conf.set('DEFAULT_DNSSEC_MODE', + 'DNSSEC_' + default_dnssec.underscorify().to_upper()) +conf.set_quoted('DEFAULT_DNSSEC_MODE_STR', default_dnssec) + want_importd = get_option('importd') if want_importd != 'false' have = (conf.get('HAVE_LIBCURL') == 1 and diff --git a/src/basic/build.c b/src/basic/build.c index 45074591a6..f8baaabb9f 100644 --- a/src/basic/build.c +++ b/src/basic/build.c @@ -48,7 +48,7 @@ const char* const systemd_features = " -SECCOMP" #endif - /* crypto libraries */ + /* cryptographic libraries */ #if HAVE_GCRYPT " +GCRYPT" diff --git a/src/basic/gcrypt-util.c b/src/basic/gcrypt-util.c index cdc308aca3..64c63cdab1 100644 --- a/src/basic/gcrypt-util.c +++ b/src/basic/gcrypt-util.c @@ -18,6 +18,7 @@ void initialize_libgcrypt(bool secmem) { gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); } +# if !PREFER_OPENSSL int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) { _cleanup_(gcry_md_closep) gcry_md_hd_t md = NULL; gcry_error_t err; @@ -47,4 +48,5 @@ int string_hashsum(const char *s, size_t len, int md_algorithm, char **out) { *out = enc; return 0; } +# endif #endif diff --git a/src/basic/gcrypt-util.h b/src/basic/gcrypt-util.h index 27dcc72028..4c40cefbed 100644 --- a/src/basic/gcrypt-util.h +++ b/src/basic/gcrypt-util.h @@ -12,23 +12,28 @@ #include "macro.h" void initialize_libgcrypt(bool secmem); -int string_hashsum(const char *s, size_t len, int md_algorithm, char **out); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gcry_md_hd_t, gcry_md_close, NULL); #endif +#if !PREFER_OPENSSL +# if HAVE_GCRYPT +int string_hashsum(const char *s, size_t len, int md_algorithm, char **out); +# endif + static inline int string_hashsum_sha224(const char *s, size_t len, char **out) { -#if HAVE_GCRYPT +# if HAVE_GCRYPT return string_hashsum(s, len, GCRY_MD_SHA224, out); -#else +# else return -EOPNOTSUPP; -#endif +# endif } static inline int string_hashsum_sha256(const char *s, size_t len, char **out) { -#if HAVE_GCRYPT +# if HAVE_GCRYPT return string_hashsum(s, len, GCRY_MD_SHA256, out); -#else +# else return -EOPNOTSUPP; -#endif +# endif } +#endif diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 20455bf7a9..5b3ceeff36 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -23,6 +23,7 @@ #include "main-func.h" #include "missing_network.h" #include "netlink-util.h" +#include "openssl-util.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 453f1175e1..a4e44f29be 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -498,14 +498,14 @@ int manager_parse_config_file(Manager *m) { return r; } -#if ! HAVE_GCRYPT +#if !HAVE_OPENSSL_OR_GCRYPT if (m->dnssec_mode != DNSSEC_NO) { - log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support."); + log_warning("DNSSEC option cannot be enabled or set to allow-downgrade when systemd-resolved is built without a cryptographic library. Turning off DNSSEC support."); m->dnssec_mode = DNSSEC_NO; } #endif -#if ! ENABLE_DNS_OVER_TLS +#if !ENABLE_DNS_OVER_TLS if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) { log_warning("DNS-over-TLS option cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support."); m->dns_over_tls_mode = DNS_OVER_TLS_NO; diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index a70ec17743..d45f87ff5d 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #if HAVE_GCRYPT -#include +# include #endif #include "alloc-util.h" @@ -776,7 +776,7 @@ int dns_packet_append_opt( static const uint8_t rfc6975[] = { 0, 5, /* OPTION_CODE: DAU */ -#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600 +#if PREFER_OPENSSL || (HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600) 0, 7, /* LIST_LENGTH */ #else 0, 6, /* LIST_LENGTH */ @@ -787,7 +787,7 @@ int dns_packet_append_opt( DNSSEC_ALGORITHM_RSASHA512, DNSSEC_ALGORITHM_ECDSAP256SHA256, DNSSEC_ALGORITHM_ECDSAP384SHA384, -#if HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600 +#if PREFER_OPENSSL || (HAVE_GCRYPT && GCRYPT_VERSION_NUMBER >= 0x010600) DNSSEC_ALGORITHM_ED25519, #endif diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index dd219f297c..0013cd0b7f 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -414,9 +414,9 @@ void link_set_dnssec_mode(Link *l, DnssecMode mode) { assert(l); -#if ! HAVE_GCRYPT +#if !HAVE_OPENSSL_OR_GCRYPT if (IN_SET(mode, DNSSEC_YES, DNSSEC_ALLOW_DOWNGRADE)) - log_warning("DNSSEC option for the link cannot be enabled or set to allow-downgrade when systemd-resolved is built without gcrypt support. Turning off DNSSEC support."); + log_warning("DNSSEC option for the link cannot be enabled or set to allow-downgrade when systemd-resolved is built without a cryptographic library. Turning off DNSSEC support."); return; #endif diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 75fed19f2e..fdfe465594 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -2,6 +2,7 @@ #include "openssl-util.h" #include "alloc-util.h" +#include "hexdecoct.h" #if HAVE_OPENSSL int openssl_hash(const EVP_MD *alg, @@ -107,4 +108,33 @@ int rsa_pkey_to_suitable_key_size( *ret_suitable_key_size = suitable_key_size; return 0; } + +# if PREFER_OPENSSL +int string_hashsum( + const char *s, + size_t len, + const EVP_MD *md_algorithm, + char **ret) { + + uint8_t hash[EVP_MAX_MD_SIZE]; + size_t hash_size; + char *enc; + int r; + + hash_size = EVP_MD_size(md_algorithm); + assert(hash_size > 0); + + r = openssl_hash(md_algorithm, s, len, hash, NULL); + if (r < 0) + return r; + + enc = hexmem(hash, hash_size); + if (!enc) + return -ENOMEM; + + *ret = enc; + return 0; + +} +# endif #endif diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 6eaf581195..d5b1855987 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -62,3 +62,15 @@ typedef const char* elliptic_curve_t; typedef gcry_md_hd_t hash_context_t; # define OPENSSL_OR_GCRYPT(a, b) (b) #endif + +#if PREFER_OPENSSL +int string_hashsum(const char *s, size_t len, hash_algorithm_t md_algorithm, char **ret); + +static inline int string_hashsum_sha224(const char *s, size_t len, char **ret) { + return string_hashsum(s, len, EVP_sha224(), ret); +} + +static inline int string_hashsum_sha256(const char *s, size_t len, char **ret) { + return string_hashsum(s, len, EVP_sha256(), ret); +} +#endif diff --git a/src/test/meson.build b/src/test/meson.build index de0b8d7fd2..71d2422caf 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -594,8 +594,10 @@ tests += [ [['src/test/test-id128.c']], - [['src/test/test-gcrypt-util.c'], - [], [], [], 'HAVE_GCRYPT'], + [['src/test/test-cryptolib.c'], + [libshared], + [lib_openssl_or_gcrypt], + [], 'HAVE_OPENSSL_OR_GCRYPT'], [['src/test/test-nss-hosts.c', 'src/test/nss-test-util.c', diff --git a/src/test/test-gcrypt-util.c b/src/test/test-cryptolib.c similarity index 54% rename from src/test/test-gcrypt-util.c rename to src/test/test-cryptolib.c index 8eb63cd385..ef39bda653 100644 --- a/src/test/test-gcrypt-util.c +++ b/src/test/test-cryptolib.c @@ -3,25 +3,34 @@ #include "alloc-util.h" #include "gcrypt-util.h" #include "macro.h" +#include "openssl-util.h" #include "string-util.h" #include "tests.h" TEST(string_hashsum) { _cleanup_free_ char *out1 = NULL, *out2 = NULL, *out3 = NULL, *out4 = NULL; - assert_se(string_hashsum("asdf", 4, GCRY_MD_SHA224, &out1) == 0); + assert_se(string_hashsum("asdf", 4, + OPENSSL_OR_GCRYPT(EVP_sha224(), GCRY_MD_SHA224), + &out1) == 0); /* echo -n 'asdf' | sha224sum - */ assert_se(streq(out1, "7872a74bcbf298a1e77d507cd95d4f8d96131cbbd4cdfc571e776c8a")); - assert_se(string_hashsum("asdf", 4, GCRY_MD_SHA256, &out2) == 0); + assert_se(string_hashsum("asdf", 4, + OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256), + &out2) == 0); /* echo -n 'asdf' | sha256sum - */ assert_se(streq(out2, "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b")); - assert_se(string_hashsum("", 0, GCRY_MD_SHA224, &out3) == 0); + assert_se(string_hashsum("", 0, + OPENSSL_OR_GCRYPT(EVP_sha224(), GCRY_MD_SHA224), + &out3) == 0); /* echo -n '' | sha224sum - */ assert_se(streq(out3, "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f")); - assert_se(string_hashsum("", 0, GCRY_MD_SHA256, &out4) == 0); + assert_se(string_hashsum("", 0, + OPENSSL_OR_GCRYPT(EVP_sha256(), GCRY_MD_SHA256), + &out4) == 0); /* echo -n '' | sha256sum - */ assert_se(streq(out4, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); } From 6e7323137ae52da36896ecd238a8de87961cc3a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 2 Nov 2021 10:45:20 +0100 Subject: [PATCH 12/15] resolved: do not use BN_dup() unnecessarilly Suggested in https://github.com/systemd/systemd/pull/21170#discussion_r738696794 --- src/resolve/resolved-dns-dnssec.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 3b645ad3f6..b1fe9d1314 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -110,8 +110,9 @@ static int dnssec_rsa_verify_raw( if (!rpubkey) return -ENOMEM; - if (RSA_set0_key(rpubkey, BN_dup(m), BN_dup(e), NULL) <= 0) + if (RSA_set0_key(rpubkey, m, e, NULL) <= 0) return -EIO; + e = m = NULL; assert((size_t) RSA_size(rpubkey) == signature_size); @@ -344,8 +345,9 @@ static int dnssec_ecdsa_verify_raw( if (!sig) return -ENOMEM; - if (ECDSA_SIG_set0(sig, BN_dup(r), BN_dup(s)) <= 0) + if (ECDSA_SIG_set0(sig, r, s) <= 0) return -EIO; + r = s = NULL; k = ECDSA_do_verify(data, data_size, sig, eckey); if (k < 0) From 684e0a560514f9aaf02813f3f6c4a017400c9d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Tue, 2 Nov 2021 14:37:19 +0100 Subject: [PATCH 13/15] ci: temporarily set -Wno-deprecated-declarations in Packit to suppress OpenSSL 3.0 deprecation warnings (until a proper solution is deployed): RSA_free, EC_KEY_free, RSA_set0_key, RSA_size, EVP_PKEY_assign, EC_KEY_set_group, and others are deprecated. --- .packit.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.packit.yml b/.packit.yml index 962c77913e..98b71fc15e 100644 --- a/.packit.yml +++ b/.packit.yml @@ -31,6 +31,9 @@ actions: # [0] https://github.com/mesonbuild/meson/issues/7360 # [1] https://github.com/systemd/systemd/pull/18908#issuecomment-792250110 - 'sed -i "/^CONFIGURE_OPTS=(/a--werror" .packit_rpm/systemd.spec' + # FIXME: temporarily disable the deprecated-declarations check to suppress + # OpenSSL 3.0 warnings in Rawhide + - 'sed -i "1 i %global optflags %{optflags} -Wno-deprecated-declarations" .packit_rpm/systemd.spec' jobs: - job: copr_build From 85bd394df57fe45c2873605e2c1d1d79e83e853d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 1 Dec 2021 12:50:01 +0100 Subject: [PATCH 14/15] ci: expand the test framework to cover openssl --- .github/workflows/unit_tests.sh | 2 ++ .github/workflows/unit_tests.yml | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit_tests.sh b/.github/workflows/unit_tests.sh index 7e7e3016e8..ec7e92a49a 100755 --- a/.github/workflows/unit_tests.sh +++ b/.github/workflows/unit_tests.sh @@ -29,6 +29,8 @@ function info() { set -ex +MESON_ARGS=(-Dcryptolib=${CRYPTOLIB:-auto}) + for phase in "${PHASES[@]}"; do case $phase in SETUP) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 1458732d2b..537074c231 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -22,10 +22,18 @@ jobs: fail-fast: false matrix: run_phase: [GCC, GCC_ASAN_UBSAN, CLANG, CLANG_ASAN_UBSAN] + cryptolib: [auto] + include: + - run_phase: GCC + cryptolib: openssl + - run_phase: CLANG + cryptolib: gcrypt steps: - name: Repository checkout uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 - name: Install build dependencies run: sudo -E .github/workflows/unit_tests.sh SETUP - - name: Build & test (${{ matrix.run_phase }}) + - name: Build & test (${{ matrix.run_phase }}-${{ matrix.cryptolib }}) run: sudo -E .github/workflows/unit_tests.sh RUN_${{ matrix.run_phase }} + env: + CRYPTOLIB: ${{ matrix.cryptolib }} From e37ad765c86d2a763a2d655068dfe59789f18e3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Thu, 2 Dec 2021 11:29:45 +0100 Subject: [PATCH 15/15] meson: disallow the combination of cryptolib=openssl and dns-over-tls=gnutls It could work, but it doesn't make much sense. If we already have openssl as the cryptolib that provides the necessary support, let's not bring in another library. Disallowing this simplifies things and reduces our support matrix. --- meson.build | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/meson.build b/meson.build index 968d752fde..4cc97d664e 100644 --- a/meson.build +++ b/meson.build @@ -1448,9 +1448,25 @@ else endif conf.set10('HAVE_DBUS', have) +# We support one or the other. If gcrypt is available, we assume it's there to +# be used, and use it in preference. +opt = get_option('cryptolib') +if opt == 'openssl' and conf.get('HAVE_OPENSSL') == 0 + error('openssl requested as the default cryptolib, but not available') +endif +conf.set10('PREFER_OPENSSL', + opt == 'openssl' or (opt == 'auto' and conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_GCRYPT') == 0)) +conf.set10('HAVE_OPENSSL_OR_GCRYPT', + conf.get('HAVE_OPENSSL') == 1 or conf.get('HAVE_GCRYPT') == 1) +lib_openssl_or_gcrypt = conf.get('PREFER_OPENSSL') == 1 ? libopenssl : libgcrypt + dns_over_tls = get_option('dns-over-tls') if dns_over_tls != 'false' - if dns_over_tls == 'openssl' + if dns_over_tls == 'gnutls' and conf.get('PREFER_OPENSSL') == 1 + error('Sorry, -Ddns-over-tls=gnutls is not supported when openssl is used as the cryptolib') + endif + + if dns_over_tls == 'openssl' or conf.get('PREFER_OPENSSL') == 1 have_gnutls = false else have_gnutls = (conf.get('HAVE_GNUTLS') == 1 and libgnutls.version().version_compare('>= 3.6.0')) @@ -1511,18 +1527,6 @@ else endif conf.set10('ENABLE_REPART', have) -# We support one or the other. If gcrypt is available, we assume it's there to -# be used, and use it in preference. -opt = get_option('cryptolib') -if opt == 'openssl' and conf.get('HAVE_OPENSSL') == 0 - error('openssl requested as the default cryptolib, but not available') -endif -conf.set10('PREFER_OPENSSL', - opt == 'openssl' or (opt == 'auto' and conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_GCRYPT') == 0)) -conf.set10('HAVE_OPENSSL_OR_GCRYPT', - conf.get('HAVE_OPENSSL') == 1 or conf.get('HAVE_GCRYPT') == 1) -lib_openssl_or_gcrypt = conf.get('PREFER_OPENSSL') == 1 ? libopenssl : libgcrypt - default_dnssec = get_option('default-dnssec') if skip_deps default_dnssec = 'no'