]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
priority: add %FORCE_SESSION_HASH modifier
authorDaiki Ueno <ueno@gnu.org>
Tue, 21 Feb 2023 05:46:09 +0000 (14:46 +0900)
committerDaiki Ueno <ueno@gnu.org>
Thu, 9 Mar 2023 02:25:25 +0000 (11:25 +0900)
This adds a new priority string modifier %FORCE_SESSION_HASH, which
requires to negotiate extended master secret and aborts the connection
if the peer does not send the extension in hello messages.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
.gitignore
doc/cha-gtls-app.texi
lib/gnutls_int.h
lib/handshake.c
lib/priority.c
lib/priority_options.gperf
tests/Makefile.am
tests/tls-force-ems.c [new file with mode: 0644]

index 597530a6a35d5de967a4febee85cef62711850d2..95cb3d89aece320b644d291cccaeed4b64fa5384 100644 (file)
@@ -793,6 +793,7 @@ tests/tls-crt_type-neg
 tests/tls-etm
 tests/tls-ext-not-in-dtls
 tests/tls-ext-register
+tests/tls-force-ems
 tests/tls-force-etm
 tests/tls-max-record
 tests/tls-neg-ext-key
index 224d178d3ce3497894e261f48ea65339f2ff5f03..77727a44ec483b9875a18c7bd856c525531cc328 100644 (file)
@@ -1566,6 +1566,11 @@ This is implied by the PFS keyword.
 will prevent the advertizing the TLS extended master secret (session hash)
 extension.
 
+@item %FORCE_SESSION_HASH @tab
+negotiate the TLS extended master secret (session hash) extension.
+Specifying both %NO_SESSION_HASH and %FORCE_SESSION_HASH is not
+supported, and the behavior is undefined.
+
 @item %SERVER_PRECEDENCE @tab
 The ciphersuite will be selected according to server priorities
 and not the client's.
index 969454b6a06b33073fd92fcb176149ab52f55e79..d4b2e280e0660fe36d2c064cb67aaddb4f5bd9a0 100644 (file)
@@ -960,6 +960,7 @@ struct gnutls_priority_st {
        bool force_etm;
        unsigned int additional_verify_flags;
        bool tls13_compat_mode;
+       bool force_ext_master_secret;
 
        /* TLS_FALLBACK_SCSV */
        bool fallback;
index 02da5117f53c823659fc11c0223f58b77bd779c6..dddf77c4f24759f0e6e692f6e5efcad706ed2a87 100644 (file)
@@ -912,6 +912,14 @@ read_client_hello(gnutls_session_t session, uint8_t * data, int datalen)
        if (_gnutls_version_priority(session, vers->id) < 0)
                return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
 
+       /* check if EMS is required */
+       if (!vers->tls13_sem &&
+           vers->id != GNUTLS_SSL3 && vers->id != GNUTLS_DTLS0_9 &&
+           session->internals.priorities->force_ext_master_secret &&
+           !session->security_parameters.ext_master_secret) {
+               return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_SECURITY);
+       }
+
        _gnutls_handshake_log("HSK[%p]: Selected version %s\n", session,
                              vers->name);
 
@@ -2112,14 +2120,26 @@ read_server_hello(gnutls_session_t session, uint8_t * data, int datalen)
        if (ret < 0)
                return gnutls_assert_val(ret);
 
-       /* check if EtM is required */
-       if (!vers->tls13_sem && session->internals.priorities->force_etm
-           && !session->security_parameters.etm) {
-               const cipher_entry_st *cipher =
-                   cipher_to_entry(session->security_parameters.
-                                   cs->block_algorithm);
-               if (_gnutls_cipher_type(cipher) == CIPHER_BLOCK)
-                       return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+       if (!vers->tls13_sem) {
+               /* check if EtM is required */
+               if (session->internals.priorities->force_etm &&
+                   !session->security_parameters.etm) {
+                       const cipher_entry_st *cipher =
+                           cipher_to_entry(session->security_parameters.
+                                           cs->block_algorithm);
+                       if (_gnutls_cipher_type(cipher) == CIPHER_BLOCK)
+                               return
+                                   gnutls_assert_val
+                                   (GNUTLS_E_UNWANTED_ALGORITHM);
+               }
+
+               /* check if EMS is required */
+               if (vers->id != GNUTLS_SSL3 && vers->id != GNUTLS_DTLS0_9 &&
+                   session->internals.priorities->force_ext_master_secret &&
+                   !session->security_parameters.ext_master_secret) {
+                       return
+                           gnutls_assert_val(GNUTLS_E_INSUFFICIENT_SECURITY);
+               }
        }
 
        ret =
index 154929eb9f0bf9caa983f35a0e7fae1682692610..15cb133894e540ed50c7187a5f05c6d5ae75bea2 100644 (file)
@@ -940,6 +940,11 @@ static void enable_no_ext_master_secret(gnutls_priority_t c)
        c->_no_ext_master_secret = 1;
 }
 
