summaryrefslogtreecommitdiffstats
path: root/lib/algorithms/protocols.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/algorithms/protocols.c')
-rw-r--r--lib/algorithms/protocols.c585
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;
+}