Blame lib/ext/server_cert_type.c

Packit Service 4684c1
/*
Packit Service 4684c1
 * Copyright (C) 2016 - 2018 ARPA2 project
Packit Service 4684c1
 *
Packit Service 4684c1
 * Author: Tom Vrancken (dev@tomvrancken.nl)
Packit Service 4684c1
 *
Packit Service 4684c1
 * This file is part of GnuTLS.
Packit Service 4684c1
 *
Packit Service 4684c1
 * The GnuTLS is free software; you can redistribute it and/or
Packit Service 4684c1
 * modify it under the terms of the GNU Lesser General Public License
Packit Service 4684c1
 * as published by the Free Software Foundation; either version 2.1 of
Packit Service 4684c1
 * the License, or (at your option) any later version.
Packit Service 4684c1
 *
Packit Service 4684c1
 * This library is distributed in the hope that it will be useful, but
Packit Service 4684c1
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 4684c1
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 4684c1
 * Lesser General Public License for more details.
Packit Service 4684c1
 *
Packit Service 4684c1
 * You should have received a copy of the GNU Lesser General Public License
Packit Service 4684c1
 * along with this program.  If not, see <https://www.gnu.org/licenses/>
Packit Service 4684c1
 *
Packit Service 4684c1
 * This file is part of the server_certificate_type extension as
Packit Service 4684c1
 * defined in RFC7250 (https://tools.ietf.org/html/rfc7250).
Packit Service 4684c1
 *
Packit Service 4684c1
 * The server_certificate_type extension in the client hello indicates
Packit Service 4684c1
 * the types of certificates the client is able to process when provided
Packit Service 4684c1
 * by the server in a subsequent certificate payload.
Packit Service 4684c1
 */
Packit Service 4684c1
Packit Service 4684c1
#include <gnutls_int.h>
Packit Service 4684c1
#include <gnutls/gnutls.h>
Packit Service 4684c1
#include "ext/cert_types.h"
Packit Service 4684c1
#include "ext/server_cert_type.h"
Packit Service 4684c1
#include "hello_ext.h"
Packit Service 4684c1
#include "hello_ext_lib.h"
Packit Service 4684c1
#include "errors.h"
Packit Service 4684c1
#include "state.h"
Packit Service 4684c1
#include "datum.h"
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
Packit Service 4684c1
						const uint8_t* data,
Packit Service 4684c1
						size_t data_size);
Packit Service 4684c1
static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
Packit Service 4684c1
						gnutls_buffer_st* data);
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
const hello_ext_entry_st ext_mod_server_cert_type = {
Packit Service 4684c1
	.name = "Server Certificate Type",
Packit Service 4684c1
	.tls_id = 20,
Packit Service 4684c1
	.gid = GNUTLS_EXTENSION_SERVER_CERT_TYPE,
Packit Service 4684c1
	.client_parse_point = GNUTLS_EXT_TLS,
Packit Service 4684c1
	.server_parse_point = GNUTLS_EXT_TLS,
Packit Service 4684c1
	.validity = GNUTLS_EXT_FLAG_TLS |
Packit Service 4684c1
		GNUTLS_EXT_FLAG_DTLS |
Packit Service 4684c1
		GNUTLS_EXT_FLAG_CLIENT_HELLO |
Packit Service 4684c1
		GNUTLS_EXT_FLAG_TLS12_SERVER_HELLO |
Packit Service 4684c1
		GNUTLS_EXT_FLAG_EE,
Packit Service 4684c1
	.recv_func = _gnutls_server_cert_type_recv_params,
Packit Service 4684c1
	.send_func = _gnutls_server_cert_type_send_params,
Packit Service 4684c1
	.pack_func = _gnutls_hello_ext_default_pack,
Packit Service 4684c1
	.unpack_func = _gnutls_hello_ext_default_unpack,
Packit Service 4684c1
	.deinit_func = _gnutls_hello_ext_default_deinit,
Packit Service 4684c1
	.cannot_be_overriden = 1
Packit Service 4684c1
};
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
static int _gnutls_server_cert_type_recv_params(gnutls_session_t session,
Packit Service 4684c1
						const uint8_t* data,
Packit Service 4684c1
						size_t data_size)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	gnutls_datum_t cert_types; // Holds the received cert types
