summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTom Vrancken <dev@tomvrancken.nl>2018-08-15 18:29:32 +0200
committerTom Vrancken <dev@tomvrancken.nl>2018-08-20 17:08:01 +0200
commit07180a416731749883234f931ac18831ff38abbb (patch)
tree0b9d13b9ad394477d566f01ba8e279c33711cc7c /lib
parenta42db538c3f01aa76e2c1a2affc39237840c2522 (diff)
downloadgnutls-07180a416731749883234f931ac18831ff38abbb.tar.gz
Implemented RFC7250 certificate type negotiation extensions.
Signed-off-by: Tom Vrancken <dev@tomvrancken.nl>
Diffstat (limited to 'lib')
-rw-r--r--lib/algorithms/cert_types.c5
-rw-r--r--lib/auth/cert.c93
-rw-r--r--lib/auth/rsa.c23
-rw-r--r--lib/auth/srp_rsa.c3
-rw-r--r--lib/cert-session.c42
-rw-r--r--lib/constate.c11
-rw-r--r--lib/ext/Makefile.am5
-rw-r--r--lib/ext/cert_types.h50
-rw-r--r--lib/ext/client_cert_type.c371
-rw-r--r--lib/ext/client_cert_type.h36
-rw-r--r--lib/ext/server_cert_type.c348
-rw-r--r--lib/ext/server_cert_type.h36
-rw-r--r--lib/gnutls_int.h32
-rw-r--r--lib/handshake.c6
-rw-r--r--lib/hello_ext.c4
-rw-r--r--lib/hello_ext_lib.c2
-rw-r--r--lib/includes/gnutls/gnutls.h.in33
-rw-r--r--lib/libgnutls.map2
-rw-r--r--lib/priority.c100
-rw-r--r--lib/session.c36
-rw-r--r--lib/session_pack.c16
-rw-r--r--lib/state.c177
-rw-r--r--lib/state.h10
-rw-r--r--lib/tls13/certificate_verify.c11
24 files changed, 1346 insertions, 106 deletions
diff --git a/lib/algorithms/cert_types.c b/lib/algorithms/cert_types.c
index a7c27e0fe3..dc9fc9a388 100644
--- a/lib/algorithms/cert_types.c
+++ b/lib/algorithms/cert_types.c
@@ -41,6 +41,8 @@ const char *gnutls_certificate_type_get_name(gnutls_certificate_type_t
if (type == GNUTLS_CRT_X509)
ret = "X.509";
+ if (type == GNUTLS_CRT_RAWPK)
+ ret = "Raw Public Key";
return ret;
}
@@ -61,6 +63,9 @@ gnutls_certificate_type_t gnutls_certificate_type_get_id(const char *name)
if (strcasecmp(name, "X.509") == 0
|| strcasecmp(name, "X509") == 0)
return GNUTLS_CRT_X509;
+ if (strcasecmp(name, "RAWPK") == 0
+ || strcasecmp(name, "RAWPUBKEY") == 0)
+ return GNUTLS_CRT_RAWPK;
return ret;
}
diff --git a/lib/auth/cert.c b/lib/auth/cert.c
index 4e2e484a2e..069968c5d3 100644
--- a/lib/auth/cert.c
+++ b/lib/auth/cert.c
@@ -308,7 +308,7 @@ get_issuers(gnutls_session_t session,
int i;
unsigned size;
- if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
+ if (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT) != GNUTLS_CRT_X509)
return 0;
/* put the requested DNs to req_dn, only in case
@@ -339,7 +339,7 @@ get_issuers(gnutls_session_t session,
return 0;
}
-/* Calls the client or server get callback.
+/* Calls the client or server certificate get callback.
*/
static int
call_get_cert_callback(gnutls_session_t session,
@@ -349,7 +349,7 @@ call_get_cert_callback(gnutls_session_t session,
{
gnutls_privkey_t local_key = NULL;
int ret = GNUTLS_E_INTERNAL_ERROR;
- gnutls_certificate_type_t type = gnutls_certificate_type_get(session);
+ gnutls_certificate_type_t type;
gnutls_certificate_credentials_t cred;
gnutls_pcert_st *pcert = NULL;
gnutls_ocsp_data_st *ocsp = NULL;
@@ -363,6 +363,19 @@ call_get_cert_callback(gnutls_session_t session,
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
+ /* Correctly set the certificate type depending on whether we
+ * have explicitly negotiated certificate types (RFC7250).
+ */
+ if (_gnutls_has_negotiate_ctypes(session)) {
+ if (IS_SERVER(session)) {
+ type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+ } else { // Client mode
+ type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT);
+ }
+ } else {
+ type = DEFAULT_CERT_TYPE;
+ }
+
if (cred->get_cert_callback3) {
struct gnutls_cert_retr_st info;
unsigned int flags = 0;
@@ -432,9 +445,9 @@ _gnutls_select_client_cert(gnutls_session_t session,
if (cred->get_cert_callback3 != NULL) {
- /* use a callback to get certificate
+ /* use a callback to get certificate
*/
- if (session->security_parameters.cert_type == GNUTLS_CRT_X509) {
+ if (session->security_parameters.client_ctype == GNUTLS_CRT_X509) {
issuers_dn_length =
get_issuers_num(session, data, data_size);
if (issuers_dn_length < 0) {
@@ -473,7 +486,7 @@ _gnutls_select_client_cert(gnutls_session_t session,
} else {
/* If we have no callbacks, try to guess.
*/
- if (session->security_parameters.cert_type == GNUTLS_CRT_X509) {
+ if (session->security_parameters.client_ctype == GNUTLS_CRT_X509) {
result =
find_x509_client_cert(session, cred, _data, _data_size,
pk_algos, pk_algos_length, &indx);
@@ -565,7 +578,7 @@ static int gen_x509_crt(gnutls_session_t session, gnutls_buffer_st * data)
int
_gnutls_gen_cert_client_crt(gnutls_session_t session, gnutls_buffer_st * data)
{
- switch (session->security_parameters.cert_type) {
+ switch (session->security_parameters.client_ctype) {
case GNUTLS_CRT_X509:
return gen_x509_crt(session, data);
default:
@@ -577,7 +590,7 @@ _gnutls_gen_cert_client_crt(gnutls_session_t session, gnutls_buffer_st * data)
int
_gnutls_gen_cert_server_crt(gnutls_session_t session, gnutls_buffer_st * data)
{
- switch (session->security_parameters.cert_type) {
+ switch (session->security_parameters.server_ctype) {
case GNUTLS_CRT_X509:
return gen_x509_crt(session, data);
default:
@@ -756,6 +769,7 @@ int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size)
{
int ret;
gnutls_certificate_credentials_t cred;
+ gnutls_certificate_type_t cert_type;
cred =
(gnutls_certificate_credentials_t) _gnutls_get_cred(session,
@@ -765,19 +779,28 @@ int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size)
return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
}
- switch (session->security_parameters.cert_type) {
- case GNUTLS_CRT_X509:
- ret = _gnutls_proc_x509_server_crt(session, data, data_size);
- break;
- default:
- gnutls_assert();
- return GNUTLS_E_INTERNAL_ERROR;
+ /* Determine what certificate type we need to process */
+ if (IS_SERVER(session)) {
+ // We are the server therefore we process the client certificate
+ cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT);
+ } else {
+ // We are the client therefore we process the server certificate
+ cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+ }
+
+ switch (cert_type) {
+ case GNUTLS_CRT_X509:
+ ret = _gnutls_proc_x509_server_crt(session, data, data_size);
+ break;
+ default:
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
}
return ret;
}
-/* Checks if we support the given signature algorithm
+/* Checks if we support the given signature algorithm
* (RSA or DSA). Returns the corresponding gnutls_pk_algorithm_t
* if true;
*/
@@ -1016,7 +1039,7 @@ _gnutls_proc_cert_client_crt_vrfy(gnutls_session_t session,
ret = _gnutls_get_auth_info_pcert(&peer_cert,
session->security_parameters.
- cert_type, info);
+ client_ctype, info);
if (ret < 0) {
gnutls_assert();
@@ -1078,7 +1101,7 @@ _gnutls_gen_cert_server_cert_req(gnutls_session_t session,
}
}
- if (session->security_parameters.cert_type == GNUTLS_CRT_X509 &&
+ if (session->security_parameters.client_ctype == GNUTLS_CRT_X509 &&
session->internals.ignore_rdn_sequence == 0) {
ret =
@@ -1215,9 +1238,17 @@ static void get_server_name(gnutls_session_t session, uint8_t * name,
return;
}
-/* Selects a signature algorithm (if required by the ciphersuite and TLS
- * version), appropriate for the certificate. If none can be selected
- * returns an error.
+/* Checks the compatibility of the pubkey in the certificate with the
+ * ciphersuite and selects a signature algorithm (if required by the
+ * ciphersuite and TLS version) appropriate for the certificate. If none
+ * can be selected returns an error.
+ *
+ * IMPORTANT
+ * Currently this function is only called from _gnutls_server_select_cert,
+ * i.e. it is only called at the server. We therefore retrieve the
+ * negotiated server certificate type within this function.
+ * If, in the future, this routine is called at the client then we
+ * need to adapt the implementation accordingly.
*/
static
int cert_select_sign_algorithm(gnutls_session_t session,
@@ -1231,8 +1262,14 @@ int cert_select_sign_algorithm(gnutls_session_t session,
unsigned key_usage;
gnutls_sign_algorithm_t algo;
const version_entry_st *ver = get_version(session);
+ gnutls_certificate_type_t ctype;
- if (session->security_parameters.cert_type != cert_type) {
+ assert(IS_SERVER(session));
+
+ /* Retrieve the server certificate type */
+ ctype = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
+ if (ctype != cert_type) {
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
}
@@ -1297,7 +1334,7 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e
* the ciphersuites.
*/
- /* If the callback which retrieves certificate has been set,
+ /* If the callback which retrieves the certificate has been set,
* use it and leave. We make sure that this is called once.
*/
if (cred->get_cert_callback3) {
@@ -1358,8 +1395,6 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e
/* found */
goto finished;
}
-
-
}
}
}
@@ -1524,6 +1559,7 @@ _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data,
const version_entry_st *ver = get_version(session);
gnutls_certificate_credentials_t cred;
unsigned vflags;
+ gnutls_certificate_type_t cert_type;
if (unlikely(info == NULL || info->ncerts == 0 || ver == NULL)) {
gnutls_assert();
@@ -1565,10 +1601,11 @@ _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data,
signature.data = data;
signature.size = sigsize;
+ // Retrieve the negotiated certificate type
+ cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
if ((ret =
- _gnutls_get_auth_info_pcert(&peer_cert,
- session->security_parameters.cert_type,
- info)) < 0) {
+ _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info)) < 0) {
gnutls_assert();
return ret;
}
diff --git a/lib/auth/rsa.c b/lib/auth/rsa.c
index f2e36bbe22..6afc91ae67 100644
--- a/lib/auth/rsa.c
+++ b/lib/auth/rsa.c
@@ -82,6 +82,18 @@ int check_key_usage_for_enc(gnutls_session_t session, unsigned key_usage)
}
/* This function reads the RSA parameters from peer's certificate;
+ *
+ * IMPORTANT:
+ * Currently this function gets only called on the client side
+ * during generation of the client kx msg. This function
+ * retrieves the RSA params from the peer's certificate. That is in
+ * this case the server's certificate. As of GNUTLS version 3.6.4 it is
+ * possible to negotiate different certificate types for client and
+ * server. Therefore the correct cert type needs to be retrieved to be
+ * used for the _gnutls_get_auth_info_pcert call. If this
+ * function is to be called on the server side in the future, extra
+ * checks need to be build in order to retrieve te correct
+ * certificate type.
*/
int
_gnutls_get_public_rsa_params(gnutls_session_t session,
@@ -91,6 +103,9 @@ _gnutls_get_public_rsa_params(gnutls_session_t session,
cert_auth_info_t info;
unsigned key_usage;
gnutls_pcert_st peer_cert;
+ gnutls_certificate_type_t cert_type;
+
+ assert(!IS_SERVER(session));
/* normal non export case */
@@ -101,10 +116,10 @@ _gnutls_get_public_rsa_params(gnutls_session_t session,
return GNUTLS_E_INTERNAL_ERROR;
}
- ret =
- _gnutls_get_auth_info_pcert(&peer_cert,
- session->security_parameters.
- cert_type, info);
+ // Get the negotiated server certificate type
+ cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
+ ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
if (ret < 0) {
gnutls_assert();
diff --git a/lib/auth/srp_rsa.c b/lib/auth/srp_rsa.c
index 2101f70a0f..06c6971d80 100644
--- a/lib/auth/srp_rsa.c
+++ b/lib/auth/srp_rsa.c
@@ -241,8 +241,7 @@ proc_srp_cert_server_kx(gnutls_session_t session, uint8_t * data,
ret =
_gnutls_get_auth_info_pcert(&peer_cert,
- session->security_parameters.
- cert_type, info);
+ session->security_parameters.server_ctype, info);
if (ret < 0) {
gnutls_assert();
diff --git a/lib/cert-session.c b/lib/cert-session.c
index 772d1417d9..580a871964 100644
--- a/lib/cert-session.c
+++ b/lib/cert-session.c
@@ -719,12 +719,12 @@ gnutls_certificate_verify_peers(gnutls_session_t session,
return GNUTLS_E_NO_CERTIFICATE_FOUND;
- switch (gnutls_certificate_type_get(session)) {
- case GNUTLS_CRT_X509:
- return _gnutls_x509_cert_verify_peers(session, data, elements,
- status);
- default:
- return GNUTLS_E_INVALID_REQUEST;
+ switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+ case GNUTLS_CRT_X509:
+ return _gnutls_x509_cert_verify_peers(session, data, elements,
+ status);
+ default:
+ return GNUTLS_E_INVALID_REQUEST;
}
}
@@ -820,14 +820,13 @@ time_t gnutls_certificate_expiration_time_peers(gnutls_session_t session)
return (time_t) - 1;
}
- switch (gnutls_certificate_type_get(session)) {
- case GNUTLS_CRT_X509:
- return
- _gnutls_x509_get_raw_crt_expiration_time(&info->
- raw_certificate_list
- [0]);
- default:
- return (time_t) - 1;
+ switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+ case GNUTLS_CRT_X509:
+ return
+ _gnutls_x509_get_raw_crt_expiration_time(&info->
+ raw_certificate_list[0]);
+ default:
+ return (time_t) - 1;
}
}
@@ -857,13 +856,12 @@ time_t gnutls_certificate_activation_time_peers(gnutls_session_t session)
return (time_t) - 1;
}
- switch (gnutls_certificate_type_get(session)) {
- case GNUTLS_CRT_X509:
- return
- _gnutls_x509_get_raw_crt_activation_time(&info->
- raw_certificate_list
- [0]);
- default:
- return (time_t) - 1;
+ switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) {
+ case GNUTLS_CRT_X509:
+ return
+ _gnutls_x509_get_raw_crt_activation_time(&info->
+ raw_certificate_list[0]);
+ default:
+ return (time_t) - 1;
}
}
diff --git a/lib/constate.c b/lib/constate.c
index 62623c10dd..456316258b 100644
--- a/lib/constate.c
+++ b/lib/constate.c
@@ -668,9 +668,9 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t
#define CPY_COMMON(tls13_sem) \
if (!tls13_sem) { \
dst->cs = src->cs; \
- memcpy( dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
- memcpy( dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
- memcpy( dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
+ memcpy(dst->master_secret, src->master_secret, GNUTLS_MASTER_SIZE); \
+ memcpy(dst->client_random, src->client_random, GNUTLS_RANDOM_SIZE); \
+ memcpy(dst->server_random, src->server_random, GNUTLS_RANDOM_SIZE); \
dst->ext_master_secret = src->ext_master_secret; \
dst->etm = src->etm; \
dst->max_record_recv_size = src->max_record_recv_size; \
@@ -679,10 +679,11 @@ int _gnutls_epoch_set_keys(gnutls_session_t session, uint16_t epoch, hs_stage_t
dst->grp = src->grp; \
dst->pversion = src->pversion; \
} \
- memcpy( dst->session_id, src->session_id, GNUTLS_MAX_SESSION_ID_SIZE); \
+ memcpy(dst->session_id, src->session_id, GNUTLS_MAX_SESSION_ID_SIZE); \
dst->session_id_size = src->session_id_size; \
dst->timestamp = src->timestamp; \
- dst->cert_type = src->cert_type; \
+ dst->client_ctype = src->client_ctype; \
+ dst->server_ctype = src->server_ctype; \
dst->client_auth_type = src->client_auth_type; \
dst->server_auth_type = src->server_auth_type
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
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index dfec39ec33..8ad6b1652d 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -191,9 +191,17 @@ typedef enum record_send_state_t {
RECORD_SEND_KEY_UPDATE_3
} record_send_state_t;
-/* the maximum size of encrypted packets */
+/* The mode check occurs a lot throughout GnuTLS and can be replaced by
+ * the following shorter macro. Also easier to update one macro
+ * in the future when the internal structure changes than all the conditionals
+ * itself.
+ */
+#define IS_SERVER(session) (session->security_parameters.entity == GNUTLS_SERVER)
+
+/* To check whether we have a DTLS session */
#define IS_DTLS(session) (session->internals.transport == GNUTLS_DGRAM)
+/* the maximum size of encrypted packets */
#define DEFAULT_MAX_RECORD_SIZE 16384
#define DEFAULT_MAX_EARLY_DATA_SIZE 16384
#define TLS_RECORD_HEADER_SIZE 5
@@ -327,6 +335,8 @@ typedef enum extensions_t {
GNUTLS_EXTENSION_MAX_RECORD_SIZE = 0,
GNUTLS_EXTENSION_STATUS_REQUEST,
GNUTLS_EXTENSION_CERT_TYPE,
+ GNUTLS_EXTENSION_CLIENT_CERT_TYPE,
+ GNUTLS_EXTENSION_SERVER_CERT_TYPE,
GNUTLS_EXTENSION_SUPPORTED_GROUPS,
GNUTLS_EXTENSION_SUPPORTED_EC_POINT_FORMATS,
GNUTLS_EXTENSION_SRP,
@@ -760,8 +770,9 @@ typedef struct {
/* The maximum amount of early data */
uint32_t max_early_data_size;
- /* holds the negotiated certificate type */
- gnutls_certificate_type_t cert_type;
+ /* holds the negotiated certificate types */
+ gnutls_certificate_type_t client_ctype;
+ gnutls_certificate_type_t server_ctype;
/* The selected (after server hello EC or DH group */
const gnutls_group_entry_st *grp;
@@ -887,7 +898,8 @@ typedef struct sign_algo_list_st {
/* For the external api */
struct gnutls_priority_st {
priority_st protocol;
- priority_st cert_type;
+ priority_st client_ctype;
+ priority_st server_ctype;
/* The following are not necessary to be stored in
* the structure; however they are required by the
@@ -1045,6 +1057,7 @@ typedef struct {
* the client finished message */
gnutls_buffer_st handshake_hash_buffer; /* used to keep the last received handshake
* message */
+
bool resumable; /* TRUE or FALSE - if we can resume that session */
send_ticket_state_t ticket_state; /* used by gnutls_session_ticket_send() */
@@ -1434,13 +1447,13 @@ void _gnutls_free_auth_info(gnutls_session_t session);
/* These two macros return the advertised TLS version of
* the peer.
*/
-#define _gnutls_get_adv_version_major( session) \
+#define _gnutls_get_adv_version_major(session) \
session->internals.adv_version_major
-#define _gnutls_get_adv_version_minor( session) \
+#define _gnutls_get_adv_version_minor(session) \
session->internals.adv_version_minor
-#define set_adv_version( session, major, minor) \
+#define set_adv_version(session, major, minor) \
session->internals.adv_version_major = major; \
session->internals.adv_version_minor = minor
@@ -1493,4 +1506,9 @@ inline static size_t max_user_send_size(gnutls_session_t session,
return max;
}
+inline static bool _gnutls_has_negotiate_ctypes(gnutls_session_t session)
+{
+ return session->internals.flags & GNUTLS_ENABLE_CERT_TYPE_NEG;
+}
+
#endif /* GNUTLS_INT_H */
diff --git a/lib/handshake.c b/lib/handshake.c
index ebea926aa5..914f8ecacc 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -198,8 +198,10 @@ static int tls12_resume_copy_required_vals(gnutls_session_t session, unsigned ti
id) < 0)
return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
- session->security_parameters.cert_type =
- session->internals.resumed_security_parameters.cert_type;
+ session->security_parameters.client_ctype =
+ session->internals.resumed_security_parameters.client_ctype;
+ session->security_parameters.server_ctype =
+ session->internals.resumed_security_parameters.server_ctype;
if (!ticket) {
memcpy(session->security_parameters.session_id,
diff --git a/lib/hello_ext.c b/lib/hello_ext.c
index 9e30bd5413..28e8e8c08e 100644
--- a/lib/hello_ext.c
+++ b/lib/hello_ext.c
@@ -55,6 +55,8 @@
#include <ext/record_size_limit.h>
#include "extv.h"
#include <num.h>
+#include <ext/client_cert_type.h>
+#include <ext/server_cert_type.h>
static void
unset_ext_data(gnutls_session_t session, const struct hello_ext_entry_st *, unsigned idx);
@@ -79,6 +81,8 @@ static hello_ext_entry_st const *extfunc[MAX_EXT_TYPES+1] = {
[GNUTLS_EXTENSION_HEARTBEAT] = &ext_mod_heartbeat,
#endif
[GNUTLS_EXTENSION_SESSION_TICKET] = &ext_mod_session_ticket,
+ [GNUTLS_EXTENSION_CLIENT_CERT_TYPE] = &ext_mod_client_cert_type,
+ [GNUTLS_EXTENSION_SERVER_CERT_TYPE] = &ext_mod_server_cert_type,
[GNUTLS_EXTENSION_SUPPORTED_GROUPS] = &ext_mod_supported_groups,
[GNUTLS_EXTENSION_SUPPORTED_EC_POINT_FORMATS] = &ext_mod_supported_ec_point_formats,
[GNUTLS_EXTENSION_SIGNATURE_ALGORITHMS] = &ext_mod_sig,
diff --git a/lib/hello_ext_lib.c b/lib/hello_ext_lib.c
index 5ee433b24e..663354caaf 100644
--- a/lib/hello_ext_lib.c
+++ b/lib/hello_ext_lib.c
@@ -40,7 +40,7 @@ void _gnutls_hello_ext_default_deinit(gnutls_ext_priv_data_t priv)
int
_gnutls_hello_ext_set_datum(gnutls_session_t session,
extensions_t id, const gnutls_datum_t *data)
-{
+{ //REMARK: we are limiting the data size to fit in a uint16 while a datum_t uses unsigned int!
gnutls_ext_priv_data_t epriv;
if (_gnutls_hello_ext_get_priv(session, id, &epriv) >= 0)
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 90ff1985d1..4d138b8446 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -406,6 +406,7 @@ typedef enum {
* applications which hide the length of transferred data via the TLS1.3 padding mechanism and
* are already taking steps to hide the data processing time. This comes at a performance
* penalty.
+ * @GNUTLS_ENABLE_CERT_TYPE_NEG: Enable certificate type negotiation extensions (RFC7250).
*
* Enumeration of different flags for gnutls_init() function. All the flags
* can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually
@@ -433,7 +434,8 @@ typedef enum {
GNUTLS_POST_HANDSHAKE_AUTH = (1<<14),
GNUTLS_NO_AUTO_REKEY = (1<<15),
GNUTLS_SAFE_PADDING_CHECK = (1<<16),
- GNUTLS_ENABLE_EARLY_START = (1<<17)
+ GNUTLS_ENABLE_EARLY_START = (1<<17),
+ GNUTLS_ENABLE_CERT_TYPE_NEG = (1<<18)
} gnutls_init_flags_t;
/* compatibility defines (previous versions of gnutls
@@ -719,7 +721,7 @@ typedef enum {
* @GNUTLS_CRT_UNKNOWN: Unknown certificate type.
* @GNUTLS_CRT_X509: X.509 Certificate.
* @GNUTLS_CRT_OPENPGP: OpenPGP certificate.
- * @GNUTLS_CRT_RAW: Raw public key (SubjectPublicKey)
+ * @GNUTLS_CRT_RAWPK: Raw public-key (SubjectPublicKeyInfo)
*
* Enumeration of different certificate types.
*/
@@ -727,7 +729,8 @@ typedef enum {
GNUTLS_CRT_UNKNOWN = 0,
GNUTLS_CRT_X509 = 1,
GNUTLS_CRT_OPENPGP = 2,
- GNUTLS_CRT_RAW = 3
+ GNUTLS_CRT_RAWPK = 3,
+ GNUTLS_CRT_MAX = GNUTLS_CRT_RAWPK
} gnutls_certificate_type_t;
/**
@@ -1060,6 +1063,24 @@ typedef enum {
GNUTLS_GOST_PARAMSET_CP_D
} gnutls_gost_paramset_t;
+/**
+ * gnutls_ctype_target_t:
+ * @GNUTLS_CTYPE_CLIENT: for requesting client certificate type values.
+ * @GNUTLS_CTYPE_SERVER: for requesting server certificate type values.
+ * @GNUTLS_CTYPE_OURS: for requesting our certificate type values.
+ * @GNUTLS_CTYPE_PEERS: for requesting the peers' certificate type values.
+ *
+ * Enumeration of certificate type targets with respect to asymmetric
+ * certificate types as specified in RFC7250 and P2P connection set up
+ * as specified in draft-vanrein-tls-symmetry-02.
+ */
+typedef enum {
+ GNUTLS_CTYPE_CLIENT,
+ GNUTLS_CTYPE_SERVER,
+ GNUTLS_CTYPE_OURS,
+ GNUTLS_CTYPE_PEERS
+} gnutls_ctype_target_t;
+
/* If you want to change this, then also change the define in
* gnutls_int.h, and recompile.
*/
@@ -1154,6 +1175,9 @@ gnutls_kx_algorithm_t gnutls_kx_get(gnutls_session_t session);
gnutls_mac_algorithm_t gnutls_mac_get(gnutls_session_t session);
gnutls_certificate_type_t
gnutls_certificate_type_get(gnutls_session_t session);
+gnutls_certificate_type_t
+gnutls_certificate_type_get2(gnutls_session_t session,
+ gnutls_ctype_target_t target);
int gnutls_sign_algorithm_get(gnutls_session_t session);
int gnutls_sign_algorithm_get_client(gnutls_session_t session);
@@ -1585,6 +1609,9 @@ int gnutls_priority_set_direct(gnutls_session_t session,
int gnutls_priority_certificate_type_list(gnutls_priority_t pcache,
const unsigned int **list);
+int gnutls_priority_certificate_type_list2(gnutls_priority_t pcache,
+ const unsigned int **list,
+ gnutls_ctype_target_t target);
int gnutls_priority_sign_list(gnutls_priority_t pcache,
const unsigned int **list);
int gnutls_priority_protocol_list(gnutls_priority_t pcache,
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index d827790745..c20dd33cfe 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1244,6 +1244,8 @@ GNUTLS_3_6_4
gnutls_ffdhe_6144_group_prime;
gnutls_ffdhe_6144_group_generator;
gnutls_ffdhe_6144_key_bits;
+ gnutls_certificate_type_get2;
+ gnutls_priority_certificate_type_list2;
} GNUTLS_3_6_3;
GNUTLS_FIPS140_3_4 {
diff --git a/lib/priority.c b/lib/priority.c
index 1d495d0e67..00681c53e8 100644
--- a/lib/priority.c
+++ b/lib/priority.c
@@ -517,6 +517,12 @@ static const int cert_type_priority_default[] = {
0
};
+static const int cert_type_priority_all[] = {
+ GNUTLS_CRT_X509,
+ GNUTLS_CRT_RAWPK,
+ 0
+};
+
typedef void (rmadd_func) (priority_st * priority_list, unsigned int alg);
static void prio_remove(priority_st * priority_list, unsigned int algo)
@@ -1621,7 +1627,9 @@ gnutls_priority_init(gnutls_priority_t * priority_cache,
if (strcasecmp(broken_list[0], LEVEL_NONE) != 0) {
_set_priority(&(*priority_cache)->protocol,
protocol_priority);
- _set_priority(&(*priority_cache)->cert_type,
+ _set_priority(&(*priority_cache)->client_ctype,
+ cert_type_priority_default);
+ _set_priority(&(*priority_cache)->server_ctype,
cert_type_priority_default);
_set_priority(&(*priority_cache)->_sign_algo,
sign_priority_default);
@@ -1755,8 +1763,39 @@ gnutls_priority_init(gnutls_priority_t * priority_cache,
goto error;
}
} else if (strncasecmp
- (&broken_list[i][1], "CTYPE-", 6) == 0) {
- continue;
+ (&broken_list[i][1], "CTYPE-", 6) == 0) { // Certificate types
+ if (strncasecmp(&broken_list[i][1], "CTYPE-ALL", 9) == 0)
+ { // Symmetric cert types, all types allowed
+ bulk_fn(&(*priority_cache)->client_ctype, cert_type_priority_all);
+ bulk_fn(&(*priority_cache)->server_ctype, cert_type_priority_all);
+ } else if (strncasecmp(&broken_list[i][1], "CTYPE-CLI-", 10) == 0)
+ { // Client certificate types
+ if (strncasecmp(&broken_list[i][1], "CTYPE-CLI-ALL", 13) == 0)
+ { // All client cert types allowed
+ bulk_fn(&(*priority_cache)->client_ctype, cert_type_priority_all);
+ } else if ((algo = gnutls_certificate_type_get_id
+ (&broken_list[i][11])) != GNUTLS_CRT_UNKNOWN)
+ { // Specific client cert type allowed
+ fn(&(*priority_cache)->client_ctype, algo);
+ } else goto error;
+ } else if (strncasecmp(&broken_list[i][1], "CTYPE-SRV-", 10) == 0)
+ { // Server certificate types
+ if (strncasecmp(&broken_list[i][1], "CTYPE-SRV-ALL", 13) == 0)
+ { // All server cert types allowed
+ bulk_fn(&(*priority_cache)->server_ctype, cert_type_priority_all);
+ } else if ((algo = gnutls_certificate_type_get_id
+ (&broken_list[i][11])) != GNUTLS_CRT_UNKNOWN)
+ { // Specific server cert type allowed
+ fn(&(*priority_cache)->server_ctype, algo);
+ } else goto error;
+ } else { // Symmetric certificate type
+ if ((algo = gnutls_certificate_type_get_id
+ (&broken_list[i][7])) != GNUTLS_CRT_UNKNOWN)
+ {
+ fn(&(*priority_cache)->client_ctype, algo);
+ fn(&(*priority_cache)->server_ctype, algo);
+ } else goto error;
+ }
} else if (strncasecmp
(&broken_list[i][1], "SIGN-", 5) == 0) {
if (strncasecmp
@@ -2207,7 +2246,13 @@ gnutls_priority_sign_list(gnutls_priority_t pcache,
* @list: will point to an integer list
*
* Get a list of available certificate types in the priority
- * structure.
+ * structure.
+ *
+ * As of version 3.6.4 this function is an alias for
+ * gnutls_priority_certificate_type_list2 with the target parameter
+ * set to:
+ * - GNUTLS_CTYPE_SERVER, if the %SERVER_PRECEDENCE option is set
+ * - GNUTLS_CTYPE_CLIENT, otherwise.
*
* Returns: the number of certificate types, or an error code.
* Since: 3.0
@@ -2216,11 +2261,50 @@ int
gnutls_priority_certificate_type_list(gnutls_priority_t pcache,
const unsigned int **list)
{
- if (pcache->cert_type.algorithms == 0)
- return 0;
+ gnutls_ctype_target_t target =
+ pcache->server_precedence ? GNUTLS_CTYPE_SERVER : GNUTLS_CTYPE_CLIENT;
- *list = pcache->cert_type.priority;
- return pcache->cert_type.algorithms;
+ return gnutls_priority_certificate_type_list2(pcache, list, target);
+}
+
+/**
+ * gnutls_priority_certificate_type_list2:
+ * @pcache: is a #gnutls_prioritity_t type.
+ * @list: will point to an integer list.
+ * @target: is a #gnutls_ctype_target_t type. Valid arguments are
+ * GNUTLS_CTYPE_CLIENT and GNUTLS_CTYPE_SERVER
+ *
+ * Get a list of available certificate types for the given target
+ * in the priority structure.
+ *
+ * Returns: the number of certificate types, or an error code.
+ *
+ * Since: 3.6.4
+ **/
+int
+gnutls_priority_certificate_type_list2(gnutls_priority_t pcache,
+ const unsigned int **list, gnutls_ctype_target_t target)
+{
+ switch (target) {
+ case GNUTLS_CTYPE_CLIENT:
+ if(pcache->client_ctype.algorithms > 0) {
+ *list = pcache->client_ctype.priority;
+ return pcache->client_ctype.algorithms;
+ }
+ break;
+ case GNUTLS_CTYPE_SERVER:
+ if(pcache->server_ctype.algorithms > 0) {
+ *list = pcache->server_ctype.priority;
+ return pcache->server_ctype.algorithms;
+ }
+ break;
+ default:
+ // Invalid target given
+ gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ // Found a matching target but non of them had any ctypes set
+ return 0;
}
/**
diff --git a/lib/session.c b/lib/session.c
index 1622b29764..5d862198b5 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -337,7 +337,7 @@ char *gnutls_session_get_desc(gnutls_session_t session)
{
gnutls_kx_algorithm_t kx;
const char *kx_str, *sign_str;
- unsigned type;
+ gnutls_certificate_type_t ctype_client, ctype_server;
char kx_name[64] = "";
char proto_name[32];
char _group_name[24];
@@ -423,17 +423,29 @@ char *gnutls_session_get_desc(gnutls_session_t session)
}
}
-
- type = gnutls_certificate_type_get(session);
- if (type == GNUTLS_CRT_X509 || type == GNUTLS_CRT_UNKNOWN)
- snprintf(proto_name, sizeof(proto_name), "%s",
- gnutls_protocol_get_name(get_num_version
- (session)));
- else
- snprintf(proto_name, sizeof(proto_name), "%s-%s",
- gnutls_protocol_get_name(get_num_version
- (session)),
- gnutls_certificate_type_get_name(type));
+ // Check whether we have negotiated certificate types
+ if (_gnutls_has_negotiate_ctypes(session)) {
+ // Get certificate types
+ ctype_client = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT);
+ ctype_server = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER);
+
+ if (ctype_client == ctype_server) {
+ // print proto version, client/server cert type
+ snprintf(proto_name, sizeof(proto_name), "%s-%s",
+ gnutls_protocol_get_name(get_num_version(session)),
+ gnutls_certificate_type_get_name(ctype_client));
+ } else {
+ // print proto version, client cert type, server cert type
+ snprintf(proto_name, sizeof(proto_name), "%s-%s-%s",
+ gnutls_protocol_get_name(get_num_version(session)),
+ gnutls_certificate_type_get_name(ctype_client),
+ gnutls_certificate_type_get_name(ctype_server));
+ }
+ } else { // Assumed default certificate type (X.509)
+ snprintf(proto_name, sizeof(proto_name), "%s",
+ gnutls_protocol_get_name(get_num_version
+ (session)));
+ }
desc = gnutls_malloc(DESC_SIZE);
if (desc == NULL)
diff --git a/lib/session_pack.c b/lib/session_pack.c
index 9fbd5b3ae8..f8b1830568 100644
--- a/lib/session_pack.c
+++ b/lib/session_pack.c
@@ -898,7 +898,8 @@ pack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
if (!session->security_parameters.pversion->tls13_sem) {
BUFFER_APPEND(ps, session->security_parameters.cs->id, 2);
- BUFFER_APPEND_NUM(ps, session->security_parameters.cert_type);
+ BUFFER_APPEND_NUM(ps, session->security_parameters.client_ctype);
+ BUFFER_APPEND_NUM(ps, session->security_parameters.server_ctype);
BUFFER_APPEND_PFX1(ps, session->security_parameters.master_secret,
GNUTLS_MASTER_SIZE);
@@ -1001,8 +1002,11 @@ unpack_security_parameters(gnutls_session_t session, gnutls_buffer_st * ps)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
BUFFER_POP_NUM(ps,
- session->internals.resumed_security_parameters.
- cert_type);
+ session->internals.resumed_security_parameters.
+ client_ctype);
+ BUFFER_POP_NUM(ps,
+ session->internals.resumed_security_parameters.
+ server_ctype);
/* master secret */
ret = _gnutls_buffer_pop_datum_prefix8(ps, &t);
@@ -1129,8 +1133,10 @@ gnutls_session_set_premaster(gnutls_session_t session, unsigned int entity,
if (session->internals.resumed_security_parameters.cs == NULL)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
- session->internals.resumed_security_parameters.cert_type =
- DEFAULT_CERT_TYPE;
+ session->internals.resumed_security_parameters.client_ctype =
+ DEFAULT_CERT_TYPE;
+ session->internals.resumed_security_parameters.server_ctype =
+ DEFAULT_CERT_TYPE;
session->internals.resumed_security_parameters.pversion =
version_to_entry(version);
if (session->internals.resumed_security_parameters.pversion ==
diff --git a/lib/state.c b/lib/state.c
index ab0021a64e..58db8f9a32 100644
--- a/lib/state.c
+++ b/lib/state.c
@@ -92,13 +92,71 @@ gnutls_cipher_algorithm_t gnutls_cipher_get(gnutls_session_t session)
* The certificate type is by default X.509, unless it is negotiated
* as a TLS extension.
*
+ * As of version 3.6.4 it is recommended to use
+ * gnutls_certificate_type_get2().
+ *
* Returns: the currently used #gnutls_certificate_type_t certificate
- * type.
+ * type as negotiated for 'our' side of the connection.
**/
gnutls_certificate_type_t
gnutls_certificate_type_get(gnutls_session_t session)
{
- return session->security_parameters.cert_type;
+ return gnutls_certificate_type_get2(session, GNUTLS_CTYPE_OURS);
+}
+
+/**
+ * gnutls_certificate_type_get2:
+ * @session: is a #gnutls_session_t type.
+ * @target: is a #gnutls_ctype_target_t type.
+ *
+ * The raw public-key extension (RFC7250) introduces a mechanism
+ * to specifcy different certificate types for the client and server. We
+ * therefore distinguish between negotiated certificate types for the
+ * client and server. The @target parameter specifies whether you want
+ * the negotiated certificate type for the client (GNUTLS_CTYPE_CLIENT)
+ * or for the server (GNUTLS_CTYPE_SERVER). Additionally, in P2P mode
+ * connection set up where you don't know in advance who will be client
+ * and who will be server you can use the flag (GNUTLS_CTYPE_OURS) and
+ * (GNUTLS_CTYPE_PEERS) to retrieve the corresponding certificate types.
+ *
+ * In case no certificate types were explicitly set via the priority
+ * strings to be negotiated during the handshake, then this function
+ * will return the default certificate type (X.509) for both the
+ * client and the server.
+ *
+ * Returns: the currently used #gnutls_certificate_type_t certificate
+ * type for the client or the server.
+ *
+ * Since: 3.6.4
+ **/
+gnutls_certificate_type_t
+gnutls_certificate_type_get2(gnutls_session_t session,
+ gnutls_ctype_target_t target)
+{
+ switch (target) {
+ case GNUTLS_CTYPE_CLIENT:
+ return session->security_parameters.client_ctype;
+ break;
+ case GNUTLS_CTYPE_SERVER:
+ return session->security_parameters.server_ctype;
+ break;
+ case GNUTLS_CTYPE_OURS:
+ if (IS_SERVER(session)) {
+ return session->security_parameters.server_ctype;
+ } else {
+ return session->security_parameters.client_ctype;
+ }
+ break;
+ case GNUTLS_CTYPE_PEERS:
+ if (IS_SERVER(session)) {
+ return session->security_parameters.client_ctype;
+ } else {
+ return session->security_parameters.server_ctype;
+ }
+ break;
+ default: // Illegal parameter passed
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
}
/**
@@ -190,6 +248,100 @@ void reset_binders(gnutls_session_t session)
memset(session->key.binders, 0, sizeof(session->key.binders));
}
+/* Check whether certificate credentials of type @cert_type are set
+ * for the current session.
+ */
+static bool _gnutls_has_cert_credentials(gnutls_session_t session,
+ gnutls_certificate_type_t cert_type)
+{
+ unsigned i;
+ unsigned cert_found = 0;
+ gnutls_certificate_credentials_t cred;
+
+ /* First, check for certificate credentials. If we have no certificate
+ * credentials set then we don't support certificates at all.
+ */
+ cred = (gnutls_certificate_credentials_t)
+ _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+
+ if (cred == NULL)
+ return false;
+
+ /* There are credentials initialized. Now check whether we can find
+ * pre-set certificates of the required type, but only if we don't
+ * use the callback functions.
+ */
+ if (cred->get_cert_callback3 == NULL) {
+ for (i = 0; i < cred->ncerts; i++) {
+ if (cred->certs[i].cert_list[0].type == cert_type) {
+ cert_found = 1;
+ break;
+ }
+ }
+
+ if (cert_found == 0) {
+ /* No matching certificate found. */
+ return false;
+ }
+ }
+
+ return true; // OK
+}
+
+/* Check if the given certificate type is supported.
+ * This means that it is enabled by the priority functions,
+ * and in some cases a matching certificate exists. A check for
+ * the latter can be toggled via the parameter @check_credentials.
+ */
+int
+_gnutls_session_cert_type_supported(gnutls_session_t session,
+ gnutls_certificate_type_t cert_type,
+ bool check_credentials,
+ gnutls_ctype_target_t target)
+{
+ unsigned i;
+ priority_st* ctype_priorities;
+
+ // Perform a credentials check if requested
+ if (check_credentials) {
+ if (!_gnutls_has_cert_credentials(session, cert_type))
+ return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+ }
+
+ /* So far so good. We have the required credentials (if needed).
+ * Now check whether we are allowed to use them according to our
+ * priorities.
+ */
+ // Which certificate type should we query?
+ switch (target) {
+ case GNUTLS_CTYPE_CLIENT:
+ ctype_priorities =
+ &(session->internals.priorities->client_ctype);
+ break;
+ case GNUTLS_CTYPE_SERVER:
+ ctype_priorities =
+ &(session->internals.priorities->server_ctype);
+ break;
+ default:
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ // No explicit priorities set, and default ctype is asked
+ if (ctype_priorities->algorithms == 0
+ && cert_type == DEFAULT_CERT_TYPE)
+ return 0; // ok
+
+ /* Now lets find out whether our cert type is in our priority
+ * list, i.e. set of allowed cert types.
+ */
+ for (i = 0; i < ctype_priorities->algorithms; i++) {
+ if (ctype_priorities->priority[i] == cert_type)
+ return 0; /* ok */
+ }
+
+ return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE;
+}
+
static void deinit_keys(gnutls_session_t session)
{
const version_entry_st *vers = get_version(session);
@@ -318,7 +470,8 @@ int gnutls_init(gnutls_session_t * session, unsigned int flags)
(flags & GNUTLS_SERVER ? GNUTLS_SERVER : GNUTLS_CLIENT);
/* the default certificate type for TLS */
- (*session)->security_parameters.cert_type = DEFAULT_CERT_TYPE;
+ (*session)->security_parameters.client_ctype = DEFAULT_CERT_TYPE;
+ (*session)->security_parameters.server_ctype = DEFAULT_CERT_TYPE;
/* Initialize buffers */
_gnutls_buffer_init(&(*session)->internals.handshake_hash_buffer);
@@ -926,6 +1079,24 @@ _gnutls_rsa_pms_set_version(gnutls_session_t session,
session->internals.rsa_pms_version[1] = minor;
}
+void _gnutls_session_client_cert_type_set(gnutls_session_t session,
+ gnutls_certificate_type_t ct)
+{
+ _gnutls_handshake_log
+ ("HSK[%p]: Selected client certificate type %s (%d)\n", session,
+ gnutls_certificate_type_get_name(ct), ct);
+ session->security_parameters.client_ctype = ct;
+}
+
+void _gnutls_session_server_cert_type_set(gnutls_session_t session,
+ gnutls_certificate_type_t ct)
+{
+ _gnutls_handshake_log
+ ("HSK[%p]: Selected server certificate type %s (%d)\n", session,
+ gnutls_certificate_type_get_name(ct), ct);
+ session->security_parameters.server_ctype = ct;
+}
+
/**
* gnutls_handshake_set_post_client_hello_function:
* @session: is a #gnutls_session_t type.
diff --git a/lib/state.h b/lib/state.h
index 75d0f35fc1..a93e5d49ce 100644
--- a/lib/state.h
+++ b/lib/state.h
@@ -25,6 +25,11 @@
#include "gnutls_int.h"
+void _gnutls_session_client_cert_type_set(gnutls_session_t session,
+ gnutls_certificate_type_t);
+void _gnutls_session_server_cert_type_set(gnutls_session_t session,
+ gnutls_certificate_type_t);
+
inline static const gnutls_group_entry_st *
get_group(gnutls_session_t session)
{
@@ -73,6 +78,11 @@ _gnutls_hello_set_default_version(gnutls_session_t session,
#endif
+
+int _gnutls_session_cert_type_supported(gnutls_session_t session,
+ gnutls_certificate_type_t cert_type,
+ bool check_credentials,
+ gnutls_ctype_target_t target);
int _gnutls_dh_set_secret_bits(gnutls_session_t session, unsigned bits);
int _gnutls_dh_set_peer_public(gnutls_session_t session, bigint_t public);
diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c
index 96076e4e46..01966b14d1 100644
--- a/lib/tls13/certificate_verify.c
+++ b/lib/tls13/certificate_verify.c
@@ -51,6 +51,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
gnutls_pcert_st peer_cert;
cert_auth_info_t info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
bool server = 0;
+ gnutls_certificate_type_t cert_type;
memset(&peer_cert, 0, sizeof(peer_cert));
@@ -73,7 +74,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
if (ret < 0)
return gnutls_assert_val(ret);
- _gnutls_handshake_log("HSK[%p]: parsing certificate verify\n", session);
+ _gnutls_handshake_log("HSK[%p]: Parsing certificate verify\n", session);
if (buf.length < 2) {
gnutls_assert();
@@ -83,7 +84,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
se = _gnutls_tls_aid_to_sign_entry(buf.data[0], buf.data[1], get_version(session));
if (se == NULL) {
- _gnutls_handshake_log("found unsupported signature (%d.%d)\n", (int)buf.data[0], (int)buf.data[1]);
+ _gnutls_handshake_log("Found unsupported signature (%d.%d)\n", (int)buf.data[0], (int)buf.data[1]);
ret = gnutls_assert_val(GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM);
goto cleanup;
}
@@ -110,8 +111,12 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session)
goto cleanup;
}
+ /* We verify the certificate of the peer. Therefore we need to
+ * retrieve the negotiated certificate type for the peer. */
+ cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS);
+
/* Verify the signature */
- ret = _gnutls_get_auth_info_pcert(&peer_cert, session->security_parameters.cert_type, info);
+ ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info);
if (ret < 0) {
gnutls_assert();
goto cleanup;