/*
* 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
*
*/
#include "gnutls_int.h"
#include
#include "errors.h"
#include
#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->transport == session->internals.transport) {
return 1;
}
return 0;
}
int _gnutls_version_mark_disabled(const char *name)
{
#ifndef DISABLE_SYSTEM_CONFIG
version_entry_st *p;
for (p = sup_versions; p->name != NULL; p++)
if (c_strcasecmp(p->name, name) == 0) {
p->supported = 0;
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;
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->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->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;
}
/* 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->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;
}