From 6d93803492f19eeeed8cafd4948badf85a7429c4 Mon Sep 17 00:00:00 2001
From: Dmitry Belyavskiy <dbelyavs@redhat.com>
Date: Mon, 21 Aug 2023 13:07:07 +0200
Subject: [PATCH 16/59] RH: Allow disabling of SHA1 signatures

Patch-name: 0049-Allow-disabling-of-SHA1-signatures.patch
Patch-id: 49
Patch-status: |
    # Selectively disallow SHA1 signatures rhbz#2070977
From-dist-git-commit: 9409bc7044cf4b5773639cce20f51399888c45fd
---
 crypto/context.c                              | 70 +++++++++++++++++++
 crypto/evp/evp_cnf.c                          | 13 ++++
 crypto/evp/m_sigver.c                         | 14 ++++
 crypto/evp/pmeth_lib.c                        | 15 ++++
 doc/man5/config.pod                           | 13 ++++
 include/crypto/context.h                      |  8 +++
 include/internal/cryptlib.h                   |  3 +-
 include/internal/sslconf.h                    |  4 ++
 providers/common/include/prov/securitycheck.h |  2 +
 providers/common/securitycheck.c              | 14 ++++
 providers/common/securitycheck_default.c      |  1 +
 providers/implementations/signature/dsa_sig.c |  1 +
 .../implementations/signature/ecdsa_sig.c     |  8 ++-
 providers/implementations/signature/rsa_sig.c | 14 +++-
 ssl/t1_lib.c                                  |  8 +++
 util/libcrypto.num                            |  2 +
 16 files changed, 183 insertions(+), 7 deletions(-)

diff --git a/crypto/context.c b/crypto/context.c
index 614c8a2c88..323615e300 100644
--- a/crypto/context.c
+++ b/crypto/context.c
@@ -85,6 +85,8 @@ struct ossl_lib_ctx_st {
 #endif
     STACK_OF(SSL_COMP) *comp_methods;
 
+    void *legacy_digest_signatures;
+
     int ischild;
     int conf_diagnostics;
 };
@@ -119,6 +121,22 @@ int ossl_lib_ctx_is_child(OSSL_LIB_CTX *ctx)
     return ctx->ischild;
 }
 
+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));
+    ldsigs->allowed = 0;
+    return ldsigs;
+}
+
 static void context_deinit_objs(OSSL_LIB_CTX *ctx);
 
 static int context_init(OSSL_LIB_CTX *ctx)
@@ -235,6 +253,10 @@ static int context_init(OSSL_LIB_CTX *ctx)
         goto err;
 #endif
 
+    ctx->legacy_digest_signatures = ossl_ctx_legacy_digest_signatures_new(ctx);
+    if (ctx->legacy_digest_signatures == NULL)
+        goto err;
+
     /* Low priority. */
 #ifndef FIPS_MODULE
     ctx->child_provider = ossl_child_prov_ctx_new(ctx);
@@ -382,6 +404,11 @@ static void context_deinit_objs(OSSL_LIB_CTX *ctx)
     }
 #endif
 
+    if (ctx->legacy_digest_signatures != NULL) {
+        ossl_ctx_legacy_digest_signatures_free(ctx->legacy_digest_signatures);
+        ctx->legacy_digest_signatures = NULL;
+    }
+
     /* Low priority. */
 #ifndef FIPS_MODULE
     if (ctx->child_provider != NULL) {
@@ -660,6 +687,9 @@ void *ossl_lib_ctx_get_data(OSSL_LIB_CTX *ctx, int index)
     case OSSL_LIB_CTX_COMP_METHODS:
         return (void *)&ctx->comp_methods;
 
+    case OSSL_LIB_CTX_LEGACY_DIGEST_SIGNATURES_INDEX:
+        return ctx->legacy_digest_signatures;
+
     default:
         return NULL;
     }
@@ -714,3 +744,43 @@ void OSSL_LIB_CTX_set_conf_diagnostics(OSSL_LIB_CTX *libctx, int value)
         return;
     libctx->conf_diagnostics = value;
 }
+
+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_INDEX);
+}
+
+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;
+}
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 c27ed6dbe9..ea1f6cbed3 100644
--- a/crypto/evp/m_sigver.c
+++ b/crypto/evp/m_sigver.c
@@ -15,6 +15,7 @@
 #include "internal/provider.h"
 #include "internal/numbers.h"   /* includes SIZE_MAX */
 #include "evp_local.h"
