From 243201772cc6d583fae9eba81cb2c2c7425bc564 Mon Sep 17 00:00:00 2001
From: Clemens Lang <cllang@redhat.com>
Date: Mon, 21 Feb 2022 17:24:44 +0100
Subject: Selectively disallow SHA1 signatures
For RHEL 9.0, we want to phase out SHA1. One of the steps to do that is
disabling SHA1 signatures. Introduce a new configuration option in the
alg_section named 'rh-allow-sha1-signatures'. This option defaults to
false. If set to false (or unset), 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).
Resolves: rhbz#2031742
---
crypto/evp/evp_cnf.c | 13 ++++
crypto/evp/m_sigver.c | 77 +++++++++++++++++++
crypto/evp/pmeth_lib.c | 15 ++++
doc/man5/config.pod | 11 +++
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, 188 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 <stdio.h>
#include <openssl/crypto.h>
#include "internal/cryptlib.h"
+#include "internal/sslconf.h"
#include <openssl/conf.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
@@ -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 9188edbc21..db1a1d7bc3 100644
--- a/crypto/evp/m_sigver.c
+++ b/crypto/evp/m_sigver.c
@@ -16,6 +16,71 @@
#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)
+{
+ return OPENSSL_zalloc(sizeof(OSSL_LEGACY_DIGEST_SIGNATURES));
+}
+
+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 0;
+#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
+
+ return ldsigs != NULL ? ldsigs->allowed : 0;
+}
+
+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 +323,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..aa1be5ca7f 100644
--- a/doc/man5/config.pod
+++ b/doc/man5/config.pod
@@ -304,6 +304,17 @@ 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<rh-allow-sha1-signatures>
+
+The value is a boolean that can be B<yes> or B<no>. If the value is not set,
+it behaves as if it was set to B<no>.
+
+When set to B<no>, any attempt to create or verify a signature with a SHA1
+digest will fail. For compatibility with older versions of OpenSSL, set this
+option to B<yes>. 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<no>.
+
=item B<fips_mode> (deprecated)
The value is a boolean that can be B<yes> or B<no>. 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 <openssl/core_names.h>
#include <openssl/obj_mac.h>
#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 <openssl/obj_mac.h>
#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 fc32bb3556..4b74ee1a34 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -20,6 +20,7 @@
#include <openssl/bn.h>
#include <openssl/provider.h>
#include <openssl/param_build.h>
+#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_TIME_print_ex 5553 3_0_0 EXIST::FUNCTION:
OPENSSL_strcasecmp 5556 3_0_3 EXIST::FUNCTION:
OPENSSL_strncasecmp 5557 3_0_3 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