From cd3f00874f63a50954cebb78edac8f580a27c0de Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Tue, 22 Mar 2022 16:16:57 +1100 Subject: [PATCH 1/3] Check that we can verify a signature at initialisation time Fedora 33 doesn't support RSASHA1 in future mode. There is no easy check for this other than by attempting to perform a verification using known good signatures. We don't attempt to sign with RSASHA1 as that would not work in FIPS mode. RSASHA1 is verify only. The test vectors were generated using OpenSSL 3.0 and util/gen-rsa-sha-vectors.c. Rerunning will generate a new set of test vectors as the private key is not preserved. e.g. cc util/gen-rsa-sha-vectors.c -I /opt/local/include \ -L /opt/local/lib -lcrypto --- lib/dns/opensslrsa_link.c | 245 ++++++++++++++++++++++++++++++++++++- util/gen-rsa-sha-vectors.c | 129 +++++++++++++++++++ 2 files changed, 370 insertions(+), 4 deletions(-) create mode 100644 util/gen-rsa-sha-vectors.c diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c index 26c1182805..2e03a2f022 100644 --- a/lib/dns/opensslrsa_link.c +++ b/lib/dns/opensslrsa_link.c @@ -1562,14 +1562,251 @@ static dst_func_t opensslrsa_functions = { NULL, /*%< restore */ }; +/* + * An RSA public key with 2048 bits + */ +static const unsigned char e_bytes[] = "\x01\x00\x01"; +static const unsigned char n_bytes[] = + "\xc3\x90\x07\xbe\xf1\x85\xfc\x1a\x43\xb1\xa5\x15\xce\x71\x34\xfc\xc1" + "\x87\x27\x28\x38\xa4\xcf\x7c\x1a\x82\xa8\xdc\x04\x14\xd0\x3f\xb4\xfe" + "\x20\x4a\xdd\xd9\x0d\xd7\xcd\x61\x8c\xbd\x61\xa8\x10\xb5\x63\x1c\x29" + "\x15\xcb\x41\xee\x43\x91\x7f\xeb\xa5\x2c\xab\x81\x75\x0d\xa3\x3d\xe4" + "\xc8\x49\xb9\xca\x5a\x55\xa1\xbb\x09\xd1\xfb\xcd\xa2\xd2\x12\xa4\x85" + "\xdf\xa5\x65\xc9\x27\x2d\x8b\xd7\x8b\xfe\x6d\xc4\xd1\xd9\x83\x1c\x91" + "\x7d\x3d\xd0\xa4\xcd\xe1\xe7\xb9\x7a\x11\x38\xf9\x8b\x3c\xec\x30\xb6" + "\x36\xb9\x92\x64\x81\x56\x3c\xbc\xf9\x49\xfb\xba\x82\xb7\xa0\xfa\x65" + "\x79\x83\xb9\x4c\xa7\xfd\x53\x0b\x5a\xe4\xde\xf9\xfc\x38\x7e\xb5\x2c" + "\xa0\xc3\xb2\xfc\x7c\x38\xb0\x63\x50\xaf\x00\xaa\xb2\xad\x49\x54\x1e" + "\x8b\x11\x88\x9b\x6e\xae\x3b\x23\xa3\xdd\x53\x51\x80\x7a\x0b\x91\x4e" + "\x6d\x32\x01\xbd\x17\x81\x12\x64\x9f\x84\xae\x76\x53\x1a\x63\xa0\xda" + "\xcc\x45\x04\x72\xb0\xa7\xfb\xfa\x02\x39\x53\xc1\x83\x1f\x88\x54\x47" + "\x88\x63\x20\x71\x5d\xe2\xaa\x7c\x53\x39\x5e\x35\x25\xee\xe6\x5c\x15" + "\x5e\x14\xbe\x99\xde\x25\x19\xe7\x13\xdb\xce\xa3\xd3\x6c\x5c\xbb\x0e" + "\x6b"; + +static const unsigned char sha1_sig[] = + "\x69\x99\x89\x28\xe0\x38\x34\x91\x29\xb6\xac\x4b\xe9\x51\xbd\xbe\xc8" + "\x1a\x2d\xb6\xca\x99\xa3\x9f\x6a\x8b\x94\x5a\x51\x37\xd5\x8d\xae\x87" + "\xed\xbc\x8e\xb8\xa3\x60\x6b\xf6\xe6\x72\xfc\x26\x2a\x39\x2b\xfe\x88" + "\x1a\xa9\xd1\x93\xc7\xb9\xf8\xb6\x45\xa1\xf9\xa1\x56\x78\x7b\x00\xec" + "\x33\x83\xd4\x93\x25\x48\xb3\x50\x09\xd0\xbc\x7f\xac\x67\xc7\xa2\x7f" + "\xfc\xf6\x5a\xef\xf8\x5a\xad\x52\x74\xf5\x71\x34\xd9\x3d\x33\x8b\x4d" + "\x99\x64\x7e\x14\x59\xbe\xdf\x26\x8a\x67\x96\x6c\x1f\x79\x85\x10\x0d" + "\x7f\xd6\xa4\xba\x57\x41\x03\x71\x4e\x8c\x17\xd5\xc4\xfb\x4a\xbe\x66" + "\x45\x15\x45\x0c\x02\xe0\x10\xe1\xbb\x33\x8d\x90\x34\x3c\x94\xa4\x4c" + "\x7c\xd0\x5e\x90\x76\x80\x59\xb2\xfa\x54\xbf\xa9\x86\xb8\x84\x1e\x28" + "\x48\x60\x2f\x9e\xa4\xbc\xd4\x9c\x20\x27\x16\xac\x33\xcb\xcf\xab\x93" + "\x7a\x3b\x74\xa0\x18\x92\xa1\x4f\xfc\x52\x19\xee\x7a\x13\x73\xba\x36" + "\xaf\x78\x5d\xb6\x1f\x96\x76\x15\x73\xee\x04\xa8\x70\x27\xf7\xe7\xfa" + "\xe8\xf6\xc8\x5f\x4a\x81\x56\x0a\x94\xf3\xc6\x98\xd2\x93\xc4\x0b\x49" + "\x6b\x44\xd3\x73\xa2\xe3\xef\x5d\x9e\x68\xac\xa7\x42\xb1\xbb\x65\xbe" + "\x59"; + +static const unsigned char sha256_sig[] = + "\x0f\x8c\xdb\xe6\xb6\x21\xc8\xc5\x28\x76\x7d\xf6\xf2\x3b\x78\x47\x77" + "\x03\x34\xc5\x5e\xc0\xda\x42\x41\xc0\x0f\x97\xd3\xd0\x53\xa1\xd6\x87" + "\xe4\x16\x29\x9a\xa5\x59\xf4\x01\xad\xc9\x04\xe7\x61\xe2\xcb\x79\x73" + "\xce\xe0\xa6\x85\xe5\x10\x8c\x4b\xc5\x68\x3b\x96\x42\x3f\x56\xb3\x6d" + "\x89\xc4\xff\x72\x36\xf2\x3f\xed\xe9\xb8\xe3\xae\xab\x3c\xb7\xaa\xf7" + "\x1f\x8f\x26\x6b\xee\xc1\xac\x72\x89\x23\x8b\x7a\xd7\x8c\x84\xf3\xf5" + "\x97\xa8\x8d\xd3\xef\xb2\x5e\x06\x04\x21\xdd\x28\xa2\x28\x83\x68\x9b" + "\xac\x34\xdd\x36\x33\xda\xdd\xa4\x59\xc7\x5a\x4d\xf3\x83\x06\xd5\xc0" + "\x0d\x1f\x4f\x47\x2f\x9f\xcc\xc2\x0d\x21\x1e\x82\xb9\x3d\xf3\xa4\x1a" + "\xa6\xd8\x0e\x72\x1d\x71\x17\x1c\x54\xad\x37\x3e\xa4\x0e\x70\x86\x53" + "\xfb\x40\xad\xb9\x14\xf8\x8d\x93\xbb\xd7\xe7\x31\xce\xe0\x98\xda\x27" + "\x1c\x18\x8e\xd8\x85\xcb\xa7\xb1\x18\xac\x8c\xa8\x9d\xa9\xe2\xf6\x30" + "\x95\xa4\x81\xf4\x1c\xa0\x31\xd5\xc7\x9d\x28\x33\xee\x7f\x08\x4f\xcb" + "\xd1\x14\x17\xdf\xd0\x88\x78\x47\x29\xaf\x6c\xb2\x62\xa6\x30\x87\x29" + "\xaa\x80\x19\x7d\x2f\x05\xe3\x7e\x23\x73\x88\x08\xcc\xbd\x50\x46\x09" + "\x2a"; + +static const unsigned char sha512_sig[] = + "\x15\xda\x87\x87\x1f\x76\x08\xd3\x9d\x3a\xb9\xd2\x6a\x0e\x3b\x7d\xdd" + "\xec\x7d\xc4\x6d\x26\xf5\x04\xd3\x76\xc7\x83\xc4\x81\x69\x35\xe9\x47" + "\xbf\x49\xd1\xc0\xf9\x01\x4e\x0a\x34\x5b\xd0\xec\x6e\xe2\x2e\xe9\x2d" + "\x00\xfd\xe0\xa0\x28\x54\x53\x19\x49\x6d\xd2\x58\xb9\x47\xfa\x45\xad" + "\xd2\x1d\x52\xac\x80\xcb\xfc\x91\x97\x84\x58\x5f\xab\x21\x62\x60\x79" + "\xb8\x8a\x83\xe1\xf1\xcb\x05\x4c\x92\x56\x62\xd9\xbf\xa7\x81\x34\x23" + "\xdf\xd7\xa7\xc4\xdf\xde\x96\x00\x57\x4b\x78\x85\xb9\x3b\xdd\x3f\x98" + "\x88\x59\x1d\x48\xcf\x5a\xa8\xb7\x2a\x8b\x77\x93\x8e\x38\x3a\x0c\xa7" + "\x8a\x5f\xe6\x9f\xcb\xf0\x9a\x6b\xb6\x91\x04\x8b\x69\x6a\x37\xee\xa2" + "\xad\x5f\x31\x20\x96\xd6\x51\x80\xbf\x62\x48\xb8\xe4\x94\x10\x86\x4e" + "\xf2\x22\x1e\xa4\xd5\x54\xfe\xe1\x35\x49\xaf\xf8\x62\xfc\x11\xeb\xf7" + "\x3d\xd5\x5e\xaf\x11\xbd\x3d\xa9\x3a\x9f\x7f\xe8\xb4\x0d\xa2\xbb\x1c" + "\xbd\x4c\xed\x9e\x81\xb1\xec\xd3\xea\xaa\x03\xe3\x14\xdf\x8c\xb3\x78" + "\x85\x5e\x87\xad\xec\x41\x1a\xa9\x4f\xd2\xe6\xc6\xbe\xfa\xb8\x10\xea" + "\x74\x25\x36\x0c\x23\xe2\x24\xb7\x21\xb7\x0d\xaf\xf6\xb4\x31\xf5\x75" + "\xf1"; + +static isc_result_t +check_algorithm(unsigned char algorithm) { + BIGNUM *n = NULL, *e = NULL; + EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_create(); + EVP_PKEY *pkey = NULL; + const EVP_MD *type = NULL; + const unsigned char *sig = NULL; + int status; + isc_result_t ret = ISC_R_SUCCESS; + size_t len; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + RSA *rsa = NULL; +#else + OSSL_PARAM *params = NULL; + OSSL_PARAM_BLD *bld = NULL; + EVP_PKEY_CTX *ctx = NULL; +#endif + + if (evp_md_ctx == NULL) { + DST_RET(ISC_R_NOMEMORY); + } + + switch (algorithm) { + case DST_ALG_RSASHA1: + case DST_ALG_NSEC3RSASHA1: + type = EVP_sha1(); /* SHA1 + RSA */ + sig = sha1_sig; + len = sizeof(sha1_sig) - 1; + break; + case DST_ALG_RSASHA256: + type = EVP_sha256(); /* SHA256 + RSA */ + sig = sha256_sig; + len = sizeof(sha256_sig) - 1; + break; + case DST_ALG_RSASHA512: + type = EVP_sha512(); + sig = sha512_sig; + len = sizeof(sha512_sig) - 1; + break; + default: + DST_RET(ISC_R_NOTIMPLEMENTED); + } + + if (type == NULL) { + DST_RET(ISC_R_NOTIMPLEMENTED); + } + + /* + * Construct pkey. + */ + e = BN_bin2bn(e_bytes, sizeof(e_bytes) - 1, NULL); + n = BN_bin2bn(n_bytes, sizeof(n_bytes) - 1, NULL); + if (e == NULL || n == NULL) { + DST_RET(ISC_R_NOMEMORY); + } + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + rsa = RSA_new(); + if (rsa == NULL) { + DST_RET(dst__openssl_toresult2("RSA_new", + DST_R_OPENSSLFAILURE)); + } + status = RSA_set0_key(rsa, n, e, NULL); + if (status != 1) { + DST_RET(dst__openssl_toresult2("RSA_set0_key", + DST_R_OPENSSLFAILURE)); + } + + /* These are now managed by OpenSSL. */ + n = NULL; + e = NULL; + + pkey = EVP_PKEY_new(); + if (pkey == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_new", + DST_R_OPENSSLFAILURE)); + } + status = EVP_PKEY_set1_RSA(pkey, rsa); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_set1_RSA", + DST_R_OPENSSLFAILURE)); + } +#else + bld = OSSL_PARAM_BLD_new(); + if (bld == NULL) { + DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_new", + DST_R_OPENSSLFAILURE)); + } + if (OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n) != 1 || + OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e) != 1) + { + DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_BN", + DST_R_OPENSSLFAILURE)); + } + params = OSSL_PARAM_BLD_to_param(bld); + if (params == NULL) { + DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_to_param", + DST_R_OPENSSLFAILURE)); + } + ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + if (ctx == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_CTX_new_from_name", + DST_R_OPENSSLFAILURE)); + } + status = EVP_PKEY_fromdata_init(ctx); + if (status != 1) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata_init", + DST_R_OPENSSLFAILURE)); + } + status = EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params); + if (status != 1 || pkey == NULL) { + DST_RET(dst__openssl_toresult2("EVP_PKEY_fromdata", + DST_R_OPENSSLFAILURE)); + } +#endif + + /* + * Check that we can verify the signature. + */ + if (EVP_DigestInit_ex(evp_md_ctx, type, NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_VerifyFinal(evp_md_ctx, sig, len, pkey) != 1) + { + DST_RET(ISC_R_NOTIMPLEMENTED); + } + +err: + BN_free(e); + BN_free(n); +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if (rsa != NULL) { + RSA_free(rsa); + } +#else + if (bld != NULL) { + OSSL_PARAM_BLD_free(bld); + } + if (ctx != NULL) { + EVP_PKEY_CTX_free(ctx); + } + if (params != NULL) { + OSSL_PARAM_free(params); + } +#endif + if (pkey != NULL) { + EVP_PKEY_free(pkey); + } + if (evp_md_ctx != NULL) { + EVP_MD_CTX_destroy(evp_md_ctx); + } + ERR_clear_error(); + return (ret); +} + isc_result_t dst__opensslrsa_init(dst_func_t **funcp, unsigned char algorithm) { + isc_result_t result; + REQUIRE(funcp != NULL); - UNUSED(algorithm); + result = check_algorithm(algorithm); - if (*funcp == NULL) { - *funcp = &opensslrsa_functions; + if (result == ISC_R_SUCCESS) { + if (*funcp == NULL) { + *funcp = &opensslrsa_functions; + } + } else if (result == ISC_R_NOTIMPLEMENTED) { + result = ISC_R_SUCCESS; } - return (ISC_R_SUCCESS); + + return (result); } diff --git a/util/gen-rsa-sha-vectors.c b/util/gen-rsa-sha-vectors.c new file mode 100644 index 0000000000..7f76036b84 --- /dev/null +++ b/util/gen-rsa-sha-vectors.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Generate test vectors for lib/dns/opensslrsa_link.c as: + * + * Fedora 33 doesn't support RSASHA1 in future mode. There is no easy + * check for this other than by attempting to perform a verification + * using known good signatures. We don't attempt to sign with RSASHA1 + * as that would not work in FIPS mode. RSASHA1 is verify only. + * + * The test vectors were generated using OpenSSL 3.0 and + * util/gen-rsa-sha-vectors.c. Rerunning will generate a new set of + * test vectors as the private key is not preserved. + * + * e.g. + * cc util/gen-rsa-sha-vectors.c -I /opt/local/include \ + * -L /opt/local/lib -lcrypto + */ + +int +main() { + BIGNUM *e = BN_new(); + BIGNUM *n = BN_new(); + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + EVP_PKEY *pkey = NULL; + unsigned char buf[512]; + size_t bytes; + EVP_MD_CTX *evp_md_ctx = EVP_MD_CTX_create(); + unsigned int siglen = sizeof(buf); + + if (e == NULL || n == NULL || ctx == NULL || evp_md_ctx == NULL) { + return (1); + } + + BN_set_bit(e, 0); + BN_set_bit(e, 16); + + if (EVP_PKEY_keygen_init(ctx) != 1 || + EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, 2048) != 1 || + EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, e) != 1 || + EVP_PKEY_keygen(ctx, &pkey) != 1 || pkey == NULL) + { + return (1); + } + + EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_RSA_N, &n); + if (n == NULL) { + return (1); + } + + bytes = BN_num_bytes(e); + BN_bn2bin(e, buf); + printf("unsigned char e_bytes[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n"); + + bytes = BN_num_bytes(n); + BN_bn2bin(n, buf); + printf("unsigned char n_bytes[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + if (EVP_DigestInit_ex(evp_md_ctx, EVP_sha1(), NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_SignFinal(evp_md_ctx, buf, &siglen, pkey) != 1) + { + return (1); + } + bytes = siglen; + printf("unsigned char sha1_sig[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + if (EVP_DigestInit_ex(evp_md_ctx, EVP_sha256(), NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_SignFinal(evp_md_ctx, buf, &siglen, pkey) != 1) + { + return (1); + } + bytes = siglen; + printf("unsigned char sha256_sig[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + if (EVP_DigestInit_ex(evp_md_ctx, EVP_sha512(), NULL) != 1 || + EVP_DigestUpdate(evp_md_ctx, "test", 4) != 1 || + EVP_SignFinal(evp_md_ctx, buf, &siglen, pkey) != 1) + { + return (1); + } + bytes = siglen; + printf("unsigned char sha512_sig[] = \""); + for (size_t i = 0; i < bytes; i++) { + printf("\\x%02x", buf[i]); + } + printf("\";\n\n"); + + return (0); +} From c549249cb9020fa2138e7c7411bc64ebd4839557 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 22 Jul 2022 09:05:31 +1000 Subject: [PATCH 2/3] CHANGES note for [GL #3469] --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index abedcfe7b3..deecead614 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +5933. [port] Automatically disable RSASHA1 and NSEC3RSASHA1 in + named on Fedorda 33, Oracle Linux 9 and RHEL9 when + they are disabled by the security policy. [GL #3469] + 5932. [bug] Fix rndc dumpdb -expired and always include expired RRsets, not just for RBTDB_VIRTUAL time window. [GL #3462] From 16b133af407e7659f6c46a81aa58e1d7891249d4 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Fri, 22 Jul 2022 09:13:09 +1000 Subject: [PATCH 3/3] Add release note for [GL #3469] --- doc/notes/notes-current.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index bbc6fc35bf..a41370d496 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -41,7 +41,12 @@ Removed Features Feature Changes ~~~~~~~~~~~~~~~ -- None. +- DNSSEC ``RSASHA1`` and ``NSEC3RSASHA1`` are automatically disabled + on systems (e.g. RHEL9) where they are disallowed by the security + policy. Primary zones using those algorithms need to be moved + off of them prior to running on these systems as graceful migration + to different DNSSEC algorithms is not possible when RSASHA1 is + disallowed by the OS. :gl:`#3469` Bug Fixes ~~~~~~~~~