diff options
Diffstat (limited to 'lib/ext')
-rw-r--r-- | lib/ext/Makefile.am | 5 | ||||
-rw-r--r-- | lib/ext/cert_types.h | 50 | ||||
-rw-r--r-- | lib/ext/client_cert_type.c | 371 | ||||
-rw-r--r-- | lib/ext/client_cert_type.h | 36 | ||||
-rw-r--r-- | lib/ext/server_cert_type.c | 348 | ||||
-rw-r--r-- | lib/ext/server_cert_type.h | 36 |
6 files changed, 845 insertions, 1 deletions
diff --git a/lib/ext/Makefile.am b/lib/ext/Makefile.am index b7f204f1d9..c8ef79101c 100644 --- a/lib/ext/Makefile.am +++ b/lib/ext/Makefile.am @@ -48,7 +48,10 @@ libgnutls_ext_la_SOURCES = max_record.c \ supported_groups.c supported_groups.h \ ec_point_formats.c ec_point_formats.h \ early_data.c early_data.h \ - record_size_limit.c record_size_limit.h + record_size_limit.c record_size_limit.h \ + client_cert_type.c client_cert_type.h \ + server_cert_type.c server_cert_type.h \ + cert_types.h if ENABLE_ALPN libgnutls_ext_la_SOURCES += alpn.c alpn.h diff --git a/lib/ext/cert_types.h b/lib/ext/cert_types.h new file mode 100644 index 0000000000..c54e0f2bfe --- /dev/null +++ b/lib/ext/cert_types.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 provides common functionality for certificate type + * handling during TLS hello extensions. + * + */ + +/* Maps IANA TLS Certificate Types identifiers to internal + * certificate type representation. + */ +static inline gnutls_certificate_type_t _gnutls_IANA2cert_type(int num) +{ + switch (num) { + case 0: + return GNUTLS_CRT_X509; + default: + return GNUTLS_CRT_UNKNOWN; + } +} + +/* Maps internal certificate type representation to + * IANA TLS Certificate Types identifiers. + */ +static inline int _gnutls_cert_type2IANA(gnutls_certificate_type_t cert_type) +{ + switch (cert_type) { + case GNUTLS_CRT_X509: + return 0; + default: + return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } +} diff --git a/lib/ext/client_cert_type.c b/lib/ext/client_cert_type.c new file mode 100644 index 0000000000..8bce721ace --- /dev/null +++ b/lib/ext/client_cert_type.c @@ -0,0 +1,371 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the client_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The client_certificate_type extension in the client hello indicates + * the certificate types the client is able to provide to the server, + * when requested using a certificate_request message. + */ + +#include "gnutls_int.h" +#include <gnutls/gnutls.h> +#include "ext/cert_types.h" +#include "ext/client_cert_type.h" +#include "hello_ext.h" +#include "hello_ext_lib.h" +#include "errors.h" +#include <state.h> +#include <datum.h> + + +static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size); +static int _gnutls_client_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data); + + +const hello_ext_entry_st ext_mod_client_cert_type = { + .name = "Client Certificate Type", + .tls_id = 19, + .gid = GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + .parse_type = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | + GNUTLS_EXT_FLAG_DTLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO | + GNUTLS_EXT_FLAG_EE, + .recv_func = _gnutls_client_cert_type_recv_params, + .send_func = _gnutls_client_cert_type_send_params, + .pack_func = _gnutls_hello_ext_default_pack, + .unpack_func = _gnutls_hello_ext_default_unpack, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 1 +}; + + +static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size) +{ + int ret; + gnutls_datum_t cert_types; // Holds the received cert types + gnutls_datum_t sent_cert_types; // Holds the previously sent cert types + gnutls_certificate_type_t cert_type; + + uint8_t i, found = 0; + ssize_t len = data_size; + const uint8_t* pdata = data; + + /* Only activate this extension if cert type negotiation is enabled + * and we have cert credentials set */ + if (!_gnutls_has_negotiate_ctypes(session) || + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL) + return 0; + + if (!IS_SERVER(session)) { // client mode + + /* Compare packet length with expected packet length. For the + * client this is a single byte. */ + if (len != 1) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + + /* The server picked one of the offered cert types iff he supports + * at least one of them and decided to do a client certificate + * request. If both parties play by the rules then we may only + * receive a cert type that we offered, i.e. one that we support. + * Because the world isn't as beautiful as it may seem, we're going + * to check it nevertheless. */ + cert_type = _gnutls_IANA2cert_type(pdata[0]); + + // Check validity of cert type + if (cert_type == GNUTLS_CRT_UNKNOWN) { + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); + } + + /* Get the cert types that we sent to the server (they were stored + * in IANA representation. + */ + ret = _gnutls_hello_ext_get_datum(session, + GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + &sent_cert_types); + if (ret < 0) { + /* This should not happen and indicate a memory corruption! + * Assertion are always on in production code so execution + * will halt here. */ + assert(false); + } + + // Check whether what we got back is actually offered by us + for (i = 0; i < sent_cert_types.size; i++) { + if (_gnutls_IANA2cert_type(sent_cert_types.data[i]) == cert_type) + found = 1; + } + + if (found) { + // Everything OK, now set the client certificate type + _gnutls_session_client_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + // No valid cert type found + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + + } else { // server mode + // Compare packet length with expected packet length. + DECR_LEN(len, 1); + if (data[0] != len) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + pdata += 1; + + // Assign the contents of our data buffer to a gnutls_datum_t + cert_types.data = (uint8_t*)pdata; // Need casting to get rid of 'discards const qualifier' warning + cert_types.size = len; + + // Store the client certificate types in our session + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + &cert_types); + + /* We receive a list of supported certificate types that the client + * is able to provide when requested via a client certificate + * request. This list is sorted by order of preference. We now check + * in this order of preference whether we support any of these + * certificate types. + */ + for (i = 0; i < cert_types.size; i++) { + // Convert to internal representation + cert_type = _gnutls_IANA2cert_type(cert_types.data[i]); + + // If we have an invalid cert id then continue to the next + if (cert_type == GNUTLS_CRT_UNKNOWN) + continue; + + // Check for support of this cert type + if (_gnutls_session_cert_type_supported + (session, cert_type, false, GNUTLS_CTYPE_CLIENT) == 0) { + found = 1; + break; + } + } + + // We found a matching ctype, we pick this one + if (found) { + _gnutls_session_client_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + /* If no supported certificate type can be found we terminate + * with a fatal alert of type "unsupported_certificate" + * (according to specification rfc7250). + */ + _gnutls_handshake_log + ("EXT[%p]: No supported client certificate type was found. " + "Aborting connection.\n", session); + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + } +} + +static int _gnutls_client_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data) +{ + int ret; + uint8_t cert_type; // Holds an IANA cert type ID + uint8_t i = 0, num_cert_types = 0; + priority_st* cert_priors; + gnutls_datum_t tmp_cert_types; // For type conversion + uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types + const version_entry_st* vers = get_version(session); + + /* Only activate this extension if cert type negotiation is enabled + * and we have cert credentials set */ + if (!_gnutls_has_negotiate_ctypes(session) || + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL) + return 0; + + if (!IS_SERVER(session)) { // Client mode + // For brevity + cert_priors = + &session->internals.priorities->client_ctype; + + /* Retrieve client certificate type priorities if any. If no + * priorities are set then the default client certificate type + * initialization values apply. This default is currently set to + * x.509 in which case we don't enable this extension. + */ + if (cert_priors->algorithms > 0) { // Priorities are explicitly set + /* If the certificate priority is explicitly set to only + * X.509 (default) then, according to spec we don't send + * this extension. We check this here to avoid further work in + * this routine. We also check it below after pruning supported + * types. + */ + if (cert_priors->algorithms == 1 && + cert_priors->priority[0] == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: Client certificate type was set to default cert type (%s). " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + // Explicitly set but default ctype, so don't send anything + return 0; + } + + /* We are only allowed to send certificate types that we support, + * i.e. have credentials for. Therefore we check this here and + * prune our original list. + */ + for (i = 0; i < cert_priors->algorithms; i++) { + if (_gnutls_session_cert_type_supported + (session, cert_priors->priority[i], + true, GNUTLS_CTYPE_CLIENT) == 0) { + /* Check whether we are allowed to store another cert type + * in our buffer. In other words, prevent a possible buffer + * overflow. This situation can occur when a user sets + * duplicate cert types in the priority strings. */ + if (num_cert_types >= GNUTLS_CRT_MAX) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + + // Convert to IANA representation + cert_type = _gnutls_cert_type2IANA(cert_priors->priority[i]); + // Add this cert type to our list with supported types + cert_types[num_cert_types] = cert_type; + num_cert_types++; + + _gnutls_handshake_log + ("EXT[%p]: Client certificate type %s (%d) was queued.\n", + session, + gnutls_certificate_type_get_name(cert_priors->priority[i]), + cert_type); + } + } + + /* Check whether there are any supported certificate types left + * after the previous pruning step. If not, we do not send this + * extension. Also, if the only supported type is the default type + * we do not send this extension (according to RFC7250). + */ + if (num_cert_types == 0) { + _gnutls_handshake_log + ("EXT[%p]: Client certificate types were set but none of them is supported. " + "You might want to check your credentials or your priorities. " + "We do not send this extension.\n", + session); + + return 0; + } else if (num_cert_types == 1 && + _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: The only supported client certificate type is (%s) which is the default. " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + return 0; + } + + /* We have data to send and store a copy internally. We convert + * our list with supported cert types to a datum_t in order to + * be able to make the ..._set_datum call. + */ + tmp_cert_types.data = cert_types; + tmp_cert_types.size = num_cert_types; + + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_CLIENT_CERT_TYPE, + &tmp_cert_types); + + /* Serialize the certificate types into a sequence of octets + * uint8: length of sequence of cert types (1 octet) + * uint8: cert types (0 <= #octets <= 255) + */ + ret = _gnutls_buffer_append_data_prefix(data, 8, + cert_types, + num_cert_types); + + // Check for errors + if (ret < 0) { + return gnutls_assert_val(ret); + } else { + // Number of bytes we are sending + return num_cert_types + 1; + } + } + } else { // Server mode + /* TLS 1.2: + * Check whether we are going to send a certificate request, + * otherwise omit the response. This is conform spec. + * (RFC7250, 4.2 case 3.). + * + * TLS 1.3: + * TLS 1.3 supports post-handshake authentication for the client. + * It means that a server can ask for a client certificate anytime + * after the handshake. In order for this to work we must always + * complete the certificate type negotiation and therefore respond + * with a cert type message. + */ + if (session->internals.send_cert_req != 0 || + vers->tls13_sem) { + /* Retrieve negotiated client certificate type and send it to + * the client. + * The scenario where we want to send a certificate request but + * do not have a matching certificate does not occur because we + * already terminate the connection at reception of this extension + * when we cannot find a matching client certificate. This is conform + * spec (RFC7250, 4.2 case 2.). + */ + cert_type = + _gnutls_cert_type2IANA(session-> + security_parameters.client_ctype); + + ret = gnutls_buffer_append_data(data, &cert_type, 1); + + if (ret < 0) + return gnutls_assert_val(ret); + + return 1; // sent one byte + } + } + + // In all other cases don't enable this extension + return 0; +} + + +/** Extension interface **/ + +/* The interface is defined in state.c: + * Public: + * - gnutls_certificate_type_get2 + * + * Private: + * - _gnutls_session_client_cert_type_set + */ diff --git a/lib/ext/client_cert_type.h b/lib/ext/client_cert_type.h new file mode 100644 index 0000000000..454e9bff56 --- /dev/null +++ b/lib/ext/client_cert_type.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the client_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The client_certificate_type extension in the client hello indicates + * the certificate types the client is able to provide to the server, + * when requested using a certificate_request message. + */ + +#ifndef EXT_CLIENT_CERT_TYPE_H +#define EXT_CLIENT_CERT_TYPE_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_client_cert_type; + +#endif diff --git a/lib/ext/server_cert_type.c b/lib/ext/server_cert_type.c new file mode 100644 index 0000000000..b1086c7f10 --- /dev/null +++ b/lib/ext/server_cert_type.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the server_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The server_certificate_type extension in the client hello indicates + * the types of certificates the client is able to process when provided + * by the server in a subsequent certificate payload. + */ + +#include <gnutls_int.h> +#include <gnutls/gnutls.h> +#include "ext/cert_types.h" +#include "ext/server_cert_type.h" +#include "hello_ext.h" +#include "hello_ext_lib.h" +#include "errors.h" +#include <state.h> +#include <datum.h> + + +static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size); +static int _gnutls_server_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data); + + +const hello_ext_entry_st ext_mod_server_cert_type = { + .name = "Server Certificate Type", + .tls_id = 20, + .gid = GNUTLS_EXTENSION_SERVER_CERT_TYPE, + .parse_type = GNUTLS_EXT_TLS, + .validity = GNUTLS_EXT_FLAG_TLS | + GNUTLS_EXT_FLAG_DTLS | + GNUTLS_EXT_FLAG_CLIENT_HELLO | + GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO | + GNUTLS_EXT_FLAG_EE, + .recv_func = _gnutls_server_cert_type_recv_params, + .send_func = _gnutls_server_cert_type_send_params, + .pack_func = _gnutls_hello_ext_default_pack, + .unpack_func = _gnutls_hello_ext_default_unpack, + .deinit_func = _gnutls_hello_ext_default_deinit, + .cannot_be_overriden = 1 +}; + + +static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, + const uint8_t* data, + size_t data_size) +{ + int ret; + gnutls_datum_t cert_types; // Holds the received cert types + gnutls_datum_t sent_cert_types; // Holds the previously sent cert types + gnutls_certificate_type_t cert_type; + + uint8_t i, found = 0; + ssize_t len = data_size; + const uint8_t* pdata = data; + + /* Only activate this extension if cert type negotiation is enabled + * and we have cert credentials set */ + if (!_gnutls_has_negotiate_ctypes(session) || + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL) + return 0; + + if (!IS_SERVER(session)) { // client mode + + /* Compare packet length with expected packet length. For the + * client this is a single byte. */ + if (len != 1) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + + /* The server picked one of the offered cert types iff he supports + * at least one of them. If both parties play by the rules then we + * may only receive a cert type that we offered, i.e. one that we + * support. Because the world isn't as beautiful as it may seem, + * we're going to check it nevertheless. */ + cert_type = _gnutls_IANA2cert_type(pdata[0]); + + // Check validity of cert type + if (cert_type == GNUTLS_CRT_UNKNOWN) { + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); + } + + /* Get the cert types that we sent to the server (they were stored + * in IANA representation. + */ + ret = _gnutls_hello_ext_get_datum(session, + GNUTLS_EXTENSION_SERVER_CERT_TYPE, + &sent_cert_types); + if (ret < 0) { + /* This should not happen and indicate a memory corruption! + * Assertion are always on in production code so execution + * will halt here. */ + assert(false); + } + + // Check whether what we got back is actually offered by us + for (i = 0; i < sent_cert_types.size; i++) { + if (_gnutls_IANA2cert_type(sent_cert_types.data[i]) == cert_type) + found = 1; + } + + if (found) { + // Everything OK, now set the server certificate type + _gnutls_session_server_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + // No valid cert type found + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + + } else { // server mode + // Compare packet length with expected packet length. + DECR_LEN(len, 1); + if (data[0] != len) { + return + gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + } + pdata += 1; + + // Assign the contents of our data buffer to a gnutls_datum_t + cert_types.data = (uint8_t*)pdata; // Need casting to get rid of 'discards const qualifier' warning + cert_types.size = len; + + // Store the server certificate types in our session + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_SERVER_CERT_TYPE, + &cert_types); + + /* We receive a list of supported certificate types that the client + * is able to process when offered by the server via a subsequent + * Certificate message. This list is sorted by order of preference. + * We now check in this order of preference whether we support any + * of these certificate types. + */ + for (i = 0; i < cert_types.size; i++) { + // Convert to internal representation + cert_type = _gnutls_IANA2cert_type(cert_types.data[i]); + + // If we have an invalid cert id then continue to the next + if (cert_type == GNUTLS_CRT_UNKNOWN) + continue; + + // Check for support of this cert type + if (_gnutls_session_cert_type_supported + (session, cert_type, true, GNUTLS_CTYPE_SERVER) == 0) { + found = 1; + break; + } + } + + // We found a matching ctype, we pick this one + if (found) { + _gnutls_session_server_cert_type_set(session, cert_type); + ret = GNUTLS_E_SUCCESS; + } else { + /* If no supported certificate type can be found we terminate + * with a fatal alert of type "unsupported_certificate" + * (according to specification rfc7250). + */ + ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + return ret; + } +} + +static int _gnutls_server_cert_type_send_params(gnutls_session_t session, + gnutls_buffer_st* data) +{ + int ret; + uint8_t cert_type; // Holds an IANA cert type ID + uint8_t i = 0, num_cert_types = 0; + priority_st* cert_priors; + gnutls_datum_t tmp_cert_types; // For type conversion + uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types + + /* Only activate this extension if cert type negotiation is enabled + * and we have cert credentials set */ + if (!_gnutls_has_negotiate_ctypes(session) || + _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL) + return 0; + + if (!IS_SERVER(session)) { // Client mode + // For brevity + cert_priors = + &session->internals.priorities->server_ctype; + + /* Retrieve server certificate type priorities if any. If no + * priorities are set then the default server certificate type + * initialization values apply. This default is currently set to + * X.509 in which case we don't enable this extension. + */ + if (cert_priors->algorithms > 0) { // Priorities are explicitly set + /* If the certificate priority is explicitly set to only + * X.509 (default) then, according to spec we don't send + * this extension. We check this here to avoid further work in + * this routine. We also check it below after pruning supported + * types. + */ + if (cert_priors->algorithms == 1 && + cert_priors->priority[0] == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: Server certificate type was set to default cert type (%s). " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + // Explicitly set but default ctype, so don't send anything + return 0; + } + + /* We are only allowed to send certificate types that we support. + * Therefore we check this here and prune our original list. + * This check might seem redundant now because we don't check for + * credentials (they are not needed for a client) and only check the + * priorities over which we already iterate. In the future, + * additional checks might be necessary and they can be easily + * added in the ..type_supported() routine without modifying the + * structure of the code here. + */ + for (i = 0; i < cert_priors->algorithms; i++) { + if (_gnutls_session_cert_type_supported + (session, cert_priors->priority[i], + false, GNUTLS_CTYPE_SERVER) == 0) { + /* Check whether we are allowed to store another cert type + * in our buffer. In other words, prevent a possible buffer + * overflow. This situation can occur when a user sets + * duplicate cert types in the priority strings. */ + if (num_cert_types >= GNUTLS_CRT_MAX) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + + // Convert to IANA representation + cert_type = _gnutls_cert_type2IANA(cert_priors->priority[i]); + // Add this cert type to our list with supported types + cert_types[num_cert_types] = cert_type; + num_cert_types++; + + _gnutls_handshake_log + ("EXT[%p]: Server certificate type %s (%d) was queued.\n", + session, + gnutls_certificate_type_get_name(cert_priors->priority[i]), + cert_type); + } + } + + /* Check whether there are any supported certificate types left + * after the previous pruning step. If not, we do not send this + * extension. Also, if the only supported type is the default type + * we do not send this extension (according to RFC7250). + */ + if (num_cert_types == 0) { // For now, this should not occur since we only check priorities while pruning. + _gnutls_handshake_log + ("EXT[%p]: Server certificate types were set but none of them is supported. " + "We do not send this extension.\n", + session); + + return 0; + } else if (num_cert_types == 1 && + _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) { + _gnutls_handshake_log + ("EXT[%p]: The only supported server certificate type is (%s) which is the default. " + "We therefore do not send this extension.\n", + session, + gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE)); + + return 0; + } + + /* We have data to send and store a copy internally. We convert + * our list with supported cert types to a datum_t in order to + * be able to make the ..._set_datum call. + */ + tmp_cert_types.data = cert_types; + tmp_cert_types.size = num_cert_types; + + _gnutls_hello_ext_set_datum(session, + GNUTLS_EXTENSION_SERVER_CERT_TYPE, + &tmp_cert_types); + + /* Serialize the certificate types into a sequence of octets + * uint8: length of sequence of cert types (1 octet) + * uint8: cert types (0 <= #octets <= 255) + */ + ret = _gnutls_buffer_append_data_prefix(data, 8, + cert_types, + num_cert_types); + + // Check for errors and cleanup in case of error + if (ret < 0) { + return gnutls_assert_val(ret); + } else { + // Number of bytes we are sending + return num_cert_types + 1; + } + } + } else { // Server mode + // Retrieve negotiated server certificate type and send it + cert_type = + _gnutls_cert_type2IANA(session->security_parameters. + server_ctype); + + ret = gnutls_buffer_append_data(data, &cert_type, 1); + + if (ret < 0) + return gnutls_assert_val(ret); + + return 1; // sent one byte + } + + // In all other cases don't enable this extension + return 0; +} + + +/** Extension interface **/ + +/* The interface is defined in state.c: + * Public: + * - gnutls_certificate_type_get2 + * + * Private: + * - _gnutls_session_server_cert_type_set + */ diff --git a/lib/ext/server_cert_type.h b/lib/ext/server_cert_type.h new file mode 100644 index 0000000000..b5eab5c9e6 --- /dev/null +++ b/lib/ext/server_cert_type.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * 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 is part of the server_certificate_type extension as + * defined in RFC7250 (https://tools.ietf.org/html/rfc7250). + * + * The server_certificate_type extension in the client hello indicates + * the certificate types the client is able to process from the server + * in order to authenticate the server. + */ + +#ifndef EXT_SERVER_CERT_TYPE_H +#define EXT_SERVER_CERT_TYPE_H + +#include <hello_ext.h> + +extern const hello_ext_entry_st ext_mod_server_cert_type; + +#endif |