]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
handshake: added support for negotiating version using extension
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Jun 2017 12:17:40 +0000 (14:17 +0200)
committerNikos Mavrogiannopoulos <nmav@redhat.com>
Mon, 19 Feb 2018 14:29:33 +0000 (15:29 +0100)
That is, introduced the TLS 1.3 supported_versions extension. It is currently
only being used if negotiating TLS 1.3 or later.

Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
lib/algorithms.h
lib/algorithms/protocols.c
lib/ext/Makefile.am
lib/ext/supported_versions.c [new file with mode: 0644]
lib/ext/supported_versions.h [new file with mode: 0644]
lib/extensions.c
lib/gnutls_int.h

index bb1d4a5a6c8bf0f1eb1934671eb5b657289ea308..9de6867ebe0f576b46e4054ed58d84938af56c2f 100644 (file)
@@ -48,6 +48,8 @@ int _gnutls_version_is_supported(gnutls_session_t session,
 gnutls_protocol_t _gnutls_version_get(uint8_t major, uint8_t minor);
 unsigned _gnutls_version_is_too_high(gnutls_session_t session, uint8_t major, uint8_t minor);
 
+int _gnutls_write_supported_versions(gnutls_session_t session, uint8_t *buffer, ssize_t buffer_size);
+
 /* Functions for feature checks */
 int
 _gnutls_figure_common_ciphersuite(gnutls_session_t session,
index 0b035adddf20a59aab9cd82506ac87babce54e08..0a1b1569e4dcf28cae778c3591fb149bd14b0726 100644 (file)
@@ -273,6 +273,60 @@ gnutls_protocol_t _gnutls_legacy_version_max(gnutls_session_t session)
        return max;
 }
 
+/* Returns the number of bytes written to buffer or a negative
+ * error code. It will return an error if there is no version
+ * >= TLS 1.3.
+ */
+int _gnutls_write_supported_versions(gnutls_session_t session, uint8_t *buffer, ssize_t buffer_size)
+{
+       gnutls_protocol_t cur_prot;
+       size_t written_bytes = 0;
+       unsigned at_least_one_new = 0;
+       unsigned i;
+       const version_entry_st *p;
+
+       for (i = 0; i < session->internals.priorities->protocol.algorithms; i++) {
+               cur_prot =
+                   session->internals.priorities->protocol.priority[i];
+
+               for (p = sup_versions; p->name != NULL; p++) {
+                       if(p->id == cur_prot) {
+                               if (p->obsolete != 0)
+                                       break;
+
+                               if (!p->supported || p->transport != session->internals.transport)
+                                       break;
+
+                               if (p->only_extension)
+                                       at_least_one_new = 1;
+
+                               if (buffer_size > 2) {
+                                       buffer[0] = p->major;
+                                       buffer[1] = p->minor;
+                                       written_bytes += 2;
+                                       buffer += 2;
+                               }
+
+                               buffer_size -= 2;
+
+                               if (buffer_size <= 0)
+                                       goto finish;
+
+                               break;
+                       }
+               }
+       }
+
+ finish:
+       if (written_bytes == 0)
+               return gnutls_assert_val(GNUTLS_E_NO_PRIORITIES_WERE_SET);
+
+       if (at_least_one_new == 0)
+               return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+       return written_bytes;
+}
+
 /* Returns true (1) if the given version is higher than the highest supported
  * and (0) otherwise */
 unsigned _gnutls_version_is_too_high(gnutls_session_t session, uint8_t major, uint8_t minor)
index 698e41667e21fd7f621ee37047d50d3bc8558350..8d75f9fd4144d6d927c6f28c667143bd8690b55d 100644 (file)
@@ -40,7 +40,8 @@ libgnutls_ext_la_SOURCES = max_record.c \
        session_ticket.h signature.h safe_renegotiation.h \
        session_ticket.c srp.c ecc.c ecc.h heartbeat.c heartbeat.h \
        status_request.h status_request.c dumbfw.c dumbfw.h \
-       ext_master_secret.c ext_master_secret.h etm.h etm.c
+       ext_master_secret.c ext_master_secret.h etm.h etm.c \
+       supported_versions.c supported_versions.h
 
 if ENABLE_ALPN
 libgnutls_ext_la_SOURCES += alpn.c alpn.h
diff --git a/lib/ext/supported_versions.c b/lib/ext/supported_versions.c
new file mode 100644 (file)
index 0000000..dda1122
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2001-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2017 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+/* This file contains the code for the Max Record Size TLS extension.
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include "num.h"
+#include <extensions.h>
+#include <ext/supported_versions.h>
+
+static int supported_versions_recv_params(gnutls_session_t session,
+                                         const uint8_t * data,
+                                         size_t data_size);
+static int supported_versions_send_params(gnutls_session_t session,
+                                         gnutls_buffer_st * extdata);
+
+const extension_entry_st ext_mod_supported_versions = {
+       .name = "Supported Versions",
+       .type = GNUTLS_EXTENSION_SUPPORTED_VERSIONS,
+       .parse_type = GNUTLS_EXT_TLS,
+
+       .recv_func = supported_versions_recv_params,
+       .send_func = supported_versions_send_params,
+       .pack_func = NULL,
+       .unpack_func = NULL,
+       .deinit_func = NULL,
+       .cannot_be_overriden = 1
+};
+
+/* Only client sends this extension. */
+static int
+supported_versions_recv_params(gnutls_session_t session,
+                              const uint8_t * data, size_t _data_size)
+{
+       ssize_t data_size = _data_size;
+       uint8_t major, minor;
+       gnutls_protocol_t proto;
+       ssize_t bytes;
+       int ret;
+
+       if (session->security_parameters.entity == GNUTLS_SERVER) {
+               DECR_LEN(data_size, 1);
+               bytes = data[0];
+               data++;
+
+               if (bytes % 2 == 1)
+                       return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+               DECR_LEN(data_size, bytes);
+
+               if (data_size != 0)
+                       return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+               while (bytes > 0) {
+                       major = data[0];
+                       minor = data[1];
+                       data += 2;
+                       bytes -= 2;
+
+                       proto = _gnutls_version_get(major, minor);
+
+                       if (_gnutls_version_is_supported(session, proto)) {
+                               ret = _gnutls_set_current_version(session, proto);
+                               if (ret < 0)
+                                       return gnutls_assert_val(ret);
+
+                               return 0;
+                       }
+               }
+
+               /* if we are here, none of the versions were acceptable */
+               return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+       } else {
+               /* a server should never send this message */
+               gnutls_assert();
+               return GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION;
+       }
+
+       return 0;
+}
+
+/* returns data_size or a negative number on failure
+ */
+static int
+supported_versions_send_params(gnutls_session_t session,
+                              gnutls_buffer_st * extdata)
+{
+       uint8_t versions[32];
+       size_t versions_size;
+       int ret;
+
+       /* this function sends the client extension data (dnsname) */
+       if (session->security_parameters.entity == GNUTLS_CLIENT) {
+               ret = _gnutls_write_supported_versions(session, versions, sizeof(versions));
+               if (ret <= 0) /* if this function doesn't succeed do not send anything */
+                       return 0;
+
+               versions_size = ret;
+
+               ret = _gnutls_buffer_append_data_prefix(extdata, 8, versions, versions_size);
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+
+               return versions_size+2;
+       }
+
+       return 0;
+}
diff --git a/lib/ext/supported_versions.h b/lib/ext/supported_versions.h
new file mode 100644 (file)
index 0000000..288851c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef EXT_SUPPORTED_VERSIONS_H
+#define EXT_SUPPORTED_VERSIONS_H
+
+#include <extensions.h>
+
+extern const extension_entry_st ext_mod_supported_versions;
+
+#endif
index 2f889df3b8b04e4bc7e9e0ba9f1bcf6239ebc2fa..1f835b497ad3c7d276e1f803ecd7162fddf5fbc0 100644 (file)
@@ -40,6 +40,7 @@
 #include <ext/ecc.h>
 #include <ext/status_request.h>
 #include <ext/ext_master_secret.h>
+#include <ext/supported_versions.h>
 #include <ext/srtp.h>
 #include <ext/alpn.h>
 #include <ext/dumbfw.h>
@@ -55,6 +56,7 @@ static void unset_resumed_ext_data(gnutls_session_t session, const struct extens
 static extension_entry_st const *extfunc[MAX_EXT_TYPES+1] = {
        &ext_mod_max_record_size,
        &ext_mod_ext_master_secret,
+       &ext_mod_supported_versions,
        &ext_mod_etm,
 #ifdef ENABLE_OCSP
        &ext_mod_status_request,
index 8cc8e8c0bcc6eb52dd5d0d3e43491a5be086002f..6c0c9843ef2fef6a14540b78b85905ebe79bbfef 100644 (file)
@@ -285,6 +285,7 @@ typedef enum extensions_t {
        GNUTLS_EXTENSION_ETM = 22,
        GNUTLS_EXTENSION_EXT_MASTER_SECRET = 23,
        GNUTLS_EXTENSION_SESSION_TICKET = 35,
+       GNUTLS_EXTENSION_SUPPORTED_VERSIONS = 43,
        GNUTLS_EXTENSION_SAFE_RENEGOTIATION = 65281     /* aka: 0xff01 */
 } extensions_t;