Blame SOURCES/0015-downstream-Allow-KRB5KDF-MD5-and-MD4-in-FIPS-mode.patch

d1ad9f
From 6aea69170c2064aaea73ad3283b6d7dd0cae47e1 Mon Sep 17 00:00:00 2001
d1ad9f
From: Julien Rische <jrische@redhat.com>
d1ad9f
Date: Thu, 19 Jan 2023 19:22:27 +0100
d1ad9f
Subject: [PATCH] [downstream] Allow KRB5KDF, MD5, and MD4 in FIPS mode
d1ad9f
d1ad9f
OpenSSL's restrictions to use KRB5KDF, MD5, and MD4 in FIPS mode are
d1ad9f
bypassed in case AES SHA-1 HMAC or RC4 encryption types are allowed by
d1ad9f
the crypto policy.
d1ad9f
---
d1ad9f
 .../crypto/openssl/hash_provider/hash_evp.c   | 97 +++++++++++++++++--
d1ad9f
 src/lib/crypto/openssl/kdf.c                  |  2 +-
d1ad9f
 2 files changed, 89 insertions(+), 10 deletions(-)
d1ad9f
d1ad9f
diff --git a/src/lib/crypto/openssl/hash_provider/hash_evp.c b/src/lib/crypto/openssl/hash_provider/hash_evp.c
d1ad9f
index 11659908bb..eb2e693e9f 100644
d1ad9f
--- a/src/lib/crypto/openssl/hash_provider/hash_evp.c
d1ad9f
+++ b/src/lib/crypto/openssl/hash_provider/hash_evp.c
d1ad9f
@@ -44,6 +44,49 @@
d1ad9f
 #define EVP_MD_CTX_free EVP_MD_CTX_destroy
d1ad9f
 #endif
d1ad9f
 
d1ad9f
+#include <openssl/provider.h>
d1ad9f
+#include <openssl/fips.h>
d1ad9f
+#include <threads.h>
d1ad9f
+
d1ad9f
+typedef struct ossl_lib_md_context {
d1ad9f
+    OSSL_LIB_CTX *libctx;
d1ad9f
+    OSSL_PROVIDER *default_provider;
d1ad9f
+    OSSL_PROVIDER *legacy_provider;
d1ad9f
+} ossl_md_context_t;
d1ad9f
+
d1ad9f
+static thread_local ossl_md_context_t *ossl_md_ctx = NULL;
d1ad9f
+
d1ad9f
+static krb5_error_code
d1ad9f
+init_ossl_md_ctx(ossl_md_context_t *ctx, const char *algo)
d1ad9f
+{
d1ad9f
+    ctx->libctx = OSSL_LIB_CTX_new();
d1ad9f
+    if (!ctx->libctx)
d1ad9f
+        return KRB5_CRYPTO_INTERNAL;
d1ad9f
+
d1ad9f
+    /* Load both legacy and default provider as both may be needed. */
d1ad9f
+    ctx->default_provider = OSSL_PROVIDER_load(ctx->libctx, "default");
d1ad9f
+    ctx->legacy_provider = OSSL_PROVIDER_load(ctx->libctx, "legacy");
d1ad9f
+
d1ad9f
+    if (!(ctx->default_provider && ctx->legacy_provider))
d1ad9f
+        return KRB5_CRYPTO_INTERNAL;
d1ad9f
+
d1ad9f
+    return 0;
d1ad9f
+}
d1ad9f
+
d1ad9f
+static void
d1ad9f
+deinit_ossl_ctx(ossl_md_context_t *ctx)
d1ad9f
+{
d1ad9f
+    if (ctx->legacy_provider)
d1ad9f
+        OSSL_PROVIDER_unload(ctx->legacy_provider);
d1ad9f
+
d1ad9f
+    if (ctx->default_provider)
d1ad9f
+        OSSL_PROVIDER_unload(ctx->default_provider);
d1ad9f
+
d1ad9f
+    if (ctx->libctx)
d1ad9f
+        OSSL_LIB_CTX_free(ctx->libctx);
d1ad9f
+}
d1ad9f
+
d1ad9f
+
d1ad9f
 static krb5_error_code
d1ad9f
 hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
d1ad9f
          krb5_data *output)
d1ad9f
@@ -60,11 +103,6 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
d1ad9f
     if (ctx == NULL)
d1ad9f
         return ENOMEM;
d1ad9f
 
