summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2021-11-29 14:20:48 +0000
committerDaiki Ueno <ueno@gnu.org>2021-11-29 14:20:48 +0000
commitd8be349fb196d569309f90f5070d7f3958128bce (patch)
tree01e4cc019a6b7520d59499b31226beabeb54f57c
parent155926489c0950e40355523043773a782a8d986a (diff)
parent0ecce7191dfd78387f2994253d37ed1df50d563d (diff)
downloadgnutls-d8be349fb196d569309f90f5070d7f3958128bce.tar.gz
Merge branch 'wip/dueno/config-allowlisting' into 'master'
priority: support allowlisting in configuration file Closes #1172 See merge request gnutls/gnutls!1427
-rw-r--r--NEWS10
-rw-r--r--devel/libgnutls.abignore14
-rw-r--r--devel/symbols.last6
-rw-r--r--doc/Makefile.am10
-rw-r--r--doc/cha-config.texi82
-rw-r--r--doc/manpages/Makefile.am5
-rw-r--r--lib/algorithms.h22
-rw-r--r--lib/algorithms/ecc.c51
-rw-r--r--lib/algorithms/groups.c18
-rw-r--r--lib/algorithms/mac.c71
-rw-r--r--lib/algorithms/protocols.c73
-rw-r--r--lib/algorithms/sign.c124
-rw-r--r--lib/gnutls_int.h3
-rw-r--r--lib/includes/gnutls/gnutls.h.in11
-rw-r--r--lib/libgnutls.map12
-rw-r--r--lib/priority.c837
-rw-r--r--tests/Makefile.am6
-rwxr-xr-xtests/system-override-curves-allowlist.sh113
-rwxr-xr-xtests/system-override-hash-allowlist.sh41
-rwxr-xr-xtests/system-override-sig-allowlist.sh43
-rwxr-xr-xtests/system-override-special-allowlist.sh177
-rwxr-xr-xtests/system-override-versions-allowlist.sh109
22 files changed, 1690 insertions, 148 deletions
diff --git a/NEWS b/NEWS
index 42c2be0124..638b3b51c2 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,11 @@ See the end for copying conditions.
* Version 3.7.3 (unreleased)
+** libgnutls: The allowlisting configuration mode has been added to the system-wide
+ settings. In this mode, all the algorithms are initially marked as insecure
+ or disabled, while the applications can re-enable them either through the
+ [overrides] section of the configuration file or the new API (#1172).
+
** certtool: Certtool can now generate, manipulate, and evaluate x25519 and
x448 public keys, private keys, and certificates.
** libgnutls: disabling a hashing algorithm through "insecure-hash"
@@ -22,6 +27,11 @@ See the end for copying conditions.
** API and ABI modifications:
GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH: new flag in gnutls_privkey_flags_t
GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH: new flag in gnutls_certificate_verify_flags
+gnutls_ecc_curve_set_enabled: Added.
+gnutls_sign_set_secure: Added.
+gnutls_sign_set_secure_for_certs: Added.
+gnutls_digest_set_secure: Added.
+gnutls_protocol_set_enabled: Added.
* Version 3.7.2 (released 2021-05-29)
diff --git a/devel/libgnutls.abignore b/devel/libgnutls.abignore
index e43e5472f3..8afa94148a 100644
--- a/devel/libgnutls.abignore
+++ b/devel/libgnutls.abignore
@@ -57,3 +57,17 @@ return_type_name = unsigned int
# The following should be removed in the new release, after updating the
# abi-dump repository:
+[suppress_function]
+name = gnutls_digest_set_secure
+
+[suppress_function]
+name = gnutls_ecc_curve_set_enabled
+
+[suppress_function]
+name = gnutls_protocol_set_enabled
+
+[suppress_function]
+name = gnutls_sign_set_secure
+
+[suppress_function]
+name = gnutls_sign_set_secure_for_certs
diff --git a/devel/symbols.last b/devel/symbols.last
index 963287687b..7bef666350 100644
--- a/devel/symbols.last
+++ b/devel/symbols.last
@@ -13,6 +13,7 @@ GNUTLS_3_6_8@GNUTLS_3_6_8
GNUTLS_3_6_9@GNUTLS_3_6_9
GNUTLS_3_7_0@GNUTLS_3_7_0
GNUTLS_3_7_2@GNUTLS_3_7_2
+GNUTLS_3_7_3@GNUTLS_3_7_3
_gnutls_global_init_skip@GNUTLS_3_4
gnutls_aead_cipher_decrypt@GNUTLS_3_4
gnutls_aead_cipher_decryptv2@GNUTLS_3_6_10
@@ -195,6 +196,7 @@ gnutls_digest_get_id@GNUTLS_3_4
gnutls_digest_get_name@GNUTLS_3_4
gnutls_digest_get_oid@GNUTLS_3_4
gnutls_digest_list@GNUTLS_3_4
+gnutls_digest_set_secure@GNUTLS_3_7_3
gnutls_dtls_cookie_send@GNUTLS_3_4
gnutls_dtls_cookie_verify@GNUTLS_3_4
gnutls_dtls_get_data_mtu@GNUTLS_3_4
@@ -213,6 +215,7 @@ gnutls_ecc_curve_get_oid@GNUTLS_3_4
gnutls_ecc_curve_get_pk@GNUTLS_3_4
gnutls_ecc_curve_get_size@GNUTLS_3_4
gnutls_ecc_curve_list@GNUTLS_3_4
+gnutls_ecc_curve_set_enabled@GNUTLS_3_7_3
gnutls_encode_ber_digest_info@GNUTLS_3_4
gnutls_encode_gost_rs_value@GNUTLS_3_6_3
gnutls_encode_rs_value@GNUTLS_3_6_0
@@ -657,6 +660,7 @@ gnutls_protocol_get_id@GNUTLS_3_4
gnutls_protocol_get_name@GNUTLS_3_4
gnutls_protocol_get_version@GNUTLS_3_4
gnutls_protocol_list@GNUTLS_3_4
+gnutls_protocol_set_enabled@GNUTLS_3_7_3
gnutls_psk_allocate_client_credentials@GNUTLS_3_4
gnutls_psk_allocate_server_credentials@GNUTLS_3_4
gnutls_psk_client_get_hint@GNUTLS_3_4
@@ -806,6 +810,8 @@ gnutls_sign_get_pk_algorithm@GNUTLS_3_4
gnutls_sign_is_secure2@GNUTLS_3_6_0
gnutls_sign_is_secure@GNUTLS_3_4
gnutls_sign_list@GNUTLS_3_4
+gnutls_sign_set_secure@GNUTLS_3_7_3
+gnutls_sign_set_secure_for_certs@GNUTLS_3_7_3
gnutls_sign_supports_pk_algorithm@GNUTLS_3_6_0
gnutls_srp_1024_group_generator@GNUTLS_3_4
gnutls_srp_1024_group_prime@GNUTLS_3_4
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 562a85fa87..4f25bf0d5e 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -974,6 +974,8 @@ FUNCS += functions/gnutls_digest_get_oid
FUNCS += functions/gnutls_digest_get_oid.short
FUNCS += functions/gnutls_digest_list
FUNCS += functions/gnutls_digest_list.short
+FUNCS += functions/gnutls_digest_set_secure
+FUNCS += functions/gnutls_digest_set_secure.short
FUNCS += functions/gnutls_dtls_cookie_send
FUNCS += functions/gnutls_dtls_cookie_send.short
FUNCS += functions/gnutls_dtls_cookie_verify
@@ -1010,6 +1012,8 @@ FUNCS += functions/gnutls_ecc_curve_get_size
FUNCS += functions/gnutls_ecc_curve_get_size.short
FUNCS += functions/gnutls_ecc_curve_list
FUNCS += functions/gnutls_ecc_curve_list.short
+FUNCS += functions/gnutls_ecc_curve_set_enabled
+FUNCS += functions/gnutls_ecc_curve_set_enabled.short
FUNCS += functions/gnutls_encode_ber_digest_info
FUNCS += functions/gnutls_encode_ber_digest_info.short
FUNCS += functions/gnutls_encode_gost_rs_value
@@ -1730,6 +1734,8 @@ FUNCS += functions/gnutls_protocol_get_version
FUNCS += functions/gnutls_protocol_get_version.short
FUNCS += functions/gnutls_protocol_list
FUNCS += functions/gnutls_protocol_list.short
+FUNCS += functions/gnutls_protocol_set_enabled
+FUNCS += functions/gnutls_protocol_set_enabled.short
FUNCS += functions/gnutls_psk_allocate_client_credentials
FUNCS += functions/gnutls_psk_allocate_client_credentials.short
FUNCS += functions/gnutls_psk_allocate_server_credentials
@@ -2024,6 +2030,10 @@ FUNCS += functions/gnutls_sign_is_secure2
FUNCS += functions/gnutls_sign_is_secure2.short
FUNCS += functions/gnutls_sign_list
FUNCS += functions/gnutls_sign_list.short
+FUNCS += functions/gnutls_sign_set_secure
+FUNCS += functions/gnutls_sign_set_secure.short
+FUNCS += functions/gnutls_sign_set_secure_for_certs
+FUNCS += functions/gnutls_sign_set_secure_for_certs.short
FUNCS += functions/gnutls_sign_supports_pk_algorithm
FUNCS += functions/gnutls_sign_supports_pk_algorithm.short
FUNCS += functions/gnutls_srp_allocate_client_credentials
diff --git a/doc/cha-config.texi b/doc/cha-config.texi
index c0f7048fc2..62def65796 100644
--- a/doc/cha-config.texi
+++ b/doc/cha-config.texi
@@ -74,6 +74,7 @@ configuration file and can be
@item @code{insecure-sig-for-cert}: to mark the signature algorithm as insecure when used in certificates.
@item @code{insecure-sig}: to mark the signature algorithm as insecure for any use.
@item @code{insecure-hash}: to mark the hash algorithm as insecure for digital signature use (provides a more generic way to disable digital signatures for broken hash algorithms).
+@item @code{disabled-curve}: to disable the specified elliptic curve.
@item @code{disabled-version}: to disable the specified TLS versions.
@item @code{tls-disabled-cipher}: to disable the specified ciphers for use in the TLS or DTLS protocols.
@item @code{tls-disabled-mac}: to disable the specified MAC algorithms for use in the TLS or DTLS protocols.
@@ -82,10 +83,54 @@ configuration file and can be
@end itemize
Each of the options can be repeated multiple times when multiple values need
-to be disabled.
+to be disabled or enabled.
The valid values for the options above can be found in the 'Protocols', 'Digests'
-'PK-signatures', 'Protocols', 'Ciphrers', and 'MACs' fields of the output of @code{gnutls-cli --list}.
+'PK-signatures', 'Protocols', 'Ciphers', and 'MACs' fields of the output of @code{gnutls-cli --list}.
+
+Sometimes the system administrator wants to enable only specific
+algorithms, despite the library defaults. GnuTLS provides an
+alternative mode of overriding: allowlisting.
+
+As shown below in the examples, it is hard to use this mode correctly,
+as it requires understanding of how algorithms are used underneath by
+the protocols. Allowlisting configuration mode is intended to be used
+by the operating system vendors that prefer laying out the library
+defaults exhaustively from scratch instead on depending on gnutls
+presets, such as @code{NORMAL}. Applications are then expected to
+optionally disable or enable only a subset algorithms on top of the
+vendor-provided configuration.
+
+In the allowlisting mode, all the algorithms are initially marked as
+insecure or disabled, and shall be explicitly turned on by the options
+listed below in the @code{[overrides]} section. As the allowlisting
+mode is mutually exclusive to the blocklisting mode, the options
+listed above for the blocklisting mode are forbidden in the
+allowlisting mode, and vice versa.
+
+@itemize
+@item @code{secure-sig-for-cert}: to mark the signature algorithm as secure when used in certificates.
+@item @code{secure-sig}: to mark the signature algorithm as secure for any use.
+@item @code{secure-hash}: to mark the hash algorithm as secure for digital signature use (provides a more generic way to enable digital signatures for broken hash algorithms).
+@item @code{enabled-curve}: to enable the specified elliptic curve.
+@item @code{enabled-version}: to enable the specified TLS versions.
+@item @code{tls-enabled-cipher}: to enable the specified ciphers for use in the TLS or DTLS protocols.
+@item @code{tls-enabled-mac}: to enable the specified MAC algorithms for use in the TLS or DTLS protocols.
+@item @code{tls-enabled-group}: to enable the specified group for use in the TLS or DTLS protocols.
+@item @code{tls-enabled-kx}: to enable the specified key exchange algorithms for use in the TLS or DTLS protocols (applies to TLS1.2 or earlier).
+@end itemize
+
+The allowlisting mode can be enabled by adding @code{override-mode =
+allowlist} in the @code{[global]} section.
+
+The following functions allow the applications to modify the setting.
+
+@showfuncE{gnutls_ecc_curve_set_enabled,gnutls_sign_set_secure,gnutls_sign_set_secure_for_certs,gnutls_digest_set_secure,gnutls_protocol_set_enabled}
+
+When the allowlisting mode is in effect, a @code{@@SYSTEM} priority
+string is automatically constructed from the options in the
+@code{[overrides]} section. For this reason, the above functions
+should be called before the @code{@@SYSTEM} priority is used.
@subsection Examples
@@ -120,6 +165,39 @@ tls-disabled-mac = sha1
tls-disabled-group = group-ffdhe8192
@end example
+The following example demonstrates the use of the allowlisting
+mode. All the signature algorithms are disabled by default but
+@code{RSA-SHA256}. Note that the hash algorithm @code{SHA256} also
+needs to be explicitly enabled.
+
+@example
+[global]
+override-mode = allowlist
+
+[overrides]
+secure-hash = sha256
+secure-sig = rsa-sha256
+@end example
+
+To enable a TLS ciphersuite in the allowlist mode requires a more
+verbose configuration, explicitly listing algorithm dependencies. The
+following example enables TLS_AES_128_GCM_SHA256, using the SECP256R1
+curve for signing and key exchange.
+
+@example
+[global]
+override-mode = allowlist
+
+[overrides]
+secure-hash = sha256
+enabled-curve = secp256r1
+secure-sig = ecdsa-secp256r1-sha256
+enabled-version = tls1.3
+tls-enabled-cipher = aes-128-gcm
+tls-enabled-mac = aead
+tls-enabled-group = secp256r1
+@end example
+
@node Querying for disabled algorithms and protocols
@section Querying for disabled algorithms and protocols
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index cee5a9644a..4f39adf0cc 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -289,6 +289,7 @@ APIMANS += gnutls_digest_get_id.3
APIMANS += gnutls_digest_get_name.3
APIMANS += gnutls_digest_get_oid.3
APIMANS += gnutls_digest_list.3
+APIMANS += gnutls_digest_set_secure.3
APIMANS += gnutls_dtls_cookie_send.3
APIMANS += gnutls_dtls_cookie_verify.3
APIMANS += gnutls_dtls_get_data_mtu.3
@@ -307,6 +308,7 @@ APIMANS += gnutls_ecc_curve_get_oid.3
APIMANS += gnutls_ecc_curve_get_pk.3
APIMANS += gnutls_ecc_curve_get_size.3
APIMANS += gnutls_ecc_curve_list.3
+APIMANS += gnutls_ecc_curve_set_enabled.3
APIMANS += gnutls_encode_ber_digest_info.3
APIMANS += gnutls_encode_gost_rs_value.3
APIMANS += gnutls_encode_rs_value.3
@@ -667,6 +669,7 @@ APIMANS += gnutls_protocol_get_id.3
APIMANS += gnutls_protocol_get_name.3
APIMANS += gnutls_protocol_get_version.3
APIMANS += gnutls_protocol_list.3
+APIMANS += gnutls_protocol_set_enabled.3
APIMANS += gnutls_psk_allocate_client_credentials.3
APIMANS += gnutls_psk_allocate_server_credentials.3
APIMANS += gnutls_psk_client_get_hint.3
@@ -814,6 +817,8 @@ APIMANS += gnutls_sign_get_pk_algorithm.3
APIMANS += gnutls_sign_is_secure.3
APIMANS += gnutls_sign_is_secure2.3
APIMANS += gnutls_sign_list.3
+APIMANS += gnutls_sign_set_secure.3
+APIMANS += gnutls_sign_set_secure_for_certs.3
APIMANS += gnutls_sign_supports_pk_algorithm.3
APIMANS += gnutls_srp_allocate_client_credentials.3
APIMANS += gnutls_srp_allocate_server_credentials.3
diff --git a/lib/algorithms.h b/lib/algorithms.h
index 5172bd2784..da72403fba 100644
--- a/lib/algorithms.h
+++ b/lib/algorithms.h
@@ -345,15 +345,27 @@ typedef enum hash_security_level_t {
_INSECURE
} hash_security_level_t;
-int _gnutls_ecc_curve_mark_disabled(const char *name);
-int _gnutls_sign_mark_insecure(const char *name, hash_security_level_t);
-int _gnutls_digest_mark_insecure(const char *name);
+int _gnutls_ecc_curve_mark_disabled(gnutls_ecc_curve_t curve);
+int _gnutls_sign_mark_insecure(gnutls_sign_algorithm_t, hash_security_level_t);
+int _gnutls_digest_mark_insecure(gnutls_digest_algorithm_t dig);
unsigned _gnutls_digest_is_insecure(gnutls_digest_algorithm_t dig);
-int _gnutls_version_mark_disabled(const char *name);
+bool _gnutls_digest_is_insecure2(gnutls_digest_algorithm_t dig, unsigned flags);
+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);
+/* 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_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 */
+#define GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE (1 << 2)
+#define GNUTLS_SIGN_FLAG_ALLOW_INSECURE_REVERTIBLE (1 << 3)
struct gnutls_sign_entry_st {
const char *name;
const char *oid;
@@ -448,6 +460,7 @@ typedef struct gnutls_ecc_curve_entry_st {
unsigned sig_size; /* the size of curve signatures in bytes (EdDSA) */
unsigned gost_curve;
bool supported;
+ bool supported_revertible;
gnutls_group_t group;
} gnutls_ecc_curve_entry_st;
@@ -459,6 +472,7 @@ unsigned _gnutls_ecc_curve_is_supported(gnutls_ecc_curve_t);
gnutls_group_t _gnutls_ecc_curve_get_group(gnutls_ecc_curve_t);
const gnutls_group_entry_st *_gnutls_tls_id_to_group(unsigned num);
const gnutls_group_entry_st * _gnutls_id_to_group(unsigned id);
+gnutls_group_t _gnutls_group_get_id(const char *name);
gnutls_ecc_curve_t _gnutls_ecc_bits_to_curve(gnutls_pk_algorithm_t pk, int bits);
#define MAX_ECC_CURVE_SIZE 66
diff --git a/lib/algorithms/ecc.c b/lib/algorithms/ecc.c
index adab4e1c18..d2c0b3585b 100644
--- a/lib/algorithms/ecc.c
+++ b/lib/algorithms/ecc.c
@@ -353,13 +353,58 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get_id(const char *name)
return ret;
}
-int _gnutls_ecc_curve_mark_disabled(const char *name)
+/* This is only called by cfg_apply in priority.c, in blocklisting mode. */
+int _gnutls_ecc_curve_mark_disabled(gnutls_ecc_curve_t curve)
{
gnutls_ecc_curve_entry_st *p;
for(p = ecc_curves; p->name != NULL; p++) {
- if (c_strcasecmp(p->name, name) == 0) {
- p->supported = 0;
+ if (p->id == curve) {
+ p->supported = false;
+ return 0;
+ }
+ }
+
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+}
+
+/* This is only called by cfg_apply in priority.c, in allowlisting mode. */
+void _gnutls_ecc_curve_mark_disabled_all(void)
+{
+ gnutls_ecc_curve_entry_st *p;
+
+ for(p = ecc_curves; p->name != NULL; p++) {
+ p->supported = false;
+ p->supported_revertible = true;
+ }
+}
+
+/**
+ * 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)
+{
+ gnutls_ecc_curve_entry_st *p;
+
+ for(p = ecc_curves; p->name != NULL; p++) {
+ if (p->id == curve) {
+ if (!p->supported_revertible) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ p->supported = enabled;
return 0;
}
}
diff --git a/lib/algorithms/groups.c b/lib/algorithms/groups.c
index d4b77beb2a..d8bf95824f 100644
--- a/lib/algorithms/groups.c
+++ b/lib/algorithms/groups.c
@@ -276,6 +276,24 @@ gnutls_group_t gnutls_group_get_id(const char *name)
return ret;
}
+
+/* Similar to gnutls_group_get_id, except that it does not check if
+ * the curve is supported.
+ */
+gnutls_group_t _gnutls_group_get_id(const char *name)
+{
+ gnutls_group_t ret = GNUTLS_GROUP_INVALID;
+
+ GNUTLS_GROUP_LOOP(
+ if (c_strcasecmp(p->name, name) == 0) {
+ ret = p->id;
+ break;
+ }
+ );
+
+ return ret;
+}
+
/**
* gnutls_group_get_name:
* @group: is an element from %gnutls_group_t
diff --git a/lib/algorithms/mac.c b/lib/algorithms/mac.c
index 518323bca1..a2c66e76bb 100644
--- a/lib/algorithms/mac.c
+++ b/lib/algorithms/mac.c
@@ -291,13 +291,14 @@ gnutls_digest_algorithm_t gnutls_digest_get_id(const char *name)
return ret;
}
-int _gnutls_digest_mark_insecure(const char *name)
+/* This is only called by cfg_apply in priority.c, in blocklisting mode. */
+int _gnutls_digest_mark_insecure(gnutls_digest_algorithm_t dig)
{
#ifndef DISABLE_SYSTEM_CONFIG
mac_entry_st *p;
for(p = hash_algorithms; p->name != NULL; p++) {
- if (p->oid != NULL && c_strcasecmp(p->name, name) == 0) {
+ if (p->oid != NULL && p->id == (gnutls_mac_algorithm_t)dig) {
p->flags |= GNUTLS_MAC_FLAG_PREIMAGE_INSECURE;
return 0;
}
@@ -307,6 +308,57 @@ int _gnutls_digest_mark_insecure(const char *name)
return GNUTLS_E_INVALID_REQUEST;
}
+/* This is only called by cfg_apply in priority.c, in allowlisting mode. */
+void _gnutls_digest_mark_insecure_all(void)
+{
+#ifndef DISABLE_SYSTEM_CONFIG
+ mac_entry_st *p;
+
+ for(p = hash_algorithms; p->name != NULL; p++) {
+ p->flags |= GNUTLS_MAC_FLAG_PREIMAGE_INSECURE_REVERTIBLE |
+ GNUTLS_MAC_FLAG_PREIMAGE_INSECURE;
+ }
+
+#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.
+ *
+ * Since: 3.7.3
+ */
+int
+gnutls_digest_set_secure(gnutls_digest_algorithm_t dig,
+ unsigned int secure)
+{
+#ifndef DISABLE_SYSTEM_CONFIG
+ mac_entry_st *p;
+
+ for(p = hash_algorithms; p->name != NULL; p++) {
+ if (p->oid != NULL && p->id == (gnutls_mac_algorithm_t)dig) {
+ if (!(p->flags & GNUTLS_MAC_FLAG_PREIMAGE_INSECURE_REVERTIBLE)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ if (secure) {
+ p->flags &= ~GNUTLS_MAC_FLAG_PREIMAGE_INSECURE;
+ } else {
+ p->flags |= GNUTLS_MAC_FLAG_PREIMAGE_INSECURE;
+ }
+ return 0;
+ }
+ }
+
+#endif
+ return GNUTLS_E_INVALID_REQUEST;
+}
+
unsigned _gnutls_digest_is_insecure(gnutls_digest_algorithm_t dig)
{
const mac_entry_st *p;
@@ -320,6 +372,21 @@ unsigned _gnutls_digest_is_insecure(gnutls_digest_algorithm_t dig)
return 1;
}
+bool _gnutls_digest_is_insecure2(gnutls_digest_algorithm_t dig, unsigned flags)
+{
+ const mac_entry_st *p;
+
+ for(p = hash_algorithms; p->name != NULL; p++) {
+ if (p->oid != NULL && p->id == (gnutls_mac_algorithm_t)dig) {
+ return (p->flags & GNUTLS_MAC_FLAG_PREIMAGE_INSECURE &&
+ !(flags & GNUTLS_MAC_FLAG_ALLOW_INSECURE_REVERTIBLE &&
+ p->flags & GNUTLS_MAC_FLAG_PREIMAGE_INSECURE_REVERTIBLE));
+ }
+ }
+
+ return true;
+}
+
/**
* gnutls_mac_get_id:
* @name: is a MAC algorithm name
diff --git a/lib/algorithms/protocols.c b/lib/algorithms/protocols.c
index a3d1edeb6c..b0f3e0bc30 100644
--- a/lib/algorithms/protocols.c
+++ b/lib/algorithms/protocols.c
@@ -198,14 +198,62 @@ version_is_valid_for_session(gnutls_session_t session,
return 0;
}
-int _gnutls_version_mark_disabled(const char *name)
+/* This is only called by cfg_apply in priority.c, in blocklisting mode. */
+int _gnutls_version_mark_disabled(gnutls_protocol_t version)
{
#ifndef DISABLE_SYSTEM_CONFIG
version_entry_st *p;
for (p = sup_versions; p->name != NULL; p++)
- if (c_strcasecmp(p->name, name) == 0) {
- p->supported = 0;
+ if (p->id == version) {
+ p->supported = false;
+ return 0;
+ }
+
+#endif
+ return GNUTLS_E_INVALID_REQUEST;
+}
+
+/* This is only called by cfg_apply in priority.c, in allowlisting mode. */
+void _gnutls_version_mark_revertible_all(void)
+{
+#ifndef DISABLE_SYSTEM_CONFIG
+ version_entry_st *p;
+
+ for (p = sup_versions; p->name != NULL; p++) {
+ 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.
+ *
+ * Returns: 0 on success or negative error code otherwise.
+ *
+ * Since: 3.7.3
+ */
+int
+gnutls_protocol_set_enabled(gnutls_protocol_t version,
+ unsigned int enabled)
+{
+#ifndef DISABLE_SYSTEM_CONFIG
+ version_entry_st *p;
+
+ for (p = sup_versions; p->name != NULL; p++)
+ if (p->id == version) {
+ if (!p->supported_revertible) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ p->supported = enabled;
return 0;
}
@@ -469,6 +517,25 @@ const gnutls_protocol_t *gnutls_protocol_list(void)
return supported_protocols;
}
+/* Return all versions, including non-supported ones.
+ */
+const gnutls_protocol_t *_gnutls_protocol_list(void)
+{
+ const version_entry_st *p;
+ static gnutls_protocol_t protocols[MAX_ALGOS] = { 0 };
+
+ if (protocols[0] == 0) {
+ int i = 0;
+
+ for (p = sup_versions; p->name != NULL; p++) {
+ protocols[i++] = p->id;
+ }
+ protocols[i++] = 0;
+ }
+
+ return protocols;
+}
+
/* Returns a version number given the major and minor numbers.
*/
gnutls_protocol_t _gnutls_version_get(uint8_t major, uint8_t minor)
diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c
index 2728a54478..543bd19bb5 100644
--- a/lib/algorithms/sign.c
+++ b/lib/algorithms/sign.c
@@ -453,16 +453,23 @@ unsigned gnutls_sign_is_secure(gnutls_sign_algorithm_t algorithm)
bool _gnutls_sign_is_secure2(const gnutls_sign_entry_st *se, unsigned int flags)
{
- if (se->hash != GNUTLS_DIG_UNKNOWN && _gnutls_digest_is_insecure(se->hash))
- return gnutls_assert_val(0);
+ if (se->hash != GNUTLS_DIG_UNKNOWN &&
+ _gnutls_digest_is_insecure2(se->hash,
+ flags & GNUTLS_SIGN_FLAG_ALLOW_INSECURE_REVERTIBLE ?
+ GNUTLS_MAC_FLAG_ALLOW_INSECURE_REVERTIBLE :
+ 0)) {
+ return gnutls_assert_val(false);
+ }
- if (flags & GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS)
- return (se->slevel==_SECURE)?1:0;
- else
- return (se->slevel==_SECURE || se->slevel == _INSECURE_FOR_CERTS)?1:0;
+ return (flags & GNUTLS_SIGN_FLAG_SECURE_FOR_CERTS ?
+ se->slevel == _SECURE :
+ (se->slevel == _SECURE || se->slevel == _INSECURE_FOR_CERTS)) ||
+ (flags & GNUTLS_SIGN_FLAG_ALLOW_INSECURE_REVERTIBLE &&
+ se->flags & GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE);
}
-int _gnutls_sign_mark_insecure(const char *name, hash_security_level_t level)
+/* This is only called by cfg_apply in priority.c, in blocklisting mode. */
+int _gnutls_sign_mark_insecure(gnutls_sign_algorithm_t sign, hash_security_level_t level)
{
#ifndef DISABLE_SYSTEM_CONFIG
gnutls_sign_entry_st *p;
@@ -471,7 +478,8 @@ int _gnutls_sign_mark_insecure(const char *name, hash_security_level_t level)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
for(p = sign_algorithms; p->name != NULL; p++) {
- if (c_strcasecmp(p->name, name) == 0) {
+ if (p->id && p->id == sign) {
+ if (p->slevel < level)
p->slevel = level;
return 0;
}
@@ -480,6 +488,106 @@ int _gnutls_sign_mark_insecure(const char *name, hash_security_level_t level)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
}
+/* This is only called by cfg_apply in priority.c, in allowlisting mode. */
+void _gnutls_sign_mark_insecure_all(hash_security_level_t level)
+{
+#ifndef DISABLE_SYSTEM_CONFIG
+ gnutls_sign_entry_st *p;
+
+ for(p = sign_algorithms; p->name != NULL; p++) {
+ if (p->slevel < level)
+ p->slevel = level;
+ p->flags |= GNUTLS_SIGN_FLAG_INSECURE_REVERTIBLE;
+ }
+#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
+ 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) {
+ 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.
+ *
+ * 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;
+ }
+ }
+ return 0;
+ }
+ }
+#endif
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+}
+
/**
* gnutls_sign_is_secure2:
* @algorithm: is a sign algorithm
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 88f0c28a00..1dbe404857 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -665,6 +665,8 @@ typedef struct gnutls_group_entry_st {
#define GNUTLS_MAC_FLAG_PREIMAGE_INSECURE 1 /* if this algorithm should not be trusted for pre-image attacks */
#define GNUTLS_MAC_FLAG_CONTINUOUS_MAC (1 << 1) /* if this MAC should be used in a 'continuous' way in TLS */
+#define GNUTLS_MAC_FLAG_PREIMAGE_INSECURE_REVERTIBLE (1 << 2) /* if this algorithm should not be trusted for pre-image attacks, but can be enabled through API */
+#define GNUTLS_MAC_FLAG_ALLOW_INSECURE_REVERTIBLE (1 << 3) /* when checking with _gnutls_digest_is_insecure2, don't treat revertible setting as fatal */
/* This structure is used both for MACs and digests
*/
typedef struct mac_entry_st {
@@ -688,6 +690,7 @@ typedef struct {
uint8_t minor; /* defined by the protocol */
transport_t transport; /* Type of transport, stream or datagram */
bool supported; /* 0 not supported, > 0 is supported */
+ bool supported_revertible;
bool explicit_iv;
bool extensions; /* whether it supports extensions */
bool selectable_sighash; /* whether signatures can be selected */
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index d69b29b443..1e883aa8eb 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -1438,6 +1438,17 @@ const char *
gnutls_mac_algorithm_t * mac,
gnutls_protocol_t * min_version);
+ /* functions for run-time enablement of algorithms */
+int gnutls_ecc_curve_set_enabled(gnutls_ecc_curve_t curve,
+ unsigned int enabled);
+int gnutls_sign_set_secure(gnutls_sign_algorithm_t sign, unsigned int secure);
+int gnutls_sign_set_secure_for_certs(gnutls_sign_algorithm_t sign,
+ unsigned int secure);
+int gnutls_digest_set_secure(gnutls_digest_algorithm_t dig,
+ unsigned int secure);
+int gnutls_protocol_set_enabled(gnutls_protocol_t version,
+ unsigned int enabled);
+
/* error functions */
int gnutls_error_is_fatal(int error) __GNUTLS_CONST__;
int gnutls_error_to_alert(int err, int *level);
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 0e6b379314..dc50c6dba9 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1355,6 +1355,18 @@ GNUTLS_3_7_2
*;
} GNUTLS_3_7_0;
+GNUTLS_3_7_3
+{
+ global:
+ gnutls_ecc_curve_set_enabled;
+ gnutls_sign_set_secure;
+ gnutls_sign_set_secure_for_certs;
+ gnutls_digest_set_secure;
+ gnutls_protocol_set_enabled;
+ local:
+ *;
+} GNUTLS_3_7_2;
+
GNUTLS_FIPS140_3_4 {
global:
gnutls_cipher_self_test;
diff --git a/lib/priority.c b/lib/priority.c
index 2851d51474..54d7b1bb45 100644
--- a/lib/priority.c
+++ b/lib/priority.c
@@ -53,6 +53,13 @@
/* This function is used by the test suite */
char *_gnutls_resolve_priorities(const char* priorities);
+
+/* This variable points to either a constant value (DEFAULT_PRIORITY_STRING or
+ * externally assigned) or heap-allocated
+ * system_wide_config.default_priority_string. We can't move this to the
+ * system_wide_config struct, because this variable is part of (private) ABI
+ * exported for testing.
+ */
const char *_gnutls_default_priority_string = DEFAULT_PRIORITY_STRING;
static void prio_remove(priority_st * priority_list, unsigned int algo);
@@ -701,6 +708,7 @@ gnutls_priority_set(gnutls_session_t session, gnutls_priority_t priority)
#define LEVEL_SUITEB128 "SUITEB128"
#define LEVEL_SUITEB192 "SUITEB192"
#define LEVEL_LEGACY "LEGACY"
+#define LEVEL_SYSTEM "SYSTEM"
struct priority_groups_st {
const char *name;
@@ -1000,17 +1008,32 @@ static void dummy_func(gnutls_priority_t c)
#include <priority_options.h>
-/* Configuration read from the config file */
struct cfg {
- gnutls_certificate_verification_profiles_t verification_profile;
+ bool allowlisting;
+
name_val_array_t priority_strings;
- unsigned default_priority_string;
- unsigned disabled_ciphers[MAX_ALGOS+1];
- unsigned disabled_macs[MAX_ALGOS+1];
- unsigned disabled_groups[MAX_ALGOS+1];
- unsigned disabled_kxs[MAX_ALGOS+1];
+ 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;
@@ -1020,18 +1043,17 @@ static const char *system_priority_file = SYSTEM_PRIORITY_FILE;
static time_t system_priority_last_mod = 0;
static unsigned system_priority_file_loaded = 0;
+#define GLOBAL_SECTION "global"
#define CUSTOM_PRIORITY_SECTION "priorities"
#define OVERRIDES_SECTION "overrides"
#define MAX_ALGO_NAME 2048
static void _clear_default_system_priority(void)
{
- if (system_wide_config.default_priority_string) {
- gnutls_free(_gnutls_default_priority_string);
- _gnutls_default_priority_string = DEFAULT_PRIORITY_STRING;
- system_wide_config.default_priority_string = 0;
- }
+ gnutls_free(system_wide_config.default_priority_string);
+ system_wide_config.default_priority_string = NULL;
+ _gnutls_default_priority_string = DEFAULT_PRIORITY_STRING;
}
gnutls_certificate_verification_profiles_t _gnutls_get_system_wide_verification_profile(void)
@@ -1059,15 +1081,223 @@ static char *clear_spaces(const char *str, char out[MAX_ALGO_NAME])
return out;
}
-/* This function parses a gnutls configuration file and updates internal
- * settings accordingly.
+struct ini_ctx {
+ struct cfg cfg;
+
+ gnutls_digest_algorithm_t *hashes;
+ size_t hashes_size;
+ gnutls_sign_algorithm_t *sigs;
+ size_t sigs_size;
+ gnutls_sign_algorithm_t *sigs_for_cert;
+ size_t sigs_for_cert_size;
+ gnutls_protocol_t *versions;
+ size_t versions_size;
+ gnutls_ecc_curve_t *curves;
+ size_t curves_size;
+};
+
+static inline void
+ini_ctx_deinit(struct ini_ctx *ctx)
+{
+ cfg_deinit(&ctx->cfg);
+ gnutls_free(ctx->hashes);
+ gnutls_free(ctx->sigs);
+ gnutls_free(ctx->sigs_for_cert);
+ gnutls_free(ctx->versions);
+ 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)
+{
+ size_t i;
+
+ cfg_steal(cfg, &ctx->cfg);
+
+ if (cfg->default_priority_string) {
+ _gnutls_default_priority_string = cfg->default_priority_string;
+ }
+
+ 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);
+ 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;
+ }
+ }
+ 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;
+ }
+ }
+ _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;
+ }
+ }
+ _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;
+ }
+ }
+ 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]);
+ if (unlikely(ret < 0)) {
+ return ret;
+ }
+ }
+ for (i = 0; i < ctx->sigs_size; i++) {
+ int 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);
+ if (unlikely(ret < 0)) {
+ return ret;
+ }
+ }
+ for (i = 0; i < ctx->versions_size; i++) {
+ int 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]);
+ if (unlikely(ret < 0)) {
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* This function parses the global section of the configuration file.
+ */
+static int global_ini_handler(void *ctx, const char *section, const char *name, const char *value)
+{
+ char *p;
+ char str[MAX_ALGO_NAME];
+ struct cfg *cfg = ctx;
+
+ if (section != NULL && c_strcasecmp(section, GLOBAL_SECTION) == 0) {
+ if (c_strcasecmp(name, "override-mode") == 0) {
+ p = clear_spaces(value, str);
+ if (c_strcasecmp(value, "allowlist") == 0) {
+ cfg->allowlisting = true;
+ } else if (c_strcasecmp(value, "blocklist") == 0) {
+ cfg->allowlisting = false;
+ } else {
+ _gnutls_debug_log("cfg: unknown override mode %s\n",
+ p);
+ if (fail_on_invalid_config)
+ return 0;
+ }
+ } else {
+ _gnutls_debug_log("unknown parameter %s\n", name);
+ if (fail_on_invalid_config)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static bool
+override_allowed(bool allowlisting, const char *name)
+{
+ static const struct {
+ const char *allowlist_name;
+ const char *blocklist_name;
+ } names[] = {
+ { "secure-hash", "insecure-hash" },
+ { "secure-sig", "insecure-sig" },
+ { "secure-sig-for-cert", "insecure-sig-for-cert" },
+ { "enabled-version", "disabled-version" },
+ { "enabled-curve", "disabled-curve" },
+ { "tls-enabled-cipher", "tls-disabled-cipher" },
+ { "tls-enabled-group", "tls-disabled-group" },
+ { "tls-enabled-kx", "tls-disabled-kx" },
+ { "tls-enabled-mac", "tls-disabled-mac" }
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
+ if (c_strcasecmp(name,
+ allowlisting ?
+ names[i].blocklist_name :
+ names[i].allowlist_name) == 0)
+ return false;
+ }
+
+ return true;
+}
+
+/* This function parses a gnutls configuration file. Updating internal settings
+ * according to the parsed configuration is done by cfg_apply.
*/
static int cfg_ini_handler(void *_ctx, const char *section, const char *name, const char *value)
{
char *p;
- int ret, type;
+ int ret;
unsigned i;
char str[MAX_ALGO_NAME];
+ struct ini_ctx *ctx = _ctx;
+ struct cfg *cfg = &ctx->cfg;
/* Note that we intentionally overwrite the value above; inih does
* not use that value after we handle it. */
@@ -1076,86 +1306,238 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
if (section == NULL || section[0] == 0 || c_strcasecmp(section, CUSTOM_PRIORITY_SECTION)==0) {
_gnutls_debug_log("cfg: adding priority: %s -> %s\n", name, value);
- ret = _name_val_array_append(&system_wide_config.priority_strings, name, value);
+ ret = _name_val_array_append(&cfg->priority_strings, name, value);
if (ret < 0)
return 0;
} else if (c_strcasecmp(section, OVERRIDES_SECTION)==0) {
- if (c_strcasecmp(name, "default-priority-string")==0) {
- _clear_default_system_priority();
+ if (!override_allowed(cfg->allowlisting, name)) {
+ _gnutls_debug_log("cfg: %s is not allowed in this mode\n",
+ name);
+ if (fail_on_invalid_config)
+ return 0;
+ } else if (c_strcasecmp(name, "default-priority-string")==0) {
+ if (cfg->default_priority_string) {
+ gnutls_free(cfg->default_priority_string);
+ cfg->default_priority_string = NULL;
+ }
p = clear_spaces(value, str);
_gnutls_debug_log("cfg: setting default-priority-string to %s\n", p);
if (strlen(p) > 0) {
- _gnutls_default_priority_string = gnutls_strdup(p);
- if (!_gnutls_default_priority_string) {
- _gnutls_default_priority_string = DEFAULT_PRIORITY_STRING;
+ cfg->default_priority_string = gnutls_strdup(p);
+ if (!cfg->default_priority_string) {
_gnutls_debug_log("cfg: failed setting default-priority-string\n");
return 0;
}
- system_wide_config.default_priority_string = 1;
} else {
_gnutls_debug_log("cfg: empty default-priority-string, using default\n");
if (fail_on_invalid_config)
return 0;
}
- } else if (c_strcasecmp(name, "insecure-hash")==0) {
+ } else if (c_strcasecmp(name, "insecure-hash") == 0 ||
+ c_strcasecmp(name, "secure-hash") == 0) {
+ gnutls_digest_algorithm_t dig, *tmp;
+
p = clear_spaces(value, str);
- _gnutls_debug_log("cfg: marking hash %s as insecure\n",
- p);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: marking hash %s as secure\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: marking hash %s as insecure\n",
+ p);
+ }
- ret = _gnutls_digest_mark_insecure(p);
- if (ret < 0) {
+ dig = gnutls_digest_get_id(p);
+ if (dig == GNUTLS_DIG_UNKNOWN) {
_gnutls_debug_log("cfg: found unknown hash %s in %s\n",
p, name);
if (fail_on_invalid_config)
return 0;
+ goto exit;
}
- } else if (c_strcasecmp(name, "insecure-sig")==0 || c_strcasecmp(name, "insecure-sig-for-cert")==0) {
+ tmp = _gnutls_reallocarray(ctx->hashes,
+ ctx->hashes_size + 1,
+ sizeof(gnutls_digest_algorithm_t));
+ if (!tmp) {
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: failed marking hash %s as secure\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: failed marking hash %s as insecure\n",
+ p);
+ }
+ if (fail_on_invalid_config)
+ return 0;
+ goto exit;
+ }
+
+ ctx->hashes = tmp;
+ ctx->hashes[ctx->hashes_size] = dig;
+ ctx->hashes_size++;
+ } else if (c_strcasecmp(name, "insecure-sig") == 0 ||
+ c_strcasecmp(name, "secure-sig") == 0) {
+ gnutls_sign_algorithm_t sig, *tmp;
+
p = clear_spaces(value, str);
- if (c_strcasecmp(name, "insecure-sig")==0) {
- type = _INSECURE;
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: marking signature %s as secure\n",
+ p);
+ } else {
_gnutls_debug_log("cfg: marking signature %s as insecure\n",
p);
+ }
+
+ sig = gnutls_sign_get_id(p);
+ if (sig == GNUTLS_SIGN_UNKNOWN) {
+ _gnutls_debug_log("cfg: found unknown signature algorithm %s in %s\n",
+ p, name);
+ if (fail_on_invalid_config)
+ return 0;
+ goto exit;
+ }
+ tmp = _gnutls_reallocarray(ctx->sigs,
+ ctx->sigs_size + 1,
+ sizeof(gnutls_sign_algorithm_t));
+ if (!tmp) {
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: failed marking signature %s as secure\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: failed marking signature %s as insecure\n",
+ p);
+ }
+ if (fail_on_invalid_config)
+ return 0;
+ goto exit;
+ }
+
+ ctx->sigs = tmp;
+ ctx->sigs[ctx->sigs_size] = sig;
+ ctx->sigs_size++;
+ } else if (c_strcasecmp(name, "insecure-sig-for-cert") == 0 ||
+ c_strcasecmp(name, "secure-sig-for-cert") == 0) {
+ gnutls_sign_algorithm_t sig, *tmp;
+
+ p = clear_spaces(value, str);
+
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: marking signature %s as secure for certs\n",
+ p);
} else {
_gnutls_debug_log("cfg: marking signature %s as insecure for certs\n",
p);
- type = _INSECURE_FOR_CERTS;
}
- ret = _gnutls_sign_mark_insecure(p, type);
- if (ret < 0) {
+ sig = gnutls_sign_get_id(p);
+ if (sig == GNUTLS_SIGN_UNKNOWN) {
_gnutls_debug_log("cfg: found unknown signature algorithm %s in %s\n",
p, name);
if (fail_on_invalid_config)
return 0;
+ goto exit;
}
- } else if (c_strcasecmp(name, "disabled-version")==0) {
+ tmp = _gnutls_reallocarray(ctx->sigs_for_cert,
+ ctx->sigs_for_cert_size + 1,
+ sizeof(gnutls_sign_algorithm_t));
+ if (!tmp) {
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: failed marking signature %s as secure for certs\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: failed marking signature %s as insecure for certs\n",
+ p);
+ }
+ if (fail_on_invalid_config)
+ return 0;
+ goto exit;
+ }
+
+ ctx->sigs_for_cert = tmp;
+ ctx->sigs_for_cert[ctx->sigs_for_cert_size] = sig;
+ ctx->sigs_for_cert_size++;
+ } else if (c_strcasecmp(name, "disabled-version") == 0 ||
+ c_strcasecmp(name, "enabled-version") == 0) {
+ gnutls_protocol_t prot, *tmp;
+
p = clear_spaces(value, str);
- _gnutls_debug_log("cfg: disabling version %s\n",
- p);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: enabling version %s\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: disabling version %s\n",
+ p);
+ }
- ret = _gnutls_version_mark_disabled(p);
- if (ret < 0) {
+ prot = gnutls_protocol_get_id(p);
+ if (prot == GNUTLS_VERSION_UNKNOWN) {
_gnutls_debug_log("cfg: found unknown version %s in %s\n",
p, name);
if (fail_on_invalid_config)
return 0;
+ goto exit;
}
- } else if (c_strcasecmp(name, "disabled-curve")==0) {
+ tmp = _gnutls_reallocarray(ctx->versions,
+ ctx->versions_size + 1,
+ sizeof(gnutls_protocol_t));
+ if (!tmp) {
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: failed enabling version %s\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: failed disabling version %s\n",
+ p);
+ }
+ if (fail_on_invalid_config)
+ return 0;
+ goto exit;
+ }
+
+ ctx->versions = tmp;
+ ctx->versions[ctx->versions_size] = prot;
+ ctx->versions_size++;
+ } else if (c_strcasecmp(name, "disabled-curve") == 0 ||
+ c_strcasecmp(name, "enabled-curve") == 0) {
+ gnutls_ecc_curve_t curve, *tmp;
+
p = clear_spaces(value, str);
- _gnutls_debug_log("cfg: disabling curve %s\n",
- p);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: enabling curve %s\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: disabling curve %s\n",
+ p);
+ }
- ret = _gnutls_ecc_curve_mark_disabled(p);
- if (ret < 0) {
+ curve = gnutls_ecc_curve_get_id(p);
+ if (curve == GNUTLS_ECC_CURVE_INVALID) {
_gnutls_debug_log("cfg: found unknown curve %s in %s\n",
p, name);
if (fail_on_invalid_config)
return 0;
+ goto exit;
}
+ tmp = _gnutls_reallocarray(ctx->curves,
+ ctx->curves_size + 1,
+ sizeof(gnutls_ecc_curve_t));
+ if (!tmp) {
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: failed enabling curve %s\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: failed disabling curve %s\n",
+ p);
+ }
+ if (fail_on_invalid_config)
+ return 0;
+ goto exit;
+ }
+
+ ctx->curves = tmp;
+ ctx->curves[ctx->curves_size] = curve;
+ ctx->curves_size++;
} else if (c_strcasecmp(name, "min-verification-profile")==0) {
gnutls_certificate_verification_profiles_t profile;
profile = gnutls_certificate_verification_profile_get_id(value);
@@ -1165,47 +1547,65 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
value, name);
if (fail_on_invalid_config)
return 0;
+ goto exit;
}
- system_wide_config.verification_profile = profile;
- } else if (c_strcasecmp(name, "tls-disabled-cipher")==0) {
- unsigned algo;
+ cfg->verification_profile = profile;
+ } else if (c_strcasecmp(name, "tls-disabled-cipher") == 0 ||
+ c_strcasecmp(name, "tls-enabled-cipher") == 0) {
+ gnutls_cipher_algorithm_t algo;
p = clear_spaces(value, str);
- _gnutls_debug_log("cfg: disabling cipher %s for TLS\n",
- p);
-
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: enabling cipher %s for TLS\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: disabling cipher %s for TLS\n",
+ p);
+ }
algo = gnutls_cipher_get_id(p);
- if (algo == 0) {
+ if (algo == GNUTLS_CIPHER_UNKNOWN) {
_gnutls_debug_log("cfg: unknown algorithm %s listed at %s\n",
p, name);
if (fail_on_invalid_config)
return 0;
+ goto exit;
}
i = 0;
- while (system_wide_config.disabled_ciphers[i] != 0)
+ while (cfg->ciphers[i] != 0)
i++;
if (i > MAX_ALGOS-1) {
- _gnutls_debug_log("cfg: too many (%d) disabled ciphers from %s\n",
- i, name);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: too many (%d) enabled ciphers from %s\n",
+ i, name);
+ } else {
+ _gnutls_debug_log("cfg: too many (%d) disabled ciphers from %s\n",
+ i, name);
+ }
if (fail_on_invalid_config)
return 0;
goto exit;
}
- system_wide_config.disabled_ciphers[i] = algo;
- system_wide_config.disabled_ciphers[i+1] = 0;
+ cfg->ciphers[i] = algo;
+ cfg->ciphers[i+1] = 0;
- } else if (c_strcasecmp(name, "tls-disabled-mac")==0) {
- unsigned algo;
+ } else if (c_strcasecmp(name, "tls-disabled-mac") == 0 ||
+ c_strcasecmp(name, "tls-enabled-mac") == 0) {
+ gnutls_mac_algorithm_t algo;
p = clear_spaces(value, str);
- _gnutls_debug_log("cfg: disabling MAC %s for TLS\n",
- p);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: enabling MAC %s for TLS\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: disabling MAC %s for TLS\n",
+ p);
+ }
algo = gnutls_mac_get_id(p);
if (algo == 0) {
@@ -1217,30 +1617,41 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
}
i = 0;
- while (system_wide_config.disabled_macs[i] != 0)
+ while (cfg->macs[i] != 0)
i++;
if (i > MAX_ALGOS-1) {
- _gnutls_debug_log("cfg: too many (%d) disabled MACs from %s\n",
- i, name);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: too many (%d) enabled MACs from %s\n",
+ i, name);
+ } else {
+ _gnutls_debug_log("cfg: too many (%d) disabled MACs from %s\n",
+ i, name);
+ }
if (fail_on_invalid_config)
return 0;
goto exit;
}
- system_wide_config.disabled_macs[i] = algo;
- system_wide_config.disabled_macs[i+1] = 0;
- } else if (c_strcasecmp(name, "tls-disabled-group")==0) {
- unsigned algo;
+ cfg->macs[i] = algo;
+ cfg->macs[i+1] = 0;
+ } else if (c_strcasecmp(name, "tls-disabled-group") == 0 ||
+ c_strcasecmp(name, "tls-enabled-group") == 0) {
+ gnutls_group_t algo;
p = clear_spaces(value, str);
- if (strlen(p) > 6)
- p += 6; // skip GROUP-
+ if (c_strncasecmp(p, "GROUP-", 6) == 0)
+ p += 6;
- _gnutls_debug_log("cfg: disabling group %s for TLS\n",
- p);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: enabling group %s for TLS\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: disabling group %s for TLS\n",
+ p);
+ }
- algo = gnutls_group_get_id(p);
+ algo = _gnutls_group_get_id(p);
if (algo == 0) {
_gnutls_debug_log("cfg: unknown group %s listed at %s\n",
p, name);
@@ -1250,25 +1661,36 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
}
i = 0;
- while (system_wide_config.disabled_groups[i] != 0)
+ while (cfg->groups[i] != 0)
i++;
if (i > MAX_ALGOS-1) {
- _gnutls_debug_log("cfg: too many (%d) disabled groups from %s\n",
- i, name);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: too many (%d) enabled groups from %s\n",
+ i, name);
+ } else {
+ _gnutls_debug_log("cfg: too many (%d) disabled groups from %s\n",
+ i, name);
+ }
if (fail_on_invalid_config)
return 0;
goto exit;
}
- system_wide_config.disabled_groups[i] = algo;
- system_wide_config.disabled_groups[i+1] = 0;
- } else if (c_strcasecmp(name, "tls-disabled-kx")==0) {
+ cfg->groups[i] = algo;
+ cfg->groups[i+1] = 0;
+ } else if (c_strcasecmp(name, "tls-disabled-kx") == 0 ||
+ c_strcasecmp(name, "tls-enabled-kx") == 0) {
unsigned algo;
p = clear_spaces(value, str);
- _gnutls_debug_log("cfg: disabling key exchange %s for TLS\n",
- p);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: enabling key exchange %s for TLS\n",
+ p);
+ } else {
+ _gnutls_debug_log("cfg: disabling key exchange %s for TLS\n",
+ p);
+ }
algo = gnutls_kx_get_id(p);
if (algo == 0) {
@@ -1280,24 +1702,29 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
}
i = 0;
- while (system_wide_config.disabled_kxs[i] != 0)
+ while (cfg->kxs[i] != 0)
i++;
if (i > MAX_ALGOS-1) {
- _gnutls_debug_log("cfg: too many (%d) disabled key exchanges from %s\n",
- i, name);
+ if (cfg->allowlisting) {
+ _gnutls_debug_log("cfg: too many (%d) enabled key exchanges from %s\n",
+ i, name);
+ } else {
+ _gnutls_debug_log("cfg: too many (%d) disabled key exchanges from %s\n",
+ i, name);
+ }
if (fail_on_invalid_config)
return 0;
goto exit;
}
- system_wide_config.disabled_kxs[i] = algo;
- system_wide_config.disabled_kxs[i+1] = 0;
+ cfg->kxs[i] = algo;
+ cfg->kxs[i+1] = 0;
} else {
_gnutls_debug_log("unknown parameter %s\n", name);
if (fail_on_invalid_config)
return 0;
}
- } else {
+ } else if (c_strcasecmp(section, GLOBAL_SECTION) != 0) {
_gnutls_debug_log("cfg: unknown section %s\n",
section);
if (fail_on_invalid_config)
@@ -1308,11 +1735,124 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name, co
return 1;
}
+static int
+update_system_wide_priority_string(void)
+{
+ gnutls_buffer_st buf;
+ int ret;
+ size_t i;
+
+ _gnutls_buffer_init(&buf);
+
+ ret = _gnutls_buffer_append_str(&buf, "NONE");
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ for (i = 0; system_wide_config.kxs[i] != 0; i++) {
+ ret = _gnutls_buffer_append_str(&buf, ":+");
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ ret = _gnutls_buffer_append_str(&buf,
+ gnutls_kx_get_name(system_wide_config.kxs[i]));
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+ }
+
+ for (i = 0; system_wide_config.groups[i] != 0; i++) {
+ ret = _gnutls_buffer_append_str(&buf, ":+GROUP-");
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ ret = _gnutls_buffer_append_str(&buf,
+ gnutls_group_get_name(system_wide_config.groups[i]));
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+ }
+
+ for (i = 0; system_wide_config.ciphers[i] != 0; i++) {
+ ret = _gnutls_buffer_append_str(&buf, ":+");
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ ret = _gnutls_buffer_append_str(&buf,
+ gnutls_cipher_get_name(system_wide_config.ciphers[i]));
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+ }
+
+ for (i = 0; system_wide_config.macs[i] != 0; i++) {
+ ret = _gnutls_buffer_append_str(&buf, ":+");
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ ret = _gnutls_buffer_append_str(&buf,
+ gnutls_mac_get_name(system_wide_config.macs[i]));
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+ }
+
+ for (i = 0; system_wide_config.sigs[i] != 0; i++) {
+ ret = _gnutls_buffer_append_str(&buf, ":+SIGN-");
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ ret = _gnutls_buffer_append_str(&buf,
+ gnutls_sign_get_name(system_wide_config.sigs[i]));
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+ }
+
+ for (i = 0; system_wide_config.versions[i] != 0; i++) {
+ ret = _gnutls_buffer_append_str(&buf, ":+VERS-");
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+
+ ret = _gnutls_buffer_append_str(&buf,
+ gnutls_protocol_get_name(system_wide_config.versions[i]));
+ if (ret < 0) {
+ _gnutls_buffer_clear(&buf);
+ return ret;
+ }
+ }
+
+ gnutls_free(system_wide_config.priority_string);
+ system_wide_config.priority_string = gnutls_strdup((char *)buf.data);
+ _gnutls_buffer_clear(&buf);
+
+ return 0;
+}
+
static int _gnutls_update_system_priorities(void)
{
int ret, err = 0;
struct stat sb;
FILE *fp;
+ struct ini_ctx ctx;
ret = gnutls_rwlock_rdlock(&system_wide_config_rwlock);
if (ret < 0) {
@@ -1351,19 +1891,50 @@ static int _gnutls_update_system_priorities(void)
system_priority_file_loaded = 0;
_name_val_array_clear(&system_wide_config.priority_strings);
+ 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;
}
- err = ini_parse_file(fp, cfg_ini_handler, NULL);
+ /* 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);
+
+ if (system_wide_config.allowlisting) {
+ ret = update_system_wide_priority_string();
+ if (ret < 0) {
+ _gnutls_debug_log("cfg: unable to build priority string: %s\n",
+ gnutls_strerror(ret));
+ if (fail_on_invalid_config)
+ exit(1);
+ goto out;
+ }
+ }
_gnutls_debug_log("cfg: loaded system priority %s mtime %lld\n",
system_priority_file,
@@ -1405,6 +1976,7 @@ void _gnutls_load_system_priorities(void)
void _gnutls_unload_system_priorities(void)
{
_name_val_array_clear(&system_wide_config.priority_strings);
+ gnutls_free(system_wide_config.priority_string);
_clear_default_system_priority();
system_priority_last_mod = 0;
}
@@ -1494,9 +2066,13 @@ char *_gnutls_resolve_priorities(const char* priorities)
gnutls_strerror(ret));
break;
}
-
- p = _name_val_array_value(system_wide_config.priority_strings,
- ss, ss_len);
+ if (system_wide_config.allowlisting &&
+ ss_len == sizeof(LEVEL_SYSTEM) - 1 &&
+ strncmp(LEVEL_SYSTEM, ss, ss_len) == 0) {
+ p = system_wide_config.priority_string;
+ } else {
+ p = _name_val_array_value(system_wide_config.priority_strings, ss, ss_len);
+ }
_gnutls_debug_log("resolved '%.*s' to '%s', next '%.*s'\n",
ss_len, ss, S(p), ss_next_len, S(ss_next));
@@ -1604,48 +2180,52 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
return gnutls_assert_val(ret);
}
- /* disable key exchanges which are globally disabled */
- z = 0;
- while (system_wide_config.disabled_kxs[z] != 0) {
- for (i = j = 0; i < priority_cache->_kx.num_priorities; i++) {
- if (priority_cache->_kx.priorities[i] != system_wide_config.disabled_kxs[z])
- priority_cache->_kx.priorities[j++] = priority_cache->_kx.priorities[i];
+ /* in blocklisting mode, apply system wide disablement of key exchanges,
+ * groups, MACs, and ciphers. */
+ if (!system_wide_config.allowlisting) {
+ /* disable key exchanges which are globally disabled */
+ z = 0;
+ while (system_wide_config.kxs[z] != 0) {
+ for (i = j = 0; i < priority_cache->_kx.num_priorities; i++) {
+ if (priority_cache->_kx.priorities[i] != system_wide_config.kxs[z])
+ priority_cache->_kx.priorities[j++] = priority_cache->_kx.priorities[i];
+ }
+ priority_cache->_kx.num_priorities = j;
+ z++;
}
- priority_cache->_kx.num_priorities = j;
- z++;
- }
- /* disable groups which are globally disabled */
- z = 0;
- while (system_wide_config.disabled_groups[z] != 0) {
- for (i = j = 0; i < priority_cache->_supported_ecc.num_priorities; i++) {
- if (priority_cache->_supported_ecc.priorities[i] != system_wide_config.disabled_groups[z])
- priority_cache->_supported_ecc.priorities[j++] = priority_cache->_supported_ecc.priorities[i];
+ /* disable groups which are globally disabled */
+ z = 0;
+ while (system_wide_config.groups[z] != 0) {
+ for (i = j = 0; i < priority_cache->_supported_ecc.num_priorities; i++) {
+ if (priority_cache->_supported_ecc.priorities[i] != system_wide_config.groups[z])
+ priority_cache->_supported_ecc.priorities[j++] = priority_cache->_supported_ecc.priorities[i];
+ }
+ priority_cache->_supported_ecc.num_priorities = j;
+ z++;
}
- priority_cache->_supported_ecc.num_priorities = j;
- z++;
- }
- /* disable ciphers which are globally disabled */
- z = 0;
- while (system_wide_config.disabled_ciphers[z] != 0) {
- for (i = j = 0; i < priority_cache->_cipher.num_priorities; i++) {
- if (priority_cache->_cipher.priorities[i] != system_wide_config.disabled_ciphers[z])
- priority_cache->_cipher.priorities[j++] = priority_cache->_cipher.priorities[i];
+ /* disable ciphers which are globally disabled */
+ z = 0;
+ while (system_wide_config.ciphers[z] != 0) {
+ for (i = j = 0; i < priority_cache->_cipher.num_priorities; i++) {
+ if (priority_cache->_cipher.priorities[i] != system_wide_config.ciphers[z])
+ priority_cache->_cipher.priorities[j++] = priority_cache->_cipher.priorities[i];
+ }
+ priority_cache->_cipher.num_priorities = j;
+ z++;
}
- priority_cache->_cipher.num_priorities = j;
- z++;
- }
- /* disable MACs which are globally disabled */
- z = 0;
- while (system_wide_config.disabled_macs[z] != 0) {
- for (i = j = 0; i < priority_cache->_mac.num_priorities; i++) {
- if (priority_cache->_mac.priorities[i] != system_wide_config.disabled_macs[z])
- priority_cache->_mac.priorities[j++] = priority_cache->_mac.priorities[i];
+ /* disable MACs which are globally disabled */
+ z = 0;
+ while (system_wide_config.macs[z] != 0) {
+ for (i = j = 0; i < priority_cache->_mac.num_priorities; i++) {
+ if (priority_cache->_mac.priorities[i] != system_wide_config.macs[z])
+ priority_cache->_mac.priorities[j++] = priority_cache->_mac.priorities[i];
+ }
+ priority_cache->_mac.num_priorities = j;
+ z++;
}
- priority_cache->_mac.num_priorities = j;
- z++;
}
for (j=0;j<priority_cache->_cipher.num_priorities;j++) {
@@ -1813,7 +2393,9 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
* compatible with the protocol's, or the algorithm is
* marked as insecure, then skip. */
if ((se->aid.tls_sem & tls_sig_sem) == 0 ||
- !_gnutls_sign_is_secure2(se, 0)) {
+ !_gnutls_sign_is_secure2(se, system_wide_config.allowlisting ?
+ GNUTLS_SIGN_FLAG_ALLOW_INSECURE_REVERTIBLE :
+ 0)) {
continue;
}
priority_cache->sigalg.entry[priority_cache->sigalg.size++] = se;
@@ -2100,6 +2682,9 @@ gnutls_priority_init(gnutls_priority_t * priority_cache,
(*priority_cache)->min_record_version = 1;
gnutls_atomic_init(&(*priority_cache)->usage_cnt);
+ if (system_wide_config.allowlisting && !priorities) {
+ priorities = "@" LEVEL_SYSTEM;
+ }
if (priorities == NULL) {
priorities = _gnutls_default_priority_string;
resolved_match = 0;
@@ -2233,7 +2818,7 @@ gnutls_priority_init(gnutls_priority_t * priority_cache,
_supported_groups_gost);
} else {
if ((algo =
- gnutls_group_get_id
+ _gnutls_group_get_id
(&broken_list[i][7])) !=
GNUTLS_GROUP_INVALID)
fn(&(*priority_cache)->
diff --git a/tests/Makefile.am b/tests/Makefile.am
index d15595a657..b7fee50488 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -523,6 +523,12 @@ dist_check_SCRIPTS += system-override-sig.sh system-override-hash.sh \
system-override-curves.sh system-override-profiles.sh system-override-tls.sh \
system-override-kx.sh system-override-default-priority-string.sh \
system-override-sig-tls.sh system-override-hash-filters-prf.sh
+
+dist_check_SCRIPTS += system-override-sig-allowlist.sh \
+ system-override-hash-allowlist.sh \
+ system-override-versions-allowlist.sh \
+ system-override-curves-allowlist.sh \
+ system-override-special-allowlist.sh
endif
dist_check_SCRIPTS += gnutls-cli-self-signed.sh gnutls-cli-invalid-crl.sh gnutls-cli-rawpk.sh
diff --git a/tests/system-override-curves-allowlist.sh b/tests/system-override-curves-allowlist.sh
new file mode 100755
index 0000000000..08f3e2ca94
--- /dev/null
+++ b/tests/system-override-curves-allowlist.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# 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/>
+
+: ${srcdir=.}
+: ${SERV=../src/gnutls-serv${EXEEXT}}
+: ${CLI=../src/gnutls-cli${EXEEXT}}
+TMPFILE=config.$$.tmp
+TMPFILE2=log.$$.tmp
+export GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID=1
+
+if ! test -x "${SERV}"; then
+ exit 77
+fi
+
+if ! test -x "${CLI}"; then
+ exit 77
+fi
+
+if test "${WINDIR}" != ""; then
+ exit 77
+fi
+
+. "${srcdir}/scripts/common.sh"
+
+# This test doesn't work in FIPS mode
+if test -n "${GNUTLS_FORCE_FIPS_MODE}" && test "${GNUTLS_FORCE_FIPS_MODE}" != 0; then
+ exit 77
+fi
+
+# We intentionally add stray spaces and tabs to check our parser
+cat <<_EOF_ > ${TMPFILE}
+[global]
+override-mode = allowlist
+
+[overrides]
+enabled-curve = secp384r1
+_EOF_
+
+export GNUTLS_SYSTEM_PRIORITY_FILE="${TMPFILE}"
+export GNUTLS_DEBUG_LEVEL=3
+
+"${CLI}" --list|grep ^Groups >${TMPFILE2}
+cat ${TMPFILE2}
+if grep -i "SECP256R1" ${TMPFILE2} || grep -i "SECP521R1" ${TMPFILE2};then
+ echo "Found disabled curve with --list"
+ exit 1
+fi
+
+if ! grep -i "SECP384R1" ${TMPFILE2};then
+ echo "Could not found secp384r1"
+ exit 1
+fi
+
+# Try whether a client connection with a disabled curve will succeed.
+
+KEY1=${srcdir}/../doc/credentials/x509/key-rsa.pem
+CERT1=${srcdir}/../doc/credentials/x509/cert-rsa.pem
+
+unset GNUTLS_SYSTEM_PRIORITY_FILE
+
+eval "${GETPORT}"
+launch_server --echo --priority "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3" --x509keyfile ${KEY1} --x509certfile ${CERT1}
+PID=$!
+wait_server ${PID}
+
+"${CLI}" -p "${PORT}" 127.0.0.1 --priority NORMAL:-CURVE-ALL:+CURVE-SECP256R1:+CURVE-SECP521R1 --insecure --logfile ${TMPFILE2} </dev/null >/dev/null ||
+ fail "expected connection to succeed (1)"
+
+export GNUTLS_SYSTEM_PRIORITY_FILE="${TMPFILE}"
+
+"${CLI}" -p "${PORT}" 127.0.0.1 --priority NORMAL:-CURVE-ALL:+CURVE-SECP256R1:+CURVE-SECP521R1 --insecure --logfile ${TMPFILE2} </dev/null >/dev/null &&
+ fail "expected connection to fail (2)"
+
+kill ${PID}
+wait
+
+# Try whether a server connection with a disabled curve will succeed.
+
+KEY1=${srcdir}/../doc/credentials/x509/key-rsa.pem
+CERT1=${srcdir}/../doc/credentials/x509/cert-rsa.pem
+
+eval "${GETPORT}"
+launch_server --echo --priority "NORMAL" --x509keyfile ${KEY1} --x509certfile ${CERT1}
+PID=$!
+wait_server ${PID}
+
+unset GNUTLS_SYSTEM_PRIORITY_FILE
+
+"${CLI}" -p "${PORT}" 127.0.0.1 --priority "NORMAL:-CURVE-ALL:+CURVE-SECP256R1:+CURVE-SECP521R1" --insecure --logfile ${TMPFILE2} </dev/null >/dev/null &&
+ fail "expected connection to fail (2)"
+
+kill ${PID}
+wait
+
+exit 0
diff --git a/tests/system-override-hash-allowlist.sh b/tests/system-override-hash-allowlist.sh
new file mode 100755
index 0000000000..651a596461
--- /dev/null
+++ b/tests/system-override-hash-allowlist.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+# Copyright (C) 2019 Nikos Mavrogiannopoulos
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# 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 General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+: ${builddir=.}
+TMPFILE=c.$$.tmp
+export GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID=1
+
+cat <<_EOF_ > ${TMPFILE}
+[global]
+override-mode = allowlist
+
+[overrides]
+secure-hash = sha384
+secure-sig = rsa-pss-sha384
+_EOF_
+
+export GNUTLS_SYSTEM_PRIORITY_FILE="${TMPFILE}"
+
+"${builddir}/system-override-hash"
+rc=$?
+rm ${TMPFILE}
+exit $rc
diff --git a/tests/system-override-sig-allowlist.sh b/tests/system-override-sig-allowlist.sh
new file mode 100755
index 0000000000..70b02f14cb
--- /dev/null
+++ b/tests/system-override-sig-allowlist.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# Copyright (C) 2019 Nikos Mavrogiannopoulos
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# 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 General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+: ${builddir=.}
+TMPFILE=c.$$.tmp
+export GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID=1
+
+cat <<_EOF_ > ${TMPFILE}
+[global]
+override-mode = allowlist
+
+[overrides]
+secure-hash = sha256
+secure-sig = rsa-sha256
+secure-hash = sha384
+secure-sig = rsa-pss-sha384
+_EOF_
+
+export GNUTLS_SYSTEM_PRIORITY_FILE="${TMPFILE}"
+
+"${builddir}/system-override-sig"
+rc=$?
+rm ${TMPFILE}
+exit $rc
diff --git a/tests/system-override-special-allowlist.sh b/tests/system-override-special-allowlist.sh
new file mode 100755
index 0000000000..47e838e580
--- /dev/null
+++ b/tests/system-override-special-allowlist.sh
@@ -0,0 +1,177 @@
+#!/bin/sh
+
+# Copyright (C) 2021 Red Hat, Inc.
+#
+# Author: Alexander Sosedkin
+#
+# 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 General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+: ${srcdir=.}
+: ${CLI=../src/gnutls-cli${EXEEXT}}
+: ${GREP=grep}
+: ${DIFF=diff}
+: ${SED=sed}
+
+if ! test -x "${CLI}"; then
+ exit 77
+fi
+
+TMPCFGFILE=cfg.$$.tmp
+TMPREFFILE=ref.$$.tmp
+TMPCMPFILE=cmp.$$.tmp
+TMPOUTFILE=out.$$.tmp
+TMPSPECIAL=spc.$$.tmp
+
+# extract the list of %SPECIALs from the sources
+
+< ${srcdir}/../lib/priority_options.gperf \
+ ${SED} -ne '/\([A-Z_0-9]\{1,\}\), .*/p' | \
+ ${SED} -e 's/\([A-Z_0-9]\{1,\}\), .*/\1/' > "${TMPSPECIAL}"
+
+if ! ${GREP} -Fqx STATELESS_COMPRESSION "${TMPSPECIAL}"; then
+ cat "${TMPSPECIAL}"
+ echo 'source-extracted list of %SPECIALs has no %STATELESS_COMPRESSION'
+ exit 1
+fi
+
+# Set up a configuration file using allowlisting
+# allowing for both TLS 1.2 and TLS 1.3
+# (so that %NO_EXTENSIONS later caps that just TLS 1.2)
+
+cat <<_EOF_ > ${TMPCFGFILE}
+[global]
+override-mode = allowlist
+
+[overrides]
+secure-hash = SHA256
+tls-enabled-mac = AEAD
+tls-enabled-group = GROUP-FFDHE3072
+secure-sig = RSA-SHA256
+tls-enabled-cipher = AES-128-GCM
+tls-enabled-kx = RSA
+enabled-version = TLS1.3
+enabled-version = TLS1.2
+_EOF_
+export GNUTLS_SYSTEM_PRIORITY_FILE="${TMPCFGFILE}"
+export GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID=1
+
+# Smoke --list, @SYSTEM
+
+${CLI} --list -d 4 --priority @SYSTEM &>"${TMPOUTFILE}"
+if test $? != 0; then
+ cat "${TMPOUTFILE}"
+ echo 'fails with just @SYSTEM'
+ exit 1
+fi
+if ! ${GREP} -Fqx 'Protocols: VERS-TLS1.3, VERS-TLS1.2' \
+ "${TMPOUTFILE}"; then
+ cat "${TMPOUTFILE}"
+ echo 'unexpected protocol list with @SYSTEM'
+ exit 1
+fi
+if ! ${GREP} -Fq TLS_AES_128_GCM_SHA256 "${TMPOUTFILE}"; then
+ cat "${TMPOUTFILE}"
+ echo 'no TLS_AES_128_GCM_SHA256 with just @SYSTEM'
+ exit 1
+fi
+if ! ${GREP} -q TLS_RSA_AES_128_GCM_SHA256 "${TMPOUTFILE}"; then
+ cat "${TMPOUTFILE}"
+ echo 'no TLS_RSA_AES_128_GCM_SHA256 with just @SYSTEM'
+ exit 1
+fi
+${SED} 's/for @SYSTEM/for ---PRIORITY---/' "${TMPOUTFILE}" > "${TMPREFFILE}"
+
+# Smoke-test a no-op %STATELESS_COMPRESSION, expect --list to stay the same
+
+${CLI} --list -d 4 --priority @SYSTEM:%STATELESS_COMPRESSION &>"${TMPOUTFILE}"
+if test $? != 0; then
+ cat "${TMPOUTFILE}"
+ echo 'fails with %STATELESS_COMPRESSION'
+ exit 1
+fi
+${SED} 's/for @SYSTEM:%STATELESS_COMPRESSION/for ---PRIORITY---/' \
+ "${TMPOUTFILE}" > "${TMPCMPFILE}"
+if ! ${DIFF} "${TMPCMPFILE}" "${TMPREFFILE}"; then
+ echo '%STATELESS_COMPRESSION has changed the output'
+ exit 1
+fi
+
+# Smoke-test %NONEXISTING_OPTION, expect a syntax error
+
+${CLI} --list -d 4 --priority @SYSTEM:%NONEXISTING_OPTION &>"${TMPOUTFILE}"
+if test $? = 0; then
+ cat "${TMPOUTFILE}"
+ echo 'unknown option was not caught'
+ exit 1
+fi
+if ! ${GREP} -Fq 'Syntax error at: @SYSTEM:%NONEXISTING_OPTION' "${TMPOUTFILE}"
+then
+ cat "${TMPOUTFILE}"
+ echo 'unknown option was not errored upon'
+ exit 1
+fi
+
+# Test impact-less %SPECIALs, expect --list to stay the same
+
+while read special; do
+ if test "$special" = NO_EXTENSIONS; then
+ continue # see below
+ fi
+ prio="@SYSTEM:%$special"
+ ${CLI} --list -d 4 --priority "$prio" &>"${TMPOUTFILE}"
+ if test $? != 0; then
+ cat "${TMPOUTFILE}"
+ echo "fails with $prio"
+ exit 1
+ fi
+ ${SED} "s/for $prio/for ---PRIORITY---/" "${TMPOUTFILE}" \
+ > "${TMPCMPFILE}"
+ if ! ${DIFF} "${TMPCMPFILE}" "${TMPREFFILE}"; then
+ echo "$special has changed the output"
+ exit 1
+ fi
+done < "${TMPSPECIAL}"
+
+# Check that %NO_EXTENSIONS changes the output, capping it to TLS 1.2
+
+${CLI} --list -d 4 --priority @SYSTEM:%NO_EXTENSIONS &>"${TMPOUTFILE}"
+if test $? != 0; then
+ cat "${TMPOUTFILE}"
+ echo 'fails with just @SYSTEM'
+ exit 1
+fi
+if ! ${GREP} -Fqx 'Protocols: VERS-TLS1.2' \
+ "${TMPOUTFILE}"; then
+ cat "${TMPOUTFILE}"
+ echo 'unexpected protocol list with @SYSTEM:%NO_EXTENSIONS'
+ exit 1
+fi
+if ${GREP} -Fq TLS_AES_128_GCM_SHA256 "${TMPOUTFILE}"; then
+ cat "${TMPOUTFILE}"
+ echo 'TLS_AES_128_GCM_SHA256 present with @SYSTEM:%NO_EXTENSIONS'
+ exit 1
+fi
+if ! ${GREP} -q TLS_RSA_AES_128_GCM_SHA256 "${TMPOUTFILE}"; then
+ cat "${TMPOUTFILE}"
+ echo 'no TLS_RSA_AES_128_GCM_SHA256 with @SYSTEM:%NO_EXTENSIONS'
+ exit 1
+fi
+
+rm "${TMPCFGFILE}" "${TMPREFFILE}" "${TMPCMPFILE}" "${TMPOUTFILE}"
+rm "${TMPSPECIAL}"
+
+exit 0
diff --git a/tests/system-override-versions-allowlist.sh b/tests/system-override-versions-allowlist.sh
new file mode 100755
index 0000000000..b708c8c7e4
--- /dev/null
+++ b/tests/system-override-versions-allowlist.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+# Copyright (C) 2019 Red Hat, Inc.
+#
+# Author: Nikos Mavrogiannopoulos
+#
+# 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 General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+: ${srcdir=.}
+: ${SERV=../src/gnutls-serv${EXEEXT}}
+: ${CLI=../src/gnutls-cli${EXEEXT}}
+TMPFILE=config.$$.tmp
+TMPFILE2=log.$$.tmp
+export GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID=1
+
+if ! test -x "${SERV}"; then
+ exit 77
+fi
+
+if ! test -x "${CLI}"; then
+ exit 77
+fi
+
+if test "${WINDIR}" != ""; then
+ exit 77
+fi
+
+. "${srcdir}/scripts/common.sh"
+
+cat <<_EOF_ > ${TMPFILE}
+[global]
+override-mode = allowlist
+
+[overrides]
+enabled-version = tls1.1
+_EOF_
+
+export GNUTLS_SYSTEM_PRIORITY_FILE="${TMPFILE}"
+export GNUTLS_DEBUG_LEVEL=3
+
+"${CLI}" --list --priority=@SYSTEM | grep Protocols >${TMPFILE2}
+cat ${TMPFILE2}
+if grep 'VERS-TLS1\.[23]' ${TMPFILE2}; then
+ echo "Found disabled protocol with --list"
+ exit 1
+fi
+
+PRIO=@SYSTEM:+CIPHER-ALL:+MAC-ALL:+GROUP-ALL
+
+"${CLI}" --priority "$PRIO" --list | grep Protocols >${TMPFILE2}
+cat ${TMPFILE2}
+if grep 'VERS-TLS1\.[23]' ${TMPFILE2}; then
+ echo "Found disabled protocol with --list --priority $PRIO"
+ exit 1
+fi
+
+# Try whether a client connection with these protocols will succeed.
+
+KEY1=${srcdir}/../doc/credentials/x509/key-rsa.pem
+CERT1=${srcdir}/../doc/credentials/x509/cert-rsa.pem
+
+unset GNUTLS_SYSTEM_PRIORITY_FILE
+
+eval "${GETPORT}"
+launch_server --echo --priority "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3" --x509keyfile ${KEY1} --x509certfile ${CERT1}
+PID=$!
+wait_server ${PID}
+
+export GNUTLS_SYSTEM_PRIORITY_FILE="${TMPFILE}"
+
+"${CLI}" -p "${PORT}" 127.0.0.1 --priority "$PRIO" --insecure --logfile ${TMPFILE2} </dev/null >/dev/null &&
+ fail "expected connection to fail (1)"
+
+kill ${PID}
+wait
+
+# Try whether a server connection with these protocols will succeed.
+
+KEY1=${srcdir}/../doc/credentials/x509/key-rsa.pem
+CERT1=${srcdir}/../doc/credentials/x509/cert-rsa.pem
+
+eval "${GETPORT}"
+launch_server --echo --priority "$PRIO" --x509keyfile ${KEY1} --x509certfile ${CERT1}
+PID=$!
+wait_server ${PID}
+
+unset GNUTLS_SYSTEM_PRIORITY_FILE
+
+"${CLI}" -p "${PORT}" 127.0.0.1 --priority "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3" --insecure --logfile ${TMPFILE2} </dev/null >/dev/null &&
+ fail "expected connection to fail (2)"
+
+kill ${PID}
+wait
+
+exit 0