From 2fdd24a7245144606e3f3ddd67e64225be741324 Mon Sep 17 00:00:00 2001 From: Clemens Lang Date: Apr 07 2022 16:16:51 +0000 Subject: [PATCH 1/2] Allow disabling of SHA1 signatures NOTE: This patch is ported from CentOS 9 / RHEL 9, where it defaults to denying SHA1 signatures. On Fedora, the default is – for now – to allow SHA1 signatures. In order to phase out SHA1 signatures, introduce a new configuration option in the alg_section named 'rh-allow-sha1-signatures'. This option defaults to true. If set to false, any signature creation or verification operations that involve SHA1 as digest will fail. This also affects TLS, where the signature_algorithms extension of any ClientHello message sent by OpenSSL will no longer include signatures with the SHA1 digest if rh-allow-sha1-signatures is false. For servers that request a client certificate, the same also applies for CertificateRequest messages sent by them. (cherry picked from commit 432cfa2baa8db94abf0daa9821a42ef40723e3c0) Resolves: rhbz#2070977 Resolves: rhbz#2071615 Related: rhbz#2031742, rhbz#2062640 Signed-off-by: Clemens Lang --- diff --git a/0049-Allow-disabling-of-SHA1-signatures.patch b/0049-Allow-disabling-of-SHA1-signatures.patch new file mode 100644 index 0000000..e316a4b --- /dev/null +++ b/0049-Allow-disabling-of-SHA1-signatures.patch @@ -0,0 +1,503 @@ +From b4f8964ad1903e24cd2ee07f42ce97c3047f4af4 Mon Sep 17 00:00:00 2001 +From: Clemens Lang +Date: Mon, 21 Feb 2022 17:24:44 +0100 +Subject: [PATCH] Allow disabling of SHA1 signatures +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +NOTE: This patch is ported from CentOS 9 / RHEL 9, where it defaults to +denying SHA1 signatures. On Fedora, the default is – for now – to allow +SHA1 signatures. + +In order to phase out SHA1 signatures, introduce a new configuration +option in the alg_section named 'rh-allow-sha1-signatures'. This option +defaults to true. If set to false, any signature creation or +verification operations that involve SHA1 as digest will fail. + +This also affects TLS, where the signature_algorithms extension of any +ClientHello message sent by OpenSSL will no longer include signatures +with the SHA1 digest if rh-allow-sha1-signatures is false. For servers +that request a client certificate, the same also applies for +CertificateRequest messages sent by them. + +For signatures created using the EVP_PKEY API, this is a best-effort +check that will deny signatures in cases where the digest algorithm is +known. This means, for example, that that following steps will still +work: + + $> openssl dgst -sha1 -binary -out sha1 infile + $> openssl pkeyutl -inkey key.pem -sign -in sha1 -out sha1sig + $> openssl pkeyutl -inkey key.pem -verify -sigfile sha1sig -in sha1 + +whereas these will not: + + $> openssl dgst -sha1 -binary -out sha1 infile + $> openssl pkeyutl -inkey kem.pem -sign -in sha1 -out sha1sig -pkeyopt digest:sha1 + $> openssl pkeyutl -inkey kem.pem -verify -sigfile sha1sig -in sha1 -pkeyopt digest:sha1 + +This happens because in the first case, OpenSSL's signature +implementation does not know that it is signing a SHA1 hash (it could be +signing arbitrary data). +--- + crypto/evp/evp_cnf.c | 13 +++ + crypto/evp/m_sigver.c | 85 +++++++++++++++++++ + crypto/evp/pmeth_lib.c | 15 ++++ + doc/man5/config.pod | 13 +++ + include/internal/cryptlib.h | 3 +- + include/internal/sslconf.h | 4 + + providers/common/securitycheck.c | 20 +++++ + providers/common/securitycheck_default.c | 9 +- + providers/implementations/signature/dsa_sig.c | 11 ++- + .../implementations/signature/ecdsa_sig.c | 4 + + providers/implementations/signature/rsa_sig.c | 20 ++++- + ssl/t1_lib.c | 8 ++ + util/libcrypto.num | 2 + + 13 files changed, 198 insertions(+), 9 deletions(-) + +diff --git a/crypto/evp/evp_cnf.c b/crypto/evp/evp_cnf.c +index 0e7fe64cf9..b9d3b6d226 100644 +--- a/crypto/evp/evp_cnf.c ++++ b/crypto/evp/evp_cnf.c +@@ -10,6 +10,7 @@ + #include + #include + #include "internal/cryptlib.h" ++#include "internal/sslconf.h" + #include + #include + #include +@@ -57,6 +58,18 @@ static int alg_module_init(CONF_IMODULE *md, const CONF *cnf) + ERR_raise(ERR_LIB_EVP, EVP_R_SET_DEFAULT_PROPERTY_FAILURE); + return 0; + } ++ } else if (strcmp(oval->name, "rh-allow-sha1-signatures") == 0) { ++ int m; ++ ++ /* Detailed error already reported. */ ++ if (!X509V3_get_value_bool(oval, &m)) ++ return 0; ++ ++ if (!ossl_ctx_legacy_digest_signatures_allowed_set( ++ NCONF_get0_libctx((CONF *)cnf), m > 0, 0)) { ++ ERR_raise(ERR_LIB_EVP, EVP_R_SET_DEFAULT_PROPERTY_FAILURE); ++ return 0; ++ } + } else { + ERR_raise_data(ERR_LIB_EVP, EVP_R_UNKNOWN_OPTION, + "name=%s, value=%s", oval->name, oval->value); +diff --git a/crypto/evp/m_sigver.c b/crypto/evp/m_sigver.c +index 76a6814b42..8da2183ce0 100644 +--- a/crypto/evp/m_sigver.c ++++ b/crypto/evp/m_sigver.c +@@ -16,6 +16,79 @@ + #include "internal/numbers.h" /* includes SIZE_MAX */ + #include "evp_local.h" + ++typedef struct ossl_legacy_digest_signatures_st { ++ int allowed; ++} OSSL_LEGACY_DIGEST_SIGNATURES; ++ ++static void ossl_ctx_legacy_digest_signatures_free(void *vldsigs) ++{ ++ OSSL_LEGACY_DIGEST_SIGNATURES *ldsigs = vldsigs; ++ ++ if (ldsigs != NULL) { ++ OPENSSL_free(ldsigs); ++ } ++} ++ ++static void *ossl_ctx_legacy_digest_signatures_new(OSSL_LIB_CTX *ctx) ++{ ++ OSSL_LEGACY_DIGEST_SIGNATURES* ldsigs = OPENSSL_zalloc(sizeof(OSSL_LEGACY_DIGEST_SIGNATURES)); ++ /* Warning: This patch differs from the same patch in CentOS and RHEL here, ++ * because the default on Fedora is to allow SHA-1 and support disabling ++ * it, while CentOS/RHEL disable it by default and allow enabling it. */ ++ ldsigs->allowed = 1; ++ return ldsigs; ++} ++ ++static const OSSL_LIB_CTX_METHOD ossl_ctx_legacy_digest_signatures_method = { ++ OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY, ++ ossl_ctx_legacy_digest_signatures_new, ++ ossl_ctx_legacy_digest_signatures_free, ++}; ++ ++static OSSL_LEGACY_DIGEST_SIGNATURES *ossl_ctx_legacy_digest_signatures( ++ OSSL_LIB_CTX *libctx, int loadconfig) ++{ ++#ifndef FIPS_MODULE ++ if (loadconfig && !OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL)) ++ return NULL; ++#endif ++ ++ return ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_LEGACY_DIGEST_SIGNATURES, ++ &ossl_ctx_legacy_digest_signatures_method); ++} ++ ++int ossl_ctx_legacy_digest_signatures_allowed(OSSL_LIB_CTX *libctx, int loadconfig) ++{ ++ OSSL_LEGACY_DIGEST_SIGNATURES *ldsigs ++ = ossl_ctx_legacy_digest_signatures(libctx, loadconfig); ++ ++#ifndef FIPS_MODULE ++ if (ossl_safe_getenv("OPENSSL_ENABLE_SHA1_SIGNATURES") != NULL) ++ /* used in tests */ ++ return 1; ++#endif ++ ++ /* Warning: This patch differs from the same patch in CentOS and RHEL here, ++ * because the default on Fedora is to allow SHA-1 and support disabling ++ * it, while CentOS/RHEL disable it by default and allow enabling it. */ ++ return ldsigs != NULL ? ldsigs->allowed : 1; ++} ++ ++int ossl_ctx_legacy_digest_signatures_allowed_set(OSSL_LIB_CTX *libctx, int allow, ++ int loadconfig) ++{ ++ OSSL_LEGACY_DIGEST_SIGNATURES *ldsigs ++ = ossl_ctx_legacy_digest_signatures(libctx, loadconfig); ++ ++ if (ldsigs == NULL) { ++ ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); ++ return 0; ++ } ++ ++ ldsigs->allowed = allow; ++ return 1; ++} ++ + #ifndef FIPS_MODULE + + static int update(EVP_MD_CTX *ctx, const void *data, size_t datalen) +@@ -258,6 +331,18 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx, + } + } + ++ if (ctx->reqdigest != NULL ++ && !EVP_PKEY_is_a(locpctx->pkey, SN_hmac) ++ && !EVP_PKEY_is_a(locpctx->pkey, SN_tls1_prf) ++ && !EVP_PKEY_is_a(locpctx->pkey, SN_hkdf)) { ++ int mdnid = EVP_MD_nid(ctx->reqdigest); ++ if (!ossl_ctx_legacy_digest_signatures_allowed(locpctx->libctx, 0) ++ && (mdnid == NID_sha1 || mdnid == NID_md5_sha1)) { ++ ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_DIGEST); ++ goto err; ++ } ++ } ++ + if (ver) { + if (signature->digest_verify_init == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); +diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c +index 2b9c6c2351..3c5a1e6f5d 100644 +--- a/crypto/evp/pmeth_lib.c ++++ b/crypto/evp/pmeth_lib.c +@@ -33,6 +33,7 @@ + #include "internal/ffc.h" + #include "internal/numbers.h" + #include "internal/provider.h" ++#include "internal/sslconf.h" + #include "evp_local.h" + + #ifndef FIPS_MODULE +@@ -946,6 +947,20 @@ static int evp_pkey_ctx_set_md(EVP_PKEY_CTX *ctx, const EVP_MD *md, + return -2; + } + ++ if (EVP_PKEY_CTX_IS_SIGNATURE_OP(ctx) ++ && md != NULL ++ && ctx->pkey != NULL ++ && !EVP_PKEY_is_a(ctx->pkey, SN_hmac) ++ && !EVP_PKEY_is_a(ctx->pkey, SN_tls1_prf) ++ && !EVP_PKEY_is_a(ctx->pkey, SN_hkdf)) { ++ int mdnid = EVP_MD_nid(md); ++ if ((mdnid == NID_sha1 || mdnid == NID_md5_sha1) ++ && !ossl_ctx_legacy_digest_signatures_allowed(ctx->libctx, 0)) { ++ ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_DIGEST); ++ return -1; ++ } ++ } ++ + if (fallback) + return EVP_PKEY_CTX_ctrl(ctx, -1, op, ctrl, 0, (void *)(md)); + +diff --git a/doc/man5/config.pod b/doc/man5/config.pod +index 77a8055e81..0c9110d28a 100644 +--- a/doc/man5/config.pod ++++ b/doc/man5/config.pod +@@ -296,6 +296,19 @@ Within the algorithm properties section, the following names have meaning: + The value may be anything that is acceptable as a property query + string for EVP_set_default_properties(). + ++=item B ++ ++The value is a boolean that can be B or B. If the value is not set, ++it behaves as if it was set to B. ++ ++When set to B, any attempt to create or verify a signature with a SHA1 ++digest will fail. To test whether your software will work with future versions ++of OpenSSL, set this option to B. This setting also affects TLS, where ++signature algorithms that use SHA1 as digest will no longer be supported if ++this option is set to B. Because TLS 1.1 or lower use MD5-SHA1 as ++pseudorandom function (PRF) to derive key material, disabling ++B requires the use of TLS 1.2 or newer. ++ + =item B (deprecated) + + The value is a boolean that can be B or B. If the value is +diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h +index 1291299b6e..e234341e6a 100644 +--- a/include/internal/cryptlib.h ++++ b/include/internal/cryptlib.h +@@ -168,7 +168,8 @@ typedef struct ossl_ex_data_global_st { + # define OSSL_LIB_CTX_PROVIDER_CONF_INDEX 16 + # define OSSL_LIB_CTX_BIO_CORE_INDEX 17 + # define OSSL_LIB_CTX_CHILD_PROVIDER_INDEX 18 +-# define OSSL_LIB_CTX_MAX_INDEXES 19 ++# define OSSL_LIB_CTX_LEGACY_DIGEST_SIGNATURES 19 ++# define OSSL_LIB_CTX_MAX_INDEXES 20 + + # define OSSL_LIB_CTX_METHOD_LOW_PRIORITY -1 + # define OSSL_LIB_CTX_METHOD_DEFAULT_PRIORITY 0 +diff --git a/include/internal/sslconf.h b/include/internal/sslconf.h +index fd7f7e3331..05464b0655 100644 +--- a/include/internal/sslconf.h ++++ b/include/internal/sslconf.h +@@ -18,4 +18,8 @@ int conf_ssl_name_find(const char *name, size_t *idx); + void conf_ssl_get_cmd(const SSL_CONF_CMD *cmd, size_t idx, char **cmdstr, + char **arg); + ++/* Methods to support disabling all signatures with legacy digests */ ++int ossl_ctx_legacy_digest_signatures_allowed(OSSL_LIB_CTX *libctx, int loadconfig); ++int ossl_ctx_legacy_digest_signatures_allowed_set(OSSL_LIB_CTX *libctx, int allow, ++ int loadconfig); + #endif +diff --git a/providers/common/securitycheck.c b/providers/common/securitycheck.c +index 699ada7c52..e534ad0a5f 100644 +--- a/providers/common/securitycheck.c ++++ b/providers/common/securitycheck.c +@@ -19,6 +19,7 @@ + #include + #include + #include "prov/securitycheck.h" ++#include "internal/sslconf.h" + + /* + * FIPS requires a minimum security strength of 112 bits (for encryption or +@@ -235,6 +236,15 @@ int ossl_digest_get_approved_nid_with_sha1(OSSL_LIB_CTX *ctx, const EVP_MD *md, + mdnid = -1; /* disallowed by security checks */ + } + # endif /* OPENSSL_NO_FIPS_SECURITYCHECKS */ ++ ++#ifndef FIPS_MODULE ++ if (!ossl_ctx_legacy_digest_signatures_allowed(ctx, 0)) ++ /* SHA1 is globally disabled, check whether we want to locally allow ++ * it. */ ++ if (mdnid == NID_sha1 && !sha1_allowed) ++ mdnid = -1; ++#endif ++ + return mdnid; + } + +@@ -244,5 +254,15 @@ int ossl_digest_is_allowed(OSSL_LIB_CTX *ctx, const EVP_MD *md) + if (ossl_securitycheck_enabled(ctx)) + return ossl_digest_get_approved_nid(md) != NID_undef; + # endif /* OPENSSL_NO_FIPS_SECURITYCHECKS */ ++ ++#ifndef FIPS_MODULE ++ { ++ int mdnid = EVP_MD_nid(md); ++ if ((mdnid == NID_sha1 || mdnid == NID_md5_sha1) ++ && !ossl_ctx_legacy_digest_signatures_allowed(ctx, 0)) ++ return 0; ++ } ++#endif ++ + return 1; + } +diff --git a/providers/common/securitycheck_default.c b/providers/common/securitycheck_default.c +index de7f0d3a0a..ce54a94fbc 100644 +--- a/providers/common/securitycheck_default.c ++++ b/providers/common/securitycheck_default.c +@@ -15,6 +15,7 @@ + #include + #include "prov/securitycheck.h" + #include "internal/nelem.h" ++#include "internal/sslconf.h" + + /* Disable the security checks in the default provider */ + int ossl_securitycheck_enabled(OSSL_LIB_CTX *libctx) +@@ -23,9 +24,10 @@ int ossl_securitycheck_enabled(OSSL_LIB_CTX *libctx) + } + + int ossl_digest_rsa_sign_get_md_nid(OSSL_LIB_CTX *ctx, const EVP_MD *md, +- ossl_unused int sha1_allowed) ++ int sha1_allowed) + { + int mdnid; ++ int ldsigs_allowed; + + static const OSSL_ITEM name_to_nid[] = { + { NID_md5, OSSL_DIGEST_NAME_MD5 }, +@@ -36,8 +38,11 @@ int ossl_digest_rsa_sign_get_md_nid(OSSL_LIB_CTX *ctx, const EVP_MD *md, + { NID_ripemd160, OSSL_DIGEST_NAME_RIPEMD160 }, + }; + +- mdnid = ossl_digest_get_approved_nid_with_sha1(ctx, md, 1); ++ ldsigs_allowed = ossl_ctx_legacy_digest_signatures_allowed(ctx, 0); ++ mdnid = ossl_digest_get_approved_nid_with_sha1(ctx, md, sha1_allowed || ldsigs_allowed); + if (mdnid == NID_undef) + mdnid = ossl_digest_md_to_nid(md, name_to_nid, OSSL_NELEM(name_to_nid)); ++ if (mdnid == NID_md5_sha1 && !ldsigs_allowed) ++ mdnid = -1; + return mdnid; + } +diff --git a/providers/implementations/signature/dsa_sig.c b/providers/implementations/signature/dsa_sig.c +index 28fd7c498e..fa3822f39f 100644 +--- a/providers/implementations/signature/dsa_sig.c ++++ b/providers/implementations/signature/dsa_sig.c +@@ -124,12 +124,17 @@ static int dsa_setup_md(PROV_DSA_CTX *ctx, + mdprops = ctx->propq; + + if (mdname != NULL) { +- int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN); + WPACKET pkt; + EVP_MD *md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); +- int md_nid = ossl_digest_get_approved_nid_with_sha1(ctx->libctx, md, +- sha1_allowed); ++ int md_nid; + size_t mdname_len = strlen(mdname); ++#ifdef FIPS_MODULE ++ int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN); ++#else ++ int sha1_allowed = 0; ++#endif ++ md_nid = ossl_digest_get_approved_nid_with_sha1(ctx->libctx, md, ++ sha1_allowed); + + if (md == NULL || md_nid < 0) { + if (md == NULL) +diff --git a/providers/implementations/signature/ecdsa_sig.c b/providers/implementations/signature/ecdsa_sig.c +index 865d49d100..99b228e82c 100644 +--- a/providers/implementations/signature/ecdsa_sig.c ++++ b/providers/implementations/signature/ecdsa_sig.c +@@ -237,7 +237,11 @@ static int ecdsa_setup_md(PROV_ECDSA_CTX *ctx, const char *mdname, + "%s could not be fetched", mdname); + return 0; + } ++#ifdef FIPS_MODULE + sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN); ++#else ++ sha1_allowed = 0; ++#endif + md_nid = ossl_digest_get_approved_nid_with_sha1(ctx->libctx, md, + sha1_allowed); + if (md_nid < 0) { +diff --git a/providers/implementations/signature/rsa_sig.c b/providers/implementations/signature/rsa_sig.c +index 325e855333..bea397f0c1 100644 +--- a/providers/implementations/signature/rsa_sig.c ++++ b/providers/implementations/signature/rsa_sig.c +@@ -26,6 +26,7 @@ + #include "internal/cryptlib.h" + #include "internal/nelem.h" + #include "internal/sizes.h" ++#include "internal/sslconf.h" + #include "crypto/rsa.h" + #include "prov/providercommon.h" + #include "prov/implementations.h" +@@ -34,6 +35,7 @@ + #include "prov/securitycheck.h" + + #define RSA_DEFAULT_DIGEST_NAME OSSL_DIGEST_NAME_SHA1 ++#define RSA_DEFAULT_DIGEST_NAME_NONLEGACY OSSL_DIGEST_NAME_SHA2_256 + + static OSSL_FUNC_signature_newctx_fn rsa_newctx; + static OSSL_FUNC_signature_sign_init_fn rsa_sign_init; +@@ -289,10 +291,15 @@ static int rsa_setup_md(PROV_RSA_CTX *ctx, const char *mdname, + + if (mdname != NULL) { + EVP_MD *md = EVP_MD_fetch(ctx->libctx, mdname, mdprops); ++ int md_nid; ++ size_t mdname_len = strlen(mdname); ++#ifdef FIPS_MODULE + int sha1_allowed = (ctx->operation != EVP_PKEY_OP_SIGN); +- int md_nid = ossl_digest_rsa_sign_get_md_nid(ctx->libctx, md, ++#else ++ int sha1_allowed = 0; ++#endif ++ md_nid = ossl_digest_rsa_sign_get_md_nid(ctx->libctx, md, + sha1_allowed); +- size_t mdname_len = strlen(mdname); + + if (md == NULL + || md_nid <= 0 +@@ -1348,8 +1355,15 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) + prsactx->pad_mode = pad_mode; + + if (prsactx->md == NULL && pmdname == NULL +- && pad_mode == RSA_PKCS1_PSS_PADDING) ++ && pad_mode == RSA_PKCS1_PSS_PADDING) { + pmdname = RSA_DEFAULT_DIGEST_NAME; ++#ifndef FIPS_MODULE ++ if (!ossl_ctx_legacy_digest_signatures_allowed(prsactx->libctx, 0)) { ++ pmdname = RSA_DEFAULT_DIGEST_NAME_NONLEGACY; ++ } ++#endif ++ } ++ + + if (pmgf1mdname != NULL + && !rsa_setup_mgf1_md(prsactx, pmgf1mdname, pmgf1mdprops)) +diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c +index 41fddf22a7..dcd487ec2e 100644 +--- a/ssl/t1_lib.c ++++ b/ssl/t1_lib.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include "internal/sslconf.h" + #include "internal/nelem.h" + #include "internal/sizes.h" + #include "internal/tlsgroups.h" +@@ -1145,11 +1146,13 @@ int ssl_setup_sig_algs(SSL_CTX *ctx) + = OPENSSL_malloc(sizeof(*lu) * OSSL_NELEM(sigalg_lookup_tbl)); + EVP_PKEY *tmpkey = EVP_PKEY_new(); + int ret = 0; ++ int ldsigs_allowed; + + if (cache == NULL || tmpkey == NULL) + goto err; + + ERR_set_mark(); ++ ldsigs_allowed = ossl_ctx_legacy_digest_signatures_allowed(ctx->libctx, 0); + for (i = 0, lu = sigalg_lookup_tbl; + i < OSSL_NELEM(sigalg_lookup_tbl); lu++, i++) { + EVP_PKEY_CTX *pctx; +@@ -1169,6 +1172,11 @@ int ssl_setup_sig_algs(SSL_CTX *ctx) + cache[i].enabled = 0; + continue; + } ++ if ((lu->hash == NID_sha1 || lu->hash == NID_md5_sha1) ++ && !ldsigs_allowed) { ++ cache[i].enabled = 0; ++ continue; ++ } + + if (!EVP_PKEY_set_type(tmpkey, lu->sig)) { + cache[i].enabled = 0; +diff --git a/util/libcrypto.num b/util/libcrypto.num +index 10b4e57d79..2d3c363bb0 100644 +--- a/util/libcrypto.num ++++ b/util/libcrypto.num +@@ -5426,3 +5426,5 @@ ASN1_item_d2i_ex 5552 3_0_0 EXIST::FUNCTION: + EVP_PKEY_get0_provider 5554 3_0_0 EXIST::FUNCTION: + EVP_PKEY_CTX_get0_provider 5555 3_0_0 EXIST::FUNCTION: + ossl_safe_getenv ? 3_0_0 EXIST::FUNCTION: ++ossl_ctx_legacy_digest_signatures_allowed ? 3_0_1 EXIST::FUNCTION: ++ossl_ctx_legacy_digest_signatures_allowed_set ? 3_0_1 EXIST::FUNCTION: +-- +2.35.1 + diff --git a/0052-Allow-SHA1-in-seclevel-1-if-rh-allow-sha1-signatures.patch b/0052-Allow-SHA1-in-seclevel-1-if-rh-allow-sha1-signatures.patch new file mode 100644 index 0000000..eea02f8 --- /dev/null +++ b/0052-Allow-SHA1-in-seclevel-1-if-rh-allow-sha1-signatures.patch @@ -0,0 +1,217 @@ +From f695f140255f9b564cac4d5e9e38ba27ec927256 Mon Sep 17 00:00:00 2001 +From: Clemens Lang +Date: Tue, 1 Mar 2022 15:44:18 +0100 +Subject: [PATCH 2/2] Allow SHA1 in seclevel 1 if rh-allow-sha1-signatures = + yes + +NOTE: This patch is ported from CentOS 9 / RHEL 9, where it allows SHA1 +in seclevel 2 if rh-allow-sha1-signatures = yes. This was chosen because +on CentOS 9 and RHEL 9, the LEGACY crypto policy sets the security level +to 2. + +On Fedora 35 (with OpenSSL 1.1) the legacy crypto policy uses security +level 1. Because Fedora 36 supports both OpenSSL 1.1 and OpenSSL 3, and +we want the legacy crypto policy to allow SHA-1 in TLS, the only option +to make this happen consistently in both OpenSSL 1.1 and OpenSSL 3 is +SECLEVEL=1 (which will allow SHA-1 in OpenSSL 1.1) and this change to +allow SHA-1 in SECLEVEL=1 with rh-allow-sha1-signatures = yes (which +will allow SHA-1 in OpenSSL 3). + +The change from CentOS 9 / RHEL 9 cannot be applied unmodified, because +rh-allow-sha1-signatures will default to yes in Fedora (according to our +current plans including until F38), and the security level in the +DEFAULT crypto policy is 2, i.e., the unmodified change would weaken the +default configuration. + +Related: rhbz#2055796 +Related: rhbz#2070977 +--- + crypto/x509/x509_vfy.c | 19 ++++++++++- + doc/man5/config.pod | 7 ++++ + ssl/t1_lib.c | 64 ++++++++++++++++++++++++++++------- + test/recipes/25-test_verify.t | 4 +-- + 4 files changed, 78 insertions(+), 16 deletions(-) + +diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c +index 2f175ca517..60aa26f552 100644 +--- a/crypto/x509/x509_vfy.c ++++ b/crypto/x509/x509_vfy.c +@@ -25,6 +25,7 @@ + #include + #include + #include "internal/dane.h" ++#include "internal/sslconf.h" + #include "crypto/x509.h" + #include "x509_local.h" + +@@ -3441,14 +3442,30 @@ static int check_sig_level(X509_STORE_CTX *ctx, X509 *cert) + { + int secbits = -1; + int level = ctx->param->auth_level; ++ int nid; ++ OSSL_LIB_CTX *libctx = NULL; + + if (level <= 0) + return 1; + if (level > NUM_AUTH_LEVELS) + level = NUM_AUTH_LEVELS; + +- if (!X509_get_signature_info(cert, NULL, NULL, &secbits, NULL)) ++ if (ctx->libctx) ++ libctx = ctx->libctx; ++ else if (cert->libctx) ++ libctx = cert->libctx; ++ else ++ libctx = OSSL_LIB_CTX_get0_global_default(); ++ ++ if (!X509_get_signature_info(cert, &nid, NULL, &secbits, NULL)) + return 0; + ++ if (nid == NID_sha1 ++ && ossl_ctx_legacy_digest_signatures_allowed(libctx, 0) ++ && ctx->param->auth_level < 2) ++ /* When rh-allow-sha1-signatures = yes and security level <= 1, ++ * explicitly allow SHA1 for backwards compatibility. */ ++ return 1; ++ + return secbits >= minbits_table[level - 1]; + } +diff --git a/doc/man5/config.pod b/doc/man5/config.pod +index 0c9110d28a..02e7ca706f 100644 +--- a/doc/man5/config.pod ++++ b/doc/man5/config.pod +@@ -309,6 +309,13 @@ this option is set to B. Because TLS 1.1 or lower use MD5-SHA1 as + pseudorandom function (PRF) to derive key material, disabling + B requires the use of TLS 1.2 or newer. + ++Note that enabling B will allow TLS signature ++algorithms that use SHA1 in security level 1, despite the definition of ++security level 1 of FIXME bits of security, which SHA1 does not meet. This ++allows using SHA1 in TLS in the LEGACY crypto-policy on Fedora without ++requiring to set the security level to 0, which would include further insecure ++algorithms. ++ + =item B (deprecated) + + The value is a boolean that can be B or B. If the value is +diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c +index dcd487ec2e..e47ddf56f1 100644 +--- a/ssl/t1_lib.c ++++ b/ssl/t1_lib.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include "crypto/x509.h" + #include "internal/sslconf.h" + #include "internal/nelem.h" + #include "internal/sizes.h" +@@ -1561,19 +1562,27 @@ int tls12_check_peer_sigalg(SSL *s, uint16_t sig, EVP_PKEY *pkey) + SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_UNKNOWN_DIGEST); + return 0; + } +- /* +- * Make sure security callback allows algorithm. For historical +- * reasons we have to pass the sigalg as a two byte char array. +- */ +- sigalgstr[0] = (sig >> 8) & 0xff; +- sigalgstr[1] = sig & 0xff; +- secbits = sigalg_security_bits(s->ctx, lu); +- if (secbits == 0 || +- !ssl_security(s, SSL_SECOP_SIGALG_CHECK, secbits, +- md != NULL ? EVP_MD_get_type(md) : NID_undef, +- (void *)sigalgstr)) { +- SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_WRONG_SIGNATURE_TYPE); +- return 0; ++ ++ if (lu->hash == NID_sha1 ++ && ossl_ctx_legacy_digest_signatures_allowed(s->ctx->libctx, 0) ++ && SSL_get_security_level(s) < 2) { ++ /* when rh-allow-sha1-signatures = yes and security level <= 1, ++ * explicitly allow SHA1 for backwards compatibility */ ++ } else { ++ /* ++ * Make sure security callback allows algorithm. For historical ++ * reasons we have to pass the sigalg as a two byte char array. ++ */ ++ sigalgstr[0] = (sig >> 8) & 0xff; ++ sigalgstr[1] = sig & 0xff; ++ secbits = sigalg_security_bits(s->ctx, lu); ++ if (secbits == 0 || ++ !ssl_security(s, SSL_SECOP_SIGALG_CHECK, secbits, ++ md != NULL ? EVP_MD_get_type(md) : NID_undef, ++ (void *)sigalgstr)) { ++ SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_R_WRONG_SIGNATURE_TYPE); ++ return 0; ++ } + } + /* Store the sigalg the peer uses */ + s->s3.tmp.peer_sigalg = lu; +@@ -2106,6 +2115,14 @@ static int tls12_sigalg_allowed(const SSL *s, int op, const SIGALG_LOOKUP *lu) + } + } + ++ if (lu->hash == NID_sha1 ++ && ossl_ctx_legacy_digest_signatures_allowed(s->ctx->libctx, 0) ++ && SSL_get_security_level(s) < 2) { ++ /* when rh-allow-sha1-signatures = yes and security level <= 1, ++ * explicitly allow SHA1 for backwards compatibility */ ++ return 1; ++ } ++ + /* Finally see if security callback allows it */ + secbits = sigalg_security_bits(s->ctx, lu); + sigalgstr[0] = (lu->sigalg >> 8) & 0xff; +@@ -2977,6 +2994,8 @@ static int ssl_security_cert_sig(SSL *s, SSL_CTX *ctx, X509 *x, int op) + { + /* Lookup signature algorithm digest */ + int secbits, nid, pknid; ++ OSSL_LIB_CTX *libctx = NULL; ++ + /* Don't check signature if self signed */ + if ((X509_get_extension_flags(x) & EXFLAG_SS) != 0) + return 1; +@@ -2985,6 +3004,25 @@ static int ssl_security_cert_sig(SSL *s, SSL_CTX *ctx, X509 *x, int op) + /* If digest NID not defined use signature NID */ + if (nid == NID_undef) + nid = pknid; ++ ++ if (x && x->libctx) ++ libctx = x->libctx; ++ else if (ctx && ctx->libctx) ++ libctx = ctx->libctx; ++ else if (s && s->ctx && s->ctx->libctx) ++ libctx = s->ctx->libctx; ++ else ++ libctx = OSSL_LIB_CTX_get0_global_default(); ++ ++ if (nid == NID_sha1 ++ && ossl_ctx_legacy_digest_signatures_allowed(libctx, 0) ++ && ((s != NULL && SSL_get_security_level(s) < 2) ++ || (ctx != NULL && SSL_CTX_get_security_level(ctx) < 2) ++ )) ++ /* When rh-allow-sha1-signatures = yes and security level <= 1, ++ * explicitly allow SHA1 for backwards compatibility. */ ++ return 1; ++ + if (s) + return ssl_security(s, op, secbits, nid, x); + else +diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t +index 700bbd849c..280477bc9d 100644 +--- a/test/recipes/25-test_verify.t ++++ b/test/recipes/25-test_verify.t +@@ -387,8 +387,8 @@ ok(verify("ee-pss-sha1-cert", "", ["root-cert"], ["ca-cert"], "-auth_level", "0" + ok(verify("ee-pss-sha256-cert", "", ["root-cert"], ["ca-cert"], ), + "CA with PSS signature using SHA256"); + +-ok(!verify("ee-pss-sha1-cert", "", ["root-cert"], ["ca-cert"], "-auth_level", "1"), +- "Reject PSS signature using SHA1 and auth level 1"); ++ok(!verify("ee-pss-sha1-cert", "", ["root-cert"], ["ca-cert"], "-auth_level", "2"), ++ "Reject PSS signature using SHA1 and auth level 2"); + + ok(verify("ee-pss-sha256-cert", "", ["root-cert"], ["ca-cert"], "-auth_level", "2"), + "PSS signature using SHA256 and auth level 2"); +-- +2.35.1 + diff --git a/openssl.spec b/openssl.spec index 91e74a0..f94d698 100644 --- a/openssl.spec +++ b/openssl.spec @@ -15,7 +15,7 @@ Summary: Utilities from the general purpose cryptography library with TLS implementation Name: openssl Version: 3.0.2 -Release: 1%{?dist} +Release: 2%{?dist} Epoch: 1 # We have to remove certain patented algorithms from the openssl source # tarball with the hobble-openssl script which is included below. @@ -56,8 +56,12 @@ Patch11: 0011-Remove-EC-curves.patch Patch12: 0012-Disable-explicit-ec.patch # Instructions to load legacy provider in openssl.cnf Patch24: 0024-load-legacy-prov.patch +# Selectively disallow SHA1 signatures rhbz#2070977 +Patch49: 0049-Allow-disabling-of-SHA1-signatures.patch # Backport of patch for RHEL for Edge rhbz #2027261 Patch51: 0051-Support-different-R_BITS-lengths-for-KBKDF.patch +# Support SHA1 in TLS in LEGACY crypto-policy (which is SECLEVEL=1) +Patch52: 0052-Allow-SHA1-in-seclevel-1-if-rh-allow-sha1-signatures.patch License: ASL 2.0 URL: http://www.openssl.org/ @@ -384,6 +388,14 @@ install -m644 %{SOURCE9} \ %ldconfig_scriptlets libs %changelog +* Thu Apr 07 2022 Clemens Lang - 1:3.0.2-2 +- Allow disabling SHA1 signature creation and verification. + Set rh-allow-sha1-signatures = no to disable. + Allow SHA1 in TLS in SECLEVEL 1 if rh-allow-sha1-signatures = yes. This will + support SHA1 in TLS in the LEGACY crypto-policy. + Resolves: rhbz#2070977, rhbz#2071615 + Related: rhbz#2031742, rhbz#2062640 + * Fri Mar 18 2022 Dmitry Belyavskiy - 1:3.0.2-1 - Rebase to upstream version 3.0.2 From fb4c39d76441f02439c0747262453ad43c166e34 Mon Sep 17 00:00:00 2001 From: Clemens Lang Date: Apr 07 2022 16:16:51 +0000 Subject: [PATCH 2/2] Silence rpmlint false positives capi.so is only useful on Windows, it does not matter that it does not have dependency information. The invalid URL warnings are expected for packages with hobbled source code archives. We explicitly allow the use of SSL_CTX_set_cipher_list in the openssl(1) binary. (cherry picked from commit 82a6212c47f483fb27a34555ca6b0d61af8521ce) Signed-off-by: Clemens Lang --- diff --git a/openssl.rpmlintrc b/openssl.rpmlintrc new file mode 100644 index 0000000..3539843 --- /dev/null +++ b/openssl.rpmlintrc @@ -0,0 +1,9 @@ +# capi.so is a dummy only used on Windows, it doesn't need dependency information +addFilter("E: shared-lib(rary)?-without-dependency-information /usr/lib64/engines-3/capi.so") + +# The sources are hobbled and thus not a valid URL. That's expected. +addFilter("W: invalid-url Source0: openssl-[0-9\\.]+-hobbled.tar.gz") + +# Technically this warning is correct, but in the case of the openssl binary we +# want to allow SSL_CTX_set_cipher_list +addFilter("W: crypto-policy-non-compliance-openssl /usr/bin/openssl SSL_CTX_set_cipher_list") diff --git a/openssl.spec b/openssl.spec index f94d698..9a95353 100644 --- a/openssl.spec +++ b/openssl.spec @@ -24,6 +24,7 @@ Source: openssl-%{version}-hobbled.tar.gz Source1: hobble-openssl Source2: Makefile.certificate Source3: genpatches +Source4: openssl.rpmlintrc Source6: make-dummy-cert Source7: renew-dummy-cert Source9: configuration-switch.h @@ -389,6 +390,9 @@ install -m644 %{SOURCE9} \ %changelog * Thu Apr 07 2022 Clemens Lang - 1:3.0.2-2 +- Silence a few rpmlint false positives. + +* Thu Apr 07 2022 Clemens Lang - 1:3.0.2-2 - Allow disabling SHA1 signature creation and verification. Set rh-allow-sha1-signatures = no to disable. Allow SHA1 in TLS in SECLEVEL 1 if rh-allow-sha1-signatures = yes. This will