git: 476d63e091c2 - main - kerberos: Fix numerous segfaults when using weak crypto

From: Cy Schubert <cy_at_FreeBSD.org>
Date: Thu, 18 Jan 2024 07:48:55 UTC
The branch main has been updated by cy:

URL: https://cgit.FreeBSD.org/src/commit/?id=476d63e091c2e663b51d18acf6acb282e1f22bbc

commit 476d63e091c2e663b51d18acf6acb282e1f22bbc
Author:     Cy Schubert <cy@FreeBSD.org>
AuthorDate: 2023-12-06 15:30:05 +0000
Commit:     Cy Schubert <cy@FreeBSD.org>
CommitDate: 2024-01-18 07:46:57 +0000

    kerberos: Fix numerous segfaults when using weak crypto
    
    Weak crypto is provided by the openssl legacy provider which is
    not load by default. Load the legacy providers as needed.
    
    When the legacy provider is loaded into the default context the default
    provider will no longer be automatically loaded. Without the default
    provider the various kerberos applicaions and functions will abort().
    
    This is the second attempt at this patch. Instead of linking
    secure/lib/libcrypto at build time we now link it at runtime, avoiding
    buildworld failures under Linux and MacOS. This is because
    TARGET_ENDIANNESS is undefined at pre-build time.
    
    PR:             272835
    MFC after:      3 days
    X-MFC:          only to stable/14
    Tested by:      netchild
                    Joerg Pulz <Joerg.Pulz@frm2.tum.de> (previous version)
---
 crypto/heimdal/lib/kadm5/create_s.c              |  4 ++
 crypto/heimdal/lib/kadm5/kadm5_locl.h            |  1 +
 crypto/heimdal/lib/krb5/context.c                |  4 ++
 crypto/heimdal/lib/krb5/crypto.c                 |  3 +
 crypto/heimdal/lib/krb5/salt.c                   |  5 ++
 crypto/heimdal/lib/roken/version-script.map      |  1 +
 kerberos5/include/crypto-headers.h               |  4 ++
 kerberos5/include/fbsd_ossl_provider.h           |  4 ++
 kerberos5/lib/libroken/Makefile                  |  8 ++-
 kerberos5/lib/libroken/fbsd_ossl_provider_load.c | 77 ++++++++++++++++++++++++
 10 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/crypto/heimdal/lib/kadm5/create_s.c b/crypto/heimdal/lib/kadm5/create_s.c
index 1033ca103239..267e9bbda2a0 100644
--- a/crypto/heimdal/lib/kadm5/create_s.c
+++ b/crypto/heimdal/lib/kadm5/create_s.c
@@ -169,6 +169,10 @@ kadm5_s_create_principal(void *server_handle,
     ent.entry.keys.len = 0;
     ent.entry.keys.val = NULL;
 
+    ret = fbsd_ossl_provider_load();
+    if (ret)
+	goto out;
+
     ret = _kadm5_set_keys(context, &ent.entry, password);
     if (ret)
 	goto out;
diff --git a/crypto/heimdal/lib/kadm5/kadm5_locl.h b/crypto/heimdal/lib/kadm5/kadm5_locl.h
index 68b6a5ebf024..63b367ab7e21 100644
--- a/crypto/heimdal/lib/kadm5/kadm5_locl.h
+++ b/crypto/heimdal/lib/kadm5/kadm5_locl.h
@@ -79,5 +79,6 @@
 #include <der.h>
 #include <parse_units.h>
 #include "private.h"
+#include "fbsd_ossl_provider.h"
 
 #endif /* __KADM5_LOCL_H__ */
diff --git a/crypto/heimdal/lib/krb5/context.c b/crypto/heimdal/lib/krb5/context.c
index 86bfe539b974..681bc9a0982f 100644
--- a/crypto/heimdal/lib/krb5/context.c
+++ b/crypto/heimdal/lib/krb5/context.c
@@ -392,6 +392,10 @@ krb5_init_context(krb5_context *context)
     }
     HEIMDAL_MUTEX_init(p->mutex);
 