+static void enable_force_ext_master_secret(gnutls_priority_t c)
+{
+       c->force_ext_master_secret = 1;
+}
+
 static void enable_no_etm(gnutls_priority_t c)
 {
        c->_no_etm = 1;
index 08b35af4a1d37f7e6979a3c2950e2c5253b5906d..11bcc6e88e15cdb5023a09aeffec4f3c09d87d90 100644 (file)
@@ -13,6 +13,7 @@ NO_TICKETS_TLS12, enable_no_tickets_tls12
 NO_ETM, enable_no_etm
 FORCE_ETM, enable_force_etm
 NO_SESSION_HASH, enable_no_ext_master_secret
+FORCE_SESSION_HASH, enable_force_ext_master_secret
 NO_STATUS_REQUEST, enable_no_status_request
 STATELESS_COMPRESSION, dummy_func
 VERIFY_ALLOW_BROKEN, enable_verify_allow_broken
index d530ad0dc33f7eae1504156f64a6101553685faa..c263ac7c5cf6ea70fac8fc62ad1ce9531be4ab8b 100644 (file)
@@ -234,7 +234,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
         set_x509_ocsp_multi_cli kdf-api keylog-func handshake-write \
         x509cert-dntypes id-on-xmppAddr tls13-compat-mode ciphersuite-name \
         x509-upnconstraint xts-key-check cipher-padding pkcs7-verify-double-free \
-        fips-rsa-sizes tls12-rehandshake-ticket pathbuf
+        fips-rsa-sizes tls12-rehandshake-ticket pathbuf tls-force-ems
 
 ctests += tls-channel-binding
 
diff --git a/tests/tls-force-ems.c b/tests/tls-force-ems.c
new file mode 100644 (file)
index 0000000..18aa232
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2023 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "utils.h"
+#include "cert-common.h"
+#include "eagain-common.h"
+
+/* This program tests whether forced extended master secret is
+ * negotiated as expected.
+ */
+
+const char *side;
+
+static void tls_log_func(int level, const char *str)
+{
+       fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+static void
+try(const char *name, const char *sprio, const char *cprio, int serr, int cerr)
+{
+       int sret, cret;
+       gnutls_certificate_credentials_t scred, ccred;
+       gnutls_session_t server, client;
+
+       success("Running %s\n", name);
+
+       assert(gnutls_certificate_allocate_credentials(&scred) >= 0);
+
+       assert(gnutls_certificate_set_x509_key_mem
+              (scred, &server_ca3_localhost_cert,
+               &server_ca3_key, GNUTLS_X509_FMT_PEM) >= 0);
+
+       assert(gnutls_certificate_allocate_credentials(&ccred) >= 0);
+
+       assert(gnutls_certificate_set_x509_trust_mem
+              (ccred, &ca3_cert, GNUTLS_X509_FMT_PEM) >= 0);
+
+       assert(gnutls_init(&server, GNUTLS_SERVER) >= 0);
+       assert(gnutls_init(&client, GNUTLS_CLIENT) >= 0);
+
+       gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred);
+       gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, ccred);
+
+       gnutls_transport_set_push_function(server, server_push);
+       gnutls_transport_set_pull_function(server, server_pull);
+       gnutls_transport_set_ptr(server, server);
+       assert(gnutls_priority_set_direct(server, sprio, 0) >= 0);
+
+       gnutls_transport_set_push_function(client, client_push);
+       gnutls_transport_set_pull_function(client, client_pull);
+       gnutls_transport_set_ptr(client, client);
+       assert(gnutls_priority_set_direct(client, cprio, 0) >= 0);
+
+       HANDSHAKE_EXPECT(client, server, cerr, serr);
+
+       gnutls_deinit(server);
+       gnutls_deinit(client);
+       gnutls_certificate_free_credentials(scred);
+       gnutls_certificate_free_credentials(ccred);
+
+       reset_buffers();
+}
+
+#define AES_GCM "NORMAL:-VERS-ALL:+VERS-TLS1.2"
+
+void doit(void)
+{
+       global_init();
+
+       /* General init. */
+       gnutls_global_set_log_function(tls_log_func);
+       if (debug)
+               gnutls_global_set_log_level(2);
+
+       try("default", AES_GCM ":%FORCE_SESSION_HASH",
+           AES_GCM ":%FORCE_SESSION_HASH", 0, 0);
+       try("both force EMS", AES_GCM ":%FORCE_SESSION_HASH",
+           AES_GCM ":%FORCE_SESSION_HASH", 0, 0);
+       try("neither negotiates EMS", AES_GCM ":%NO_SESSION_HASH",
+           AES_GCM ":%NO_SESSION_HASH", 0, 0);
+       try("server doesn't negotiate EMS, client forces EMS",
+           AES_GCM ":%NO_SESSION_HASH", AES_GCM ":%FORCE_SESSION_HASH",
+           GNUTLS_E_AGAIN, GNUTLS_E_INSUFFICIENT_SECURITY);
+       try("server forces EMS, client doesn't negotiate EMS",
+           AES_GCM ":%FORCE_SESSION_HASH", AES_GCM ":%NO_SESSION_HASH",
+           GNUTLS_E_INSUFFICIENT_SECURITY, GNUTLS_E_AGAIN);
+
+       gnutls_global_deinit();
+}