/*
* 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
*
* 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
#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 we have cert credentials set
* and alternative cert types are allowed */
if (!are_alternative_cert_types_allowed(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 if 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 = 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 (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 = 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_priorities;
gnutls_datum_t tmp_cert_types; // For type conversion
uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types. Inv: 0 <= cert type Id < 256
const version_entry_st* vers = get_version(session);
/* Only activate this extension if we have cert credentials set
* and alternative cert types are allowed */
if (!are_alternative_cert_types_allowed(session) ||
(_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL))
return 0;
if (!IS_SERVER(session)) { // Client mode
// For brevity
cert_priorities =
&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_priorities->num_priorities > 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_priorities->num_priorities == 1 &&
cert_priorities->priorities[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_priorities->num_priorities; i++) {
if (_gnutls_session_cert_type_supported
(session, cert_priorities->priorities[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
ret = cert_type2IANA(cert_priorities->priorities[i]);
if (ret < 0)
return gnutls_assert_val(ret);
cert_type = ret; // For readability
// 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_priorities->priorities[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 &&
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.).
*/
ret = cert_type2IANA(get_certificate_type(
session, GNUTLS_CTYPE_CLIENT));
if (ret < 0)
return gnutls_assert_val(ret);
cert_type = ret; // For readability
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
*/