From: Nikos Mavrogiannopoulos Date: Mon, 19 Jun 2017 12:17:40 +0000 (+0200) Subject: handshake: added support for negotiating version using extension X-Git-Tag: gnutls_3_6_3~451 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=4cbfc784ea3cf533d575f0517d7c07e22b24313d;p=thirdparty%2Fgnutls.git handshake: added support for negotiating version using extension 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 --- diff --git a/lib/algorithms.h b/lib/algorithms.h index bb1d4a5a6c..9de6867ebe 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -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, diff --git a/lib/algorithms/protocols.c b/lib/algorithms/protocols.c index 0b035adddf..0a1b1569e4 100644 --- a/lib/algorithms/protocols.c +++ b/lib/algorithms/protocols.c @@ -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) diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am index 698e41667e..8d75f9fd41 100644 --- a/lib/ext/Makefile.am +++ b/lib/ext/Makefile.am @@ -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 index 0000000000..dda1122185 --- /dev/null +++ b/lib/ext/supported_versions.c @@ -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 + * + */ + +/* This file contains the code for the Max Record Size TLS extension. + */ + +#include "gnutls_int.h" +#include "errors.h" +#include "num.h" +#include +#include + +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 index 0000000000..288851c5db --- /dev/null +++ b/lib/ext/supported_versions.h @@ -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 + * + */ + +#ifndef EXT_SUPPORTED_VERSIONS_H +#define EXT_SUPPORTED_VERSIONS_H + +#include + +extern const extension_entry_st ext_mod_supported_versions; + +#endif diff --git a/lib/extensions.c b/lib/extensions.c index 2f889df3b8..1f835b497a 100644 --- a/lib/extensions.c +++ b/lib/extensions.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -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, diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 8cc8e8c0bc..6c0c9843ef 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -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;