From 07180a416731749883234f931ac18831ff38abbb Mon Sep 17 00:00:00 2001 From: Tom Vrancken Date: Wed, 15 Aug 2018 18:29:32 +0200 Subject: Implemented RFC7250 certificate type negotiation extensions. Signed-off-by: Tom Vrancken --- NEWS | 9 + doc/Makefile.am | 5 + doc/cha-gtls-app.texi | 12 ++ doc/manpages/Makefile.am | 2 + lib/algorithms/cert_types.c | 5 + lib/auth/cert.c | 93 +++++++--- lib/auth/rsa.c | 23 ++- lib/auth/srp_rsa.c | 3 +- lib/cert-session.c | 42 +++-- lib/constate.c | 11 +- lib/ext/Makefile.am | 5 +- lib/ext/cert_types.h | 50 ++++++ lib/ext/client_cert_type.c | 371 ++++++++++++++++++++++++++++++++++++++++ lib/ext/client_cert_type.h | 36 ++++ lib/ext/server_cert_type.c | 348 +++++++++++++++++++++++++++++++++++++ lib/ext/server_cert_type.h | 36 ++++ lib/gnutls_int.h | 32 +++- lib/handshake.c | 6 +- lib/hello_ext.c | 4 + lib/hello_ext_lib.c | 2 +- lib/includes/gnutls/gnutls.h.in | 33 +++- lib/libgnutls.map | 2 + lib/priority.c | 100 ++++++++++- lib/session.c | 36 ++-- lib/session_pack.c | 16 +- lib/state.c | 177 ++++++++++++++++++- lib/state.h | 10 ++ lib/tls13/certificate_verify.c | 11 +- src/common.c | 5 +- src/tests.c | 1 + symbols.last | 2 + tests/Makefile.am | 5 +- tests/crt_type-neg-common.c | 209 ++++++++++++++++++++++ tests/tls-crt_type-neg.c | 303 ++++++++++++++++++++++++++++++++ 34 files changed, 1895 insertions(+), 110 deletions(-) create mode 100644 lib/ext/cert_types.h create mode 100644 lib/ext/client_cert_type.c create mode 100644 lib/ext/client_cert_type.h create mode 100644 lib/ext/server_cert_type.c create mode 100644 lib/ext/server_cert_type.h create mode 100644 tests/crt_type-neg-common.c create mode 100644 tests/tls-crt_type-neg.c diff --git a/NEWS b/NEWS index 7b9a53d00a..57d2703903 100644 --- a/NEWS +++ b/NEWS @@ -18,9 +18,18 @@ See the end for copying conditions. ** libgnutls: The 'record size limit' extension is added and preferred to the 'max record size' extension when possible. +** Added support for seperately negotiating client and server certificate types as + defined in RFC7250. This mechanism must be explicitly enabled via the + GNUTLS_ENABLE_CERT_TYPE_NEG flag in gnutls_init(). + + ** API and ABI modifications: GNUTLS_ENABLE_EARLY_START: Added gnutls_record_set_max_early_data_size: Added +gnutls_certificate_type_get2: Added +gnutls_priority_certificate_type_list2: Added +GNUTLS_ENABLE_CERT_TYPE_NEG: Added +gnutls_ctype_target_t: New enumeration * Version 3.6.3 (released 2018-07-16) diff --git a/doc/Makefile.am b/doc/Makefile.am index 5d6cd0c1bc..782b3cf55f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -547,6 +547,7 @@ ENUMS += enums/gnutls_cipher_algorithm_t ENUMS += enums/gnutls_close_request_t ENUMS += enums/gnutls_compression_method_t ENUMS += enums/gnutls_credentials_type_t +ENUMS += enums/gnutls_ctype_target_t ENUMS += enums/gnutls_digest_algorithm_t ENUMS += enums/gnutls_ecc_curve_t ENUMS += enums/gnutls_ext_flags_t @@ -796,6 +797,8 @@ FUNCS += functions/gnutls_certificate_set_x509_trust_mem FUNCS += functions/gnutls_certificate_set_x509_trust_mem.short FUNCS += functions/gnutls_certificate_type_get FUNCS += functions/gnutls_certificate_type_get.short +FUNCS += functions/gnutls_certificate_type_get2 +FUNCS += functions/gnutls_certificate_type_get2.short FUNCS += functions/gnutls_certificate_type_get_id FUNCS += functions/gnutls_certificate_type_get_id.short FUNCS += functions/gnutls_certificate_type_get_name @@ -1528,6 +1531,8 @@ FUNCS += functions/gnutls_prf_rfc5705 FUNCS += functions/gnutls_prf_rfc5705.short FUNCS += functions/gnutls_priority_certificate_type_list FUNCS += functions/gnutls_priority_certificate_type_list.short +FUNCS += functions/gnutls_priority_certificate_type_list2 +FUNCS += functions/gnutls_priority_certificate_type_list2.short FUNCS += functions/gnutls_priority_cipher_list FUNCS += functions/gnutls_priority_cipher_list.short FUNCS += functions/gnutls_priority_compression_list diff --git a/doc/cha-gtls-app.texi b/doc/cha-gtls-app.texi index 8fd31b2add..9a4cf29933 100644 --- a/doc/cha-gtls-app.texi +++ b/doc/cha-gtls-app.texi @@ -1292,6 +1292,18 @@ Catch all which enables all curves from NORMAL priority is CURVE-ALL. Note that the CURVE keyword is kept for backwards compatibility only, for new applications see the GROUP keyword above. +@item Certificate types @tab +Certificate type negotitation must be explicitly enabled via the +GNUTLS_ENABLE_CERT_TYPE_NEG flag in gnutls_init(). +Certificate types can be given in a symmetric fashion (i.e. the same for +both client and server) or, as of GnuTLS 3.6.4, in an asymmetric fashion +(i.e. different for the client than for the server). + +Currently supported types are: +CTYPE-X509 or CTYPE-X.509. Catch all is CTYPE-ALL. +CTYPE-CLI-X509 or CTYPE-CLI-X.509, CTYPE-SRV-X509 or CTYPE-SRV-X.509. +Catch all is CTYPE-CLI-ALL and CTYPE-SRV-ALL. + @end multitable @caption{The supported algorithm keywords in priority strings.} @end float diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index b4dc4ae8c9..241d9870af 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -193,6 +193,7 @@ APIMANS += gnutls_certificate_set_x509_trust_dir.3 APIMANS += gnutls_certificate_set_x509_trust_file.3 APIMANS += gnutls_certificate_set_x509_trust_mem.3 APIMANS += gnutls_certificate_type_get.3 +APIMANS += gnutls_certificate_type_get2.3 APIMANS += gnutls_certificate_type_get_id.3 APIMANS += gnutls_certificate_type_get_name.3 APIMANS += gnutls_certificate_type_list.3 @@ -559,6 +560,7 @@ APIMANS += gnutls_prf.3 APIMANS += gnutls_prf_raw.3 APIMANS += gnutls_prf_rfc5705.3 APIMANS += gnutls_priority_certificate_type_list.3 +APIMANS += gnutls_priority_certificate_type_list2.3 APIMANS += gnutls_priority_cipher_list.3 APIMANS += gnutls_priority_compression_list.3 APIMANS += gnutls_priority_deinit.3 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 + * + * 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 + * + * 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 +#include + + +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 + * + * 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 + +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 + * + * 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 +#include +#include "ext/cert_types.h" +#include "ext/server_cert_type.h" +#include "hello_ext.h" +#include "hello_ext_lib.h" +#include "errors.h" +#include +#include + + +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 + * + * 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 + +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 #include "extv.h" #include +#include +#include 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; diff --git a/src/common.c b/src/common.c index a376fdacd8..d94253311c 100644 --- a/src/common.c +++ b/src/common.c @@ -656,8 +656,9 @@ void print_list(const char *priorities, int verbose) #if 0 { ret = - gnutls_priority_certificate_type_list(pcache, - &list); + gnutls_priority_certificate_type_list2(pcache, + &list, + GNUTLS_CTYPE_CLIENT); printf("Certificate types: "); if (ret == 0) diff --git a/src/tests.c b/src/tests.c index ffa772553a..ff4d7b07f4 100644 --- a/src/tests.c +++ b/src/tests.c @@ -117,6 +117,7 @@ char prio_str[512] = ""; #define BLOCK_CIPHERS "+3DES-CBC:+AES-128-CBC:+CAMELLIA-128-CBC:+AES-256-CBC:+CAMELLIA-256-CBC" #define ALL_COMP "+COMP-NULL" #define ALL_MACS "+MAC-ALL:+MD5:+SHA1" +#define ALL_CERTTYPES "+CTYPE-X509" #define ALL_KX "+RSA:+DHE-RSA:+DHE-DSS:+ANON-DH:+ECDHE-RSA:+ECDHE-ECDSA:+ANON-ECDH" #define INIT_STR "NONE:" char rest[128] = "%UNSAFE_RENEGOTIATION:+SIGN-ALL:+GROUP-ALL"; diff --git a/symbols.last b/symbols.last index e75cdfb09c..f55663209f 100644 --- a/symbols.last +++ b/symbols.last @@ -95,6 +95,7 @@ gnutls_certificate_set_x509_trust@GNUTLS_3_4 gnutls_certificate_set_x509_trust_dir@GNUTLS_3_4 gnutls_certificate_set_x509_trust_file@GNUTLS_3_4 gnutls_certificate_set_x509_trust_mem@GNUTLS_3_4 +gnutls_certificate_type_get2@GNUTLS_3_6_4 gnutls_certificate_type_get@GNUTLS_3_4 gnutls_certificate_type_get_id@GNUTLS_3_4 gnutls_certificate_type_get_name@GNUTLS_3_4 @@ -541,6 +542,7 @@ gnutls_pkcs_schema_get_oid@GNUTLS_3_4 gnutls_prf@GNUTLS_3_4 gnutls_prf_raw@GNUTLS_3_4 gnutls_prf_rfc5705@GNUTLS_3_4 +gnutls_priority_certificate_type_list2@GNUTLS_3_6_4 gnutls_priority_certificate_type_list@GNUTLS_3_4 gnutls_priority_cipher_list@GNUTLS_3_4 gnutls_priority_compression_list@GNUTLS_3_4 diff --git a/tests/Makefile.am b/tests/Makefile.am index 3a42b90bf9..c0ecfed3f2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -67,7 +67,8 @@ EXTRA_DIST = suppressions.valgrind eagain-common.h cert-common.h test-chains.h \ data/pkcs7-cat-ca.pem data/long.crl data/long.pem data/large-cert.pem \ testpkcs11.pkcs15 testpkcs11.softhsm testpkcs11.sc-hsm testpkcs11-certs/ca.crt testpkcs11-certs/ca-tmpl \ testpkcs11-certs/client.key testpkcs11-certs/server.crt testpkcs11-certs/server-tmpl \ - testpkcs11-certs/ca.key testpkcs11-certs/client.crt testpkcs11-certs/client-tmpl testpkcs11-certs/server.key + testpkcs11-certs/ca.key testpkcs11-certs/client.crt testpkcs11-certs/client-tmpl testpkcs11-certs/server.key \ + crt_type-neg-common.c AM_CFLAGS = $(WARN_CFLAGS) $(WERROR_CFLAGS) AM_CPPFLAGS = \ @@ -202,7 +203,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei ip-check mini-x509-ipaddr trust-store base64-raw random-art dhex509self \ dss-sig-val sign-pk-api tls-session-ext-override record-pad \ tls13-server-kx-neg gnutls_ext_raw_parse_dtls key-export-pkcs8 \ - null_retrieve_function tls-record-size-limit + null_retrieve_function tls-record-size-limit tls-crt_type-neg if HAVE_SECCOMP_TESTS ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp diff --git a/tests/crt_type-neg-common.c b/tests/crt_type-neg-common.c new file mode 100644 index 0000000000..ac99e20984 --- /dev/null +++ b/tests/crt_type-neg-common.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2017 - 2018 ARPA2 project + * + * Author: Tom Vrancken + * + * This file is part of GnuTLS. + * + * GnuTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuTLS 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 + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + */ +#include +#include +#include +#include + +// Credential type flags +#define CRED_EMPTY 1<<0 +#define CRED_X509 1<<1 +#define CRED_RAWPK 1<<2 + +// Test case definition +typedef struct test_case_st { + const char *name; + const char *client_prio; + const char *server_prio; + const char set_cli_creds; + const char set_srv_creds; + gnutls_certificate_type_t expected_cli_ctype; + gnutls_certificate_type_t expected_srv_ctype; + int client_err; + int server_err; + bool enable_cert_type_neg_cli; + bool enable_cert_type_neg_srv; +} test_case_st; + + +static void try(test_case_st * test) +{ + int sret, cret; // Needed for HANDSHAKE macro + /* To hold negotiated certificate types */ + gnutls_certificate_type_t srv_srv_ctype, srv_cli_ctype; + gnutls_certificate_type_t cli_srv_ctype, cli_cli_ctype; + /* To hold certificate credentials */ + gnutls_certificate_credentials_t client_creds = NULL; + gnutls_certificate_credentials_t server_creds = NULL; + + gnutls_session_t server, client; + gnutls_pubkey_t rawpk = NULL; // For RawPubKey tmp + gnutls_privkey_t privkey = NULL; // For RawPubKey tmp + + sret = cret = GNUTLS_E_AGAIN; + + // Initialize creds + assert(gnutls_certificate_allocate_credentials(&client_creds) >= 0); + assert(gnutls_certificate_allocate_credentials(&server_creds) >= 0); + + // Print test + success("Running %s...\n", test->name); + + // Init client/server + if(test->enable_cert_type_neg_cli) { + assert(gnutls_init(&client, GNUTLS_CLIENT | GNUTLS_ENABLE_CERT_TYPE_NEG) >= 0); + } else { + assert(gnutls_init(&client, GNUTLS_CLIENT) >= 0); + } + + if (test->enable_cert_type_neg_srv) { + assert(gnutls_init(&server, GNUTLS_SERVER | GNUTLS_ENABLE_CERT_TYPE_NEG) >= 0); + } else { + assert(gnutls_init(&server, GNUTLS_SERVER) >= 0); + } + + /* Set up our credentials for this handshake */ + // Test for using empty cli credentials + if (test->set_cli_creds == CRED_EMPTY) { + gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, client_creds); + } else { + // Test for using X509 cli credentials + if (test->set_cli_creds & CRED_X509) { + assert(gnutls_certificate_set_x509_key_mem + (client_creds, &cli_ca3_cert, &cli_ca3_key, GNUTLS_X509_FMT_PEM) >= 0); + } + + // Test for using RawPubKey cli credentials + if (test->set_cli_creds & CRED_RAWPK) { + // TODO set client RawPubKey when support is ready + } + + // -- Add extra ctype creds here in the future -- + + // Finally set the credentials + gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, client_creds); + } + + // Test for using empty srv credentials + if (test->set_srv_creds == CRED_EMPTY) { + gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, server_creds); + } else { + // Test for using X509 srv credentials + if (test->set_srv_creds & CRED_X509) { + assert(gnutls_certificate_set_x509_key_mem + (server_creds, &server_ca3_localhost_rsa_decrypt_cert, + &server_ca3_key, GNUTLS_X509_FMT_PEM) >= 0); + assert(gnutls_certificate_set_x509_key_mem + (server_creds, &server_ca3_localhost_ecc_cert, + &server_ca3_ecc_key, GNUTLS_X509_FMT_PEM) >= 0); + assert(gnutls_certificate_set_x509_key_mem + (server_creds, &server_ca3_localhost_rsa_sign_cert, + &server_ca3_key, GNUTLS_X509_FMT_PEM) >= 0); + gnutls_certificate_set_known_dh_params(server_creds, + GNUTLS_SEC_PARAM_MEDIUM); + } + + // Test for using RawPubKey srv credentials + if( test->set_srv_creds & CRED_RAWPK ) { + //TODO when RawPK support is finished + } + + // -- Add extra ctype creds here in the future -- + + // Finally set the credentials + gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, server_creds); + } + + // Server settings + gnutls_transport_set_push_function(server, server_push); + gnutls_transport_set_pull_function(server, server_pull); + gnutls_transport_set_ptr(server, server); + assert(gnutls_priority_set_direct(server, test->server_prio, 0) >= 0); + + // Client settings + gnutls_transport_set_push_function(client, client_push); + gnutls_transport_set_pull_function(client, client_pull); + gnutls_transport_set_ptr(client, client); + assert(gnutls_priority_set_direct(client, test->client_prio, 0) >= 0); + + // Try handshake + if (test->client_err && test->server_err) { + /* We expect errors during the handshake and don't check + * any negotiated certificate types */ + HANDSHAKE_EXPECT(client, server, test->client_err, test->server_err); + } else { + /* We expect a handshake without errors and check the negotiated + * certificate types */ + HANDSHAKE(client, server); + + /* Get the negotiated certificate types */ + srv_srv_ctype = + gnutls_certificate_type_get2(server, GNUTLS_CTYPE_SERVER); + srv_cli_ctype = + gnutls_certificate_type_get2(server, GNUTLS_CTYPE_CLIENT); + cli_srv_ctype = + gnutls_certificate_type_get2(client, GNUTLS_CTYPE_SERVER); + cli_cli_ctype = + gnutls_certificate_type_get2(client, GNUTLS_CTYPE_CLIENT); + + /* Check whether the API functions return the correct cert types for OURS and PEERS */ + assert(srv_srv_ctype == gnutls_certificate_type_get2(server, GNUTLS_CTYPE_OURS)); + assert(srv_srv_ctype == gnutls_certificate_type_get2(client, GNUTLS_CTYPE_PEERS)); + assert(cli_cli_ctype == gnutls_certificate_type_get2(server, GNUTLS_CTYPE_PEERS)); + assert(cli_cli_ctype == gnutls_certificate_type_get2(client, GNUTLS_CTYPE_OURS)); + + // For debugging + if (debug) { + success("Srv srv ctype: %s\n", gnutls_certificate_type_get_name(srv_srv_ctype)); + success("Srv cli ctype: %s\n", gnutls_certificate_type_get_name(srv_cli_ctype)); + success("Cli srv ctype: %s\n", gnutls_certificate_type_get_name(cli_srv_ctype)); + success("Cli srv ctype: %s\n", gnutls_certificate_type_get_name(cli_cli_ctype)); + } + + /* Check whether the negotiated certificate types match the expected results */ + // Matching server ctype + if (srv_srv_ctype != cli_srv_ctype) { + fail("%s: client negotiated different server ctype than server (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(cli_srv_ctype), gnutls_certificate_type_get_name(srv_srv_ctype)); + } + // Matching client ctype + if (srv_cli_ctype != cli_cli_ctype) { + fail("%s: client negotiated different client ctype than server (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(cli_cli_ctype), gnutls_certificate_type_get_name(srv_cli_ctype)); + } + // Matching expected server ctype + if (srv_srv_ctype != test->expected_srv_ctype) { + fail("%s: negotiated server ctype diffs the expected (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(srv_srv_ctype), gnutls_certificate_type_get_name(test->expected_srv_ctype)); + } + // Matching expected client ctype + if (srv_cli_ctype != test->expected_cli_ctype) { + fail("%s: negotiated server ctype diffs the expected (%s, %s)!\n", test->name, gnutls_certificate_type_get_name(srv_cli_ctype), gnutls_certificate_type_get_name(test->expected_cli_ctype)); + } + } + + // Cleanup + gnutls_deinit(server); + gnutls_deinit(client); + gnutls_certificate_free_credentials(client_creds); + gnutls_certificate_free_credentials(server_creds); + gnutls_pubkey_deinit(rawpk); + gnutls_privkey_deinit(privkey); + + reset_buffers(); +} diff --git a/tests/tls-crt_type-neg.c b/tests/tls-crt_type-neg.c new file mode 100644 index 0000000000..ff5aa08885 --- /dev/null +++ b/tests/tls-crt_type-neg.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2017 - 2018 ARPA2 project + * + * Author: Tom Vrancken + * + * This file is part of GnuTLS. + * + * GnuTLS is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuTLS 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 + * 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 + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +/* This program tests the certificate type negotiation mechnism for + * the handshake as specified in RFC7250 */ + +#include +#include +#include +#include +#include +#include "utils.h" +#include "cert-common.h" +#include "eagain-common.h" +#include "crt_type-neg-common.c" + +test_case_st tests[] = { + /* Tests with only a single credential set for client/server. + * Tests for X.509 cases. + */ + { + /* Default case A + * + * Priority cli: NORMAL + * Priority srv: NORMAL + * Certificate negotiation mechanism: disabled + * Cli creds: None + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: cert types should default to X.509 + */ + .name = "Default case A. Neg off (default). Creds set (CLI/SRV): None/X509.", + .client_prio = "NORMAL", + .server_prio = "NORMAL", + .set_cli_creds = CRED_EMPTY, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = false, + .enable_cert_type_neg_srv = false}, + { + /* Default case B + * + * Priority: NORMAL + * Certificate negotiation mechanism: disabled + * Cli creds: X.509 + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: cert types should default to X.509 + */ + .name = "Default case B. Neg off (default). Creds set (CLI/SRV): X509/X509.", + .client_prio = "NORMAL", + .server_prio = "NORMAL", + .set_cli_creds = CRED_X509, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = false, + .enable_cert_type_neg_srv = false}, + { + /* No server credentials + * + * Priority: NORMAL + * Certificate negotiation mechanism: disabled + * Cli creds: None + * Srv creds: None + * Handshake: results in errors + * Negotiation: cert types are not evaluated + */ + .name = "No server creds. Creds set (CLI/SRV): None/None.", + .client_prio = "NORMAL", + .server_prio = "NORMAL", + .set_cli_creds = CRED_EMPTY, + .set_srv_creds = CRED_EMPTY, + .client_err = GNUTLS_E_AGAIN, + .server_err = GNUTLS_E_NO_CIPHER_SUITES, + .enable_cert_type_neg_cli = false, + .enable_cert_type_neg_srv = false}, + { + /* Client can negotiate, server not + * + * Priority: NORMAL + * Certificate negotiation mechanism (cli/srv): enabled/disabled + * Cli creds: None + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: cert types should default to X.509 + */ + .name = "Client can negotiate, server not", + .client_prio = "NORMAL", + .server_prio = "NORMAL", + .set_cli_creds = CRED_EMPTY, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = false}, + { + /* Server can negotiate, client not + * + * Priority: NORMAL + * Certificate negotiation mechanism (cli/srv): disabled/enabled + * Cli creds: None + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: cert types should default to X.509 + */ + .name = "Server can negotiate, client not", + .client_prio = "NORMAL", + .server_prio = "NORMAL", + .set_cli_creds = CRED_EMPTY, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = false, + .enable_cert_type_neg_srv = true}, + { + /* Client and server can negotiate + * + * Priority: NORMAL + * Certificate negotiation mechanism (cli/srv): enabled/enabled + * Cli creds: None + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: cert types should default to X.509 + */ + .name = "Client and server can negotiate", + .client_prio = "NORMAL", + .server_prio = "NORMAL", + .set_cli_creds = CRED_EMPTY, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = true}, + { + /* Negotiate both, cli creds x509, srv creds x509 + * + * Priority: NORMAL + request x509 for cli and srv + * Certificate negotiation mechanism (cli/srv): enabled/enabled + * Cli creds: X.509 + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: Fallback to default cli X.509, srv X.509 because + * we advertise with only the cert type defaults. + */ + .name = "Negotiate CLI X.509 + SRV X.509, cli/srv X.509 creds set", + .client_prio = "NORMAL:+CTYPE-CLI-X509:+CTYPE-SRV-X509", + .server_prio = "NORMAL:+CTYPE-CLI-X509:+CTYPE-SRV-X509", + .set_cli_creds = CRED_X509, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = true}, + { + /* Negotiate cli x509, cli creds x509, srv creds x509 + * + * Priority: NORMAL + request x509 for cli + * Certificate negotiation mechanism (cli/srv): enabled/enabled + * Cli creds: X.509 + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: Fallback to default cli X.509, srv X.509 because + * we advertise with only the cert type defaults. + */ + .name = "Negotiate CLI X.509, cli/srv X.509 creds set", + .client_prio = "NORMAL:+CTYPE-CLI-X509", + .server_prio = "NORMAL:+CTYPE-CLI-X509", + .set_cli_creds = CRED_X509, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = true}, + { + /* Negotiate srv x509, cli creds x509, srv creds x509 + * + * Priority: NORMAL + request x509 for srv + * Certificate negotiation mechanism (cli/srv): enabled/enabled + * Cli creds: X.509 + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: Fallback to default cli X.509, srv X.509 because + * we advertise with only the cert type defaults. + */ + .name = "Negotiate SRV X.509, cli/srv X.509 creds set", + .client_prio = "NORMAL:+CTYPE-SRV-X509", + .server_prio = "NORMAL:+CTYPE-SRV-X509", + .set_cli_creds = CRED_X509, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = true}, + { + /* All types allowed for CLI, cli creds x509, srv creds x509 + * + * Priority: NORMAL + allow all client cert types + * Certificate negotiation mechanism (cli/srv): enabled/enabled + * Cli creds: X.509 + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: Fallback to default cli X.509, srv X.509 because + * we advertise with only the cert type defaults. + */ + .name = "Negotiate CLI all, cli/srv X.509 creds set", + .client_prio = "NORMAL:+CTYPE-CLI-ALL", + .server_prio = "NORMAL:+CTYPE-CLI-ALL", + .set_cli_creds = CRED_X509, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = true}, + { + /* All types allowed for SRV, cli creds x509, srv creds x509 + * + * Priority: NORMAL + allow all server cert types + * Certificate negotiation mechanism (cli/srv): enabled/enabled + * Cli creds: X.509 + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: Fallback to default cli X.509, srv X.509 because + * we advertise with only the cert type defaults. + */ + .name = "Negotiate SRV all, cli/srv X.509 creds set", + .client_prio = "NORMAL:+CTYPE-SRV-ALL", + .server_prio = "NORMAL:+CTYPE-SRV-ALL", + .set_cli_creds = CRED_X509, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = true}, + { + /* All types allowed for CLI/SRV, cli creds x509, srv creds x509 + * + * Priority: NORMAL + allow all client and server cert types + * Certificate negotiation mechanism (cli/srv): enabled/enabled + * Cli creds: X.509 + * Srv creds: X.509 + * Handshake: should complete without errors + * Negotiation: Fallback to default cli X.509, srv X.509 because + * we advertise with only the cert type defaults. + */ + .name = "Negotiate CLI/SRV all, cli/srv X.509 creds set", + .client_prio = "NORMAL:+CTYPE-CLI-ALL:+CTYPE-SRV-ALL", + .server_prio = "NORMAL:+CTYPE-CLI-ALL:+CTYPE-SRV-ALL", + .set_cli_creds = CRED_X509, + .set_srv_creds = CRED_X509, + .expected_cli_ctype = GNUTLS_CRT_X509, + .expected_srv_ctype = GNUTLS_CRT_X509, + .enable_cert_type_neg_cli = true, + .enable_cert_type_neg_srv = true} + + /* Tests with only a single credential set for client/server. + * Tests for Raw public-key cases. + */ + //TODO implement when Raw public key support is finished + + /* Tests with only a single credential set for client/server. + * Tests for KDH cases. + */ + //TODO implement when KDH support is finished + + /* Tests with multiple credentials set for client/server. */ + //TODO implement when support for more cert types is ready +}; + +void doit(void) +{ + unsigned i; + global_init(); + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + try(&tests[i]); + } + + gnutls_global_deinit(); +} -- cgit v1.2.1