diff --git a/SOURCES/gnutls-3.7.3-allowlist-api.patch b/SOURCES/gnutls-3.7.3-allowlist-api.patch
new file mode 100644
index 0000000..1019858
--- /dev/null
+++ b/SOURCES/gnutls-3.7.3-allowlist-api.patch
@@ -0,0 +1,2440 @@
+From 495ad3d82aff125f237f370a70882b97843edb6f Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Mon, 14 Feb 2022 12:44:57 +0100
+Subject: [PATCH 1/8] lib/priority: split up update_system_wide_priority_string
+
+This is done in preparation for deferring priority string evaluation.
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/priority.c | 77 ++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 47 insertions(+), 30 deletions(-)
+
+diff --git a/lib/priority.c b/lib/priority.c
+index e7698ba7eb..755729da18 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -1735,110 +1735,127 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
+ 	return 1;
+ }
+ 
+-static int
+-update_system_wide_priority_string(void)
++static int /* not locking system_wide_config */
++construct_system_wide_priority_string(gnutls_buffer_st* buf)
+ {
+-	gnutls_buffer_st buf;
+ 	int ret;
+ 	size_t i;
+ 
+-	_gnutls_buffer_init(&buf);
++	_gnutls_buffer_init(buf);
+ 
+-	ret = _gnutls_buffer_append_str(&buf, "NONE");
++	ret = _gnutls_buffer_append_str(buf, "NONE");
+ 	if (ret < 0) {
+-		_gnutls_buffer_clear(&buf);
++		_gnutls_buffer_clear(buf);
+ 		return ret;
+ 	}
+ 
+ 	for (i = 0; system_wide_config.kxs[i] != 0; i++) {
+-		ret = _gnutls_buffer_append_str(&buf, ":+");
++		ret = _gnutls_buffer_append_str(buf, ":+");
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 
+-		ret = _gnutls_buffer_append_str(&buf,
++		ret = _gnutls_buffer_append_str(buf,
+ 						gnutls_kx_get_name(system_wide_config.kxs[i]));
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 	}
+ 
+ 	for (i = 0; system_wide_config.groups[i] != 0; i++) {
+-		ret = _gnutls_buffer_append_str(&buf, ":+GROUP-");
++		ret = _gnutls_buffer_append_str(buf, ":+GROUP-");
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 
+-		ret = _gnutls_buffer_append_str(&buf,
++		ret = _gnutls_buffer_append_str(buf,
+ 						gnutls_group_get_name(system_wide_config.groups[i]));
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 	}
+ 
+ 	for (i = 0; system_wide_config.ciphers[i] != 0; i++) {
+-		ret = _gnutls_buffer_append_str(&buf, ":+");
++		ret = _gnutls_buffer_append_str(buf, ":+");
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 
+-		ret = _gnutls_buffer_append_str(&buf,
++		ret = _gnutls_buffer_append_str(buf,
+ 						gnutls_cipher_get_name(system_wide_config.ciphers[i]));
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 	}
+ 
+ 	for (i = 0; system_wide_config.macs[i] != 0; i++) {
+-		ret = _gnutls_buffer_append_str(&buf, ":+");
++		ret = _gnutls_buffer_append_str(buf, ":+");
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 
+-		ret = _gnutls_buffer_append_str(&buf,
++		ret = _gnutls_buffer_append_str(buf,
+ 						gnutls_mac_get_name(system_wide_config.macs[i]));
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 	}
+ 
+ 	for (i = 0; system_wide_config.sigs[i] != 0; i++) {
+-		ret = _gnutls_buffer_append_str(&buf, ":+SIGN-");
++		ret = _gnutls_buffer_append_str(buf, ":+SIGN-");
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 
+-		ret = _gnutls_buffer_append_str(&buf,
++		ret = _gnutls_buffer_append_str(buf,
+ 						gnutls_sign_get_name(system_wide_config.sigs[i]));
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 	}
+ 
+ 	for (i = 0; system_wide_config.versions[i] != 0; i++) {
+-		ret = _gnutls_buffer_append_str(&buf, ":+VERS-");
++		ret = _gnutls_buffer_append_str(buf, ":+VERS-");
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 
+-		ret = _gnutls_buffer_append_str(&buf,
++		ret = _gnutls_buffer_append_str(buf,
+ 						gnutls_protocol_get_name(system_wide_config.versions[i]));
+ 		if (ret < 0) {
+-			_gnutls_buffer_clear(&buf);
++			_gnutls_buffer_clear(buf);
+ 			return ret;
+ 		}
+ 	}
++	return 0;
++}
++
++static int /* not locking system_wide_config */
++update_system_wide_priority_string(void)
++{
++	/* doesn't do locking, _gnutls_update_system_priorities does */
++	gnutls_buffer_st buf;
++	int ret;
++
++	ret = construct_system_wide_priority_string(&buf);
++	if (ret < 0) {
++		_gnutls_debug_log("cfg: unable to construct "
++				  "system-wide priority string: %s",
++				  gnutls_strerror(ret));
++		_gnutls_buffer_clear(&buf);
++		return ret;
++	}
+ 
+ 	gnutls_free(system_wide_config.priority_string);
+ 	system_wide_config.priority_string = gnutls_strdup((char *)buf.data);
+-- 
+2.34.1
+
+
+From a74633975e97e491e1e1cdf12416ce160699b703 Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Mon, 14 Feb 2022 13:48:37 +0100
+Subject: [PATCH 2/8] lib/priority: defer setting system-wide priority string
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/global.c   |   2 +-
+ lib/global.h   |   2 +-
+ lib/priority.c | 112 ++++++++++++++++++++++++++++---------------------
+ 3 files changed, 66 insertions(+), 50 deletions(-)
+
+diff --git a/lib/global.c b/lib/global.c
+index 65c0b81709..faa7f0afb2 100644
+--- a/lib/global.c
++++ b/lib/global.c
+@@ -365,7 +365,7 @@ static int _gnutls_global_init(unsigned constructor)
+ 		_gnutls_fips_mode_reset_zombie();
+ 	}
+ #endif
+-	_gnutls_load_system_priorities();
++	_gnutls_prepare_to_load_system_priorities();
+ 	_gnutls_switch_lib_state(LIB_STATE_OPERATIONAL);
+ 	ret = 0;
+ 
+diff --git a/lib/global.h b/lib/global.h
+index e30187e7ad..16fde08b5c 100644
+--- a/lib/global.h
++++ b/lib/global.h
+@@ -46,7 +46,7 @@ extern void gnutls_crypto_deinit(void);
+ extern void _gnutls_tpm_global_deinit(void);
+ extern void _gnutls_nss_keylog_deinit(void);
+ 
+-extern void _gnutls_load_system_priorities(void);
++extern void _gnutls_prepare_to_load_system_priorities(void);
+ extern void _gnutls_unload_system_priorities(void);
+ 
+ #endif /* GNUTLS_LIB_GLOBAL_H */
+diff --git a/lib/priority.c b/lib/priority.c
+index 755729da18..4faf96fabf 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -1864,11 +1864,12 @@ update_system_wide_priority_string(void)
+ 	return 0;
+ }
+ 
+-static int _gnutls_update_system_priorities(void)
++static int _gnutls_update_system_priorities(bool defer_system_wide)
+ {
+ 	int ret, err = 0;
+ 	struct stat sb;
+ 	FILE *fp;
++	gnutls_buffer_st buf;
+ 	struct ini_ctx ctx;
+ 
+ 	ret = gnutls_rwlock_rdlock(&system_wide_config_rwlock);
+@@ -1883,10 +1884,12 @@ static int _gnutls_update_system_priorities(void)
+ 	}
+ 
+ 	if (system_priority_file_loaded &&
+-	    sb.st_mtime == system_priority_last_mod) {
++	    system_priority_last_mod == sb.st_mtime) {
+ 		_gnutls_debug_log("cfg: system priority %s has not changed\n",
+ 				  system_priority_file);
+-		goto out;
++		if (system_wide_config.priority_string) {
++			goto out;  /* nothing to do */
++		}
+ 	}
+ 
+ 	(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
+@@ -1896,54 +1899,71 @@ static int _gnutls_update_system_priorities(void)
+ 		return gnutls_assert_val(ret);
+ 	}
+ 
+-	/* Another thread has successfully updated the system wide config (with
+-	 * the same modification time as checked above), while upgrading to
+-	 * write lock; no need to reload.
++	/* Another thread could have successfully re-read system-wide config,
++	 * skip re-reading if the mtime it has used is exactly the same.
+ 	 */
+-	if (system_priority_file_loaded &&
+-	    system_priority_last_mod == sb.st_mtime) {
+-		goto out;
++	if (system_priority_file_loaded) {
++		system_priority_file_loaded =
++			(system_priority_last_mod == sb.st_mtime);
+ 	}
+ 
+-	system_priority_file_loaded = 0;
+-	_name_val_array_clear(&system_wide_config.priority_strings);
++	if (!system_priority_file_loaded) {
++		_name_val_array_clear(&system_wide_config.priority_strings);
+ 
+-	gnutls_free(system_wide_config.priority_string);
+-	system_wide_config.priority_string = NULL;
++		gnutls_free(system_wide_config.priority_string);
++		system_wide_config.priority_string = NULL;
+ 
+-	fp = fopen(system_priority_file, "re");
+-	if (fp == NULL) {
+-		_gnutls_debug_log("cfg: unable to open: %s: %d\n",
+-				  system_priority_file, errno);
+-		goto out;
+-	}
+-	/* Parsing the configuration file needs to be done in 2 phases: first
+-	 * parsing the [global] section and then the other sections, because the
+-	 * [global] section modifies the parsing behavior.
+-	 */
+-	memset(&ctx, 0, sizeof(ctx));
+-	err = ini_parse_file(fp, global_ini_handler, &ctx);
+-	if (!err) {
+-		if (fseek(fp, 0L, SEEK_SET) < 0) {
+-			_gnutls_debug_log("cfg: unable to rewind: %s\n",
+-					  system_priority_file);
+-			if (fail_on_invalid_config)
+-				exit(1);
++		fp = fopen(system_priority_file, "re");
++		if (fp == NULL) {
++			_gnutls_debug_log("cfg: unable to open: %s: %d\n",
++					  system_priority_file, errno);
++			goto out;
+ 		}
+-		err = ini_parse_file(fp, cfg_ini_handler, &ctx);
+-	}
+-	fclose(fp);
+-	if (err) {
++		/* Parsing the configuration file needs to be done in 2 phases:
++		 * first parsing the [global] section
++		 * and then the other sections,
++		 * because the [global] section modifies the parsing behavior.
++		 */
++		memset(&ctx, 0, sizeof(ctx));
++		err = ini_parse_file(fp, global_ini_handler, &ctx);
++		if (!err) {
++			if (fseek(fp, 0L, SEEK_SET) < 0) {
++				_gnutls_debug_log("cfg: unable to rewind: %s\n",
++						  system_priority_file);
++				if (fail_on_invalid_config)
++					exit(1);
++			}
++			err = ini_parse_file(fp, cfg_ini_handler, &ctx);
++		}
++		fclose(fp);
++		if (err) {
++			ini_ctx_deinit(&ctx);
++			_gnutls_debug_log("cfg: unable to parse: %s: %d\n",
++					  system_priority_file, err);
++			goto out;
++		}
++		cfg_apply(&system_wide_config, &ctx);
+ 		ini_ctx_deinit(&ctx);
+-		_gnutls_debug_log("cfg: unable to parse: %s: %d\n",
+-				  system_priority_file, err);
+-		goto out;
++		_gnutls_debug_log("cfg: loaded system config %s mtime %lld\n",
++				  system_priority_file,
++				  (unsigned long long)sb.st_mtime);
++
+ 	}
+-	cfg_apply(&system_wide_config, &ctx);
+-	ini_ctx_deinit(&ctx);
+ 
+ 	if (system_wide_config.allowlisting) {
+-		ret = update_system_wide_priority_string();
++		if (defer_system_wide) {
++			/* try constructing a priority string,
++			 * but don't apply it yet, at this point
++			 * we're only interested in whether we can */
++			ret = construct_system_wide_priority_string(&buf);
++			_gnutls_buffer_clear(&buf);
++			_gnutls_debug_log("cfg: deferred setting "
++					  "system-wide priority string\n");
++		} else {
++			ret = update_system_wide_priority_string();
++			_gnutls_debug_log("cfg: finalized "
++					  "system-wide priority string\n");
++		}
+ 		if (ret < 0) {
+ 			_gnutls_debug_log("cfg: unable to build priority string: %s\n",
+ 					  gnutls_strerror(ret));
+@@ -1953,10 +1973,6 @@ static int _gnutls_update_system_priorities(void)
+ 		}
+ 	}
+ 
+-	_gnutls_debug_log("cfg: loaded system priority %s mtime %lld\n",
+-			  system_priority_file,
+-			  (unsigned long long)sb.st_mtime);
+-
+ 	system_priority_file_loaded = 1;
+ 	system_priority_last_mod = sb.st_mtime;
+ 
+@@ -1970,7 +1986,7 @@ static int _gnutls_update_system_priorities(void)
+ 	return ret;
+ }
+ 
+-void _gnutls_load_system_priorities(void)
++void _gnutls_prepare_to_load_system_priorities(void)
+ {
+ 	const char *p;
+ 	int ret;
+@@ -1983,7 +1999,7 @@ void _gnutls_load_system_priorities(void)
+ 	if (p != NULL && p[0] == '1' && p[1] == 0)
+ 		fail_on_invalid_config = 1;
+ 
+-	ret = _gnutls_update_system_priorities();
++	ret = _gnutls_update_system_priorities(true /* defer_system_wide */);
+ 	if (ret < 0) {
+ 		_gnutls_debug_log("failed to update system priorities: %s\n",
+ 				  gnutls_strerror(ret));
+@@ -2050,7 +2066,7 @@ char *_gnutls_resolve_priorities(const char* priorities)
+ 	/* Always try to refresh the cached data, to allow it to be
+ 	 * updated without restarting all applications.
+ 	 */
+-	ret = _gnutls_update_system_priorities();
++	ret = _gnutls_update_system_priorities(false /* defer_system_wide */);
+ 	if (ret < 0) {
+ 		_gnutls_debug_log("failed to update system priorities: %s\n",
+ 				  gnutls_strerror(ret));
+-- 
+2.34.1
+
+
+From 52d5c8627165ff9bc0e99564b772f178ad1f80dd Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Mon, 21 Feb 2022 18:19:25 +0100
+Subject: [PATCH 3/8] lib/algorithms: add UB warnings on late allowlisting API
+ invocations
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/algorithms/ecc.c       | 3 +++
+ lib/algorithms/mac.c       | 3 +++
+ lib/algorithms/protocols.c | 3 +++
+ lib/algorithms/sign.c      | 6 ++++++
+ 4 files changed, 15 insertions(+)
+
+diff --git a/lib/algorithms/ecc.c b/lib/algorithms/ecc.c
+index 736e5dd07f..52ae1db0e4 100644
+--- a/lib/algorithms/ecc.c
++++ b/lib/algorithms/ecc.c
+@@ -389,6 +389,9 @@ void _gnutls_ecc_curve_mark_disabled_all(void)
+  * enabled through the allowlisting mode in the configuration file, or
+  * when the setting is modified with a prior call to this function.
+  *
++ * This function must be called prior to any session priority setting functions;
++ * otherwise the behavior is undefined.
++ *
+  * Returns: 0 on success or negative error code otherwise.
+  *
+  * Since: 3.7.3
+diff --git a/lib/algorithms/mac.c b/lib/algorithms/mac.c
+index a2c66e76bb..166d51d552 100644
+--- a/lib/algorithms/mac.c
++++ b/lib/algorithms/mac.c
+@@ -332,6 +332,9 @@ void _gnutls_digest_mark_insecure_all(void)
+  * through the allowlisting mode in the configuration file, or when
+  * the setting is modified with a prior call to this function.
+  *
++ * This function must be called prior to any session priority setting functions;
++ * otherwise the behavior is undefined.
++ *
+  * Since: 3.7.3
+  */
+ int
+diff --git a/lib/algorithms/protocols.c b/lib/algorithms/protocols.c
+index b0f3e0bc30..64c86eba3c 100644
+--- a/lib/algorithms/protocols.c
++++ b/lib/algorithms/protocols.c
+@@ -237,6 +237,9 @@ void _gnutls_version_mark_revertible_all(void)
+  * enabled through the allowlisting mode in the configuration file, or
+  * when the setting is modified with a prior call to this function.
+  *
++ * This function must be called prior to any session priority setting functions;
++ * otherwise the behavior is undefined.
++ *
+  * Returns: 0 on success or negative error code otherwise.
+  *
+  * Since: 3.7.3
+diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c
+index 543bd19bb5..06abdb4cf8 100644
+--- a/lib/algorithms/sign.c
++++ b/lib/algorithms/sign.c
+@@ -516,6 +516,9 @@ void _gnutls_sign_mark_insecure_all(hash_security_level_t level)
+  * use in certificates.  Use gnutls_sign_set_secure_for_certs() to
+  * mark it secure as well for certificates.
+  *
++ * This function must be called prior to any session priority setting functions;
++ * otherwise the behavior is undefined.
++ *
+  * Since: 3.7.3
+  */
+ int
+@@ -560,6 +563,9 @@ gnutls_sign_set_secure(gnutls_sign_algorithm_t sign,
+  * for the use in certificates.  Use gnutls_sign_set_secure() to mark
+  * it insecure for any uses.
+  *
++ * This function must be called prior to any session priority setting functions;
++ * otherwise the behavior is undefined.
++ *
+  * Since: 3.7.3
+  */
+ int
+-- 
+2.34.1
+
+
+From 52067e1e5b16c30b2f3ce6488a4f72d19d906721 Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Mon, 14 Feb 2022 18:00:25 +0100
+Subject: [PATCH 4/8] lib/priority: move sigalgs filtering to
+ set_ciphersuite_list
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/priority.c | 25 +++++++------------------
+ 1 file changed, 7 insertions(+), 18 deletions(-)
+
+diff --git a/lib/priority.c b/lib/priority.c
+index 4faf96fabf..9775040410 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -1140,9 +1140,6 @@ cfg_apply(struct cfg *cfg, struct ini_ctx *ctx)
+ 	}
+ 
+ 	if (cfg->allowlisting) {
+-		unsigned tls_sig_sem = 0;
+-		size_t j;
+-
+ 		_gnutls_digest_mark_insecure_all();
+ 		for (i = 0; i < ctx->hashes_size; i++) {
+ 			int ret = gnutls_digest_set_secure(ctx->hashes[i], 1);
+@@ -1156,6 +1153,7 @@ cfg_apply(struct cfg *cfg, struct ini_ctx *ctx)
+ 			if (unlikely(ret < 0)) {
+ 				return ret;
+ 			}
++			cfg->sigs[i] = ctx->sigs[i];
+ 		}
+ 		for (i = 0; i < ctx->sigs_for_cert_size; i++) {
+ 			int ret = gnutls_sign_set_secure_for_certs(ctx->sigs_for_cert[i],
+@@ -1165,13 +1163,13 @@ cfg_apply(struct cfg *cfg, struct ini_ctx *ctx)
+ 			}
+ 		}
+ 		_gnutls_version_mark_revertible_all();
+-		for (i = 0, j = 0; i < ctx->versions_size; i++) {
+-			const version_entry_st *vers;
+-			vers = version_to_entry(ctx->versions[i]);
+-			if (vers && vers->supported) {
+-				tls_sig_sem |= vers->tls_sig_sem;
+-				cfg->versions[j++] = vers->id;
++		for (i = 0; i < ctx->versions_size; i++) {
++			int ret;
++			ret = gnutls_protocol_set_enabled(ctx->versions[i], 1);
++			if (unlikely(ret < 0)) {
++				return gnutls_assert_val(ret);
+ 			}
++			cfg->versions[i] = ctx->versions[i];
+ 		}
+ 		_gnutls_ecc_curve_mark_disabled_all();
+ 		for (i = 0; i < ctx->curves_size; i++) {
+@@ -1180,15 +1178,6 @@ cfg_apply(struct cfg *cfg, struct ini_ctx *ctx)
+ 				return ret;
+ 			}
+ 		}
+-		for (i = 0, j = 0; i < ctx->sigs_size; i++) {
+-			const gnutls_sign_entry_st *se;
+-
+-			se = _gnutls_sign_to_entry(ctx->sigs[i]);
+-			if (se != NULL && se->aid.tls_sem & tls_sig_sem &&
+-			    _gnutls_sign_is_secure2(se, 0)) {
+-				cfg->sigs[j++] = se->id;
+-			}
+-		}
+ 	} else {
+ 		for (i = 0; i < ctx->hashes_size; i++) {
+ 			int ret = _gnutls_digest_mark_insecure(ctx->hashes[i]);
+-- 
+2.34.1
+
+
+From fa056709aaa974360d5e5dd8ceb52089b4da7858 Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Tue, 15 Feb 2022 14:41:53 +0100
+Subject: [PATCH 5/8] lib/config_int.h: split struct cfg off lib/priority.c
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/Makefile.am  |  3 +-
+ lib/config_int.h | 84 ++++++++++++++++++++++++++++++++++++++++++++++++
+ lib/priority.c   | 48 +--------------------------
+ 3 files changed, 87 insertions(+), 48 deletions(-)
+ create mode 100644 lib/config_int.h
+
+diff --git a/lib/Makefile.am b/lib/Makefile.am
+index 35df35ee8d..5b540db142 100644
+--- a/lib/Makefile.am
++++ b/lib/Makefile.am
+@@ -134,7 +134,8 @@ HFILES = abstract_int.h debug.h cipher.h	 \
+ 	srp.h auth/srp_kx.h auth/srp_passwd.h	\
+ 	file.h supplemental.h crypto.h random.h system.h\
+ 	locks.h mbuffers.h ecc.h pin.h fips.h \
+-	priority_options.h secrets.h stek.h cert-cred.h
++	priority_options.h secrets.h stek.h cert-cred.h \
++	config_int.h
+ 
+ if ENABLE_PKCS11
+ HFILES += pkcs11_int.h pkcs11x.h
+diff --git a/lib/config_int.h b/lib/config_int.h
+new file mode 100644
+index 0000000000..733536bb98
+--- /dev/null
++++ b/lib/config_int.h
+@@ -0,0 +1,84 @@
++/*
++ * Copyright (C) 2004-2015 Free Software Foundation, Inc.
++ * Copyright (C) 2015-2022 Red Hat, Inc.
++ *
++ * Author: Nikos Mavrogiannopoulos
++ *
++ * This file is part of GnuTLS.
++ *
++ * The GnuTLS is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public License
++ * as published by the Free Software Foundation; either version 2.1 of
++ * the License, or (at your option) any later version.
++ *
++ * This library is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this program.  If not, see <https://www.gnu.org/licenses/>
++ *
++ */
++
++/* Code split off from priority.c, `struct cfg` and some operations on it */
++
++#ifndef GNUTLS_LIB_CONFIG_INT_H
++#define GNUTLS_LIB_CONFIG_INT_H
++
++/*
++ * struct cfg
++ */
++
++struct cfg {
++	bool allowlisting;
++
++	name_val_array_t priority_strings;
++	char *priority_string;
++	char *default_priority_string;
++	gnutls_certificate_verification_profiles_t verification_profile;
++
++	gnutls_cipher_algorithm_t ciphers[MAX_ALGOS+1];
++	gnutls_mac_algorithm_t macs[MAX_ALGOS+1];
++	gnutls_group_t groups[MAX_ALGOS+1];
++	gnutls_kx_algorithm_t kxs[MAX_ALGOS+1];
++	gnutls_sign_algorithm_t sigs[MAX_ALGOS+1];
++	gnutls_protocol_t versions[MAX_ALGOS+1];
++};
++
++/*
++ * deinit / partial duplication. no initialization, must be zero-initialized
++ */
++
++static inline void
++cfg_deinit(struct cfg *cfg)
++{
++	if (cfg->priority_strings) {
++		_name_val_array_clear(&cfg->priority_strings);
++	}
++	gnutls_free(cfg->priority_string);
++	gnutls_free(cfg->default_priority_string);
++}
++
++static inline void
++cfg_steal(struct cfg *dst, struct cfg *src)
++{
++	dst->verification_profile = src->verification_profile;
++
++	dst->priority_strings = src->priority_strings;
++	src->priority_strings = NULL;
++
++	dst->priority_string = src->priority_string;
++	src->priority_string = NULL;
++
++	dst->default_priority_string = src->default_priority_string;
++	src->default_priority_string = NULL;
++
++	dst->allowlisting = src->allowlisting;
++	memcpy(dst->ciphers, src->ciphers, sizeof(src->ciphers));
++	memcpy(dst->macs, src->macs, sizeof(src->macs));
++	memcpy(dst->groups, src->groups, sizeof(src->groups));
++	memcpy(dst->kxs, src->kxs, sizeof(src->kxs));
++}
++
++#endif /* GNUTLS_LIB_CONFIG_INT_H */
+diff --git a/lib/priority.c b/lib/priority.c
+index 9775040410..80b737b938 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -42,6 +42,7 @@
+ #include "locks.h"
+ #include "profiles.h"
+ #include "name_val_array.h"
++#include "config_int.h"
+ 
+ #define MAX_ELEMENTS GNUTLS_MAX_ALGORITHM_NUM
+ 
+@@ -1008,32 +1009,6 @@ static void dummy_func(gnutls_priority_t c)
+ 
+ #include <priority_options.h>
+ 
+-struct cfg {
+-	bool allowlisting;
+-
+-	name_val_array_t priority_strings;
+-	char *priority_string;
+-	char *default_priority_string;
+-	gnutls_certificate_verification_profiles_t verification_profile;
+-
+-	gnutls_cipher_algorithm_t ciphers[MAX_ALGOS+1];
+-	gnutls_mac_algorithm_t macs[MAX_ALGOS+1];
+-	gnutls_group_t groups[MAX_ALGOS+1];
+-	gnutls_kx_algorithm_t kxs[MAX_ALGOS+1];
+-	gnutls_sign_algorithm_t sigs[MAX_ALGOS+1];
+-	gnutls_protocol_t versions[MAX_ALGOS+1];
+-};
+-
+-static inline void
+-cfg_deinit(struct cfg *cfg)
+-{
+-	if (cfg->priority_strings) {
+-		_name_val_array_clear(&cfg->priority_strings);
+-	}
+-	gnutls_free(cfg->priority_string);
+-	gnutls_free(cfg->default_priority_string);
+-}
+-
+ /* Lock for reading and writing system_wide_config */
+ GNUTLS_RWLOCK(system_wide_config_rwlock);
+ static struct cfg system_wide_config;
+@@ -1107,27 +1082,6 @@ ini_ctx_deinit(struct ini_ctx *ctx)
+ 	gnutls_free(ctx->curves);
+ }
+ 
+-static inline void
+-cfg_steal(struct cfg *dst, struct cfg *src)
+-{
+-	dst->verification_profile = src->verification_profile;
+-
+-	dst->priority_strings = src->priority_strings;
+-	src->priority_strings = NULL;
+-
+-	dst->priority_string = src->priority_string;
+-	src->priority_string = NULL;
+-
+-	dst->default_priority_string = src->default_priority_string;
+-	src->default_priority_string = NULL;
+-
+-	dst->allowlisting = src->allowlisting;
+-	memcpy(dst->ciphers, src->ciphers, sizeof(src->ciphers));
+-	memcpy(dst->macs, src->macs, sizeof(src->macs));
+-	memcpy(dst->groups, src->groups, sizeof(src->groups));
+-	memcpy(dst->kxs, src->kxs, sizeof(src->kxs));
+-}
+-
+ static inline int
+ cfg_apply(struct cfg *cfg, struct ini_ctx *ctx)
+ {
+-- 
+2.34.1
+
+
+From 9600788ef81bd424de8c9fcf053bcb717dd50c92 Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Tue, 15 Feb 2022 16:26:52 +0100
+Subject: [PATCH 6/8] lib/priority: extract parts of cfg_apply into
+ cfg_*_set_array*
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/config_int.h | 151 ++++++++++++++++++++++++++++++++++++++++++++++-
+ lib/priority.c   |  68 +++++++++------------
+ 2 files changed, 179 insertions(+), 40 deletions(-)
+
+diff --git a/lib/config_int.h b/lib/config_int.h
+index 733536bb98..be8c71e414 100644
+--- a/lib/config_int.h
++++ b/lib/config_int.h
+@@ -44,6 +44,10 @@ struct cfg {
+ 	gnutls_kx_algorithm_t kxs[MAX_ALGOS+1];
+ 	gnutls_sign_algorithm_t sigs[MAX_ALGOS+1];
+ 	gnutls_protocol_t versions[MAX_ALGOS+1];
++
++	gnutls_digest_algorithm_t hashes[MAX_ALGOS+1];
++	gnutls_ecc_curve_t ecc_curves[MAX_ALGOS+1];
++	gnutls_sign_algorithm_t sigs_for_cert[MAX_ALGOS+1];
+ };
+ 
+ /*
+@@ -79,6 +83,151 @@ cfg_steal(struct cfg *dst, struct cfg *src)
+ 	memcpy(dst->macs, src->macs, sizeof(src->macs));
+ 	memcpy(dst->groups, src->groups, sizeof(src->groups));
+ 	memcpy(dst->kxs, src->kxs, sizeof(src->kxs));
++	memcpy(dst->hashes, src->hashes, sizeof(src->hashes));
++	memcpy(dst->ecc_curves, src->ecc_curves, sizeof(src->ecc_curves));
++	memcpy(dst->sigs, src->sigs, sizeof(src->sigs));
++	memcpy(dst->sigs_for_cert, src->sigs_for_cert,
++	       sizeof(src->sigs_for_cert));
++}
++
++/*
++ * synchronizing changes from struct cfg to global `lib/algorithms` arrays
++ */
++
++/* global side-effect! modifies `flags` in `hash_algorithms[]` */
++static inline int /* allowlisting-only */
++_cfg_hashes_remark(struct cfg* cfg)
++{
++	size_t i;
++	_gnutls_digest_mark_insecure_all();
++	for (i = 0; cfg->hashes[i] != 0; i++) {
++		int ret = gnutls_digest_set_secure(cfg->hashes[i], 1);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
++		}
++	}
++	return 0;
++}
++
++/* global side-effect! modifies `flags` in `sign_algorithms[]` */
++static inline int /* allowlisting-only */
++_cfg_sigs_remark(struct cfg* cfg)
++{
++	size_t i;
++	_gnutls_sign_mark_insecure_all(_INSECURE);
++	for (i = 0; cfg->sigs[i] != 0; i++) {
++		int ret = gnutls_sign_set_secure(cfg->sigs[i], 1);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
++		}
++	}
++	for (i = 0; cfg->sigs_for_cert[i] != 0; i++) {
++		int ret = gnutls_sign_set_secure_for_certs(
++				cfg->sigs_for_cert[i], 1
++		);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
++		}
++	}
++	return 0;
++}
++
++/* global side-effect! modifies `supported` in `sup_versions[]` */
++static inline int /* allowlisting-only */
++_cfg_versions_remark(struct cfg* cfg)
++{
++	size_t i;
++	_gnutls_version_mark_revertible_all();
++	for (i = 0; cfg->versions[i] != 0; i++) {
++		int ret = gnutls_protocol_set_enabled(cfg->versions[i], 1);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
++		}
++	}
++	return 0;
++}
++
++/* global side-effect! modifies `supported` in `ecc_curves[]` */
++static inline int /* allowlisting-only */
++_cfg_ecc_curves_remark(struct cfg* cfg)
++{
++	size_t i;
++	_gnutls_ecc_curve_mark_disabled_all();
++	for (i = 0; cfg->ecc_curves[i] != 0; i++) {
++		int ret = gnutls_ecc_curve_set_enabled(cfg->ecc_curves[i], 1);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
++		}
++	}
++	return 0;
++}
++
++/*
++ * setting arrays of struct cfg: from other arrays
++ */
++
++static inline int /* allowlisting-only */
++cfg_hashes_set_array(struct cfg* cfg,
++		     gnutls_digest_algorithm_t* src, size_t len)
++{
++	if (unlikely(len >= MAX_ALGOS)) {
++		return gnutls_assert_val(GNUTLS_A_INTERNAL_ERROR);
++	}
++	if (len) {
++		memcpy(cfg->hashes,
++		       src, sizeof(gnutls_digest_algorithm_t) * len);
++	}
++	cfg->hashes[len] = 0;
++	return _cfg_hashes_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_sigs_set_arrays(struct cfg* cfg,
++		    gnutls_sign_algorithm_t* src, size_t len,
++		    gnutls_sign_algorithm_t* src_for_cert, size_t len_for_cert)
++{
++	if (unlikely(len >= MAX_ALGOS)) {
++		return gnutls_assert_val(GNUTLS_A_INTERNAL_ERROR);
++	}
++	if (unlikely(len_for_cert >= MAX_ALGOS)) {
++		return gnutls_assert_val(GNUTLS_A_INTERNAL_ERROR);
++	}
++	if (len) {
++		memcpy(cfg->sigs, src, sizeof(gnutls_sign_algorithm_t) * len);
++	}
++	if (len_for_cert) {
++		memcpy(cfg->sigs_for_cert, src_for_cert,
++		       sizeof(gnutls_sign_algorithm_t) * len_for_cert);
++	}
++	cfg->sigs[len] = 0;
++	cfg->sigs_for_cert[len_for_cert] = 0;
++	return _cfg_sigs_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_versions_set_array(struct cfg* cfg, gnutls_protocol_t* src, size_t len)
++{
++	if (unlikely(len >= MAX_ALGOS)) {
++		return gnutls_assert_val(GNUTLS_A_INTERNAL_ERROR);
++	}
++	if (len) {
++		memcpy(cfg->versions, src, sizeof(gnutls_protocol_t) * len);
++	}
++	cfg->versions[len] = 0;
++	return _cfg_versions_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_ecc_curves_set_array(struct cfg* cfg, gnutls_ecc_curve_t* src, size_t len)
++{
++	if (unlikely(len >= MAX_ALGOS)) {
++		return gnutls_assert_val(GNUTLS_A_INTERNAL_ERROR);
++	}
++	if (len) {
++		memcpy(cfg->ecc_curves, src, sizeof(gnutls_ecc_curve_t) * len);
++	}
++	cfg->ecc_curves[len] = 0;
++	return _cfg_ecc_curves_remark(cfg);
+ }
+ 
+-#endif /* GNUTLS_LIB_CONFIG_INT_H */
++#endif  /* GNUTLS_LIB_CONFIG_INT_H */
+diff --git a/lib/priority.c b/lib/priority.c
+index 80b737b938..8d8428e1da 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -1086,6 +1086,7 @@ static inline int
+ cfg_apply(struct cfg *cfg, struct ini_ctx *ctx)
+ {
+ 	size_t i;
++	int ret;
+ 
+ 	cfg_steal(cfg, &ctx->cfg);
+ 
+@@ -1094,72 +1095,61 @@ cfg_apply(struct cfg *cfg, struct ini_ctx *ctx)
+ 	}
+ 
+ 	if (cfg->allowlisting) {
+-		_gnutls_digest_mark_insecure_all();
+-		for (i = 0; i < ctx->hashes_size; i++) {
+-			int ret = gnutls_digest_set_secure(ctx->hashes[i], 1);
+-			if (unlikely(ret < 0)) {
+-				return ret;
+-			}
+-		}
+-		_gnutls_sign_mark_insecure_all(_INSECURE);
+-		for (i = 0; i < ctx->sigs_size; i++) {
+-			int ret = gnutls_sign_set_secure(ctx->sigs[i], 1);
+-			if (unlikely(ret < 0)) {
+-				return ret;
+-			}
+-			cfg->sigs[i] = ctx->sigs[i];
++		/* also updates `flags` of global `hash_algorithms[]` */
++		ret = cfg_hashes_set_array(cfg, ctx->hashes, ctx->hashes_size);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
+ 		}
+-		for (i = 0; i < ctx->sigs_for_cert_size; i++) {
+-			int ret = gnutls_sign_set_secure_for_certs(ctx->sigs_for_cert[i],
+-								   1);
+-			if (unlikely(ret < 0)) {
+-				return ret;
+-			}
++		/* also updates `flags` of global `sign_algorithms[]` */
++		ret = cfg_sigs_set_arrays(cfg, ctx->sigs, ctx->sigs_size,
++					  ctx->sigs_for_cert,
++					  ctx->sigs_for_cert_size);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
+ 		}
+-		_gnutls_version_mark_revertible_all();
+-		for (i = 0; i < ctx->versions_size; i++) {
+-			int ret;
+-			ret = gnutls_protocol_set_enabled(ctx->versions[i], 1);
+-			if (unlikely(ret < 0)) {
+-				return gnutls_assert_val(ret);
+-			}
+-			cfg->versions[i] = ctx->versions[i];
++		/* also updates `supported` field of global `sup_versions[]` */
++		ret = cfg_versions_set_array(cfg,
++					     ctx->versions, ctx->versions_size);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
+ 		}
+-		_gnutls_ecc_curve_mark_disabled_all();
+-		for (i = 0; i < ctx->curves_size; i++) {
+-			int ret = gnutls_ecc_curve_set_enabled(ctx->curves[i], 1);
+-			if (unlikely(ret < 0)) {
+-				return ret;
+-			}
++		/* also updates `supported` field of global `ecc_curves[]` */
++		ret = cfg_ecc_curves_set_array(cfg,
++					       ctx->curves, ctx->curves_size);
++		if (unlikely(ret < 0)) {
++			return gnutls_assert_val(ret);
+ 		}
+ 	} else {
++		/* updates same global arrays as above, but doesn't store
++		 * the algorithms into the `struct cfg` as allowlisting does.
++		 * blocklisting doesn't allow relaxing the restrictions */
+ 		for (i = 0; i < ctx->hashes_size; i++) {
+-			int ret = _gnutls_digest_mark_insecure(ctx->hashes[i]);
++			ret = _gnutls_digest_mark_insecure(ctx->hashes[i]);
+ 			if (unlikely(ret < 0)) {
+ 				return ret;
+ 			}
+ 		}
+ 		for (i = 0; i < ctx->sigs_size; i++) {
+-			int ret = _gnutls_sign_mark_insecure(ctx->sigs[i],
++			ret = _gnutls_sign_mark_insecure(ctx->sigs[i],
+ 							     _INSECURE);
+ 			if (unlikely(ret < 0)) {
+ 				return ret;
+ 			}
+ 		}
+ 		for (i = 0; i < ctx->sigs_for_cert_size; i++) {
+-			int ret = _gnutls_sign_mark_insecure(ctx->sigs_for_cert[i], _INSECURE_FOR_CERTS);
++			ret = _gnutls_sign_mark_insecure(ctx->sigs_for_cert[i], _INSECURE_FOR_CERTS);
+ 			if (unlikely(ret < 0)) {
+ 				return ret;
+ 			}
+ 		}
+ 		for (i = 0; i < ctx->versions_size; i++) {
+-			int ret = _gnutls_version_mark_disabled(ctx->versions[i]);
++			ret = _gnutls_version_mark_disabled(ctx->versions[i]);
+ 			if (unlikely(ret < 0)) {
+ 				return ret;
+ 			}
+ 		}
+ 		for (i = 0; i < ctx->curves_size; i++) {
+-			int ret = _gnutls_ecc_curve_mark_disabled(ctx->curves[i]);
++			ret = _gnutls_ecc_curve_mark_disabled(ctx->curves[i]);
+ 			if (unlikely(ret < 0)) {
+ 				return ret;
+ 			}
+-- 
+2.34.1
+
+
+From 1767ced4c0abf9d372d334a749b87fd00cd8ab5d Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Wed, 16 Feb 2022 14:28:18 +0100
+Subject: [PATCH 7/8] plumb allowlisting API through the config, restrict usage
+ to early times
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/algorithms.h                |   7 +-
+ lib/algorithms/ecc.c            |  20 +--
+ lib/algorithms/mac.c            |  18 +-
+ lib/algorithms/protocols.c      |  47 +++--
+ lib/algorithms/sign.c           |  78 +--------
+ lib/config_int.h                | 156 ++++++++++++++++-
+ lib/global.h                    |   1 +
+ lib/priority.c                  | 259 +++++++++++++++++++++++++++-
+ tests/protocol-set-allowlist.c  |  47 +++--
+ tests/protocol-set-allowlist.sh | 296 +++++++++++++++++++++-----------
+ 10 files changed, 663 insertions(+), 266 deletions(-)
+
+diff --git a/lib/algorithms.h b/lib/algorithms.h
+index da72403fba..2c33a7210f 100644
+--- a/lib/algorithms.h
++++ b/lib/algorithms.h
+@@ -354,13 +354,18 @@ const gnutls_protocol_t *_gnutls_protocol_list(void);
+ int _gnutls_version_mark_disabled(gnutls_protocol_t version);
+ gnutls_protocol_t _gnutls_protocol_get_id_if_supported(const char *name);
+ 
++int _gnutls_digest_set_secure(gnutls_digest_algorithm_t dig, unsigned int secure);
++int _gnutls_sign_set_secure(gnutls_sign_algorithm_t sign, hash_security_level_t slevel);
++int _gnutls_protocol_set_enabled(gnutls_protocol_t version, unsigned int enabled);
++int _gnutls_ecc_curve_set_enabled(gnutls_ecc_curve_t curve, unsigned int enabled);
++
+ /* these functions are for revertible settings, meaning that algorithms marked
+  * as disabled/insecure with mark_*_all functions can be re-enabled with
+  * mark_{enabled,secure} functions */
+ void _gnutls_ecc_curve_mark_disabled_all(void);
++void _gnutls_version_mark_disabled_all(void);
+ void _gnutls_sign_mark_insecure_all(hash_security_level_t level);
+ void _gnutls_digest_mark_insecure_all(void);
+-void _gnutls_version_mark_revertible_all(void);
+ 
+ #define GNUTLS_SIGN_FLAG_TLS13_OK	1 /* if it is ok to use under TLS1.3 */
+ #define GNUTLS_SIGN_FLAG_CRT_VRFY_REVERSE (1 << 1) /* reverse order of bytes in CrtVrfy signature */
+diff --git a/lib/algorithms/ecc.c b/lib/algorithms/ecc.c
+index 52ae1db0e4..303a42612f 100644
+--- a/lib/algorithms/ecc.c
++++ b/lib/algorithms/ecc.c
+@@ -379,26 +379,8 @@ void _gnutls_ecc_curve_mark_disabled_all(void)
+ 	}
+ }
+ 
+-/**
+- * gnutls_ecc_curve_set_enabled:
+- * @curve: is an ECC curve
+- * @enabled: whether to enable the curve
+- *
+- * Modify the previous system wide setting that marked @curve as
+- * enabled or disabled.  This only has effect when the curve is
+- * enabled through the allowlisting mode in the configuration file, or
+- * when the setting is modified with a prior call to this function.
+- *
+- * This function must be called prior to any session priority setting functions;
+- * otherwise the behavior is undefined.
+- *
+- * Returns: 0 on success or negative error code otherwise.
+- *
+- * Since: 3.7.3
+- */
+ int
+-gnutls_ecc_curve_set_enabled(gnutls_ecc_curve_t curve,
+-			     unsigned int enabled)
++_gnutls_ecc_curve_set_enabled(gnutls_ecc_curve_t curve, unsigned int enabled)
+ {
+ 	gnutls_ecc_curve_entry_st *p;
+ 
+diff --git a/lib/algorithms/mac.c b/lib/algorithms/mac.c
+index 166d51d552..47fbc226bd 100644
+--- a/lib/algorithms/mac.c
++++ b/lib/algorithms/mac.c
+@@ -322,24 +322,8 @@ void _gnutls_digest_mark_insecure_all(void)
+ #endif
+ }
+ 
+-/**
+- * gnutls_digest_set_secure:
+- * @dig: is a digest algorithm
+- * @secure: whether to mark the digest algorithm secure
+- *
+- * Modify the previous system wide setting that marked @dig as secure
+- * or insecure. This only has effect when the algorithm is enabled
+- * through the allowlisting mode in the configuration file, or when
+- * the setting is modified with a prior call to this function.
+- *
+- * This function must be called prior to any session priority setting functions;
+- * otherwise the behavior is undefined.
+- *
+- * Since: 3.7.3
+- */
+ int
+-gnutls_digest_set_secure(gnutls_digest_algorithm_t dig,
+-			 unsigned int secure)
++_gnutls_digest_set_secure(gnutls_digest_algorithm_t dig, unsigned int secure)
+ {
+ #ifndef DISABLE_SYSTEM_CONFIG
+ 	mac_entry_st *p;
+diff --git a/lib/algorithms/protocols.c b/lib/algorithms/protocols.c
+index 64c86eba3c..5a88123470 100644
+--- a/lib/algorithms/protocols.c
++++ b/lib/algorithms/protocols.c
+@@ -192,10 +192,11 @@ static int
+ version_is_valid_for_session(gnutls_session_t session,
+ 			     const version_entry_st *v)
+ {
+-	if (v->supported && v->transport == session->internals.transport) {
+-		return 1;
+-	}
+-	return 0;
++	if (!v->supported && !(v->supported_revertible && _gnutls_allowlisting_mode()))
++		return 0;
++	if (v->transport != session->internals.transport)
++		return 0;
++	return 1;
+ }
+ 
+ /* This is only called by cfg_apply in priority.c, in blocklisting mode. */
+@@ -215,37 +216,20 @@ int _gnutls_version_mark_disabled(gnutls_protocol_t version)
+ }
+ 
+ /* This is only called by cfg_apply in priority.c, in allowlisting mode. */
+-void _gnutls_version_mark_revertible_all(void)
++void _gnutls_version_mark_disabled_all(void)
+ {
+ #ifndef DISABLE_SYSTEM_CONFIG
+ 	version_entry_st *p;
+ 
+ 	for (p = sup_versions; p->name != NULL; p++) {
++		p->supported = false;
+ 		p->supported_revertible = true;
+ 	}
+-
+ #endif
+ }
+ 
+-/**
+- * gnutls_protocol_set_enabled:
+- * @version: is a (gnutls) version number
+- * @enabled: whether to enable the protocol
+- *
+- * Mark the previous system wide setting that marked @version as
+- * enabled or disabled. This only has effect when the version is
+- * enabled through the allowlisting mode in the configuration file, or
+- * when the setting is modified with a prior call to this function.
+- *
+- * This function must be called prior to any session priority setting functions;
+- * otherwise the behavior is undefined.
+- *
+- * Returns: 0 on success or negative error code otherwise.
+- *
+- * Since: 3.7.3
+- */
+ int
+-gnutls_protocol_set_enabled(gnutls_protocol_t version,
++_gnutls_protocol_set_enabled(gnutls_protocol_t version,
+ 			    unsigned int enabled)
+ {
+ #ifndef DISABLE_SYSTEM_CONFIG
+@@ -331,7 +315,10 @@ const version_entry_st *_gnutls_version_max(gnutls_session_t session)
+ 				if (p->obsolete != 0)
+ 					break;
+ #endif
+-				if (!p->supported || p->transport != session->internals.transport)
++				if (!p->supported && !(p->supported_revertible && _gnutls_allowlisting_mode()))
++					break;
++
++				if (p->transport != session->internals.transport)
+ 					break;
+ 
+ 				if (p->tls13_sem && (session->internals.flags & INT_FLAG_NO_TLS13))
+@@ -386,7 +373,10 @@ int _gnutls_write_supported_versions(gnutls_session_t session, uint8_t *buffer,
+ 				if (p->obsolete != 0)
+ 					break;
+ 
+-				if (!p->supported || p->transport != session->internals.transport)
++				if (!p->supported && !(p->supported_revertible && _gnutls_allowlisting_mode()))
++					break;
++
++				if (p->transport != session->internals.transport)
+ 					break;
+ 
+ 				if (p->only_extension)
+@@ -569,7 +559,10 @@ _gnutls_nversion_is_supported(gnutls_session_t session,
+ 			if (p->tls13_sem && (session->internals.flags & INT_FLAG_NO_TLS13))
+ 				return 0;
+ 
+-			if (!p->supported || p->transport != session->internals.transport)
++			if (!p->supported && !(p->supported_revertible && _gnutls_allowlisting_mode()))
++				return 0;
++
++			if (p->transport != session->internals.transport)
+ 				return 0;
+ 
+ 			version = p->id;
+diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c
+index 06abdb4cf8..26816feef5 100644
+--- a/lib/algorithms/sign.c
++++ b/lib/algorithms/sign.c
+@@ -502,28 +502,9 @@ void _gnutls_sign_mark_insecure_all(hash_security_level_t level)
+ #endif
+ }
+ 
+-/**
+- * gnutls_sign_set_secure:
+- * @sign: the sign algorithm
+- * @secure: whether to mark the sign algorithm secure
+- *
+- * Modify the previous system wide setting that marked @sign as secure
+- * or insecure.  This only has effect when the algorithm is marked as
+- * secure through the allowlisting mode in the configuration file, or
+- * when the setting is modified with a prior call to this function.
+- *
+- * Even when @secure is true, @sign is not marked as secure for the
+- * use in certificates.  Use gnutls_sign_set_secure_for_certs() to
+- * mark it secure as well for certificates.
+- *
+- * This function must be called prior to any session priority setting functions;
+- * otherwise the behavior is undefined.
+- *
+- * Since: 3.7.3
+- */
+ int
+-gnutls_sign_set_secure(gnutls_sign_algorithm_t sign,
+-		       unsigned int secure)
++_gnutls_sign_set_secure(gnutls_sign_algorithm_t sign,
++			hash_security_level_t slevel)
+ {
+ #ifndef DISABLE_SYSTEM_CONFIG
+ 	gnutls_sign_entry_st *p;
+@@ -533,60 +514,7 @@ gnutls_sign_set_secure(gnutls_sign_algorithm_t sign,
+ 			if (!(p->flags & GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE)) {
+ 				return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ 			}
+-			if (secure) {
+-				if (p->slevel > _INSECURE_FOR_CERTS) {
+-					p->slevel = _INSECURE_FOR_CERTS;
+-				}
+-			} else {
+-				p->slevel = _INSECURE;
+-			}
+-			return 0;
+-		}
+-	}
+-#endif
+-	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+-}
+-
+-/**
+- * gnutls_sign_set_secure_for_certs:
+- * @sign: the sign algorithm
+- * @secure: whether to mark the sign algorithm secure for certificates
+- *
+- * Modify the previous system wide setting that marked @sign as secure
+- * or insecure for the use in certificates. This only has effect when
+- * the algorithm is marked as secure through the allowlisting mode in
+- * the configuration file, or when the setting is modified with a
+- * prior call to this function.
+- *
+- * When @secure is true, @sign is marked as secure for any use unlike
+- * gnutls_sign_set_secure().  Otherwise, it is marked as insecure only
+- * for the use in certificates.  Use gnutls_sign_set_secure() to mark
+- * it insecure for any uses.
+- *
+- * This function must be called prior to any session priority setting functions;
+- * otherwise the behavior is undefined.
+- *
+- * Since: 3.7.3
+- */
+-int
+-gnutls_sign_set_secure_for_certs(gnutls_sign_algorithm_t sign,
+-				 unsigned int secure)
+-{
+-#ifndef DISABLE_SYSTEM_CONFIG
+-	gnutls_sign_entry_st *p;
+-
+-	for(p = sign_algorithms; p->name != NULL; p++) {
+-		if (p->id && p->id == sign) {
+-			if (!(p->flags & GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE)) {
+-				return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+-			}
+-			if (secure) {
+-				p->slevel = _SECURE;
+-			} else {
+-				if (p->slevel < _INSECURE_FOR_CERTS) {
+-					p->slevel = _INSECURE_FOR_CERTS;
+-				}
+-			}
++			p->slevel = slevel;
+ 			return 0;
+ 		}
+ 	}
+diff --git a/lib/config_int.h b/lib/config_int.h
+index be8c71e414..df39f2bf83 100644
+--- a/lib/config_int.h
++++ b/lib/config_int.h
+@@ -101,7 +101,7 @@ _cfg_hashes_remark(struct cfg* cfg)
+ 	size_t i;
+ 	_gnutls_digest_mark_insecure_all();
+ 	for (i = 0; cfg->hashes[i] != 0; i++) {
+-		int ret = gnutls_digest_set_secure(cfg->hashes[i], 1);
++		int ret = _gnutls_digest_set_secure(cfg->hashes[i], 1);
+ 		if (unlikely(ret < 0)) {
+ 			return gnutls_assert_val(ret);
+ 		}
+@@ -116,15 +116,15 @@ _cfg_sigs_remark(struct cfg* cfg)
+ 	size_t i;
+ 	_gnutls_sign_mark_insecure_all(_INSECURE);
+ 	for (i = 0; cfg->sigs[i] != 0; i++) {
+-		int ret = gnutls_sign_set_secure(cfg->sigs[i], 1);
++		int ret = _gnutls_sign_set_secure(cfg->sigs[i],
++						  _INSECURE_FOR_CERTS);
+ 		if (unlikely(ret < 0)) {
+ 			return gnutls_assert_val(ret);
+ 		}
+ 	}
+ 	for (i = 0; cfg->sigs_for_cert[i] != 0; i++) {
+-		int ret = gnutls_sign_set_secure_for_certs(
+-				cfg->sigs_for_cert[i], 1
+-		);
++		int ret = _gnutls_sign_set_secure(cfg->sigs_for_cert[i],
++		                                  _SECURE);
+ 		if (unlikely(ret < 0)) {
+ 			return gnutls_assert_val(ret);
+ 		}
+@@ -137,9 +137,9 @@ static inline int /* allowlisting-only */
+ _cfg_versions_remark(struct cfg* cfg)
+ {
+ 	size_t i;
+-	_gnutls_version_mark_revertible_all();
++	_gnutls_version_mark_disabled_all();
+ 	for (i = 0; cfg->versions[i] != 0; i++) {
+-		int ret = gnutls_protocol_set_enabled(cfg->versions[i], 1);
++		int ret = _gnutls_protocol_set_enabled(cfg->versions[i], 1);
+ 		if (unlikely(ret < 0)) {
+ 			return gnutls_assert_val(ret);
+ 		}
+@@ -154,7 +154,7 @@ _cfg_ecc_curves_remark(struct cfg* cfg)
+ 	size_t i;
+ 	_gnutls_ecc_curve_mark_disabled_all();
+ 	for (i = 0; cfg->ecc_curves[i] != 0; i++) {
+-		int ret = gnutls_ecc_curve_set_enabled(cfg->ecc_curves[i], 1);
++		int ret = _gnutls_ecc_curve_set_enabled(cfg->ecc_curves[i], 1);
+ 		if (unlikely(ret < 0)) {
+ 			return gnutls_assert_val(ret);
+ 		}
+@@ -168,7 +168,7 @@ _cfg_ecc_curves_remark(struct cfg* cfg)
+ 
+ static inline int /* allowlisting-only */
+ cfg_hashes_set_array(struct cfg* cfg,
+-		     gnutls_digest_algorithm_t* src, size_t len)
++                     gnutls_digest_algorithm_t* src, size_t len)
+ {
+ 	if (unlikely(len >= MAX_ALGOS)) {
+ 		return gnutls_assert_val(GNUTLS_A_INTERNAL_ERROR);
+@@ -230,4 +230,142 @@ cfg_ecc_curves_set_array(struct cfg* cfg, gnutls_ecc_curve_t* src, size_t len)
+ 	return _cfg_ecc_curves_remark(cfg);
+ }
+ 
++/*
++ * appending to arrays of struct cfg
++ */
++
++/* polymorphic way to DRY this operation. other possible approaches:
++ * 1. just unmacro (long)
++ * 2. cast to ints and write a function operating on ints
++ *    (hacky, every call is +4 lines, needs a portable static assert)
++ * 3. macro whole functions, not just this operation (harder to find/read)
++ */
++#define APPEND_TO_NULL_TERMINATED_ARRAY(dst, element) \
++	do { \
++		size_t i; \
++		for (i = 0; dst[i] != 0; i++) { \
++			if (dst[i] == element) { \
++				return 0; \
++			} \
++		} \
++		if (unlikely(i >= MAX_ALGOS)) { \
++			return gnutls_assert_val(GNUTLS_A_INTERNAL_ERROR); \
++		} \
++		dst[i] = element; \
++		dst[i + 1] = 0; \
++	} while (0)
++
++static inline int /* allowlisting-only */
++cfg_hashes_add(struct cfg *cfg, gnutls_digest_algorithm_t dig)
++{
++	_gnutls_debug_log("cfg: enabling digest algorithm %s\n",
++	                  gnutls_digest_get_name(dig));
++	APPEND_TO_NULL_TERMINATED_ARRAY(cfg->hashes, dig);
++	return _cfg_hashes_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_sigs_add(struct cfg *cfg, gnutls_sign_algorithm_t sig)
++{
++	_gnutls_debug_log("cfg: enabling signature algorithm "
++	                  "(for non-certificate usage) "
++	                  "%s\n", gnutls_sign_get_name(sig));
++	APPEND_TO_NULL_TERMINATED_ARRAY(cfg->sigs, sig);
++	return _cfg_sigs_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_sigs_for_cert_add(struct cfg *cfg, gnutls_sign_algorithm_t sig)
++{
++	_gnutls_debug_log("cfg: enabling signature algorithm"
++	                  "(for certificate usage) "
++	                  "%s\n", gnutls_sign_get_name(sig));
++	APPEND_TO_NULL_TERMINATED_ARRAY(cfg->sigs_for_cert, sig);
++	return _cfg_sigs_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_versions_add(struct cfg *cfg, gnutls_protocol_t prot)
++{
++	_gnutls_debug_log("cfg: enabling version %s\n",
++	                  gnutls_protocol_get_name(prot));
++	APPEND_TO_NULL_TERMINATED_ARRAY(cfg->versions, prot);
++	return _cfg_versions_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_ecc_curves_add(struct cfg *cfg, gnutls_ecc_curve_t curve)
++{
++	_gnutls_debug_log("cfg: enabling curve %s\n",
++	                  gnutls_ecc_curve_get_name(curve));
++	APPEND_TO_NULL_TERMINATED_ARRAY(cfg->ecc_curves, curve);
++	return _cfg_ecc_curves_remark(cfg);
++}
++
++#undef APPEND_TO_NULL_TERMINATED_ARRAY
++
++/*
++ * removing from arrays of struct cfg
++ */
++
++/* polymorphic way to DRY this removal, see APPEND_TO_NULL_TERMINATED_ARRAY */
++#define REMOVE_FROM_NULL_TERMINATED_ARRAY(dst, element) \
++	do { \
++		size_t i, j; \
++		for (i = 0; dst[i] != 0; i++) { \
++			if (dst[i] == element) { \
++				for (j = i; dst[j] != 0; j++) { \
++					dst[j] = dst[j + 1]; \
++				} \
++			} \
++		} \
++	} while (0)
++
++static inline int /* allowlisting-only */
++cfg_hashes_remove(struct cfg *cfg, gnutls_digest_algorithm_t dig)
++{
++	_gnutls_debug_log("cfg: disabling digest algorithm %s\n",
++	                  gnutls_digest_get_name(dig));
++	REMOVE_FROM_NULL_TERMINATED_ARRAY(cfg->hashes, dig);
++	return _cfg_hashes_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_sigs_remove(struct cfg *cfg, gnutls_sign_algorithm_t sig)
++{
++	_gnutls_debug_log("cfg: disabling signature algorithm "
++	                  "(for non-certificate usage) "
++	                  "%s\n", gnutls_sign_get_name(sig));
++	REMOVE_FROM_NULL_TERMINATED_ARRAY(cfg->sigs, sig);
++	return _cfg_sigs_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_sigs_for_cert_remove(struct cfg *cfg, gnutls_sign_algorithm_t sig)
++{
++	_gnutls_debug_log("cfg: disabling signature algorithm"
++	                  "(for certificate usage) "
++	                  "%s\n", gnutls_sign_get_name(sig));
++	REMOVE_FROM_NULL_TERMINATED_ARRAY(cfg->sigs_for_cert, sig);
++	return _cfg_sigs_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_versions_remove(struct cfg *cfg, gnutls_protocol_t prot)
++{
++	_gnutls_debug_log("cfg: disabling version %s\n",
++	                  gnutls_protocol_get_name(prot));
++	REMOVE_FROM_NULL_TERMINATED_ARRAY(cfg->versions, prot);
++	return _cfg_versions_remark(cfg);
++}
++
++static inline int /* allowlisting-only */
++cfg_ecc_curves_remove(struct cfg *cfg, gnutls_ecc_curve_t curve)
++{
++	_gnutls_debug_log("cfg: disabling curve %s\n",
++	                  gnutls_ecc_curve_get_name(curve));
++	REMOVE_FROM_NULL_TERMINATED_ARRAY(cfg->ecc_curves, curve);
++	return _cfg_ecc_curves_remark(cfg);
++}
++
+ #endif  /* GNUTLS_LIB_CONFIG_INT_H */
+diff --git a/lib/global.h b/lib/global.h
+index 16fde08b5c..6bd70df8e3 100644
+--- a/lib/global.h
++++ b/lib/global.h
+@@ -48,5 +48,6 @@ extern void _gnutls_nss_keylog_deinit(void);
+ 
+ extern void _gnutls_prepare_to_load_system_priorities(void);
+ extern void _gnutls_unload_system_priorities(void);
++extern bool _gnutls_allowlisting_mode(void);
+ 
+ #endif /* GNUTLS_LIB_GLOBAL_H */
+diff --git a/lib/priority.c b/lib/priority.c
+index 8d8428e1da..c187284024 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -1023,6 +1023,11 @@ static unsigned system_priority_file_loaded = 0;
+ #define OVERRIDES_SECTION "overrides"
+ #define MAX_ALGO_NAME 2048
+ 
++bool _gnutls_allowlisting_mode(void)
++{
++	return system_wide_config.allowlisting;
++}
++
+ static void _clear_default_system_priority(void)
+ {
+ 	gnutls_free(system_wide_config.default_priority_string);
+@@ -2215,7 +2220,9 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
+ 	/* disable TLS versions which are added but are unsupported */
+ 	for (i = j = 0; i < priority_cache->protocol.num_priorities; i++) {
+ 		vers = version_to_entry(priority_cache->protocol.priorities[i]);
+-		if (!vers || vers->supported)
++		if (!vers || vers->supported ||
++				(system_wide_config.allowlisting && \
++				 vers->supported_revertible))
+ 			priority_cache->protocol.priorities[j++] = priority_cache->protocol.priorities[i];
+ 	}
+ 	priority_cache->protocol.num_priorities = j;
+@@ -3393,3 +3400,253 @@ gnutls_priority_string_list(unsigned iter, unsigned int flags)
+ 	}
+ 	return NULL;
+ }
++
++/*
++ * high-level interface for overriding configuration files
++ */
++
++static inline int /* not locking system_wide_config */
++system_wide_config_is_malleable(void) {
++	if (!system_wide_config.allowlisting) {
++		_gnutls_debug_log("allowlisting is not enabled!\n");
++		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
++	}
++	if (system_wide_config.priority_string) {
++		_gnutls_debug_log("priority strings have already been "
++				"initialized!\n");
++		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
++	}
++	return 1;
++}
++
++/**
++ * gnutls_digest_set_secure:
++ * @dig: is a digest algorithm
++ * @secure: whether to mark the digest algorithm secure
++ *
++ * Modify the previous system wide setting that marked @dig as secure
++ * or insecure. This only has effect when the algorithm is enabled
++ * through the allowlisting mode in the configuration file, or when
++ * the setting is modified with a prior call to this function.
++ *
++ * Since: 3.7.3
++ */
++int
++gnutls_digest_set_secure(gnutls_digest_algorithm_t dig, unsigned int secure)
++{
++#ifndef DISABLE_SYSTEM_CONFIG
++	int ret;
++	ret = gnutls_rwlock_wrlock(&system_wide_config_rwlock);
++	if (ret < 0) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++	ret = system_wide_config_is_malleable();
++	if (ret != 1) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++
++	if (secure) {
++		ret = cfg_hashes_add(&system_wide_config, dig);
++	} else {
++		ret = cfg_hashes_remove(&system_wide_config, dig);
++	}
++
++	(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++	return ret;
++#else
++	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
++#endif
++}
++
++/**
++ * gnutls_sign_set_secure:
++ * @sign: the sign algorithm
++ * @secure: whether to mark the sign algorithm secure
++ *
++ * Modify the previous system wide setting that marked @sign as secure
++ * or insecure.  This only has effect when the algorithm is marked as
++ * secure through the allowlisting mode in the configuration file, or
++ * when the setting is modified with a prior call to this function.
++ *
++ * Even when @secure is true, @sign is not marked as secure for the
++ * use in certificates.  Use gnutls_sign_set_secure_for_certs() to
++ * mark it secure as well for certificates.
++ *
++ * Since: 3.7.3
++ */
++int
++gnutls_sign_set_secure(gnutls_sign_algorithm_t sign, unsigned int secure)
++{
++#ifndef DISABLE_SYSTEM_CONFIG
++	int ret;
++	ret = gnutls_rwlock_wrlock(&system_wide_config_rwlock);
++	if (ret < 0) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++	ret = system_wide_config_is_malleable();
++	if (ret != 1) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++
++	if (secure) {
++		ret = cfg_sigs_add(&system_wide_config, sign);
++	} else {
++		ret = cfg_sigs_remove(&system_wide_config, sign);
++		if (ret < 0) {
++			(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++			return ret;
++		}
++		/* irregularity, distrusting also means distrusting for certs */
++		ret = cfg_sigs_for_cert_remove(&system_wide_config, sign);
++	}
++
++	(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++	return ret;
++#else
++	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
++#endif
++}
++
++/**
++ * gnutls_sign_set_secure_for_certs:
++ * @sign: the sign algorithm
++ * @secure: whether to mark the sign algorithm secure for certificates
++ *
++ * Modify the previous system wide setting that marked @sign as secure
++ * or insecure for the use in certificates. This only has effect when
++ * the algorithm is marked as secure through the allowlisting mode in
++ * the configuration file, or when the setting is modified with a
++ * prior call to this function.
++ *
++ * When @secure is true, @sign is marked as secure for any use unlike
++ * gnutls_sign_set_secure().  Otherwise, it is marked as insecure only
++ * for the use in certificates.  Use gnutls_sign_set_secure() to mark
++ * it insecure for any uses.
++ *
++ * Since: 3.7.3
++ */
++int
++gnutls_sign_set_secure_for_certs(gnutls_sign_algorithm_t sign,
++				 unsigned int secure)
++{
++#ifndef DISABLE_SYSTEM_CONFIG
++	int ret;
++	ret = gnutls_rwlock_wrlock(&system_wide_config_rwlock);
++	if (ret < 0) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++	ret = system_wide_config_is_malleable();
++	if (ret != 1) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++
++	if (secure) {
++		/* irregularity, trusting for certs means trusting in general */
++		ret = cfg_sigs_add(&system_wide_config, sign);
++		if (ret < 0) {
++			(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++			return ret;
++		}
++		ret = cfg_sigs_for_cert_add(&system_wide_config, sign);
++	} else {
++		ret = cfg_sigs_for_cert_remove(&system_wide_config, sign);
++	}
++
++	(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++	return ret;
++#else
++	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
++#endif
++}
++
++/**
++ * gnutls_protocol_set_enabled:
++ * @version: is a (gnutls) version number
++ * @enabled: whether to enable the protocol
++ *
++ * Mark the previous system wide setting that marked @version as
++ * enabled or disabled. This only has effect when the version is
++ * enabled through the allowlisting mode in the configuration file, or
++ * when the setting is modified with a prior call to this function.
++ *
++ * Returns: 0 on success or negative error code otherwise.
++ *
++ * Since: 3.7.3
++ */
++int /* allowlisting-only */ /* not thread-safe */
++gnutls_protocol_set_enabled(gnutls_protocol_t version, unsigned int enabled)
++{
++#ifndef DISABLE_SYSTEM_CONFIG
++	int ret;
++	ret = gnutls_rwlock_wrlock(&system_wide_config_rwlock);
++	if (ret < 0) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++	ret = system_wide_config_is_malleable();
++	if (ret != 1) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++
++	if (enabled) {
++		ret = cfg_versions_add(&system_wide_config, version);
++	} else {
++		ret = cfg_versions_remove(&system_wide_config, version);
++	}
++
++	(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++	return ret;
++#else
++	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
++#endif
++}
++
++/**
++ * gnutls_ecc_curve_set_enabled:
++ * @curve: is an ECC curve
++ * @enabled: whether to enable the curve
++ *
++ * Modify the previous system wide setting that marked @curve as
++ * enabled or disabled.  This only has effect when the curve is
++ * enabled through the allowlisting mode in the configuration file, or
++ * when the setting is modified with a prior call to this function.
++ *
++ * Returns: 0 on success or negative error code otherwise.
++ *
++ * Since: 3.7.3
++ */
++int
++gnutls_ecc_curve_set_enabled(gnutls_ecc_curve_t curve, unsigned int enabled)
++{
++#ifndef DISABLE_SYSTEM_CONFIG
++	int ret;
++	ret = gnutls_rwlock_wrlock(&system_wide_config_rwlock);
++	if (ret < 0) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++	ret = system_wide_config_is_malleable();
++	if (ret != 1) {
++		(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++		return gnutls_assert_val(ret);
++	}
++
++	if (enabled) {
++		ret = cfg_ecc_curves_add(&system_wide_config, curve);
++	} else {
++		ret = cfg_ecc_curves_remove(&system_wide_config, curve);
++	}
++
++	(void)gnutls_rwlock_unlock(&system_wide_config_rwlock);
++	return ret;
++#else
++	return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
++#endif
++}
+diff --git a/tests/protocol-set-allowlist.c b/tests/protocol-set-allowlist.c
+index 754e4d12d1..744d70b315 100644
+--- a/tests/protocol-set-allowlist.c
++++ b/tests/protocol-set-allowlist.c
+@@ -37,15 +37,21 @@
+  * This is not a test by itself.
+  * This is a helper for the real test in protocol-set-allowlist.sh.
+  * It executes sequences of commands like:
+- *     > connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+- *     > protocol_set_disabled TLS1.2 -> OK
+- *     > connect -> bad priority: (actually, any arrow-less text can go here)
++ *     > protocol_set_disabled TLS1.2
++ *     > protocol_set_enabled TLS1.1
++ *     > connect
++ *     > protocol_set_enabled TLS1.2
++ *     > protocol_set_disabled TLS1.1
++ *     > connect -> connection established
+  * where `connect` connects to $TEST_SERVER_PORT using $TEST_SERVER_CA,
+  * and gnutls_protocol_set_enabled simply call the underlying API.
+  * leaving the outer test to check return code and output:
+- *     connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+  *     protocol_set_disabled TLS1.2 -> OK
+- *     connect -> bad priority: No or insufficient priorities were set.
++ *     protocol_set_enabled TLS1.1 -> OK
++ *     connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
++ *     protocol_set_enabled TLS1.2 -> INVALID_REQUEST
++ *     protocol_set_disabled TLS1.1 -> INVALID_REQUEST
++ *     connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
+  */
+ 
+ #define _assert(cond, format, ...) if (!(cond)) \
+@@ -58,6 +64,7 @@ void test_echo_server(gnutls_session_t session);
+ void cmd_connect(const char* ca_file, unsigned port);
+ void cmd_protocol_set_disabled(const char* name);
+ void cmd_protocol_set_enabled(const char* name);
++void cmd_reinit(void);
+ const char* unprefix(const char* s, const char* prefix);
+ 
+ 
+@@ -167,15 +174,32 @@ void cmd_connect(const char* ca_file, unsigned port)
+ 
+ void cmd_protocol_set_disabled(const char* name)
+ {
+-	_check(gnutls_protocol_set_enabled(parse_protocol(name), 0) >= 0);
+-	printf("protocol_set_disabled %s -> OK\n", name);
++	int ret;
++	ret = gnutls_protocol_set_enabled(parse_protocol(name), 0);
++	printf("protocol_set_disabled %s -> %s\n", name,
++			ret == 0 ? "OK" :
++			ret == GNUTLS_E_INVALID_REQUEST ? "INVALID_REQUEST" :
++			gnutls_strerror(ret));
+ }
+ 
+ 
+ void cmd_protocol_set_enabled(const char* name)
+ {
+-	_check(gnutls_protocol_set_enabled(parse_protocol(name), 1) >= 0);
+-	printf("protocol_set_enabled %s -> OK\n", name);
++	int ret;
++	ret = gnutls_protocol_set_enabled(parse_protocol(name), 1);
++	printf("protocol_set_enabled %s -> %s\n", name,
++			ret == 0 ? "OK" :
++			ret == GNUTLS_E_INVALID_REQUEST ? "INVALID_REQUEST" :
++			gnutls_strerror(ret));
++}
++
++
++void cmd_reinit(void)
++{
++	int ret;
++	gnutls_global_deinit();
++	ret = gnutls_global_init();
++	printf("reinit -> %s\n", ret == 0 ? "OK" : gnutls_strerror(ret));
+ }
+ 
+ 
+@@ -204,8 +228,6 @@ void doit(void)
+ 	_assert(port_str, "TEST_SERVER_PORT is not set");
+ 	port = parse_port(port_str);
+ 
+-	_check(gnutls_global_init() >= 0);
+-
+ 	while (!feof(stdin)) {
+ 		memset(cmd_buf, '\0', MAX_CMD_LEN + 1);
+ 		fgets(cmd_buf, MAX_CMD_LEN, stdin);
+@@ -220,6 +242,8 @@ void doit(void)
+ 			cmd_protocol_set_disabled(p);
+ 		else if ((p = unprefix(cmd_buf, "> protocol_set_enabled ")))
+ 			cmd_protocol_set_enabled(p);
++		else if (!strcmp(cmd_buf, "> reinit"))
++			cmd_reinit();
+ 		else if ((p = unprefix(cmd_buf, "> ")))
+ 			_fail("Unknown command `%s`\n", p);
+ 		else
+@@ -227,6 +251,5 @@ void doit(void)
+ 					cmd_buf);
+ 	}
+ 
+-	gnutls_global_deinit();
+ 	exit(0);
+ }
+diff --git a/tests/protocol-set-allowlist.sh b/tests/protocol-set-allowlist.sh
+index 907f37562f..ee2fe649bd 100755
+--- a/tests/protocol-set-allowlist.sh
++++ b/tests/protocol-set-allowlist.sh
+@@ -25,6 +25,7 @@
+ # from within the shell wrapper protocol-set-allowlist.sh
+ # The shell part of it feeds commands into a C helper
+ # and compares its output to the reference output.
++# Commands are derived from the reference output.
+ 
+ : ${srcdir=.}
+ : ${builddir=.}
+@@ -161,6 +162,9 @@ fi
+ ### Harness for the actual tests
+ 
+ test_with_helper() {
++	echo '#'
++	echo "# $1"
++	echo '#'
+ 	${CAT} > "$TMPFILE_EXPECTED_LOG"
+ 	${SED} 's/\(.*\) -> .*/> \1/' "${TMPFILE_EXPECTED_LOG}" \
+ 		> "${TMPFILE_INPUT_SCRIPT}"
+@@ -197,152 +201,234 @@ launch_server --echo --priority "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2" \
+ SERVER_PID=$!
+ wait_server ${SERVER_PID}
+ 
+-# ["gnutls_protocol_set_enabled can disable, TLS"]
+-# With a configuration file allowlisting a specific TLS protocol version (1.2),
+-# gnutls_protocol_set_enabled can disable it.
+-test_with_helper <<EOF
++test_with_helper 'connects by default with 1.2' <<EOF
+ connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-protocol_set_disabled TLS1.2 -> OK
+-connect -> bad priority: No or insufficient priorities were set.
+ EOF
+ 
+-# ["gnutls_protocol_set_enabled disables revertibly, TLS"]
+-# consecutive gnutls_protocol_set_enabled can make connection possible
+-# (with a different session handle).
+-test_with_helper <<EOF
++test_with_helper 'connecting prevents new API from working' <<EOF
+ connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-protocol_set_disabled TLS1.2 -> OK
+-connect -> bad priority: No or insufficient priorities were set.
+-protocol_set_enabled TLS1.2 -> OK
++protocol_set_disabled TLS1.2 -> INVALID_REQUEST
+ connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+ EOF
+ 
+-# Just a random long-ish scenario
+-test_with_helper <<EOF
+-connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++test_with_helper 'disabling TLS 1.2 leaves us with no versions' <<EOF
+ protocol_set_disabled TLS1.2 -> OK
+ connect -> bad priority: No or insufficient priorities were set.
+-protocol_set_enabled TLS1.3 -> OK
++protocol_set_enabled TLS1.2 -> INVALID_REQUEST
+ connect -> bad priority: No or insufficient priorities were set.
+-protocol_set_disabled TLS1.3 -> OK
++EOF
++
++test_with_helper \
++	'disabling is revertible if done before the first gnutls_init' << EOF
++protocol_set_disabled TLS1.2 -> OK
+ protocol_set_enabled TLS1.2 -> OK
+ connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++protocol_set_disabled TLS1.2 -> INVALID_REQUEST
++protocol_set_enabled TLS1.2 -> INVALID_REQUEST
++connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+ EOF
+ 
+-# !!! CURRENTLY NOT WORKING AS EXPECTED !!!
+-# Insufficient priority vs handshake failed
+-#test_with_helper <<EOF
++# Reinit after restricting algorithms has problems with FIPS self-tests
++#test_with_helper 'library reinitialization resets changes' <<EOF
++#protocol_set_disabled TLS1.2 -> OK
++#connect -> bad priority: No or insufficient priorities were set.
++#reinit -> OK
++#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++#EOF
++
++# Reinit after restricting algorithms has problems with FIPS self-tests
++#test_with_helper \
++#	'library reinitialization allows new API again, but resets changes' \
++#	<<EOF
+ #protocol_set_disabled TLS1.2 -> OK
+ #connect -> bad priority: No or insufficient priorities were set.
+-#protocol_set_enabled TLS1.3 -> OK
+-#connect -> handshake failed: A packet with illegal or unsupported version was received.
++#protocol_set_enabled TLS1.2 -> INVALID_REQUEST
++#connect -> bad priority: No or insufficient priorities were set.
++#reinit -> OK
++#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++#protocol_set_disabled TLS1.2 -> INVALID_REQUEST
++#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++#reinit -> OK
++#protocol_set_disabled TLS1.2 -> OK
++#protocol_set_enabled TLS1.2 -> OK
++#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++#protocol_set_disabled TLS1.2 -> INVALID_REQUEST
+ #EOF
+ 
++test_with_helper 'Insufficient priority vs handshake failed: 1/2' <<EOF
++protocol_set_disabled TLS1.2 -> OK
++connect -> bad priority: No or insufficient priorities were set.
++EOF
++
++test_with_helper 'Insufficient priority vs handshake failed: 2/2' <<EOF
++protocol_set_disabled TLS1.2 -> OK
++protocol_set_enabled TLS1.3 -> OK
++connect -> handshake failed: A TLS fatal alert has been received.
++EOF
++# TLS 1.3 does some masquerading as TLS 1.2, I guess, so it's not
++# handshake failed: A packet with illegal or unsupported version was received.
++
+ terminate_proc ${SERVER_PID}
+ 
+ ### Tests against a NORMAL server (all three TLS versions enabled)
+ 
+ eval "${GETPORT}"
+ # server is launched without allowlisting config file in effect
+-launch_server -d9 --echo --priority NORMAL \
++launch_server --echo --priority NORMAL \
+ 	--x509keyfile "${TMPFILE_KEY}" --x509certfile "${TMPFILE_CERT}"
+ SERVER_PID=$!
+ wait_server ${SERVER_PID}
+ 
+-# !!! CURRENTLY NOT WORKING AS EXPECTED !!!
+-# smoke-test enabling with protocol_set
+-#test_with_helper <<EOF
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#protocol_set_enabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
+-#EOF
++# sanity-test
++test_with_helper 'sanity test against liberal server' <<EOF
++connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++EOF
+ 
+-# !!! CURRENTLY NOT WORKING AS EXPECTED !!!
+-# ["gnutls_protocol_set_enabled enables, TLS"]
+-# with a configuration file not allowlisting a specific TLS protocol version,
+-# enabling that version with gnutls_protocol_set_enabled
+-# allows connecting to a server accepting this TLS protocol version alone
+-#test_with_helper <<EOF
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#protocol_set_enabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
+-#EOF
++test_with_helper 'smoke-test enabling' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
++EOF
+ 
+-# !!! CURRENTLY NOT WORKING AS EXPECTED !!!
+-# ["gnutls_protocol_set_enabled enables revertibly, TLS"]
+-# consecutive gnutls_protocol_set
+-# can prevent the client from connecting (with a different session handle)
+-#test_with_helper <<EOF
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#protocol_set_enabled TLS1.1 -> OK
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#protocol_set_disabled TLS1.2 -> OK
+-#connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
+-#protocol_set_disabled TLS1.1 -> OK
+-#connect -> bad priority: No or insufficient priorities were set.
+-#EOF
+-# Alternative one
+-#test_with_helper <<EOF
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#protocol_set_enabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
+-#protocol_set_disabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#EOF
++test_with_helper 'going down to TLS1.1' <<EOF
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_disabled TLS1.2 -> OK
++connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
++EOF
+ 
+-# !!! CURRENTLY NOT WORKING AS EXPECTED !!!
+-# ["gnutls_protocol_set_disabled disables selectively, TLS"]
+-# gnutls_protocol_set_disabled with a specific version
+-# doesn't disable other previously enabled version.
+-# ["gnutls_protocol_set_enabled enables selectively, TLS"]
+-# gnutls_protocol_set_enabled enabling a specific version
+-# doesn't enable other previously disabled version.
+-#test_with_helper <<EOF
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#protocol_set_enabled TLS1.3 -> OK
+-#protocol_set_enabled TLS1.2 -> OK
+-#protocol_set_enabled TLS1.1 -> OK
+-#connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
+-#protocol_set_disabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
+-#protocol_set_disabled TLS1.2 -> OK
+-#connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
+-#protocol_set_disabled TLS1.1 -> OK
+-#connect -> bad priority: No or insufficient priorities were set.
+-#protocol_set_enabled TLS1.1 -> OK
+-#connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
+-#protocol_set_enabled TLS1.2 -> OK
+-#connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
+-#protocol_set_enabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
+-#EOF
++test_with_helper 'going up to TLS 1.3' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
++EOF
++
++test_with_helper 'useless toggles' <<EOF
++protocol_set_disabled TLS1.2 -> OK
++protocol_set_disabled TLS1.2 -> OK
++protocol_set_enabled TLS1.2 -> OK
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_enabled TLS1.3 -> OK
++protocol_set_disabled TLS1.1 -> OK
++protocol_set_disabled TLS1.3 -> OK
++connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++EOF
++
++test_with_helper 'disable does not overdisable: 1/2' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++protocol_set_enabled TLS1.2 -> OK
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_disabled TLS1.3 -> OK
++protocol_set_disabled TLS1.1 -> OK
++connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++EOF
++
++test_with_helper 'disable does not overdisable: 2/2' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++protocol_set_enabled TLS1.2 -> OK
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_disabled TLS1.3 -> OK
++protocol_set_disabled TLS1.2 -> OK
++connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
++EOF
+ 
+ terminate_proc ${SERVER_PID}
+ 
+-### Tests against a TLS 1.1 & 1.3 server (1.2 disabled)
++#### Tests against a TLS 1.3 server
++#
++eval "${GETPORT}"
++# server is launched without allowlisting config file in effect
++launch_server --echo \
++	--priority "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3" \
++	--x509keyfile "${TMPFILE_KEY}" --x509certfile "${TMPFILE_CERT}"
++SERVER_PID=$!
++wait_server ${SERVER_PID}
++
++test_with_helper 'sanity negative' <<EOF
++connect -> handshake failed: A TLS fatal alert has been received.
++protocol_set_enabled TLS1.3 -> INVALID_REQUEST
++protocol_set_enabled TLS1.1 -> INVALID_REQUEST
++protocol_set_disabled TLS1.2 -> INVALID_REQUEST
++connect -> handshake failed: A TLS fatal alert has been received.
++EOF
++
++test_with_helper 'enable 1.3' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
++EOF
++
++test_with_helper 'enable 1.3 only' <<EOF
++protocol_set_disabled TLS1.2 -> OK
++protocol_set_enabled TLS1.3 -> OK
++connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
++EOF
++
++test_with_helper 'enable 1.1' <<EOF
++protocol_set_enabled TLS1.1 -> OK
++connect -> handshake failed: A TLS fatal alert has been received.
++EOF
+ 
++# A special case according to a comment in set_ciphersuite_list:
++# > we require TLS1.2 to be enabled if TLS1.3 is asked for, and
++# > a pre-TLS1.2 protocol is there; that is because servers which
++# > do not support TLS1.3 will negotiate TLS1.2 if seen a TLS1.3 handshake
++test_with_helper 'enable 1.1 and 1.3 only - does not work as you expect' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++protocol_set_disabled TLS1.2 -> OK
++protocol_set_enabled TLS1.1 -> OK
++connect -> handshake failed: A packet with illegal or unsupported version was received.
++EOF
++
++test_with_helper 'enable 1.1 and 1.3' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++protocol_set_enabled TLS1.1 -> OK
++connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
++EOF
++
++test_with_helper 'enable 1.1 and 1.3, different order' <<EOF
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_enabled TLS1.3 -> OK
++connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
++EOF
++
++terminate_proc ${SERVER_PID}
++
++#### Tests against a TLS 1.1 + TLS 1.2 server
++#
+ eval "${GETPORT}"
+ # server is launched without allowlisting config file in effect
+-launch_server -d9 --echo \
+-	--priority "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.1:+VERS-TLS1.3" \
++launch_server --echo \
++	--priority "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.1:+VERS-TLS1.2" \
+ 	--x509keyfile "${TMPFILE_KEY}" --x509certfile "${TMPFILE_CERT}"
+ SERVER_PID=$!
+ wait_server ${SERVER_PID}
+ 
+-# !!! CURRENTLY NOT WORKING AS EXPECTED !!!
+-#test_with_helper <<EOF
+-#connect -> handshake failed: A packet with illegal or unsupported version was received.
+-#protocol_set_enabled TLS1.1 -> OK
+-#connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
+-#protocol_set_enabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)
+-#protocol_set_disabled TLS1.3 -> OK
+-#connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
+-#protocol_set_disabled TLS1.1 -> OK
+-#connect -> handshake failed: A packet with illegal or unsupported version was received.
+-#protocol_set_disabled TLS1.2 -> OK
+-#connect -> bad priority: No or insufficient priorities were set.
+-#EOF
++test_with_helper 'sanity 1.2' <<EOF
++connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++EOF
++
++test_with_helper 'enable 1.1' <<EOF
++protocol_set_enabled TLS1.1 -> OK
++connect -> connection established: (TLS1.2)-(RSA)-(AES-128-GCM)
++EOF
++
++test_with_helper 'enable 1.1 only' <<EOF
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_disabled TLS1.2 -> OK
++connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
++EOF
++
++test_with_helper 'enable 1.1 and 1.3 only' <<EOF
++protocol_set_enabled TLS1.3 -> OK
++protocol_set_disabled TLS1.2 -> OK
++protocol_set_enabled TLS1.1 -> OK
++connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
++EOF
++
++test_with_helper 'enable 1.1 and 1.3 only, different order' <<EOF
++protocol_set_enabled TLS1.1 -> OK
++protocol_set_disabled TLS1.2 -> OK
++protocol_set_enabled TLS1.3 -> OK
++connect -> connection established: (TLS1.1)-(RSA)-(AES-128-CBC)-(SHA1)
++EOF
+ 
+ terminate_proc ${SERVER_PID}
+ 
+-- 
+2.34.1
+
+
+From ebcf38f6ac4dce0fe62bdd43114cb7415eee8d16 Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Wed, 16 Feb 2022 14:36:48 +0100
+Subject: [PATCH 8/8] update documentation on allowlisting API
+
+(in a separate commit so that it's easier to compare)
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/priority.c | 69 +++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 54 insertions(+), 15 deletions(-)
+
+diff --git a/lib/priority.c b/lib/priority.c
+index c187284024..4e9d2d9cc4 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -3466,14 +3466,25 @@ gnutls_digest_set_secure(gnutls_digest_algorithm_t dig, unsigned int secure)
+  * @secure: whether to mark the sign algorithm secure
+  *
+  * Modify the previous system wide setting that marked @sign as secure
+- * or insecure.  This only has effect when the algorithm is marked as
+- * secure through the allowlisting mode in the configuration file, or
+- * when the setting is modified with a prior call to this function.
++ * or insecure.  Calling this function is allowed
++ * only if allowlisting mode is set in the configuration file,
++ * and only if the system-wide TLS priority string
++ * has not been initialized yet.
++ * The intended usage is to provide applications with a way
++ * to expressly deviate from the distribution or site defaults
++ * inherited from the configuration file.
++ * The modification is composable with further modifications
++ * performed through the priority string mechanism.
++ *
++ * This function is not thread-safe and is intended to be called
++ * in the main thread at the beginning of the process execution.
+  *
+  * Even when @secure is true, @sign is not marked as secure for the
+  * use in certificates.  Use gnutls_sign_set_secure_for_certs() to
+  * mark it secure as well for certificates.
+  *
++ * Returns: 0 on success or negative error code otherwise.
++ *
+  * Since: 3.7.3
+  */
+ int
+@@ -3517,16 +3528,26 @@ gnutls_sign_set_secure(gnutls_sign_algorithm_t sign, unsigned int secure)
+  * @secure: whether to mark the sign algorithm secure for certificates
+  *
+  * Modify the previous system wide setting that marked @sign as secure
+- * or insecure for the use in certificates. This only has effect when
+- * the algorithm is marked as secure through the allowlisting mode in
+- * the configuration file, or when the setting is modified with a
+- * prior call to this function.
+- *
++ * or insecure for the use in certificates.  Calling this fuction is allowed
++ * only if allowlisting mode is set in the configuration file,
++ * and only if the system-wide TLS priority string
++ * has not been initialized yet.
++ * The intended usage is to provide applications with a way
++ * to expressly deviate from the distribution or site defaults
++ * inherited from the configuration file.
++ * The modification is composable with further modifications
++ * performed through the priority string mechanism.
++ *
++ * This function is not thread-safe and is intended to be called
++ * in the main thread at the beginning of the process execution.
++
+  * When @secure is true, @sign is marked as secure for any use unlike
+  * gnutls_sign_set_secure().  Otherwise, it is marked as insecure only
+  * for the use in certificates.  Use gnutls_sign_set_secure() to mark
+  * it insecure for any uses.
+  *
++ * Returns: 0 on success or negative error code otherwise.
++ *
+  * Since: 3.7.3
+  */
+ int
+@@ -3570,10 +3591,19 @@ gnutls_sign_set_secure_for_certs(gnutls_sign_algorithm_t sign,
+  * @version: is a (gnutls) version number
+  * @enabled: whether to enable the protocol
+  *
+- * Mark the previous system wide setting that marked @version as
+- * enabled or disabled. This only has effect when the version is
+- * enabled through the allowlisting mode in the configuration file, or
+- * when the setting is modified with a prior call to this function.
++ * Control the previous system-wide setting that marked @version as
++ * enabled or disabled.  Calling this fuction is allowed
++ * only if allowlisting mode is set in the configuration file,
++ * and only if the system-wide TLS priority string
++ * has not been initialized yet.
++ * The intended usage is to provide applications with a way
++ * to expressly deviate from the distribution or site defaults
++ * inherited from the configuration file.
++ * The modification is composable with further modifications
++ * performed through the priority string mechanism.
++ *
++ * This function is not thread-safe and is intended to be called
++ * in the main thread at the beginning of the process execution.
+  *
+  * Returns: 0 on success or negative error code otherwise.
+  *
+@@ -3614,9 +3644,18 @@ gnutls_protocol_set_enabled(gnutls_protocol_t version, unsigned int enabled)
+  * @enabled: whether to enable the curve
+  *
+  * Modify the previous system wide setting that marked @curve as
+- * enabled or disabled.  This only has effect when the curve is
+- * enabled through the allowlisting mode in the configuration file, or
+- * when the setting is modified with a prior call to this function.
++ * enabled or disabled.  Calling this fuction is allowed
++ * only if allowlisting mode is set in the configuration file,
++ * and only if the system-wide TLS priority string
++ * has not been initialized yet.
++ * The intended usage is to provide applications with a way
++ * to expressly deviate from the distribution or site defaults
++ * inherited from the configuration file.
++ * The modification is composable with further modifications
++ * performed through the priority string mechanism.
++ *
++ * This function is not thread-safe and is intended to be called
++ * in the main thread at the beginning of the process execution.
+  *
+  * Returns: 0 on success or negative error code otherwise.
+  *
+-- 
+2.34.1
+
diff --git a/SOURCES/gnutls-3.7.3-disable-config-reload.patch b/SOURCES/gnutls-3.7.3-disable-config-reload.patch
index 2fc5c35..29d806a 100644
--- a/SOURCES/gnutls-3.7.3-disable-config-reload.patch
+++ b/SOURCES/gnutls-3.7.3-disable-config-reload.patch
@@ -1,19 +1,26 @@
-diff --color -ru a/lib/priority.c b/lib/priority.c
---- a/lib/priority.c	2022-01-14 07:53:21.000000000 +0100
-+++ b/lib/priority.c	2022-02-15 09:31:36.388485784 +0100
-@@ -2030,15 +2030,6 @@
+diff --git a/lib/priority.c b/lib/priority.c
+index 9feec47fe2..40511710fd 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -2001,13 +2001,14 @@ char *_gnutls_resolve_priorities(const char* priorities)
  		additional++;
  	}
  
 -	/* Always try to refresh the cached data, to allow it to be
 -	 * updated without restarting all applications.
 -	 */
--	ret = _gnutls_update_system_priorities();
+-	ret = _gnutls_update_system_priorities(false /* defer_system_wide */);
 -	if (ret < 0) {
 -		_gnutls_debug_log("failed to update system priorities: %s\n",
 -				  gnutls_strerror(ret));
--	}
--
++	/* If priority string is not constructed yet, construct and finalize */
++	if (!system_wide_config.priority_string) {
++		ret = _gnutls_update_system_priorities(false
++		                                       /* defer_system_wide */);
++		if (ret < 0) {
++			_gnutls_debug_log("failed to update system priorities: "
++			                  " %s\n", gnutls_strerror(ret));
++		}
+ 	}
+ 
  	do {
- 		ss_next = strchr(ss, ',');
- 		if (ss_next) {
diff --git a/SOURCES/gnutls-3.7.3-gost-ifdef.patch b/SOURCES/gnutls-3.7.3-gost-ifdef.patch
new file mode 100644
index 0000000..068f41a
--- /dev/null
+++ b/SOURCES/gnutls-3.7.3-gost-ifdef.patch
@@ -0,0 +1,259 @@
+From 85a881cbca6f8e8578af7a026163ac3075ea267c Mon Sep 17 00:00:00 2001
+From: Daiki Ueno <ueno@gnu.org>
+Date: Mon, 21 Feb 2022 16:28:49 +0100
+Subject: [PATCH] priority: compile out GOST algorithms IDs if they are
+ disabled
+
+When compiled with --disable-gost, gnutls-cli --priority NORMAL --list
+still prints GOST algorithms for ciphers, MACs, and signatures.  This
+change adds compile time checks to suppress them.
+
+Signed-off-by: Daiki Ueno <ueno@gnu.org>
+---
+ lib/priority.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/lib/priority.c b/lib/priority.c
+index 54d7b1bb45..0c7ac65d7b 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -309,7 +309,9 @@ static const int _kx_priority_secure[] = {
+ static const int* kx_priority_secure = _kx_priority_secure;
+ 
+ static const int _kx_priority_gost[] = {
++#ifdef ENABLE_GOST
+ 	GNUTLS_KX_VKO_GOST_12,
++#endif
+ 	0,
+ };
+ static const int* kx_priority_gost = _kx_priority_gost;
+@@ -507,9 +509,10 @@ static const int _sign_priority_secure192[] = {
+ static const int* sign_priority_secure192 = _sign_priority_secure192;
+ 
+ static const int _sign_priority_gost[] = {
++#ifdef ENABLE_GOST
+ 	GNUTLS_SIGN_GOST_256,
+ 	GNUTLS_SIGN_GOST_512,
+-
++#endif
+ 	0
+ };
+ static const int* sign_priority_gost = _sign_priority_gost;
+@@ -531,13 +534,17 @@ static const int *cipher_priority_normal = _cipher_priority_normal_default;
+ static const int *mac_priority_normal = mac_priority_normal_default;
+ 
+ static const int _cipher_priority_gost[] = {
++#ifdef ENABLE_GOST
+ 	GNUTLS_CIPHER_GOST28147_TC26Z_CNT,
++#endif
+ 	0
+ };
+ static const int *cipher_priority_gost = _cipher_priority_gost;
+ 
+ static const int _mac_priority_gost[] = {
++#ifdef ENABLE_GOST
+ 	GNUTLS_MAC_GOST28147_TC26Z_IMIT,
++#endif
+ 	0
+ };
+ static const int *mac_priority_gost = _mac_priority_gost;
+-- 
+2.34.1
+
+From f2bb30f68922d72f8bed29cc8b2a065087a0969f Mon Sep 17 00:00:00 2001
+From: Daiki Ueno <ueno@gnu.org>
+Date: Tue, 22 Feb 2022 17:09:46 +0100
+Subject: [PATCH] algorithms: ensure _list() exclude non-existing algorithms
+
+This aligns the behavior of _list() function for sign/pk to the one
+for cipher/mac: the former previously returned all the algorithms
+defined, while the latter returns only algorithms compiled in.
+
+Signed-off-by: Daiki Ueno <ueno@gnu.org>
+---
+ lib/algorithms/publickey.c |  8 +++-
+ lib/algorithms/sign.c      |  4 +-
+ lib/crypto-backend.h       |  2 +
+ lib/nettle/pk.c            | 86 ++++++++++++++++++++++++++++++++++++++
+ lib/pk.h                   |  2 +
+ 5 files changed, 99 insertions(+), 3 deletions(-)
+
+diff --git a/lib/algorithms/publickey.c b/lib/algorithms/publickey.c
+index b4cd6b1df0..caf53972ab 100644
+--- a/lib/algorithms/publickey.c
++++ b/lib/algorithms/publickey.c
+@@ -24,6 +24,7 @@
+ #include <algorithms.h>
+ #include "errors.h"
+ #include <x509/common.h>
++#include "pk.h"
+ 
+ 
+ /* KX mappings to PK algorithms */
+@@ -203,8 +204,11 @@ const gnutls_pk_algorithm_t *gnutls_pk_list(void)
+ 		int i = 0;
+ 
+ 		GNUTLS_PK_LOOP(
+-			if (p->id != GNUTLS_PK_UNKNOWN && supported_pks[i > 0 ? (i - 1) : 0] != p->id)
+-				supported_pks[i++] = p->id
++			if (p->id != GNUTLS_PK_UNKNOWN &&
++			    supported_pks[i > 0 ? (i - 1) : 0] != p->id &&
++			    _gnutls_pk_exists(p->id)) {
++				supported_pks[i++] = p->id;
++			}
+ 		);
+ 		supported_pks[i++] = 0;
+ 	}
+diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c
+index 06abdb4cf8..4a5aaa75e1 100644
+--- a/lib/algorithms/sign.c
++++ b/lib/algorithms/sign.c
+@@ -27,6 +27,7 @@
+ #include <x509/common.h>
+ #include <assert.h>
+ #include "c-strcase.h"
++#include "pk.h"
+ 
+ /* signature algorithms;
+  */
+@@ -631,7 +632,8 @@ const gnutls_sign_algorithm_t *gnutls_sign_list(void)
+ 
+ 		GNUTLS_SIGN_LOOP(
+ 			/* list all algorithms, but not duplicates */
+-			if (supported_sign[i] != p->id) {
++			if (supported_sign[i] != p->id &&
++			    _gnutls_pk_sign_exists(p->id)) {
+ 				assert(i+1 < MAX_ALGOS);
+ 				supported_sign[i++] = p->id;
+ 				supported_sign[i+1] = 0;
+diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h
+index 9874033221..f0f68c337e 100644
+--- a/lib/crypto-backend.h
++++ b/lib/crypto-backend.h
+@@ -418,6 +418,8 @@ typedef struct gnutls_crypto_pk {
+ 		       unsigned int flags);
+ 
+ 	int (*curve_exists) (gnutls_ecc_curve_t);	/* true/false */
++	int (*pk_exists) (gnutls_pk_algorithm_t);	/* true/false */
++	int (*sign_exists) (gnutls_sign_algorithm_t);	/* true/false */
+ } gnutls_crypto_pk_st;
+ 
+ /* priority: infinity for backend algorithms, 90 for kernel
+diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c
+index a146568266..eba246f0b3 100644
+--- a/lib/nettle/pk.c
++++ b/lib/nettle/pk.c
+@@ -1883,6 +1883,90 @@ static int _wrap_nettle_pk_curve_exists(gnutls_ecc_curve_t curve)
+ 	}
+ }
+ 
++static int _wrap_nettle_pk_exists(gnutls_pk_algorithm_t pk)
++{
++	switch (pk) {
++	case GNUTLS_PK_RSA:
++	case GNUTLS_PK_DSA:
++	case GNUTLS_PK_DH:
++	case GNUTLS_PK_ECDSA:
++	case GNUTLS_PK_ECDH_X25519:
++	case GNUTLS_PK_RSA_PSS:
++	case GNUTLS_PK_EDDSA_ED25519:
++#if ENABLE_GOST
++	case GNUTLS_PK_GOST_01:
++	case GNUTLS_PK_GOST_12_256:
++	case GNUTLS_PK_GOST_12_512:
++#endif
++	case GNUTLS_PK_ECDH_X448:
++	case GNUTLS_PK_EDDSA_ED448:
++		return 1;
++	default:
++		return 0;
++	}
++}
++
++static int _wrap_nettle_pk_sign_exists(gnutls_sign_algorithm_t sign)
++{
++	switch (sign) {
++	case GNUTLS_SIGN_RSA_SHA1:
++	case GNUTLS_SIGN_DSA_SHA1:
++	case GNUTLS_SIGN_RSA_MD5:
++	case GNUTLS_SIGN_RSA_MD2:
++	case GNUTLS_SIGN_RSA_RMD160:
++	case GNUTLS_SIGN_RSA_SHA256:
++	case GNUTLS_SIGN_RSA_SHA384:
++	case GNUTLS_SIGN_RSA_SHA512:
++	case GNUTLS_SIGN_RSA_SHA224:
++	case GNUTLS_SIGN_DSA_SHA224:
++	case GNUTLS_SIGN_DSA_SHA256:
++	case GNUTLS_SIGN_ECDSA_SHA1:
++	case GNUTLS_SIGN_ECDSA_SHA224:
++	case GNUTLS_SIGN_ECDSA_SHA256:
++	case GNUTLS_SIGN_ECDSA_SHA384:
++	case GNUTLS_SIGN_ECDSA_SHA512:
++	case GNUTLS_SIGN_DSA_SHA384:
++	case GNUTLS_SIGN_DSA_SHA512:
++	case GNUTLS_SIGN_ECDSA_SHA3_224:
++	case GNUTLS_SIGN_ECDSA_SHA3_256:
++	case GNUTLS_SIGN_ECDSA_SHA3_384:
++	case GNUTLS_SIGN_ECDSA_SHA3_512:
++
++	case GNUTLS_SIGN_DSA_SHA3_224:
++	case GNUTLS_SIGN_DSA_SHA3_256:
++	case GNUTLS_SIGN_DSA_SHA3_384:
++	case GNUTLS_SIGN_DSA_SHA3_512:
++	case GNUTLS_SIGN_RSA_SHA3_224:
++	case GNUTLS_SIGN_RSA_SHA3_256:
++	case GNUTLS_SIGN_RSA_SHA3_384:
++	case GNUTLS_SIGN_RSA_SHA3_512:
++
++	case GNUTLS_SIGN_RSA_PSS_SHA256:
++	case GNUTLS_SIGN_RSA_PSS_SHA384:
++	case GNUTLS_SIGN_RSA_PSS_SHA512:
++	case GNUTLS_SIGN_EDDSA_ED25519:
++	case GNUTLS_SIGN_RSA_RAW:
++
++	case GNUTLS_SIGN_ECDSA_SECP256R1_SHA256:
++	case GNUTLS_SIGN_ECDSA_SECP384R1_SHA384:
++	case GNUTLS_SIGN_ECDSA_SECP521R1_SHA512:
++
++	case GNUTLS_SIGN_RSA_PSS_RSAE_SHA256:
++	case GNUTLS_SIGN_RSA_PSS_RSAE_SHA384:
++	case GNUTLS_SIGN_RSA_PSS_RSAE_SHA512:
++
++#if ENABLE_GOST
++	case GNUTLS_SIGN_GOST_94:
++	case GNUTLS_SIGN_GOST_256:
++	case GNUTLS_SIGN_GOST_512:
++#endif
++	case GNUTLS_SIGN_EDDSA_ED448:
++		return 1;
++	default:
++		return 0;
++	}
++}
++
+ /* Generates algorithm's parameters. That is:
+  *  For DSA: p, q, and g are generated.
+  *  For RSA: nothing
+@@ -3872,4 +3956,6 @@ gnutls_crypto_pk_st _gnutls_pk_ops = {
+ 	.pk_fixup_private_params = wrap_nettle_pk_fixup,
+ 	.derive = _wrap_nettle_pk_derive,
+ 	.curve_exists = _wrap_nettle_pk_curve_exists,
++	.pk_exists = _wrap_nettle_pk_exists,
++	.sign_exists = _wrap_nettle_pk_sign_exists
+ };
+diff --git a/lib/pk.h b/lib/pk.h
+index cc61e08cef..7f3c9995da 100644
+--- a/lib/pk.h
++++ b/lib/pk.h
+@@ -40,6 +40,8 @@ extern gnutls_crypto_pk_st _gnutls_pk_ops;
+ #define _gnutls_pk_generate_params( algo, bits, priv) _gnutls_pk_ops.generate_params( algo, bits, priv)
+ #define _gnutls_pk_hash_algorithm( pk, sig, params, hash) _gnutls_pk_ops.hash_algorithm(pk, sig, params, hash)
+ #define _gnutls_pk_curve_exists( curve) _gnutls_pk_ops.curve_exists(curve)
++#define _gnutls_pk_exists(algo) _gnutls_pk_ops.pk_exists(algo)
++#define _gnutls_pk_sign_exists(algo) _gnutls_pk_ops.sign_exists(algo)
+ 
+ inline static int
+ _gnutls_pk_fixup(gnutls_pk_algorithm_t algo, gnutls_direction_t direction,
+-- 
+2.34.1
+
diff --git a/SOURCES/gnutls-3.7.3-libtss2-dlopen.patch b/SOURCES/gnutls-3.7.3-libtss2-dlopen.patch
new file mode 100644
index 0000000..b4cf45b
--- /dev/null
+++ b/SOURCES/gnutls-3.7.3-libtss2-dlopen.patch
@@ -0,0 +1,719 @@
+From f5e5ab910b8b1d69f962ca033d1295c3e1e1e131 Mon Sep 17 00:00:00 2001
+From: Daiki Ueno <ueno@gnu.org>
+Date: Wed, 23 Feb 2022 19:48:52 +0100
+Subject: [PATCH] tpm2: dynamically load tss2 libraries as needed
+
+libtss2-esys links to OpenSSL or mbed TLS for cryptography, which may
+cause packaging issues.  This instead dlopen's tss2 libraries as
+needed so non-TPM applications continue working without loading
+multiple crypto libraries.
+
+Signed-off-by: Daiki Ueno <ueno@gnu.org>
+---
+ configure.ac        |  11 +-
+ lib/Makefile.am     |   6 +-
+ lib/tpm2.c          |   2 +-
+ lib/tpm2.h          |   2 +-
+ lib/tpm2_esys.c     | 273 ++++++++++++++++++++++++++++++++++++--------
+ tests/Makefile.am   |   3 +-
+ tests/sanity-lib.sh |  40 +++++++
+ tests/tpm2.sh       |  14 ++-
+ 8 files changed, 296 insertions(+), 55 deletions(-)
+ create mode 100644 tests/sanity-lib.sh
+
+diff --git a/configure.ac b/configure.ac
+index 53c3aefca1..721ff208f0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -882,6 +882,8 @@ AM_CONDITIONAL(P11KIT_0_23_11_API, $PKG_CONFIG --atleast-version=0.23.11 p11-kit
+ 
+ AM_CONDITIONAL(ENABLE_PKCS11, test "$with_p11_kit" != "no")
+ 
++need_ltlibdl=no
++
+ AC_ARG_WITH(tpm2,
+ 	AS_HELP_STRING([--without-tpm2],
+ 		[Disable TPM2 support.]),
+@@ -892,6 +894,7 @@ if test "$with_tpm2" != "no"; then
+ 	if test "$have_tpm2" = "yes"; then
+ 		tss2lib="tss2-esys tss2-mu tss2-tctildr"
+ 		AC_DEFINE([HAVE_TSS2], 1, [Have TSS2])
++		need_ltlibdl=yes
+ 	elif test "$with_tpm2" = "yes"; then
+ 		AC_MSG_ERROR([[
+ ***
+@@ -920,7 +923,8 @@ if test "$with_tpm" != "no"; then
+ 		   AC_SUBST([TSS_LIBS], [-ltspi])
+ 		   AC_SUBST([TSS_CFLAGS], [])
+ 		   AC_DEFINE([HAVE_TROUSERS], 1, [Enable TPM])
+-		   with_tpm=yes],
++		   with_tpm=yes,
++		   need_ltlibdl=yes],
+ 		  [AC_MSG_RESULT(no)
+ 		   AC_MSG_WARN([[
+ *** 
+@@ -957,6 +961,9 @@ fi
+ AC_DEFINE_UNQUOTED([TROUSERS_LIB], ["$ac_trousers_lib"], [the location of the trousers library])
+ AC_SUBST(TROUSERS_LIB)
+ 
++
++AM_CONDITIONAL(NEED_LTLIBDL, test "$need_ltlibdl" = yes)
++
+ # For minitasn1.
+ AC_CHECK_SIZEOF(unsigned long int, 4)
+ AC_CHECK_SIZEOF(unsigned int, 4)
+@@ -1312,7 +1319,7 @@ AC_MSG_NOTICE([External hardware support:
+   Random gen. variant:  $rnd_variant
+   PKCS#11 support:      $with_p11_kit
+   TPM support:          $with_tpm
+-  TPM2 support:         $have_tpm2
++  TPM2 support:         $with_tpm2
+   KTLS support:         $enable_ktls
+ ])
+ 
+diff --git a/lib/Makefile.am b/lib/Makefile.am
+index 35df35ee8d..e61ee1b6ae 100644
+--- a/lib/Makefile.am
++++ b/lib/Makefile.am
+@@ -44,7 +44,7 @@ AM_CPPFLAGS = \
+ 	-I$(srcdir)/x509			\
+ 	$(LIBTASN1_CFLAGS)			\
+ 	$(P11_KIT_CFLAGS)			\
+-	$(TPM2_CFLAGS)
++	$(TSS2_CFLAGS)
+ 
+ if !HAVE_LIBUNISTRING
+ SUBDIRS += unistring
+@@ -156,7 +156,7 @@ libgnutls_la_LIBADD = ../gl/libgnu.la x509/libgnutls_x509.la \
+ 	auth/libgnutls_auth.la algorithms/libgnutls_alg.la \
+ 	extras/libgnutls_extras.la
+ thirdparty_libadd = $(LTLIBZ) $(LTLIBINTL) $(LIBSOCKET) $(LTLIBNSL) \
+-	$(P11_KIT_LIBS) $(LIB_SELECT) $(TSS2_LIBS) $(GNUTLS_LIBS_PRIVATE)
++	$(P11_KIT_LIBS) $(LIB_SELECT) $(GNUTLS_LIBS_PRIVATE)
+ 
+ if HAVE_LIBIDN2
+ thirdparty_libadd += $(LIBIDN2_LIBS)
+@@ -203,7 +203,7 @@ all-local: $(hmac_files)
+ CLEANFILES = $(hmac_files)
+ endif
+ 
+-if ENABLE_TROUSERS
++if NEED_LTLIBDL
+ thirdparty_libadd += $(LTLIBDL)
+ endif
+ 
+diff --git a/lib/tpm2.c b/lib/tpm2.c
+index 076cc7f407..750eadc777 100644
+--- a/lib/tpm2.c
++++ b/lib/tpm2.c
+@@ -297,5 +297,5 @@ int _gnutls_load_tpm2_key(gnutls_privkey_t pkey, const gnutls_datum_t *fdata)
+ 
+ void _gnutls_tpm2_deinit(void)
+ {
+-	tpm2_tcti_deinit();
++	tpm2_esys_deinit();
+ }
+diff --git a/lib/tpm2.h b/lib/tpm2.h
+index e40dc01df7..7966e2d811 100644
+--- a/lib/tpm2.h
++++ b/lib/tpm2.h
+@@ -37,7 +37,7 @@ struct tpm2_info_st;
+ 
+ struct tpm2_info_st *tpm2_info_init(struct pin_info_st *pin);
+ 
+-void tpm2_tcti_deinit(void);
++void tpm2_esys_deinit(void);
+ 
+ void release_tpm2_ctx(struct tpm2_info_st *info);
+ 
+diff --git a/lib/tpm2_esys.c b/lib/tpm2_esys.c
+index 93e54413ba..4000c1b76e 100644
+--- a/lib/tpm2_esys.c
++++ b/lib/tpm2_esys.c
+@@ -72,6 +72,170 @@
+ #include <tss2/tss2_esys.h>
+ #include <tss2/tss2_tctildr.h>
+ 
++#include <dlfcn.h>
++
++/* We don't want to link to libtss2-esys, as it brings in other
++ * crypto libraries.  Instead, only dlopen it as needed.
++ */
++
++static void *_gnutls_tss2_esys_dlhandle;
++static void *_gnutls_tss2_mu_dlhandle;
++static void *_gnutls_tss2_tctildr_dlhandle;
++
++static TSS2_RC
++(*_gnutls_tss2_Esys_GetCapability)(ESYS_CONTEXT *esysContext,
++				   ESYS_TR shandle1,
++				   ESYS_TR shandle2,
++				   ESYS_TR shandle3,
++				   TPM2_CAP capability,
++				   UINT32 property,
++				   UINT32 propertyCount,
++				   TPMI_YES_NO *moreData,
++				   TPMS_CAPABILITY_DATA **capabilityData);
++static void (*_gnutls_tss2_Esys_Free)(void *__ptr);
++static TSS2_RC (*_gnutls_tss2_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext,
++					       ESYS_TR handle,
++					       TPM2B_AUTH const *authValue);
++static TSS2_RC
++(*_gnutls_tss2_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext,
++				   ESYS_TR primaryHandle,
++				   ESYS_TR shandle1,
++				   ESYS_TR shandle2,
++				   ESYS_TR shandle3,
++				   const TPM2B_SENSITIVE_CREATE *inSensitive,
++				   const TPM2B_PUBLIC *inPublic,
++				   const TPM2B_DATA *outsideInfo,
++				   const TPML_PCR_SELECTION *creationPCR,
++				   ESYS_TR *objectHandle,
++				   TPM2B_PUBLIC **outPublic,
++				   TPM2B_CREATION_DATA **creationData,
++				   TPM2B_DIGEST **creationHash,
++				   TPMT_TK_CREATION **creationTicket);
++static TSS2_RC (*_gnutls_tss2_Esys_Initialize)(ESYS_CONTEXT **esys_context,
++					       TSS2_TCTI_CONTEXT *tcti,
++					       TSS2_ABI_VERSION *abiVersion);
++static TSS2_RC (*_gnutls_tss2_Esys_Startup)(ESYS_CONTEXT *esysContext,
++					    TPM2_SU startupType);
++static TSS2_RC (*_gnutls_tss2_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext,
++						     TPM2_HANDLE tpm_handle,
++						     ESYS_TR optionalSession1,
++						     ESYS_TR optionalSession2,
++						     ESYS_TR optionalSession3,
++						     ESYS_TR *object);
++static TSS2_RC (*_gnutls_tss2_Esys_ReadPublic)(ESYS_CONTEXT *esysContext,
++					       ESYS_TR objectHandle,
++					       ESYS_TR shandle1,
++					       ESYS_TR shandle2,
++					       ESYS_TR shandle3,
++					       TPM2B_PUBLIC **outPublic,
++					       TPM2B_NAME **name,
++					       TPM2B_NAME **qualifiedName);
++static TSS2_RC (*_gnutls_tss2_Esys_Load)(ESYS_CONTEXT *esysContext,
++					 ESYS_TR parentHandle,
++					 ESYS_TR shandle1,
++					 ESYS_TR shandle2,
++					 ESYS_TR shandle3,
++					 const TPM2B_PRIVATE *inPrivate,
++					 const TPM2B_PUBLIC *inPublic,
++					 ESYS_TR *objectHandle);
++static TSS2_RC (*_gnutls_tss2_Esys_FlushContext)(ESYS_CONTEXT *esysContext,
++						 ESYS_TR flushHandle);
++static void (*_gnutls_tss2_Esys_Finalize)(ESYS_CONTEXT **context);
++static TSS2_RC
++(*_gnutls_tss2_Esys_RSA_Decrypt)(ESYS_CONTEXT *esysContext,
++				 ESYS_TR keyHandle,
++				 ESYS_TR shandle1,
++				 ESYS_TR shandle2,
++				 ESYS_TR shandle3,
++				 const TPM2B_PUBLIC_KEY_RSA *cipherText,
++				 const TPMT_RSA_DECRYPT *inScheme,
++				 const TPM2B_DATA *label,
++				 TPM2B_PUBLIC_KEY_RSA **message);
++static TSS2_RC (*_gnutls_tss2_Esys_Sign)(ESYS_CONTEXT *esysContext,
++					 ESYS_TR keyHandle,
++					 ESYS_TR shandle1,
++					 ESYS_TR shandle2,
++					 ESYS_TR shandle3,
++					 const TPM2B_DIGEST *digest,
++					 const TPMT_SIG_SCHEME *inScheme,
++					 const TPMT_TK_HASHCHECK *validation,
++					 TPMT_SIGNATURE **signature);
++
++static TSS2_RC
++(*_gnutls_tss2_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[],
++						size_t buffer_size,
++						size_t *offset,
++						TPM2B_PRIVATE *dest);
++static TSS2_RC
++(*_gnutls_tss2_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[],
++					       size_t buffer_size,
++					       size_t *offset,
++					       TPM2B_PUBLIC *dest);
++
++static TSS2_RC
++(*_gnutls_tss2_Tss2_TctiLdr_Initialize)(const char *nameConf,
++					TSS2_TCTI_CONTEXT **context);
++static void (*_gnutls_tss2_Tss2_TctiLdr_Finalize)(TSS2_TCTI_CONTEXT **context);
++
++#define DLSYM_TSS2(sys, sym)						\
++	_gnutls_tss2_##sym = dlsym(_gnutls_tss2_##sys##_dlhandle, #sym); \
++	if (!_gnutls_tss2_##sym) {					\
++		return -1;						\
++	}
++
++static int
++init_tss2_funcs(void)
++{
++	if (!_gnutls_tss2_esys_dlhandle) {
++		_gnutls_tss2_esys_dlhandle =
++			dlopen("libtss2-esys.so.0", RTLD_NOW | RTLD_GLOBAL);
++		if (!_gnutls_tss2_esys_dlhandle) {
++			_gnutls_debug_log("tpm2: unable to dlopen libtss2-esys\n");
++			return -1;
++		}
++	}
++
++	DLSYM_TSS2(esys, Esys_GetCapability)
++	DLSYM_TSS2(esys, Esys_Free)
++	DLSYM_TSS2(esys, Esys_TR_SetAuth)
++	DLSYM_TSS2(esys, Esys_CreatePrimary)
++	DLSYM_TSS2(esys, Esys_Initialize)
++	DLSYM_TSS2(esys, Esys_Startup)
++	DLSYM_TSS2(esys, Esys_TR_FromTPMPublic)
++	DLSYM_TSS2(esys, Esys_ReadPublic)
++	DLSYM_TSS2(esys, Esys_Load)
++	DLSYM_TSS2(esys, Esys_FlushContext)
++	DLSYM_TSS2(esys, Esys_Finalize)
++	DLSYM_TSS2(esys, Esys_RSA_Decrypt)
++	DLSYM_TSS2(esys, Esys_Sign)
++
++	if (!_gnutls_tss2_mu_dlhandle) {
++		_gnutls_tss2_mu_dlhandle =
++			dlopen("libtss2-mu.so.0", RTLD_NOW | RTLD_GLOBAL);
++		if (!_gnutls_tss2_mu_dlhandle) {
++			_gnutls_debug_log("tpm2: unable to dlopen libtss2-mu\n");
++			return -1;
++		}
++	}
++
++	DLSYM_TSS2(mu, Tss2_MU_TPM2B_PRIVATE_Unmarshal)
++	DLSYM_TSS2(mu, Tss2_MU_TPM2B_PUBLIC_Unmarshal)
++
++	if (!_gnutls_tss2_tctildr_dlhandle) {
++		_gnutls_tss2_tctildr_dlhandle =
++			dlopen("libtss2-tctildr.so.0", RTLD_NOW | RTLD_GLOBAL);
++		if (!_gnutls_tss2_tctildr_dlhandle) {
++			_gnutls_debug_log("tpm2: unable to dlopen libtss2-tctildr\n");
++			return -1;
++		}
++	}
++
++	DLSYM_TSS2(tctildr, Tss2_TctiLdr_Initialize)
++	DLSYM_TSS2(tctildr, Tss2_TctiLdr_Finalize)
++
++	return 0;
++}
++
+ struct tpm2_info_st {
+ 	TPM2B_PUBLIC pub;
+ 	TPM2B_PRIVATE priv;
+@@ -227,10 +391,10 @@ get_primary_template(ESYS_CONTEXT *ctx)
+ 	UINT32 i;
+ 	TSS2_RC rc;
+ 
+-	rc = Esys_GetCapability (ctx,
+-				 ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
+-				 TPM2_CAP_ALGS, 0, TPM2_MAX_CAP_ALGS,
+-				 NULL, &capability_data);
++	rc = _gnutls_tss2_Esys_GetCapability(ctx,
++				ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
++				TPM2_CAP_ALGS, 0, TPM2_MAX_CAP_ALGS,
++				NULL, &capability_data);
+ 	if (rc) {
+ 		_gnutls_debug_log("tpm2: Esys_GetCapability failed: 0x%x\n", rc);
+ 		return NULL;
+@@ -239,7 +403,7 @@ get_primary_template(ESYS_CONTEXT *ctx)
+ 	for (i = 0; i < capability_data->data.algorithms.count; i++) {
+ 		if (capability_data->data.algorithms.algProperties[i].alg ==
+ 		    TPM2_ALG_ECC) {
+-			Esys_Free(capability_data);
++			_gnutls_tss2_Esys_Free(capability_data);
+ 			return &primary_template_ecc;
+ 		}
+ 	}
+@@ -247,12 +411,12 @@ get_primary_template(ESYS_CONTEXT *ctx)
+ 	for (i = 0; i < capability_data->data.algorithms.count; i++) {
+ 		if (capability_data->data.algorithms.algProperties[i].alg ==
+ 		    TPM2_ALG_RSA) {
+-			Esys_Free(capability_data);
++			_gnutls_tss2_Esys_Free(capability_data);
+ 			return &primary_template_rsa;
+ 		}
+         }
+ 
+-	Esys_Free(capability_data);
++	_gnutls_tss2_Esys_Free(capability_data);
+ 	_gnutls_debug_log("tpm2: unable to find primary template\n");
+ 	return NULL;
+ }
+@@ -320,7 +484,7 @@ static int init_tpm2_primary(struct tpm2_info_st *info,
+ 		install_tpm_passphrase(&info->ownerauth, pass);
+ 		info->need_ownerauth = false;
+ 	}
+-	rc = Esys_TR_SetAuth(ctx, hierarchy, &info->ownerauth);
++	rc = _gnutls_tss2_Esys_TR_SetAuth(ctx, hierarchy, &info->ownerauth);
+ 	if (rc) {
+ 		_gnutls_debug_log("tpm2: Esys_TR_SetAuth failed: 0x%x\n", rc);
+ 		return gnutls_assert_val(GNUTLS_E_TPM_ERROR);
+@@ -329,7 +493,7 @@ static int init_tpm2_primary(struct tpm2_info_st *info,
+ 	if (!primary_template) {
+ 		return gnutls_assert_val(GNUTLS_E_TPM_ERROR);
+ 	}
+-	rc = Esys_CreatePrimary(ctx, hierarchy,
++	rc = _gnutls_tss2_Esys_CreatePrimary(ctx, hierarchy,
+ 				ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
+ 				&primary_sensitive,
+ 				primary_template,
+@@ -359,14 +523,14 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 
+ 	_gnutls_debug_log("tpm2: establishing connection with TPM\n");
+ 
+-	rc = Esys_Initialize(ctx, tcti_ctx, NULL);
++	rc = _gnutls_tss2_Esys_Initialize(ctx, tcti_ctx, NULL);
+ 	if (rc) {
+ 		gnutls_assert();
+ 		_gnutls_debug_log("tpm2: Esys_Initialize failed: 0x%x\n", rc);
+ 		goto error;
+ 	}
+ 
+-	rc = Esys_Startup(*ctx, TPM2_SU_CLEAR);
++	rc = _gnutls_tss2_Esys_Startup(*ctx, TPM2_SU_CLEAR);
+ 	if (rc == TPM2_RC_INITIALIZE) {
+ 		_gnutls_debug_log("tpm2: was already started up thus false positive failing in tpm2tss log\n");
+ 	} else if (rc) {
+@@ -381,7 +545,7 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 			goto error;
+ 		}
+ 	} else {
+-		rc = Esys_TR_FromTPMPublic(*ctx, info->parent,
++		rc = _gnutls_tss2_Esys_TR_FromTPMPublic(*ctx, info->parent,
+ 					   ESYS_TR_NONE,
+ 					   ESYS_TR_NONE,
+ 					   ESYS_TR_NONE,
+@@ -399,7 +563,7 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 		if (!info->did_ownerauth && !info->ownerauth.size) {
+ 			TPM2B_PUBLIC *pub = NULL;
+ 
+-			rc = Esys_ReadPublic(*ctx, parent_handle,
++			rc = _gnutls_tss2_Esys_ReadPublic(*ctx, parent_handle,
+ 					     ESYS_TR_NONE,
+ 					     ESYS_TR_NONE,
+ 					     ESYS_TR_NONE,
+@@ -408,7 +572,7 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 			    !(pub->publicArea.objectAttributes & TPMA_OBJECT_NODA)) {
+ 				info->need_ownerauth = true;
+ 			}
+-			Esys_Free(pub);
++			_gnutls_tss2_Esys_Free(pub);
+ 		}
+ 	reauth:
+ 		if (info->need_ownerauth) {
+@@ -420,7 +584,7 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 			install_tpm_passphrase(&info->ownerauth, pass);
+ 			info->need_ownerauth = false;
+ 		}
+-		rc = Esys_TR_SetAuth(*ctx, parent_handle, &info->ownerauth);
++		rc = _gnutls_tss2_Esys_TR_SetAuth(*ctx, parent_handle, &info->ownerauth);
+ 		if (rc) {
+ 			gnutls_assert();
+ 			_gnutls_debug_log("tpm2: Esys_TR_SetAuth failed: 0x%x\n",
+@@ -432,7 +596,7 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 	_gnutls_debug_log("tpm2: loading TPM2 key blob, parent handle 0x%x\n",
+ 			  parent_handle);
+ 
+-	rc = Esys_Load(*ctx, parent_handle,
++	rc = _gnutls_tss2_Esys_Load(*ctx, parent_handle,
+ 		       ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
+ 		       &info->priv, &info->pub,
+ 		       key_handle);
+@@ -450,7 +614,7 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 	info->did_ownerauth = true;
+ 
+ 	if (parent_is_generated(info->parent)) {
+-		rc = Esys_FlushContext(*ctx, parent_handle);
++		rc = _gnutls_tss2_Esys_FlushContext(*ctx, parent_handle);
+ 		if (rc) {
+ 			_gnutls_debug_log("tpm2: Esys_FlushContext for generated primary failed: 0x%x\n",
+ 					  rc);
+@@ -461,14 +625,14 @@ static int init_tpm2_key(ESYS_CONTEXT **ctx, ESYS_TR *key_handle,
+ 	return 0;
+  error:
+ 	if (parent_is_generated(info->parent) && parent_handle != ESYS_TR_NONE) {
+-		Esys_FlushContext(*ctx, parent_handle);
++		_gnutls_tss2_Esys_FlushContext(*ctx, parent_handle);
+ 	}
+ 	if (*key_handle != ESYS_TR_NONE) {
+-		Esys_FlushContext(*ctx, *key_handle);
++		_gnutls_tss2_Esys_FlushContext(*ctx, *key_handle);
+ 	}
+ 	*key_handle = ESYS_TR_NONE;
+ 
+-	Esys_Finalize(ctx);
++	_gnutls_tss2_Esys_Finalize(ctx);
+ 	return GNUTLS_E_TPM_ERROR;
+ }
+ 
+@@ -488,7 +652,7 @@ auth_tpm2_key(struct tpm2_info_st *info, ESYS_CONTEXT *ctx, ESYS_TR key_handle)
+ 		info->need_userauth = false;
+ 	}
+ 
+-	rc = Esys_TR_SetAuth(ctx, key_handle, &info->userauth);
++	rc = _gnutls_tss2_Esys_TR_SetAuth(ctx, key_handle, &info->userauth);
+ 	if (rc) {
+ 		_gnutls_debug_log("tpm2: Esys_TR_SetAuth failed: 0x%x\n", rc);
+ 		return gnutls_assert_val(GNUTLS_E_TPM_ERROR);
+@@ -574,7 +738,7 @@ int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
+ 		goto out;
+ 	}
+ 
+-	rc = Esys_RSA_Decrypt(ectx, key_handle,
++	rc = _gnutls_tss2_Esys_RSA_Decrypt(ectx, key_handle,
+ 			      ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
+ 			      &digest, &in_scheme, &label, &tsig);
+ 	if (rc_is_key_auth_failed(rc)) {
+@@ -591,14 +755,14 @@ int tpm2_rsa_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
+ 
+ 	ret = _gnutls_set_datum(sig, tsig->buffer, tsig->size);
+  out:
+-	Esys_Free(tsig);
++	_gnutls_tss2_Esys_Free(tsig);
+ 
+ 	if (key_handle != ESYS_TR_NONE) {
+-		Esys_FlushContext(ectx, key_handle);
++		_gnutls_tss2_Esys_FlushContext(ectx, key_handle);
+ 	}
+ 
+ 	if (ectx) {
+-		Esys_Finalize(&ectx);
++		_gnutls_tss2_Esys_Finalize(&ectx);
+ 	}
+ 
+ 	return ret;
+@@ -661,7 +825,7 @@ int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
+ 		goto out;
+ 	}
+ 
+-	rc = Esys_Sign(ectx, key_handle,
++	rc = _gnutls_tss2_Esys_Sign(ectx, key_handle,
+ 		       ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE,
+ 		       &digest, &in_scheme, &validation,
+ 		       &tsig);
+@@ -682,31 +846,23 @@ int tpm2_ec_sign_hash_fn(gnutls_privkey_t key, gnutls_sign_algorithm_t algo,
+ 
+ 	ret = gnutls_encode_rs_value(sig, &sig_r, &sig_s);
+  out:
+-	Esys_Free(tsig);
++	_gnutls_tss2_Esys_Free(tsig);
+ 
+ 	if (key_handle != ESYS_TR_NONE) {
+-		Esys_FlushContext(ectx, key_handle);
++		_gnutls_tss2_Esys_FlushContext(ectx, key_handle);
+ 	}
+ 
+ 	if (ectx) {
+-		Esys_Finalize(&ectx);
++		_gnutls_tss2_Esys_Finalize(&ectx);
+ 	}
+ 
+ 	return ret;
+ }
+ 
+-GNUTLS_ONCE(tcti_once);
+-
+-void
+-tpm2_tcti_deinit(void)
+-{
+-	if (tcti_ctx) {
+-		Tss2_TctiLdr_Finalize(&tcti_ctx);
+-	}
+-}
++GNUTLS_ONCE(tpm2_esys_once);
+ 
+ static void
+-tcti_once_init(void)
++tpm2_esys_once_init(void)
+ {
+ 	const char *tcti;
+ 	const char * const tcti_vars[] = {
+@@ -718,6 +874,11 @@ tcti_once_init(void)
+ 	size_t i;
+ 	TSS2_RC rc;
+ 
++	if (init_tss2_funcs() < 0) {
++		_gnutls_debug_log("tpm2: unable to initialize TSS2 functions\n");
++		return;
++	}
++
+ 	for (i = 0; i < sizeof(tcti_vars) / sizeof(tcti_vars[0]); i++) {
+ 		tcti = secure_getenv(tcti_vars[i]);
+ 		if (tcti && *tcti != '\0') {
+@@ -727,7 +888,7 @@ tcti_once_init(void)
+ 		}
+ 	}
+ 	if (tcti && *tcti != '\0') {
+-		rc = Tss2_TctiLdr_Initialize(tcti, &tcti_ctx);
++		rc = _gnutls_tss2_Tss2_TctiLdr_Initialize(tcti, &tcti_ctx);
+ 		if (rc) {
+ 			_gnutls_debug_log("tpm2: TSS2_TctiLdr_Initialize failed: 0x%x\n",
+ 					  rc);
+@@ -735,13 +896,35 @@ tcti_once_init(void)
+ 	}
+ }
+ 
++/* called by the global destructor through _gnutls_tpm2_deinit */
++void
++tpm2_esys_deinit(void)
++{
++	if (tcti_ctx) {
++		_gnutls_tss2_Tss2_TctiLdr_Finalize(&tcti_ctx);
++		tcti_ctx = NULL;
++	}
++	if (_gnutls_tss2_esys_dlhandle) {
++		dlclose(_gnutls_tss2_esys_dlhandle);
++		_gnutls_tss2_esys_dlhandle = NULL;
++	}
++	if (_gnutls_tss2_mu_dlhandle) {
++		dlclose(_gnutls_tss2_mu_dlhandle);
++		_gnutls_tss2_mu_dlhandle = NULL;
++	}
++	if (_gnutls_tss2_tctildr_dlhandle) {
++		dlclose(_gnutls_tss2_tctildr_dlhandle);
++		_gnutls_tss2_tctildr_dlhandle = NULL;
++	}
++}
++
+ int install_tpm2_key(struct tpm2_info_st *info, gnutls_privkey_t pkey,
+ 		     unsigned int parent, bool emptyauth,
+ 		     gnutls_datum_t *privdata, gnutls_datum_t *pubdata)
+ {
+ 	TSS2_RC rc;
+ 
+-	(void)gnutls_once(&tcti_once, tcti_once_init);
++	(void)gnutls_once(&tpm2_esys_once, tpm2_esys_once_init);
+ 
+ 	if (!tcti_ctx) {
+ 		return gnutls_assert_val(GNUTLS_E_TPM_ERROR);
+@@ -757,16 +940,16 @@ int install_tpm2_key(struct tpm2_info_st *info, gnutls_privkey_t pkey,
+ 
+ 	info->parent = parent;
+ 
+-	rc = Tss2_MU_TPM2B_PRIVATE_Unmarshal(privdata->data, privdata->size, NULL,
+-					     &info->priv);
++	rc = _gnutls_tss2_Tss2_MU_TPM2B_PRIVATE_Unmarshal(privdata->data, privdata->size, NULL,
++							     &info->priv);
+ 	if (rc) {
+ 		_gnutls_debug_log("tpm2: failed to import private key data: 0x%x\n",
+ 				  rc);
+ 		return gnutls_assert_val(GNUTLS_E_TPM_ERROR);
+ 	}
+ 
+-	rc = Tss2_MU_TPM2B_PUBLIC_Unmarshal(pubdata->data, pubdata->size, NULL,
+-					    &info->pub);
++	rc = _gnutls_tss2_Tss2_MU_TPM2B_PUBLIC_Unmarshal(pubdata->data, pubdata->size, NULL,
++							    &info->pub);
+ 	if (rc) {
+ 		_gnutls_debug_log("tpm2: failed to import public key data: 0x%x\n",
+ 				  rc);
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 529f1cc077..64ce470a02 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -515,7 +515,8 @@ dist_check_SCRIPTS += fastopen.sh pkgconfig.sh starttls.sh starttls-ftp.sh start
+ 	psktool.sh ocsp-tests/ocsp-load-chain.sh gnutls-cli-save-data.sh gnutls-cli-debug.sh \
+ 	sni-resume.sh ocsp-tests/ocsptool.sh cert-reencoding.sh pkcs7-cat.sh long-crl.sh \
+ 	serv-udp.sh logfile-option.sh gnutls-cli-resume.sh profile-tests.sh \
+-	server-weak-keys.sh ocsp-tests/ocsp-signer-verify.sh cfg-test.sh
++	server-weak-keys.sh ocsp-tests/ocsp-signer-verify.sh cfg-test.sh \
++	sanity-lib.sh
+ 
+ if !DISABLE_SYSTEM_CONFIG
+ dist_check_SCRIPTS += system-override-sig.sh system-override-hash.sh \
+diff --git a/tests/sanity-lib.sh b/tests/sanity-lib.sh
+new file mode 100644
+index 0000000000..1e3612781b
+--- /dev/null
++++ b/tests/sanity-lib.sh
+@@ -0,0 +1,40 @@
++#!/bin/sh
++
++# Copyright (C) 2022 Red Hat, Inc.
++#
++# Author: Daiki Ueno
++#
++# This file is part of GnuTLS.
++#
++# GnuTLS is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by the
++# Free Software Foundation; either version 3 of the License, or (at
++# your option) any later version.
++#
++# GnuTLS is distributed in the hope that it will be useful, but
++# WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++# General Public License for more details.
++#
++# You should have received a copy of the GNU Lesser General Public License
++# along with this program.  If not, see <https://www.gnu.org/licenses/>
++
++: ${top_builddir=..}
++: ${CLI_DEBUG=../src/gnutls-cli-debug${EXEEXT}}
++: ${LDD=ldd}
++: ${LIBTOOL=libtool}
++
++if ! test -x "${CLI_DEBUG}"; then
++	exit 77
++fi
++
++# ldd.sh doesn't check recursive dependencies
++${LDD} --version >/dev/null || exit 77
++
++# We use gnutls-cli-debug, as it has the fewest dependencies among our
++# commands (e.g., gnutls-cli pulls in OpenSSL through libunbound).
++if ${LIBTOOL} --mode=execute ${LDD} ${CLI_DEBUG} | \
++    grep '^[[:space:]]*\(libcrypto\.\|libssl\.\|libgcrypt\.\)'; then
++    echo "gnutls-cli-debug links to other crypto library"
++    exit 1
++fi
+diff --git a/tests/tpm2.sh b/tests/tpm2.sh
+index 854986c552..6f8e44c64b 100755
+--- a/tests/tpm2.sh
++++ b/tests/tpm2.sh
+@@ -21,8 +21,6 @@
+ # along with GnuTLS; if not, write to the Free Software Foundation,
+ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ 
+-set +e
+-
+ : ${srcdir=.}
+ : ${CERTTOOL=../src/certtool${EXEEXT}}
+ KEYPEMFILE=tpmkey.$$.key.pem
+@@ -192,6 +190,10 @@ run_tests()
+ 
+ 	echo " - Generating ${KEYPEMFILE}"
+ 	tpm2tss-genkey -a ${kalg} -o ${OPASS} ${KEYPEMFILE}
++	if [ $? -ne 0 ]; then
++		echo "unable to generate key"
++		return 1
++	fi
+ 	cat ${KEYPEMFILE}
+ 
+ 	echo " - Generating certificate based on key"
+@@ -200,6 +202,10 @@ run_tests()
+ 	"${CERTTOOL}" --generate-self-signed -d 3 \
+ 		--load-privkey "${KEYPEMFILE}" \
+ 		--template "${srcdir}/cert-tests/templates/template-test.tmpl"
++	if [ $? -ne 0 ]; then
++		echo "unable to generate certificate"
++		return 1
++	fi
+ 
+ 	if test "${kalg}" = "rsa";then
+ 		echo " - Generating RSA-PSS certificate based on key"
+@@ -207,6 +213,10 @@ run_tests()
+ 			--load-privkey "${KEYPEMFILE}" \
+ 			--sign-params rsa-pss \
+ 			--template "${srcdir}/cert-tests/templates/template-test.tmpl"
++		if [ $? -ne 0 ]; then
++			echo "unable to generate certificate"
++			return 1
++		fi
+ 	fi
+ 
+ 	stop_swtpm
+-- 
+2.34.1
+
diff --git a/SOURCES/gnutls-3.7.3-max-algos.patch b/SOURCES/gnutls-3.7.3-max-algos.patch
new file mode 100644
index 0000000..d283ceb
--- /dev/null
+++ b/SOURCES/gnutls-3.7.3-max-algos.patch
@@ -0,0 +1,44 @@
+From b5a2cbce49d94a04a68acbbc31caaa0c5d7b3321 Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Fri, 18 Feb 2022 11:05:15 +0100
+Subject: [PATCH] bump GNUTLS_MAX_ALGORITHM_NUM / MAX_ALGOS
+
+Fedora 36 LEGACY crypto-policy uses allowlisting format
+and is long enough to blow past the 64 priority string
+elements mark, causing, effectively, priority string truncation.
+
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+---
+ lib/includes/gnutls/gnutls.h.in | 2 +-
+ lib/priority.c                  | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
+index 6359a0edb6..16140c8787 100644
+--- a/lib/includes/gnutls/gnutls.h.in
++++ b/lib/includes/gnutls/gnutls.h.in
+@@ -408,7 +408,7 @@ typedef enum {
+   /* exported for other gnutls headers. This is the maximum number of
+    * algorithms (ciphers, kx or macs).
+    */
+-#define GNUTLS_MAX_ALGORITHM_NUM 64
++#define GNUTLS_MAX_ALGORITHM_NUM 128
+ #define GNUTLS_MAX_SESSION_ID_SIZE 32
+ 
+ 
+diff --git a/lib/priority.c b/lib/priority.c
+index 54d7b1bb45..e7698ba7eb 100644
+--- a/lib/priority.c
++++ b/lib/priority.c
+@@ -43,7 +43,7 @@
+ #include "profiles.h"
+ #include "name_val_array.h"
+ 
+-#define MAX_ELEMENTS 64
++#define MAX_ELEMENTS GNUTLS_MAX_ALGORITHM_NUM
+ 
+ #define ENABLE_PROFILE(c, profile) do { \
+ 	c->additional_verify_flags &= 0x00ffffff; \
+-- 
+2.34.1
+
diff --git a/SPECS/gnutls.spec b/SPECS/gnutls.spec
index 0086256..959127c 100644
--- a/SPECS/gnutls.spec
+++ b/SPECS/gnutls.spec
@@ -13,17 +13,24 @@ print(string.sub(hash, 0, 16))
 }
 
 Version: 3.7.3
-Release: 5%{?dist}
+Release: 9%{?dist}
 Patch1:	gnutls-3.6.7-no-now-guile.patch
 Patch2:	gnutls-3.2.7-rpath.patch
 Patch3:	gnutls-3.7.2-enable-intel-cet.patch
 Patch4: gnutls-3.7.2-no-explicit-init.patch
-Patch5: gnutls-3.7.3-disable-config-reload.patch
-Patch6: gnutls-3.7.3-fips-rsa-keygen.patch
-Patch7: gnutls-3.7.3-ktls-stub.patch
-Patch8: gnutls-3.7.3-fips-pkcs12.patch
-Patch9: gnutls-3.7.3-fix-tests-in-fips.patch
-%bcond_with bootstrap
+Patch5: gnutls-3.7.3-fips-rsa-keygen.patch
+Patch6: gnutls-3.7.3-ktls-stub.patch
+Patch7: gnutls-3.7.3-fips-pkcs12.patch
+Patch8: gnutls-3.7.3-fix-tests-in-fips.patch
+Patch9: gnutls-3.7.3-gost-ifdef.patch
+Patch10: gnutls-3.7.3-max-algos.patch
+Patch11: gnutls-3.7.3-allowlist-api.patch
+Patch12: gnutls-3.7.3-libtss2-dlopen.patch
+
+# not upstreamed
+Patch100: gnutls-3.7.3-disable-config-reload.patch
+
+%bcond_without bootstrap
 %bcond_without dane
 %if 0%{?rhel}
 %bcond_with guile
@@ -43,8 +50,9 @@ License: GPLv3+ and LGPLv2+
 BuildRequires: p11-kit-devel >= 0.21.3, gettext-devel
 BuildRequires: zlib-devel, readline-devel, libtasn1-devel >= 4.3
 %if %{with bootstrap}
-BuildRequires: automake, autoconf, gperf, libtool, texinfo
+BuildRequires: automake, autoconf, gperf, libtool
 %endif
+BuildRequires: texinfo
 BuildRequires: nettle-devel >= 3.5.1
 %if %{with tpm12}
 BuildRequires: trousers-devel >= 0.3.11.2
@@ -335,6 +343,21 @@ make check %{?_smp_mflags} GNUTLS_SYSTEM_PRIORITY_FILE=/dev/null
 %endif
 
 %changelog
+* Fri Feb 25 2022 Daiki Ueno <dueno@redhat.com> - 3.7.3-9
+- Stop using typeof keyword for tss2 function prototypes (#2057490)
+- Ensure allowlist API is called before priority string construction (#1975421)
+
+* Thu Feb 24 2022 Daiki Ueno <dueno@redhat.com> - 3.7.3-8
+- Fix previous change for loading libtss2* (#2057490)
+
+* Wed Feb 23 2022 Daiki Ueno <dueno@redhat.com> - 3.7.3-7
+- Increase GNUTLS_MAX_ALGORITHM_NUM for allowlisting (#2033220)
+- Ensure allowlisting API is called before priority string is constructed (#2042532)
+- Use dlopen for loading libtss2* to avoid OpenSSL dependency (#2057490)
+
+* Tue Feb 22 2022 Daiki Ueno <dueno@redhat.com> - 3.7.3-6
+- Compile out GOST algorithm IDs (#1945292)
+
 * Thu Feb 17 2022 Zoltan Fridrich <zfridric@redhat.com> - 3.7.3-5
 - Fix upstream testsuite in fips mode (#2051637)