+    ret = fbsd_ossl_provider_load();
+    if(ret)
+	goto out;
+
     p->flags |= KRB5_CTX_F_HOMEDIR_ACCESS;
 
     ret = krb5_get_default_config_files(&files);
diff --git a/crypto/heimdal/lib/krb5/crypto.c b/crypto/heimdal/lib/krb5/crypto.c
index 67ecef62e875..6ee22609a4d5 100644
--- a/crypto/heimdal/lib/krb5/crypto.c
+++ b/crypto/heimdal/lib/krb5/crypto.c
@@ -2054,6 +2054,9 @@ krb5_crypto_init(krb5_context context,
 	*crypto = NULL;
 	return ret;
     }
+    ret = fbsd_ossl_provider_load();
+    if (ret)
+	return ret;
     (*crypto)->key.schedule = NULL;
     (*crypto)->num_key_usage = 0;
     (*crypto)->key_usage = NULL;
diff --git a/crypto/heimdal/lib/krb5/salt.c b/crypto/heimdal/lib/krb5/salt.c
index 5e4c8a1c8572..2b1fbee80ab6 100644
--- a/crypto/heimdal/lib/krb5/salt.c
+++ b/crypto/heimdal/lib/krb5/salt.c
@@ -43,6 +43,8 @@ krb5_salttype_to_string (krb5_context context,
     struct _krb5_encryption_type *e;
     struct salt_type *st;
 
+    (void) fbsd_ossl_provider_load();
+
     e = _krb5_find_enctype (etype);
     if (e == NULL) {
 	krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
@@ -75,6 +77,8 @@ krb5_string_to_salttype (krb5_context context,
     struct _krb5_encryption_type *e;
     struct salt_type *st;
 
+    (void) fbsd_ossl_provider_load();
+
     e = _krb5_find_enctype (etype);
     if (e == NULL) {
 	krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
@@ -196,6 +200,7 @@ krb5_string_to_key_data_salt_opaque (krb5_context context,
 			       enctype);
 	return KRB5_PROG_ETYPE_NOSUPP;
     }
+    (void) fbsd_ossl_provider_load();
     for(st = et->keytype->string_to_key; st && st->type; st++)
 	if(st->type == salt.salttype)
 	    return (*st->string_to_key)(context, enctype, password,
diff --git a/crypto/heimdal/lib/roken/version-script.map b/crypto/heimdal/lib/roken/version-script.map
index 72d2ea7e4f7c..bb2139ed74cc 100644
--- a/crypto/heimdal/lib/roken/version-script.map
+++ b/crypto/heimdal/lib/roken/version-script.map
@@ -13,6 +13,7 @@ HEIMDAL_ROKEN_1.0 {
 		ct_memcmp;
 		err;
 		errx;
+		fbsd_ossl_provider_load;
 		free_getarg_strings;
 		get_default_username;
 		get_window_size;
diff --git a/kerberos5/include/crypto-headers.h b/kerberos5/include/crypto-headers.h
index 3ae0d9624ffd..2cc870642964 100644
--- a/kerberos5/include/crypto-headers.h
+++ b/kerberos5/include/crypto-headers.h
@@ -17,5 +17,9 @@
 #include <openssl/ec.h>
 #include <openssl/ecdsa.h>
 #include <openssl/ecdh.h>
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+#include <openssl/provider.h>
+#include "fbsd_ossl_provider.h"
+#endif
 
 #endif /* __crypto_headers_h__ */
diff --git a/kerberos5/include/fbsd_ossl_provider.h b/kerberos5/include/fbsd_ossl_provider.h
new file mode 100644
index 000000000000..013983ca9f83
--- /dev/null
+++ b/kerberos5/include/fbsd_ossl_provider.h
@@ -0,0 +1,4 @@
+#ifndef __fbsd_ossl_provider_h
+#define __fbsd_ossl_provider_h
+int  fbsd_ossl_provider_load(void);
+#endif
diff --git a/kerberos5/lib/libroken/Makefile b/kerberos5/lib/libroken/Makefile
index 0c46ba6c4cb5..ca6d090e64f0 100644
--- a/kerberos5/lib/libroken/Makefile
+++ b/kerberos5/lib/libroken/Makefile
@@ -74,9 +74,13 @@ SRCS=	base64.c \
 	vis.c \
 	warnerr.c \
 	write_pid.c \
-	xfree.c
+	xfree.c \
+	fbsd_ossl_provider_load.c
 
-CFLAGS+=-I${KRB5DIR}/lib/roken -I.
+CFLAGS+=-I${KRB5DIR}/lib/roken \
+	-I${SRCTOP}/kerberos5/include \
+	-I${KRB5DIR}/lib/krb5 \
+	-I${SRCTOP}/crypto/openssl/include -I.
 
 CLEANFILES= roken.h
 
diff --git a/kerberos5/lib/libroken/fbsd_ossl_provider_load.c b/kerberos5/lib/libroken/fbsd_ossl_provider_load.c
new file mode 100644
index 000000000000..497b32124f96
--- /dev/null
+++ b/kerberos5/lib/libroken/fbsd_ossl_provider_load.c
@@ -0,0 +1,77 @@
+#include <dlfcn.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <openssl/provider.h>
+
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+static void fbsd_ossl_provider_unload(void);
+static void print_dlerror(char *);
+static OSSL_PROVIDER *legacy;
+static OSSL_PROVIDER *deflt;
+static int providers_loaded = 0;
+static OSSL_PROVIDER * (*ossl_provider_load)(OSSL_LIB_CTX *, const char*) = NULL;
+static int (*ossl_provider_unload)(OSSL_PROVIDER *) = NULL;
+static void *crypto_lib_handle = NULL;
+
+static void
+fbsd_ossl_provider_unload(void)
+{
+	if (ossl_provider_unload == NULL) {
+		if (!(ossl_provider_unload = (int (*)(OSSL_PROVIDER*)) dlsym(crypto_lib_handle, "OSSL_PROVIDER_unload"))) {
+			print_dlerror("Unable to link OSSL_PROVIDER_unload");
+			return;
+		}
+	}
+	if (providers_loaded == 1) {
+		(*ossl_provider_unload)(legacy);
+		(*ossl_provider_unload)(deflt);
+		providers_loaded = 0;
+	}
+}
+
+static void
+print_dlerror(char *message)
+{
+	char *errstr;
+
+	if ((errstr = dlerror()) != NULL)
+		fprintf(stderr, "%s: %s\n",
+			message, errstr);
+}
+#endif
+
+int
+fbsd_ossl_provider_load(void)
+{
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+	if (crypto_lib_handle == NULL) {
+		if (!(crypto_lib_handle = dlopen("/usr/lib/libcrypto.so",
+		    RTLD_LAZY|RTLD_GLOBAL))) {
+			print_dlerror("Unable to load libcrypto.so");
+			return (EINVAL);
+		}
+	}
+	if (ossl_provider_load == NULL) {
+		if (!(ossl_provider_load = (OSSL_PROVIDER * (*)(OSSL_LIB_CTX*, const char *)) dlsym(crypto_lib_handle, "OSSL_PROVIDER_load"))) {
+			print_dlerror("Unable to link OSSL_PROVIDER_load");
+			return(ENOENT);
+		}
+	}
+
+	if (providers_loaded == 0) {
+		if ((legacy = (*ossl_provider_load)(NULL, "legacy")) == NULL)
+			return (EINVAL);
+		if ((deflt = (*ossl_provider_load)(NULL, "default")) == NULL) {
+			(*ossl_provider_unload)(legacy);
+			return (EINVAL);
+		}
+		if (atexit(fbsd_ossl_provider_unload)) {
+			fbsd_ossl_provider_unload();
+			return (errno);
+		}
+		providers_loaded = 1;
+	}
+#endif
+	return (0);
+}