d1ad9f
-    if (type == EVP_md4() || type == EVP_md5()) {
d1ad9f
-        /* See comments below in hash_md4() and hash_md5(). */
d1ad9f
-        EVP_MD_CTX_set_flags(ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
d1ad9f
-    }
d1ad9f
-
d1ad9f
     ok = EVP_DigestInit_ex(ctx, type, NULL);
d1ad9f
     for (i = 0; i < num_data; i++) {
d1ad9f
         if (!SIGN_IOV(&data[i]))
d1ad9f
@@ -77,6 +115,43 @@ hash_evp(const EVP_MD *type, const krb5_crypto_iov *data, size_t num_data,
d1ad9f
     return ok ? 0 : KRB5_CRYPTO_INTERNAL;
d1ad9f
 }
d1ad9f
 
d1ad9f
+static krb5_error_code
d1ad9f
+hash_legacy_evp(const char *algo, const krb5_crypto_iov *data, size_t num_data,
d1ad9f
+                krb5_data *output)
d1ad9f
+{
d1ad9f
+    krb5_error_code err;
d1ad9f
+    EVP_MD *md = NULL;
d1ad9f
+
d1ad9f
+    if (!ossl_md_ctx) {
d1ad9f
+        ossl_md_ctx = malloc(sizeof(ossl_md_context_t));
d1ad9f
+        if (!ossl_md_ctx) {
d1ad9f
+            err = ENOMEM;
d1ad9f
+            goto end;
d1ad9f
+        }
d1ad9f
+
d1ad9f
+        err = init_ossl_md_ctx(ossl_md_ctx, algo);
d1ad9f
+        if (err) {
d1ad9f
+            deinit_ossl_ctx(ossl_md_ctx);
d1ad9f
+            free(ossl_md_ctx);
d1ad9f
+            ossl_md_ctx = NULL;
d1ad9f
+            goto end;
d1ad9f
+        }
d1ad9f
+    }
d1ad9f
+
d1ad9f
+    md = EVP_MD_fetch(ossl_md_ctx->libctx, algo, NULL);
d1ad9f
+    if (!md) {
d1ad9f
+        err = KRB5_CRYPTO_INTERNAL;
d1ad9f
+        goto end;
d1ad9f
+    }
d1ad9f
+
d1ad9f
+    err = hash_evp(md, data, num_data, output);
d1ad9f
+
d1ad9f
+end:
d1ad9f
+    if (md)
d1ad9f
+        EVP_MD_free(md);
d1ad9f
+
d1ad9f
+    return err;
d1ad9f
+}
d1ad9f
 #endif
d1ad9f
 
d1ad9f
 #ifdef K5_OPENSSL_MD4
d1ad9f
@@ -88,7 +163,8 @@ hash_md4(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
d1ad9f
      * by IPA.  These keys are only used along a (separately) secured channel
d1ad9f
      * for legacy reasons when performing trusts to Active Directory.
d1ad9f
      */
d1ad9f
-    return hash_evp(EVP_md4(), data, num_data, output);
d1ad9f
+    return FIPS_mode() ? hash_legacy_evp("MD4", data, num_data, output)
d1ad9f
+                       : hash_evp(EVP_md4(), data, num_data, output);
d1ad9f
 }
d1ad9f
 
d1ad9f
 const struct krb5_hash_provider krb5int_hash_md4 = {
d1ad9f
@@ -100,9 +176,12 @@ const struct krb5_hash_provider krb5int_hash_md4 = {
d1ad9f
 static krb5_error_code
d1ad9f
 hash_md5(const krb5_crypto_iov *data, size_t num_data, krb5_data *output)
d1ad9f
 {
d1ad9f
-    /* MD5 is needed in FIPS mode for communication with RADIUS servers.  This
d1ad9f
-     * is gated in libkrad by libdefaults->radius_md5_fips_override. */
d1ad9f
-    return hash_evp(EVP_md5(), data, num_data, output);
d1ad9f
+    /*
d1ad9f
+     * MD5 is needed in FIPS mode for communication with RADIUS servers.  This
d1ad9f
+     * is gated in libkrad by libdefaults->radius_md5_fips_override.
d1ad9f
+     */
d1ad9f
+    return FIPS_mode() ? hash_legacy_evp("MD5", data, num_data, output)
d1ad9f
+                       : hash_evp(EVP_md5(), data, num_data, output);
d1ad9f
 }
d1ad9f
 
d1ad9f
 const struct krb5_hash_provider krb5int_hash_md5 = {
d1ad9f
diff --git a/src/lib/crypto/openssl/kdf.c b/src/lib/crypto/openssl/kdf.c
d1ad9f
index 5a43c3d9eb..8528ddc4a9 100644
d1ad9f
--- a/src/lib/crypto/openssl/kdf.c
d1ad9f
+++ b/src/lib/crypto/openssl/kdf.c
d1ad9f
@@ -198,7 +198,7 @@ k5_derive_random_rfc3961(const struct krb5_enc_provider *enc, krb5_key key,
d1ad9f
         goto done;
d1ad9f
     }
d1ad9f
 
d1ad9f
-    kdf = EVP_KDF_fetch(NULL, "KRB5KDF", NULL);
d1ad9f
+    kdf = EVP_KDF_fetch(NULL, "KRB5KDF", "-fips");
d1ad9f
     if (kdf == NULL) {
d1ad9f
         ret = KRB5_CRYPTO_INTERNAL;
d1ad9f
         goto done;
d1ad9f
-- 
d1ad9f
2.38.1
d1ad9f