Packit Service 4684c1
	gnutls_datum_t sent_cert_types; // Holds the previously sent cert types
Packit Service 4684c1
	gnutls_certificate_type_t cert_type;
Packit Service 4684c1
Packit Service 4684c1
	uint8_t i, found = 0;
Packit Service 4684c1
	const uint8_t* pdata = data;
Packit Service 4684c1
Packit Service 4684c1
	/* Only activate this extension if we have cert credentials set
Packit Service 4684c1
	 * and alternative cert types are allowed */
Packit Service 4684c1
	if (!are_alternative_cert_types_allowed(session) ||
Packit Service 4684c1
		(_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL))
Packit Service 4684c1
		return 0;
Packit Service 4684c1
Packit Service 4684c1
	if (!IS_SERVER(session)) {	// client mode
Packit Service 4684c1
Packit Service 4684c1
		/* Compare packet length with expected packet length. For the
Packit Service 4684c1
		 * client this is a single byte. */
Packit Service 4684c1
		if (data_size != 1) {
Packit Service 4684c1
			return
Packit Service 4684c1
					gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		/* The server picked one of the offered cert types if he supports
Packit Service 4684c1
		 * at least one of them. If both parties play by the rules then we
Packit Service 4684c1
		 * may only receive a cert type that we offered, i.e. one that we
Packit Service 4684c1
		 * support. Because the world isn't as beautiful as it may seem,
Packit Service 4684c1
		 * we're going to check it nevertheless. */
Packit Service 4684c1
		cert_type = IANA2cert_type(pdata[0]);
Packit Service 4684c1
Packit Service 4684c1
		// Check validity of cert type
Packit Service 4684c1
		if (cert_type == GNUTLS_CRT_UNKNOWN) {
Packit Service 4684c1
			return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		/* Get the cert types that we sent to the server (they were stored
Packit Service 4684c1
		 * in IANA representation.
Packit Service 4684c1
		 */
Packit Service 4684c1
		ret = _gnutls_hello_ext_get_datum(session,
Packit Service 4684c1
							 GNUTLS_EXTENSION_SERVER_CERT_TYPE,
Packit Service 4684c1
							 &sent_cert_types);
Packit Service 4684c1
		if (ret < 0) {
Packit Service 4684c1
			/* This should not happen and indicate a memory corruption!
Packit Service 4684c1
			 * Assertion are always on in production code so execution
Packit Service 4684c1
			 * will halt here. */
Packit Service 4684c1
			assert(false);
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		// Check whether what we got back is actually offered by us
Packit Service 4684c1
		for (i = 0; i < sent_cert_types.size; i++) {
Packit Service 4684c1
			if (IANA2cert_type(sent_cert_types.data[i]) == cert_type)
Packit Service 4684c1
				found = 1;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		if (found) {
Packit Service 4684c1
			// Everything OK, now set the server certificate type
Packit Service 4684c1
			_gnutls_session_server_cert_type_set(session, cert_type);
Packit Service 4684c1
			ret = GNUTLS_E_SUCCESS;
Packit Service 4684c1
		} else {
Packit Service 4684c1
			// No valid cert type found
Packit Service 4684c1
			ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		return ret;
Packit Service 4684c1
Packit Service 4684c1
	} else {		// server mode
Packit Service 4684c1
		// Compare packet length with expected packet length.
Packit Service 4684c1
		DECR_LEN(data_size, 1);
Packit Service 4684c1
		if (data[0] != data_size) {
Packit Service 4684c1
			return
Packit Service 4684c1
					gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
Packit Service 4684c1
		}
Packit Service 4684c1
		pdata += 1;
Packit Service 4684c1
Packit Service 4684c1
		// Assign the contents of our data buffer to a gnutls_datum_t
Packit Service 4684c1
		cert_types.data = (uint8_t*)pdata; // Need casting to get rid of 'discards const qualifier' warning
Packit Service 4684c1
		cert_types.size = data_size;
Packit Service 4684c1
Packit Service 4684c1
		// Store the server certificate types in our session
Packit Service 4684c1
		_gnutls_hello_ext_set_datum(session,
Packit Service 4684c1
							 GNUTLS_EXTENSION_SERVER_CERT_TYPE,
Packit Service 4684c1
							 &cert_types);
Packit Service 4684c1
Packit Service 4684c1
		/* We receive a list of supported certificate types that the client
Packit Service 4684c1
		 * is able to process when offered by the server via a subsequent
Packit Service 4684c1
		 * Certificate message. This list is sorted by order of preference.
Packit Service 4684c1
		 * We now check in this order of preference whether we support any
Packit Service 4684c1
		 * of these certificate types.
Packit Service 4684c1
		 */
Packit Service 4684c1
		for (i = 0; i < cert_types.size; i++) {
Packit Service 4684c1
			// Convert to internal representation
Packit Service 4684c1
			cert_type = IANA2cert_type(cert_types.data[i]);
Packit Service 4684c1
Packit Service 4684c1
			// If we have an invalid cert id then continue to the next
Packit Service 4684c1
			if (cert_type == GNUTLS_CRT_UNKNOWN)
Packit Service 4684c1
				continue;
Packit Service 4684c1
Packit Service 4684c1
			// Check for support of this cert type
Packit Service 4684c1
			if (_gnutls_session_cert_type_supported
Packit Service 4684c1
					(session, cert_type, true, GNUTLS_CTYPE_SERVER) == 0) {
Packit Service 4684c1
				found = 1;
Packit Service 4684c1
				break;
Packit Service 4684c1
			}
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		// We found a matching ctype, we pick this one
Packit Service 4684c1
		if (found) {
Packit Service 4684c1
			_gnutls_session_server_cert_type_set(session, cert_type);
Packit Service 4684c1
			ret = GNUTLS_E_SUCCESS;
Packit Service 4684c1
		} else {
Packit Service 4684c1
			/* If no supported certificate type can be found we terminate
Packit Service 4684c1
			 * with a fatal alert of type "unsupported_certificate"
Packit Service 4684c1
			 * (according to specification rfc7250).
Packit Service 4684c1
			 */
Packit Service 4684c1
			ret = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
Packit Service 4684c1
		}
Packit Service 4684c1
Packit Service 4684c1
		return ret;
Packit Service 4684c1
	}
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
static int _gnutls_server_cert_type_send_params(gnutls_session_t session,
Packit Service 4684c1
						gnutls_buffer_st* data)
Packit Service 4684c1
{
Packit Service 4684c1
	int ret;
Packit Service 4684c1
	uint8_t cert_type; // Holds an IANA cert type ID
Packit Service 4684c1
	uint8_t i = 0, num_cert_types = 0;
Packit Service 4684c1
	priority_st* cert_priorities;
Packit Service 4684c1
	gnutls_datum_t tmp_cert_types; // For type conversion
Packit Service 4684c1
	uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types. Inv: 0 <= cert type Id < 256
Packit Service 4684c1
Packit Service 4684c1
	/* Only activate this extension if we have cert credentials set
Packit Service 4684c1
	 * and alternative cert types are allowed */
Packit Service 4684c1
	if (!are_alternative_cert_types_allowed(session) ||
Packit Service 4684c1
		(_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL))
Packit Service 4684c1
		return 0;
Packit Service 4684c1
Packit Service 4684c1
	if (!IS_SERVER(session)) {	// Client mode
Packit Service 4684c1
		// For brevity
Packit Service 4684c1
		cert_priorities =
Packit Service 4684c1
				&session->internals.priorities->server_ctype;
Packit Service 4684c1
Packit Service 4684c1
		/* Retrieve server certificate type priorities if any. If no
Packit Service 4684c1
		 * priorities are set then the default server certificate type
Packit Service 4684c1
		 * initialization values apply. This default is currently set to
Packit Service 4684c1
		 * X.509 in which case we don't enable this extension.
Packit Service 4684c1
		 */
Packit Service 4684c1
		if (cert_priorities->num_priorities > 0) {	// Priorities are explicitly set
Packit Service 4684c1
			/* If the certificate priority is explicitly set to only
Packit Service 4684c1
			 * X.509 (default) then, according to spec we don't send
Packit Service 4684c1
			 * this extension. We check this here to avoid further work in
Packit Service 4684c1
			 * this routine. We also check it below after pruning supported
Packit Service 4684c1
			 * types.
Packit Service 4684c1
			 */
Packit Service 4684c1
			if (cert_priorities->num_priorities == 1 &&
Packit Service 4684c1
					cert_priorities->priorities[0] == DEFAULT_CERT_TYPE) {
Packit Service 4684c1
				_gnutls_handshake_log
Packit Service 4684c1
						("EXT[%p]: Server certificate type was set to default cert type (%s). "
Packit Service 4684c1
						 "We therefore do not send this extension.\n",
Packit Service 4684c1
						 session,
Packit Service 4684c1
						 gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE));
Packit Service 4684c1
Packit Service 4684c1
				// Explicitly set but default ctype, so don't send anything
Packit Service 4684c1
				return 0;
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			/* We are only allowed to send certificate types that we support.
Packit Service 4684c1
			 * Therefore we check this here and prune our original list.
Packit Service 4684c1
			 * This check might seem redundant now because we don't check for
Packit Service 4684c1
			 * credentials (they are not needed for a client) and only check the
Packit Service 4684c1
			 * priorities over which we already iterate. In the future,
Packit Service 4684c1
			 * additional checks might be necessary and they can be easily
Packit Service 4684c1
			 * added in the ..type_supported() routine without modifying the
Packit Service 4684c1
			 * structure of the code here.
Packit Service 4684c1
			 */
Packit Service 4684c1
			for (i = 0; i < cert_priorities->num_priorities; i++) {
Packit Service 4684c1
				if (_gnutls_session_cert_type_supported
Packit Service 4684c1
						(session, cert_priorities->priorities[i],
Packit Service 4684c1
						 false, GNUTLS_CTYPE_SERVER) == 0) {
Packit Service 4684c1
					/* Check whether we are allowed to store another cert type
Packit Service 4684c1
					 * in our buffer. In other words, prevent a possible buffer
Packit Service 4684c1
					 * overflow. This situation can occur when a user sets
Packit Service 4684c1
					 * duplicate cert types in the priority strings. */
Packit Service 4684c1
					if (num_cert_types >= GNUTLS_CRT_MAX)
Packit Service 4684c1
						return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
Packit Service 4684c1
Packit Service 4684c1
					// Convert to IANA representation
Packit Service 4684c1
					ret = cert_type2IANA(cert_priorities->priorities[i]);
Packit Service 4684c1
Packit Service 4684c1
					if (ret < 0)
Packit Service 4684c1
						return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
					cert_type = ret; // For readability
Packit Service 4684c1
Packit Service 4684c1
					// Add this cert type to our list with supported types
Packit Service 4684c1
					cert_types[num_cert_types] = cert_type;
Packit Service 4684c1
					num_cert_types++;
Packit Service 4684c1
Packit Service 4684c1
					_gnutls_handshake_log
Packit Service 4684c1
							("EXT[%p]: Server certificate type %s (%d) was queued.\n",
Packit Service 4684c1
							 session,
Packit Service 4684c1
							 gnutls_certificate_type_get_name(cert_priorities->priorities[i]),
Packit Service 4684c1
							 cert_type);
Packit Service 4684c1
				}
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			/* Check whether there are any supported certificate types left
Packit Service 4684c1
			 * after the previous pruning step. If not, we do not send this
Packit Service 4684c1
			 * extension. Also, if the only supported type is the default type
Packit Service 4684c1
			 * we do not send this extension (according to RFC7250).
Packit Service 4684c1
			 */
Packit Service 4684c1
			if (num_cert_types == 0) {	// For now, this should not occur since we only check priorities while pruning.
Packit Service 4684c1
				_gnutls_handshake_log
Packit Service 4684c1
						("EXT[%p]: Server certificate types were set but none of them is supported. "
Packit Service 4684c1
						 "We do not send this extension.\n",
Packit Service 4684c1
						 session);
Packit Service 4684c1
Packit Service 4684c1
				return 0;
Packit Service 4684c1
			} else if (num_cert_types == 1 &&
Packit Service 4684c1
					 IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) {
Packit Service 4684c1
				_gnutls_handshake_log
Packit Service 4684c1
						("EXT[%p]: The only supported server certificate type is (%s) which is the default. "
Packit Service 4684c1
						 "We therefore do not send this extension.\n",
Packit Service 4684c1
						 session,
Packit Service 4684c1
						 gnutls_certificate_type_get_name(DEFAULT_CERT_TYPE));
Packit Service 4684c1
Packit Service 4684c1
				return 0;
Packit Service 4684c1
			}
Packit Service 4684c1
Packit Service 4684c1
			/* We have data to send and store a copy internally. We convert
Packit Service 4684c1
			 * our list with supported cert types to a datum_t in order to
Packit Service 4684c1
			 * be able to make the ..._set_datum call.
Packit Service 4684c1
			 */
Packit Service 4684c1
			tmp_cert_types.data = cert_types;
Packit Service 4684c1
			tmp_cert_types.size = num_cert_types;
Packit Service 4684c1
Packit Service 4684c1
			_gnutls_hello_ext_set_datum(session,
Packit Service 4684c1
								 GNUTLS_EXTENSION_SERVER_CERT_TYPE,
Packit Service 4684c1
								 &tmp_cert_types);
Packit Service 4684c1
Packit Service 4684c1
			/* Serialize the certificate types into a sequence of octets
Packit Service 4684c1
			 * uint8: length of sequence of cert types (1 octet)
Packit Service 4684c1
			 * uint8: cert types (0 <= #octets <= 255)
Packit Service 4684c1
			 */
Packit Service 4684c1
			ret = _gnutls_buffer_append_data_prefix(data, 8,
Packit Service 4684c1
								cert_types,
Packit Service 4684c1
								num_cert_types);
Packit Service 4684c1
Packit Service 4684c1
			// Check for errors and cleanup in case of error
Packit Service 4684c1
			if (ret < 0) {
Packit Service 4684c1
				return gnutls_assert_val(ret);
Packit Service 4684c1
			} else {
Packit Service 4684c1
				// Number of bytes we are sending
Packit Service 4684c1
				return num_cert_types + 1;
Packit Service 4684c1
			}
Packit Service 4684c1
		}
Packit Service 4684c1
	} else {	// Server mode
Packit Service 4684c1
		// Retrieve negotiated server certificate type and send it
Packit Service 4684c1
		ret = cert_type2IANA(get_certificate_type(
Packit Service 4684c1
					session, GNUTLS_CTYPE_SERVER));
Packit Service 4684c1
Packit Service 4684c1
		if (ret < 0)
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
		cert_type = ret; // For readability
Packit Service 4684c1
Packit Service 4684c1
		ret = gnutls_buffer_append_data(data, &cert_type, 1);
Packit Service 4684c1
Packit Service 4684c1
		if (ret < 0)
Packit Service 4684c1
			return gnutls_assert_val(ret);
Packit Service 4684c1
Packit Service 4684c1
		return 1;	// sent one byte
Packit Service 4684c1
	}
Packit Service 4684c1
Packit Service 4684c1
	// In all other cases don't enable this extension
Packit Service 4684c1
	return 0;
Packit Service 4684c1
}
Packit Service 4684c1
Packit Service 4684c1
Packit Service 4684c1
/** Extension interface **/
Packit Service 4684c1
Packit Service 4684c1
/* The interface is defined in state.c:
Packit Service 4684c1
 * Public:
Packit Service 4684c1
 * - gnutls_certificate_type_get2
Packit Service 4684c1
 *
Packit Service 4684c1
 * Private:
Packit Service 4684c1
 * - _gnutls_session_server_cert_type_set
Packit Service 4684c1
 */