diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 07:33:12 +0000 |
commit | 36082a2fe36ecd800d784ae44c14f1f18c66a7e9 (patch) | |
tree | 6c68e0c0097987aff85a01dabddd34b862309a7c /lib/algorithms/protocols.c | |
parent | Initial commit. (diff) | |
download | gnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.tar.xz gnutls28-36082a2fe36ecd800d784ae44c14f1f18c66a7e9.zip |
Adding upstream version 3.7.9.upstream/3.7.9upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/algorithms/protocols.c')
-rw-r--r-- | lib/algorithms/protocols.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/lib/algorithms/protocols.c b/lib/algorithms/protocols.c new file mode 100644 index 0000000..a4fd483 --- /dev/null +++ b/lib/algorithms/protocols.c @@ -0,0 +1,585 @@ +/* + * Copyright (C) 2011-2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <algorithms.h> +#include "errors.h" +#include <x509/common.h> +#include "c-strcase.h" + +/* TLS Versions */ + +static SYSTEM_CONFIG_OR_CONST +version_entry_st sup_versions[] = { + {.name = "SSL3.0", + .id = GNUTLS_SSL3, + .age = 0, + .major = 3, + .minor = 0, + .transport = GNUTLS_STREAM, +#ifdef ENABLE_SSL3 + .supported = 1, +#endif + .explicit_iv = 0, + .extensions = 0, + .selectable_sighash = 0, + .selectable_prf = 0, + .obsolete = 1, + .only_extension = 0, + .tls_sig_sem = SIG_SEM_PRE_TLS12, + .false_start = 0 + }, + {.name = "TLS1.0", + .id = GNUTLS_TLS1, + .age = 1, + .major = 3, + .minor = 1, + .transport = GNUTLS_STREAM, + .supported = 1, + .explicit_iv = 0, + .extensions = 1, + .selectable_sighash = 0, + .selectable_prf = 0, + .obsolete = 0, + .only_extension = 0, + .tls_sig_sem = SIG_SEM_PRE_TLS12, + .false_start = 0 + }, + {.name = "TLS1.1", + .id = GNUTLS_TLS1_1, + .age = 2, + .major = 3, + .minor = 2, + .transport = GNUTLS_STREAM, + .supported = 1, + .explicit_iv = 1, + .extensions = 1, + .selectable_sighash = 0, + .selectable_prf = 0, + .obsolete = 0, + .only_extension = 0, + .tls_sig_sem = SIG_SEM_PRE_TLS12, + .false_start = 0 + }, + {.name = "TLS1.2", + .id = GNUTLS_TLS1_2, + .age = 3, + .major = 3, + .minor = 3, + .transport = GNUTLS_STREAM, + .supported = 1, + .explicit_iv = 1, + .extensions = 1, + .selectable_sighash = 1, + .selectable_prf = 1, + .obsolete = 0, + .only_extension = 0, + .tls_sig_sem = SIG_SEM_PRE_TLS12, + .false_start = 1 + }, + {.name = "TLS1.3", + .id = GNUTLS_TLS1_3, + .age = 5, + .major = 3, + .minor = 4, + .transport = GNUTLS_STREAM, + .supported = 1, + .explicit_iv = 0, + .extensions = 1, + .selectable_sighash = 1, + .selectable_prf = 1, + .tls13_sem = 1, + .obsolete = 0, + .only_extension = 1, + .post_handshake_auth = 1, + .multi_ocsp = 1, + .key_shares = 1, + .false_start = 0, /* doesn't make sense */ + .tls_sig_sem = SIG_SEM_TLS13 + }, + {.name = "DTLS0.9", /* Cisco AnyConnect (based on about OpenSSL 0.9.8e) */ + .id = GNUTLS_DTLS0_9, + .age = 200, + .major = 1, + .minor = 0, + .transport = GNUTLS_DGRAM, + .supported = 1, + .explicit_iv = 1, + .extensions = 1, + .selectable_sighash = 0, + .selectable_prf = 0, + .obsolete = 0, + .only_extension = 0, + .tls_sig_sem = SIG_SEM_PRE_TLS12, + .false_start = 0 + }, + {.name = "DTLS1.0", + .id = GNUTLS_DTLS1_0, + .age = 201, + .major = 254, + .minor = 255, + .transport = GNUTLS_DGRAM, + .supported = 1, + .explicit_iv = 1, + .extensions = 1, + .selectable_sighash = 0, + .selectable_prf = 0, + .obsolete = 0, + .only_extension = 0, + .tls_sig_sem = SIG_SEM_PRE_TLS12, + .false_start = 0 + }, + {.name = "DTLS1.2", + .id = GNUTLS_DTLS1_2, + .age = 202, + .major = 254, + .minor = 253, + .transport = GNUTLS_DGRAM, + .supported = 1, + .explicit_iv = 1, + .extensions = 1, + .selectable_sighash = 1, + .selectable_prf = 1, + .obsolete = 0, + .only_extension = 0, + .tls_sig_sem = SIG_SEM_PRE_TLS12, + .false_start = 1 + }, + {0, 0, 0, 0, 0} +}; + +const version_entry_st *version_to_entry(gnutls_protocol_t version) +{ + const version_entry_st *p; + + for (p = sup_versions; p->name != NULL; p++) + if (p->id == version) + return p; + return NULL; +} + +const version_entry_st *nversion_to_entry(uint8_t major, uint8_t minor) +{ + const version_entry_st *p; + + for (p = sup_versions; p->name != NULL; p++) { + if ((p->major == major) && (p->minor == minor)) + return p; + } + return NULL; +} + +static int +version_is_valid_for_session(gnutls_session_t session, + const version_entry_st *v) +{ + if (!v->supported && !(v->supported_revertible && _gnutls_allowlisting_mode())) + return 0; + if (v->transport != session->internals.transport) + return 0; + return 1; +} + +/* This is only called by cfg_apply in priority.c, in blocklisting mode. */ +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 (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_disabled_all(void) +{ +#ifndef DISABLE_SYSTEM_CONFIG + version_entry_st *p; + + for (p = sup_versions; p->name != NULL; p++) { + p->supported = false; + p->supported_revertible = true; + } +#endif +} + +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; + } + +#endif + return GNUTLS_E_INVALID_REQUEST; +} + +/* Return the priority of the provided version number */ +int +_gnutls_version_priority(gnutls_session_t session, + gnutls_protocol_t version) +{ + unsigned int i; + + for (i = 0; i < session->internals.priorities->protocol.num_priorities; + i++) { + if (session->internals.priorities->protocol.priorities[i] == + version) + return i; + } + return -1; +} + +/* Returns the lowest TLS version number in the priorities. + */ +const version_entry_st *_gnutls_version_lowest(gnutls_session_t session) +{ + unsigned int i; + gnutls_protocol_t cur_prot; + const version_entry_st *v, *min_v = NULL; + const version_entry_st *backup = NULL; + + for (i=0;i < session->internals.priorities->protocol.num_priorities;i++) { + cur_prot = + session->internals.priorities->protocol.priorities[i]; + v = version_to_entry(cur_prot); + + if (v != NULL && version_is_valid_for_session(session, v)) { + if (min_v == NULL) { + if (v->obsolete != 0) + backup = v; + else + min_v = v; + } else if (v->obsolete == 0 && v->age < min_v->age) { + min_v = v; + } + } + } + + if (min_v == NULL) + return backup; + + return min_v; +} + +/* Returns the maximum version in the priorities + */ +const version_entry_st *_gnutls_version_max(gnutls_session_t session) +{ + unsigned int i; + gnutls_protocol_t cur_prot; + const version_entry_st *p, *max = NULL; + + if (!session->internals.priorities) { + gnutls_assert(); + return NULL; + } + + for (i = 0; i < session->internals.priorities->protocol.num_priorities; + i++) { + cur_prot = + session->internals.priorities->protocol.priorities[i]; + + for (p = sup_versions; p->name != NULL; p++) { + if(p->id == cur_prot) { +#ifndef ENABLE_SSL3 + if (p->obsolete != 0) + break; +#endif + if (!p->supported && !(p->supported_revertible && _gnutls_allowlisting_mode())) + break; + + if (p->transport != session->internals.transport) + break; + + if (p->tls13_sem && (session->internals.flags & INT_FLAG_NO_TLS13)) + break; + + if (max == NULL || cur_prot > max->id) { + max = p; + } + + break; + } + } + } + + return max; +} + +const version_entry_st *_gnutls_legacy_version_max(gnutls_session_t session) +{ + const version_entry_st *max = _gnutls_version_max(session); + + if (max && max->only_extension != 0) { + /* TLS 1.3 or later found */ + if (max->transport == GNUTLS_STREAM) { + return version_to_entry(GNUTLS_TLS1_2); + } else { + return version_to_entry(GNUTLS_DTLS1_2); + } + } + + return max; +} + +/* Returns the number of bytes written to buffer or a negative + * error code. It will return GNUTLS_E_UNSUPPORTED_VERSION_PACKET + * 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.num_priorities; i++) { + cur_prot = + session->internals.priorities->protocol.priorities[i]; + + for (p = sup_versions; p->name != NULL; p++) { + if(p->id == cur_prot) { + if (p->obsolete != 0) + break; + + if (!p->supported && !(p->supported_revertible && _gnutls_allowlisting_mode())) + break; + + if (p->transport != session->internals.transport) + break; + + if (p->only_extension) + at_least_one_new = 1; + + if (buffer_size > 2) { + _gnutls_debug_log("Advertizing version %d.%d\n", (int)p->major, (int)p->minor); + 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_E_UNSUPPORTED_VERSION_PACKET; + + 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) +{ + const version_entry_st *e; + + e = _gnutls_legacy_version_max(session); + if (e == NULL) /* we don't know; but that means something is unconfigured */ + return 1; + + if (e->transport == GNUTLS_DGRAM) { + if (major < e->major) + return 1; + + if (e->major == major && minor < e->minor) + return 1; + } else { + if (major > e->major) + return 1; + + if (e->major == major && minor > e->minor) + return 1; + } + + return 0; +} + +/** + * gnutls_protocol_get_name: + * @version: is a (gnutls) version number + * + * Convert a #gnutls_protocol_t value to a string. + * + * Returns: a string that contains the name of the specified TLS + * version (e.g., "TLS1.0"), or %NULL. + **/ +const char *gnutls_protocol_get_name(gnutls_protocol_t version) +{ + const version_entry_st *p; + /* avoid prefix */ + for (p = sup_versions; p->name != NULL; p++) + if (p->id == version) + return p->name; + return NULL; +} + +/** + * gnutls_protocol_get_id: + * @name: is a protocol name + * + * The names are compared in a case insensitive way. + * + * Returns: an id of the specified protocol, or + * %GNUTLS_VERSION_UNKNOWN on error. + **/ +gnutls_protocol_t gnutls_protocol_get_id(const char *name) +{ + const version_entry_st *p; + gnutls_protocol_t ret = GNUTLS_VERSION_UNKNOWN; + + for (p = sup_versions; p->name != NULL; p++) { + if (c_strcasecmp(p->name, name) == 0) { + ret = p->id; + break; + } + } + + return ret; +} + +/** + * gnutls_protocol_list: + * + * Get a list of supported protocols, e.g. SSL 3.0, TLS 1.0 etc. + * + * This function is not thread safe. + * + * Returns: a (0)-terminated list of #gnutls_protocol_t integers + * indicating the available protocols. + * + **/ +const gnutls_protocol_t *gnutls_protocol_list(void) +{ + const version_entry_st *p; + static gnutls_protocol_t supported_protocols[MAX_ALGOS] = { 0 }; + + if (supported_protocols[0] == 0) { + int i = 0; + + for (p = sup_versions; p->name != NULL; p++) { + if (!p->supported) + continue; + supported_protocols[i++] = p->id; + } + supported_protocols[i++] = 0; + } + + 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) +{ + const version_entry_st *p; + int ret = GNUTLS_VERSION_UNKNOWN; + + for (p = sup_versions; p->name != NULL; p++) + if ((p->major == major) && (p->minor == minor)) + ret = p->id; + return ret; +} + +/* Version Functions */ + +int +_gnutls_nversion_is_supported(gnutls_session_t session, + unsigned char major, unsigned char minor) +{ + const version_entry_st *p; + int version = 0; + + for (p = sup_versions; p->name != NULL; p++) { + if(p->major == major && p->minor == minor) { +#ifndef ENABLE_SSL3 + if (p->obsolete != 0) return 0; +#endif + if (p->tls13_sem && (session->internals.flags & INT_FLAG_NO_TLS13)) + return 0; + + if (!p->supported && !(p->supported_revertible && _gnutls_allowlisting_mode())) + return 0; + + if (p->transport != session->internals.transport) + return 0; + + version = p->id; + break; + } + } + + if (version == 0) + return 0; + + if (_gnutls_version_priority(session, version) < 0) + return 0; /* disabled by the user */ + else + return 1; +} |