+#include "internal/sslconf.h"
 
 static int update(EVP_MD_CTX *ctx, const void *data, size_t datalen)
 {
@@ -253,6 +254,19 @@ static int do_sigver_init(EVP_MD_CTX *ctx, EVP_PKEY_CTX **pctx,
     }
 
     desc = signature->description != NULL ? signature->description : "";
+
+    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_data(ERR_LIB_EVP, EVP_R_PROVIDER_SIGNATURE_NOT_SUPPORTED,
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index 08c0d6a7b2..b936ad4447 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
@@ -963,6 +964,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 39fa468320..b994081924 100644
--- a/doc/man5/config.pod
+++ b/doc/man5/config.pod
@@ -315,6 +315,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<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<yes>.
+
+When set to B<no>, 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<no>.  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>.  Because TLS 1.1 or lower use MD5-SHA1 as
+pseudorandom function (PRF) to derive key material, disabling
+B<rh-allow-sha1-signatures> requires the use of TLS 1.2 or newer.
+
 =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/crypto/context.h b/include/crypto/context.h
index 1c181933e0..35bdfdb52d 100644
--- a/include/crypto/context.h
+++ b/include/crypto/context.h
@@ -48,3 +48,11 @@ void ossl_release_default_drbg_ctx(void);
 #if defined(OPENSSL_THREADS)
 void ossl_threads_ctx_free(void *);
 #endif
+
+#ifndef OSSL_LEGACY_DIGEST_SIGNATURES_STRUCT
+#define OSSL_LEGACY_DIGEST_SIGNATURES_STRUCT
+typedef struct ossl_legacy_digest_signatures_st {
+    int allowed;
+} OSSL_LEGACY_DIGEST_SIGNATURES;
+#endif
+
diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h
index da442f8a86..44a5e8a99a 100644
--- a/include/internal/cryptlib.h
+++ b/include/internal/cryptlib.h
@@ -120,7 +120,8 @@ typedef struct ossl_ex_data_global_st {
 # define OSSL_LIB_CTX_DECODER_CACHE_INDEX           20
 # define OSSL_LIB_CTX_COMP_METHODS                  21
 # define OSSL_LIB_CTX_INDICATOR_CB_INDEX            22
-# define OSSL_LIB_CTX_MAX_INDEXES                   22
+# define OSSL_LIB_CTX_LEGACY_DIGEST_SIGNATURES_INDEX 23
+# define OSSL_LIB_CTX_MAX_INDEXES                   23
 
 OSSL_LIB_CTX *ossl_lib_ctx_get_concrete(OSSL_LIB_CTX *ctx);
 int ossl_lib_ctx_is_default(OSSL_LIB_CTX *ctx);
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/include/prov/securitycheck.h b/providers/common/include/prov/securitycheck.h
index 29a2b7fbf8..a48cbb03d2 100644
--- a/providers/common/include/prov/securitycheck.h
+++ b/providers/common/include/prov/securitycheck.h
@@ -37,3 +37,5 @@ int ossl_digest_get_approved_nid(const EVP_MD *md);
 /* Functions that have different implementations for the FIPS_MODULE */
 int ossl_digest_rsa_sign_get_md_nid(const EVP_MD *md);
 int ossl_fips_config_securitycheck_enabled(OSSL_LIB_CTX *libctx);
+
+int rh_digest_signatures_allowed(OSSL_LIB_CTX *libctx, int mdnid);
diff --git a/providers/common/securitycheck.c b/providers/common/securitycheck.c
index 8ef8dc2a81..79a9c48ce2 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"
 
 #define OSSL_FIPS_MIN_SECURITY_STRENGTH_BITS 112
 
@@ -219,3 +220,16 @@ int ossl_dh_check_key(const DH *dh)
     return (L == 2048 && (N == 224 || N == 256));
 }
 #endif /* OPENSSL_NO_DH */
+
+int rh_digest_signatures_allowed(OSSL_LIB_CTX *libctx, int mdnid)
+{
+#ifndef FIPS_MODULE
+    if (!ossl_ctx_legacy_digest_signatures_allowed(libctx, 0))
+        /* SHA1 is globally disabled, check whether we want to locally allow
+         * it. */
+#endif
+        if (mdnid == NID_sha1)
+            mdnid = -1;
+
+     return mdnid;
+}
diff --git a/providers/common/securitycheck_default.c b/providers/common/securitycheck_default.c
index dd71fd91eb..9019fd2a80 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_fips_config_securitycheck_enabled(OSSL_LIB_CTX *libctx)
diff --git a/providers/implementations/signature/dsa_sig.c b/providers/implementations/signature/dsa_sig.c
index 887f6cbb90..595aed7e07 100644
--- a/providers/implementations/signature/dsa_sig.c
+++ b/providers/implementations/signature/dsa_sig.c
@@ -163,6 +163,7 @@ static int dsa_setup_md(PROV_DSA_CTX *ctx,
 
         md = EVP_MD_fetch(ctx->libctx, mdname, mdprops);
         md_nid = ossl_digest_get_approved_nid(md);
+        md_nid = rh_digest_signatures_allowed(ctx->libctx, md_nid);
 
         if (md == NULL) {
             ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
diff --git a/providers/implementations/signature/ecdsa_sig.c b/providers/implementations/signature/ecdsa_sig.c
index 73bfbf4aa9..88d83275b1 100644
--- a/providers/implementations/signature/ecdsa_sig.c
+++ b/providers/implementations/signature/ecdsa_sig.c
@@ -197,13 +197,15 @@ static int ecdsa_setup_md(PROV_ECDSA_CTX *ctx,
         goto err;
     }
     md_nid = ossl_digest_get_approved_nid(md);
-#ifdef FIPS_MODULE
-    if (md_nid == NID_undef) {
+
+    md_nid = rh_digest_signatures_allowed(ctx->libctx, md_nid);
+    /* KECCAK-256 is explicitly allowed for ECDSA despite it doesn't have a NID*/
+    if (md_nid <= 0 && !(EVP_MD_is_a(md, "KECCAK-256"))) {
         ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
                        "digest=%s", mdname);
         goto err;
     }
-#endif
+
     /* XOF digests don't work */
     if (EVP_MD_xof(md)) {
         ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
diff --git a/providers/implementations/signature/rsa_sig.c b/providers/implementations/signature/rsa_sig.c
index d8357cfe15..29be5f5028 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;
@@ -387,7 +389,8 @@ static int rsa_setup_md(PROV_RSA_CTX *ctx, const char *mdname,
             goto err;
         }
         md_nid = ossl_digest_rsa_sign_get_md_nid(md);
-        if (md_nid == NID_undef) {
+        md_nid = rh_digest_signatures_allowed(ctx->libctx, md_nid);
+        if (md_nid <= 0) {
             ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
                            "digest=%s", mdname);
             goto err;
@@ -1765,8 +1768,13 @@ 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)
-        pmdname = RSA_DEFAULT_DIGEST_NAME;
+        && pad_mode == RSA_PKCS1_PSS_PADDING) {
+        if (ossl_ctx_legacy_digest_signatures_allowed(prsactx->libctx, 0)) {
+            pmdname = RSA_DEFAULT_DIGEST_NAME;
+        } else {
+            pmdname = RSA_DEFAULT_DIGEST_NAME_NONLEGACY;
+        }
+    }
 
     if (pmgf1mdname != NULL
         && !rsa_setup_mgf1_md(prsactx, pmgf1mdname, pmgf1mdprops))
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 2f71f95438..bea5cab253 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -21,6 +21,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"
@@ -2178,6 +2179,7 @@ int ssl_setup_sigalgs(SSL_CTX *ctx)
     EVP_PKEY *tmpkey = EVP_PKEY_new();
     int istls;
     int ret = 0;
+    int ldsigs_allowed;
 
     if (ctx == NULL)
         goto err;
@@ -2195,6 +2197,7 @@ int ssl_setup_sigalgs(SSL_CTX *ctx)
         goto err;
 
     ERR_set_mark();
+    ldsigs_allowed = ossl_ctx_legacy_digest_signatures_allowed(ctx->libctx, 0);
     /* First fill cache and tls12_sigalgs list from legacy algorithm list */
     for (i = 0, lu = sigalg_lookup_tbl;
          i < OSSL_NELEM(sigalg_lookup_tbl); lu++, i++) {
@@ -2215,6 +2218,11 @@ int ssl_setup_sigalgs(SSL_CTX *ctx)
             cache[i].available = 0;
             continue;
         }
+        if ((lu->hash == NID_sha1 || lu->hash == NID_md5_sha1)
+                && !ldsigs_allowed) {
+            cache[i].available = 0;
+            continue;
+        }
 
         if (!EVP_PKEY_set_type(tmpkey, lu->sig)) {
             cache[i].available = 0;
diff --git a/util/libcrypto.num b/util/libcrypto.num
index d377d542db..c2c55129ae 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -5928,3 +5928,5 @@ OSSL_AA_DIST_POINT_free                 6051	3_5_0	EXIST::FUNCTION:
 OSSL_AA_DIST_POINT_new                  6052	3_5_0	EXIST::FUNCTION:
 OSSL_AA_DIST_POINT_it                   6053	3_5_0	EXIST::FUNCTION:
 PEM_ASN1_write_bio_ctx                  6054	3_5_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.51.0

