diff options
author | Tom Vrancken <email@tomvrancken.nl> | 2017-08-26 14:22:44 +0200 |
---|---|---|
committer | Tom Vrancken <dev@tomvrancken.nl> | 2018-12-15 19:05:03 +0100 |
commit | 565efaeac828e89d2c1bac7a88c27303d1b62547 (patch) | |
tree | 5cc0e7c43c4933b743d2ba5889bad13f86e98323 /lib | |
parent | 71276d301a602926e44df818259ba1d99264a179 (diff) | |
download | gnutls-565efaeac828e89d2c1bac7a88c27303d1b62547.tar.gz |
Implemented support for raw public-key functionality (RFC7250).
Signed-off-by: Tom Vrancken <dev@tomvrancken.nl>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile.am | 6 | ||||
-rw-r--r-- | lib/algorithms/cert_types.c | 4 | ||||
-rw-r--r-- | lib/auth/cert.c | 312 | ||||
-rw-r--r-- | lib/auth/cert.h | 13 | ||||
-rw-r--r-- | lib/auth/rsa.c | 2 | ||||
-rw-r--r-- | lib/cert-cred-rawpk.c | 360 | ||||
-rw-r--r-- | lib/cert-cred-x509.c | 241 | ||||
-rw-r--r-- | lib/cert-cred.c | 197 | ||||
-rw-r--r-- | lib/cert-cred.h | 53 | ||||
-rw-r--r-- | lib/cert-session.c | 18 | ||||
-rw-r--r-- | lib/ext/cert_types.h | 37 | ||||
-rw-r--r-- | lib/ext/client_cert_type.c | 48 | ||||
-rw-r--r-- | lib/ext/server_cert_type.c | 48 | ||||
-rw-r--r-- | lib/gnutls_int.h | 34 | ||||
-rw-r--r-- | lib/includes/gnutls/abstract.h | 8 | ||||
-rw-r--r-- | lib/includes/gnutls/gnutls.h.in | 50 | ||||
-rw-r--r-- | lib/libgnutls.map | 9 | ||||
-rw-r--r-- | lib/pcert.c | 152 | ||||
-rw-r--r-- | lib/pkcs11x.c | 2 | ||||
-rw-r--r-- | lib/session.c | 13 | ||||
-rw-r--r-- | lib/state.c | 36 | ||||
-rw-r--r-- | lib/str_array.h | 20 | ||||
-rw-r--r-- | lib/tls13/certificate_verify.c | 2 | ||||
-rw-r--r-- | lib/verify-tofu.c | 116 | ||||
-rw-r--r-- | lib/x509.h | 3 | ||||
-rw-r--r-- | lib/x509/common.c | 2 | ||||
-rw-r--r-- | lib/x509/common.h | 2 |
27 files changed, 1347 insertions, 441 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 32a8511b33..e7c5c7f40e 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -80,7 +80,7 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls system-keys.h urls.c urls.h prf.c auto-verify.c dh-session.c \ cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \ crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \ - hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c + hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c if WINDOWS COBJECTS += system/keys-win.c @@ -124,7 +124,7 @@ HFILES = abstract_int.h debug.h cipher.h \ srp.h auth/srp_kx.h auth/srp_passwd.h \ file.h supplemental.h crypto.h random.h system.h\ locks.h mbuffers.h ecc.h pin.h fips.h \ - priority_options.h secrets.h stek.h + priority_options.h secrets.h stek.h cert-cred.h if ENABLE_PKCS11 HFILES += pkcs11_int.h pkcs11x.h @@ -216,7 +216,7 @@ libgnutlsxx_la_CPPFLAGS = -I$(top_srcdir)/includes -I$(top_builddir)/includes -I AM_CXXFLAGS = \ -I$(srcdir)/includes \ - -I$(builddir)/includes + -I$(builddir)/includes lib_LTLIBRARIES += libgnutlsxx.la diff --git a/lib/algorithms/cert_types.c b/lib/algorithms/cert_types.c index 6ae4b7e160..9671e508d9 100644 --- a/lib/algorithms/cert_types.c +++ b/lib/algorithms/cert_types.c @@ -64,8 +64,7 @@ gnutls_certificate_type_t gnutls_certificate_type_get_id(const char *name) if (c_strcasecmp(name, "X.509") == 0 || c_strcasecmp(name, "X509") == 0) return GNUTLS_CRT_X509; - if (c_strcasecmp(name, "RAWPK") == 0 - || c_strcasecmp(name, "RAWPUBKEY") == 0) + if (c_strcasecmp(name, "RAWPK") == 0) return GNUTLS_CRT_RAWPK; return ret; @@ -73,6 +72,7 @@ gnutls_certificate_type_t gnutls_certificate_type_get_id(const char *name) static const gnutls_certificate_type_t supported_certificate_types[] = { GNUTLS_CRT_X509, + GNUTLS_CRT_RAWPK, 0 }; diff --git a/lib/auth/cert.c b/lib/auth/cert.c index 574514649c..b6bd3bf91e 100644 --- a/lib/auth/cert.c +++ b/lib/auth/cert.c @@ -60,7 +60,7 @@ selected_certs_set(gnutls_session_t session, typedef enum CertificateSigType { RSA_SIGN = 1, DSA_SIGN = 2, ECDSA_SIGN = 64 } CertificateSigType; -/* Moves data from a internal certificate struct (gnutls_pcert_st) to +/* Moves data from an internal certificate struct (gnutls_pcert_st) to * another internal certificate struct (cert_auth_info_t), and deinitializes * the former. */ @@ -188,9 +188,14 @@ find_x509_client_cert(gnutls_session_t session, * then send that one. */ if (cred->ncerts == 1 && - (data_size == 0 || (session->internals.flags & GNUTLS_FORCE_CLIENT_CERT))) { + (data_size == 0 + || (session->internals.flags & GNUTLS_FORCE_CLIENT_CERT))) { + if (cred->certs[0].cert_list[0].type == GNUTLS_CRT_X509) { + /* This check is necessary to prevent sending other certificate + * credentials that are set (e.g. raw public-key). */ *indx = 0; return 0; + } } do { @@ -250,6 +255,47 @@ find_x509_client_cert(gnutls_session_t session, } + +/* Locates the first raw public-key. + * Currently it only makes sense to associate one raw pubkey per session. + * Associating more raw pubkeys with a session has no use because we + * don't know how to select the correct one. + */ +static int +find_rawpk_client_cert(gnutls_session_t session, + const gnutls_certificate_credentials_t cred, + const gnutls_pk_algorithm_t* pk_algos, + int pk_algos_length, int* indx) +{ + unsigned i; + gnutls_pk_algorithm_t pk; + + *indx = -1; + + for (i = 0; i < cred->ncerts; i++) { + /* We know that our list length will be 1, therefore we can + * ignore the rest. + */ + if (cred->certs[i].cert_list_length == 1) { + pk = gnutls_pubkey_get_pk_algorithm(cred->certs[i]. + cert_list[0].pubkey, NULL); + + /* Check whether the public-key algorithm of our credential is in + * the list with supported public-key algorithms and whether the + * cert type matches. */ + if ((check_pk_algo_in_list(pk_algos, pk_algos_length, pk) == 0) + && (cred->certs[i].cert_list[0].type == GNUTLS_CRT_RAWPK)) { + // We found a compatible credential + *indx = i; + break; + } + } + } + + return 0; +} + + /* Returns the number of issuers in the server's * certificate request packet. */ @@ -308,7 +354,7 @@ get_issuers(gnutls_session_t session, int i; unsigned size; - if (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT) != GNUTLS_CRT_X509) + if (get_certificate_type(session, GNUTLS_CTYPE_CLIENT) != GNUTLS_CRT_X509) return 0; /* put the requested DNs to req_dn, only in case @@ -363,19 +409,10 @@ 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; - } + /* Correctly set the certificate type for ourselves */ + type = get_certificate_type(session, GNUTLS_CTYPE_OURS); + /* Check whether a callback is set and call it */ if (cred->get_cert_callback3) { struct gnutls_cert_retr_st info; unsigned int flags = 0; @@ -411,8 +448,7 @@ call_get_cert_callback(gnutls_session_t session, return 0; } else { - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } } @@ -435,6 +471,7 @@ _gnutls_select_client_cert(gnutls_session_t session, ssize_t data_size = _data_size; int issuers_dn_length; gnutls_datum_t *issuers_dn = NULL; + gnutls_certificate_type_t cert_type; cred = (gnutls_certificate_credentials_t) _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE); @@ -443,11 +480,13 @@ _gnutls_select_client_cert(gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } + cert_type = get_certificate_type(session, GNUTLS_CTYPE_CLIENT); + if (cred->get_cert_callback3 != NULL) { /* use a callback to get certificate */ - if (session->security_parameters.client_ctype == GNUTLS_CRT_X509) { + if (cert_type == GNUTLS_CRT_X509) { issuers_dn_length = get_issuers_num(session, data, data_size); if (issuers_dn_length < 0) { @@ -486,16 +525,23 @@ _gnutls_select_client_cert(gnutls_session_t session, } else { /* If we have no callbacks, try to guess. */ - 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); - } else { - result = GNUTLS_E_UNIMPLEMENTED_FEATURE; + switch (cert_type) { + case GNUTLS_CRT_X509: + result = find_x509_client_cert(session, cred, _data, + _data_size, pk_algos, + pk_algos_length, &indx); + break; + case GNUTLS_CRT_RAWPK: + result = find_rawpk_client_cert(session, cred, + pk_algos, pk_algos_length, &indx); + break; + default: + result = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + break; } + if (result < 0) { - gnutls_assert(); - return result; + return gnutls_assert_val(result); } if (indx >= 0) { @@ -575,27 +621,83 @@ static int gen_x509_crt(gnutls_session_t session, gnutls_buffer_st * data) return data->length - init_pos; } + +/* Generates a Raw Public Key certificate message that holds only the + * SubjectPublicKeyInfo part of a regular certificate message. + * + * Returns the number of bytes sent or a negative error code. + */ +int +_gnutls_gen_rawpk_crt(gnutls_session_t session, gnutls_buffer_st* data) +{ + int ret; + gnutls_pcert_st *apr_cert_list; + gnutls_privkey_t apr_pkey; + int apr_cert_list_length; + + if((ret = _gnutls_get_selected_cert(session, &apr_cert_list, + &apr_cert_list_length, &apr_pkey)) < 0) { + return gnutls_assert_val(ret); + } + + /* Since we are transmitting a raw public key with no additional + * certificate credentials attached to it, it doesn't make sense to + * have more than one certificate set (i.e. to have a certificate chain). + * This is enforced by the API so having a value other than 1 should + * be an impossible situation. + */ + assert(apr_cert_list_length == 1); + + /* Write our certificate containing only the SubjectPublicKeyInfo to + * the output buffer. We always have exactly one certificate that + * contains our raw public key. Our message looks like: + * <length++certificate> where + * length = 3 bytes and + * certificate = length bytes. + */ + ret = _gnutls_buffer_append_data_prefix(data, 24, + apr_cert_list[0].cert.data, + apr_cert_list[0].cert.size); + + if (ret < 0) return gnutls_assert_val(ret); + + return data->length; +} + + int _gnutls_gen_cert_client_crt(gnutls_session_t session, gnutls_buffer_st * data) { - switch (session->security_parameters.client_ctype) { - case GNUTLS_CRT_X509: - return gen_x509_crt(session, data); - default: - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + gnutls_certificate_type_t cert_type; + + // Retrieve the (negotiated) certificate type for the client + cert_type = get_certificate_type(session, GNUTLS_CTYPE_CLIENT); + + switch (cert_type) { + case GNUTLS_CRT_X509: + return gen_x509_crt(session, data); + case GNUTLS_CRT_RAWPK: + return _gnutls_gen_rawpk_crt(session, data); + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } } int _gnutls_gen_cert_server_crt(gnutls_session_t session, gnutls_buffer_st * data) { - switch (session->security_parameters.server_ctype) { - case GNUTLS_CRT_X509: - return gen_x509_crt(session, data); - default: - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + gnutls_certificate_type_t cert_type; + + // Retrieve the (negotiated) certificate type for the server + cert_type = get_certificate_type(session, GNUTLS_CTYPE_SERVER); + + switch (cert_type) { + case GNUTLS_CRT_X509: + return gen_x509_crt(session, data); + case GNUTLS_CRT_RAWPK: + return _gnutls_gen_rawpk_crt(session, data); + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } } @@ -765,6 +867,99 @@ _gnutls_proc_x509_crt(gnutls_session_t session, } + +int _gnutls_proc_rawpk_crt(gnutls_session_t session, + uint8_t * data, size_t data_size) +{ + int cert_size, ret; + cert_auth_info_t info; + gnutls_pcert_st* peer_certificate; + gnutls_datum_t tmp_cert; + + uint8_t *p = data; + ssize_t dsize = data_size; + + /* We assume data != null and data_size > 0 because + * the caller checks this for us. */ + + /* Read the length of our certificate. We always have exactly + * one certificate that contains our raw public key. Our message + * looks like: + * <length++certificate> where + * length = 3 bytes and + * certificate = length bytes. + */ + DECR_LEN(dsize, 3); + cert_size = _gnutls_read_uint24(p); + p += 3; + + /* Ensure no discrepancy in data */ + if (cert_size != dsize) + return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH); + + + if (cert_size == 0) { + // No certificate was sent. This is not OK. + return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND); + } + + DECR_LEN_FINAL(dsize, cert_size); + + /* We are now going to read our certificate and store it into + * the authentication info structure. + */ + tmp_cert.size = cert_size; + tmp_cert.data = p; + + peer_certificate = gnutls_calloc(1, sizeof(*peer_certificate)); + if (peer_certificate == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + // Import our raw certificate holding only a raw public key into this pcert + ret = gnutls_pcert_import_rawpk_raw(peer_certificate, &tmp_cert, GNUTLS_X509_FMT_DER, 0, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + // Check whether the PK algo is compatible with the negotiated KX + ret = check_pk_compat(session, peer_certificate->pubkey); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_auth_info_init(session, GNUTLS_CRD_CERTIFICATE, + sizeof(cert_auth_info_st), 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE); + + /* Copy our imported certificate into the auth info structure + * and free our temporary cert storage peer_certificate. + */ + ret = _gnutls_pcert_to_auth_info(info, peer_certificate, 1); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + return GNUTLS_E_SUCCESS; + +cleanup: + if (peer_certificate != NULL) { + gnutls_pcert_deinit(peer_certificate); + gnutls_free(peer_certificate); + } + + return ret; +} + + int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size) { int ret; @@ -779,22 +974,17 @@ int _gnutls_proc_crt(gnutls_session_t session, uint8_t * data, size_t data_size) return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - /* 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); - } + /* Determine what certificate type we need to process. + * We need to process the certificate of the peer. */ + cert_type = get_certificate_type(session, GNUTLS_CTYPE_PEERS); switch (cert_type) { case GNUTLS_CRT_X509: - ret = _gnutls_proc_x509_crt(session, data, data_size); - break; + return _gnutls_proc_x509_crt(session, data, data_size); + case GNUTLS_CRT_RAWPK: + return _gnutls_proc_rawpk_crt(session, data, data_size); default: - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } return ret; @@ -1148,8 +1338,7 @@ _gnutls_get_selected_cert(gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - } else { /* CLIENT SIDE - */ + } else { /* CLIENT SIDE */ /* _gnutls_select_client_cert() must have been called before. */ *apr_cert_list = session->internals.selected_cert_list; @@ -1267,7 +1456,7 @@ int cert_select_sign_algorithm(gnutls_session_t session, assert(IS_SERVER(session)); /* Retrieve the server certificate type */ - ctype = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER); + ctype = get_certificate_type(session, GNUTLS_CTYPE_SERVER); if (ctype != cert_type) { return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); @@ -1333,10 +1522,6 @@ _gnutls_select_server_cert(gnutls_session_t session, const gnutls_cipher_suite_e * certificate and then check its compatibility with * the ciphersuites. */ - - /* 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) { if (session->internals.selected_cert_list_length == 0) { ret = call_get_cert_callback(session, NULL, 0, NULL, 0); @@ -1407,10 +1592,10 @@ _gnutls_select_server_cert(gnutls_session_t session, const gnutls_cipher_suite_e _gnutls_handshake_log ("HSK[%p]: checking compat of %s with certificate[%d] (%s/%s)\n", session, cs->name, i, - gnutls_pk_get_name(cred->certs[i].cert_list[0].pubkey->params.algo), - gnutls_certificate_type_get_name(cred->certs - [i].cert_list - [0].type)); + gnutls_pk_get_name(cred->certs[i].cert_list[0].pubkey-> + params.algo), + gnutls_certificate_type_get_name(cred->certs[i]. + cert_list[0].type)); ret = cert_select_sign_algorithm(session, &cred->certs[i].cert_list[0], @@ -1457,9 +1642,8 @@ _gnutls_select_server_cert(gnutls_session_t session, const gnutls_cipher_suite_e ocsp_func, ocsp_ptr); } else { - gnutls_assert(); /* Certificate does not support REQUESTED_ALGO. */ - return GNUTLS_E_INSUFFICIENT_CREDENTIALS; + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); } return 0; @@ -1603,7 +1787,7 @@ _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data, signature.size = sigsize; // Retrieve the negotiated certificate type - cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER); + cert_type = get_certificate_type(session, GNUTLS_CTYPE_SERVER); if ((ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info)) < 0) { diff --git a/lib/auth/cert.h b/lib/auth/cert.h index fe3210f922..3f57ec1c74 100644 --- a/lib/auth/cert.h +++ b/lib/auth/cert.h @@ -32,6 +32,12 @@ #define MAX_OCSP_RESPONSES 8 +/* We use the structure below to hold a certificate chain + * with corresponding public/private key pair. This structure will + * also be used when raw public keys are used. The cert_list will + * then not hold the cert chain but only a raw public-key. In that case + * the list length is always 1. + */ typedef struct { gnutls_pcert_st *cert_list; /* a certificate chain */ unsigned int cert_list_length; /* its length */ @@ -73,7 +79,7 @@ typedef struct gnutls_certificate_credentials_st { /* X509 specific stuff */ gnutls_x509_trust_list_t tlist; unsigned flags; /* gnutls_certificate_flags */ - unsigned int verify_flags; /* flags to be used at + unsigned int verify_flags; /* flags to be used at * certificate verification. */ unsigned int verify_depth; @@ -161,4 +167,9 @@ int _gnutls_proc_dhe_signature(gnutls_session_t session, uint8_t * data, size_t _data_size, gnutls_datum_t * vparams); +int _gnutls_gen_rawpk_crt(gnutls_session_t session, gnutls_buffer_st* data); +int _gnutls_proc_rawpk_crt(gnutls_session_t session, + uint8_t * data, size_t data_size); + + #endif diff --git a/lib/auth/rsa.c b/lib/auth/rsa.c index 488569d3b7..c2203c7ed3 100644 --- a/lib/auth/rsa.c +++ b/lib/auth/rsa.c @@ -117,7 +117,7 @@ _gnutls_get_public_rsa_params(gnutls_session_t session, } // Get the negotiated server certificate type - cert_type = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER); + cert_type = get_certificate_type(session, GNUTLS_CTYPE_SERVER); ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info); diff --git a/lib/cert-cred-rawpk.c b/lib/cert-cred-rawpk.c new file mode 100644 index 0000000000..76cd653204 --- /dev/null +++ b/lib/cert-cred-rawpk.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2017 - 2018 ARPA2 project + * + * Author: Tom Vrancken (dev@tomvrancken.nl) + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include <gnutls/gnutls.h> +#include "datum.h" +#include "auth/cert.h" +#include "x509.h" +#include "cert-cred.h" +#include "read-file.h" +#include <stdint.h> + + +/** + * gnutls_certificate_set_rawpk_key_mem: + * @cred: is a #gnutls_certificate_credentials_t type. + * @spki: contains a raw public key in + * PKIX.SubjectPublicKeyInfo format. + * @pkey: contains a raw private key. + * @format: encoding of the keys. DER or PEM. + * @pass: an optional password to unlock the private key pkey. + * @key_usage: An ORed sequence of %GNUTLS_KEY_* flags. + * @names: is an array of DNS names belonging to the public-key (NULL if none). + * @names_length: holds the length of the names list. + * @flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t. + * These apply to the private key pkey. + * + * This function sets a public/private keypair in the + * #gnutls_certificate_credentials_t type to be used for authentication + * and/or encryption. @spki and @privkey should match otherwise set + * signatures cannot be validated. In case of no match this function + * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should + * be called once for the client because there is currently no mechanism + * to determine which raw public-key to select for the peer when there + * are multiple present. Multiple raw public keys for the server can be + * distinghuished by setting the @names. + * + * Note here that @spki is a raw public-key as defined + * in RFC7250. It means that there is no surrounding certificate that + * holds the public key and that there is therefore no direct mechanism + * to prove the authenticity of this key. The keypair can be used during + * a TLS handshake but its authenticity should be established via a + * different mechanism (e.g. TOFU or known fingerprint). + * + * The supported formats are basic unencrypted key, PKCS8, PKCS12, + * and the openssl format and will be autodetected. + * + * If the raw public-key and the private key are given in PEM encoding + * then the strings that hold their values must be null terminated. + * + * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly + * set because there is no certificate structure around the key to define + * this value. See for more info gnutls_x509_crt_get_key_usage(). + * + * Note that, this function by default returns zero on success and a + * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 + * is set using gnutls_certificate_set_flags() it returns an index + * (greater or equal to zero). That index can be used in other functions + * to refer to the added key-pair. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the + * key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned, + * in other erroneous cases a different negative error code is returned. + * + * Since: 3.6.6 + **/ +int gnutls_certificate_set_rawpk_key_mem(gnutls_certificate_credentials_t cred, + const gnutls_datum_t* spki, + const gnutls_datum_t* pkey, + gnutls_x509_crt_fmt_t format, + const char* pass, + unsigned int key_usage, + const char **names, + unsigned int names_length, + unsigned int flags) +{ + int ret; + gnutls_privkey_t privkey; + gnutls_pcert_st* pcert; + gnutls_str_array_t str_names; + unsigned int i; + + if (pkey == NULL || spki == NULL) { + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + } + + /* Import our private key. This function does all the necessary + * inits, checks and imports. */ + ret = _gnutls_read_key_mem(cred, pkey->data, pkey->size, + format, pass, flags, &privkey); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + /* We now convert our raw public key to a parsed certificate (pcert) structure */ + pcert = gnutls_calloc(1, sizeof(*pcert)); + if (pcert == NULL) { + gnutls_privkey_deinit(privkey); + + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + // Import our raw public key to the pcert structure + ret = gnutls_pcert_import_rawpk_raw(pcert, spki, + format, key_usage, 0); + if (ret < 0) { + gnutls_privkey_deinit(privkey); + + return gnutls_assert_val(ret); + } + + /* Process the names, if any */ + _gnutls_str_array_init(&str_names); + + if (names != NULL && names_length > 0) { + for (i = 0; i < names_length; i++) { + ret = + _gnutls_str_array_append_idna(&str_names, names[i], + strlen(names[i])); + if (ret < 0) { + gnutls_privkey_deinit(privkey); + _gnutls_str_array_clear(&str_names); + + return gnutls_assert_val(ret); + } + } + } + + /* Now that we have converted the key material to our internal structures + * we can now add them to the credentials structure */ + ret = _gnutls_certificate_credential_append_keypair(cred, privkey, str_names, pcert, 1); + // Check for errors + if (ret < 0) { + gnutls_privkey_deinit(privkey); + gnutls_pcert_deinit(pcert); + gnutls_free(pcert); + + return gnutls_assert_val(ret); + } + // Successfully added a certificate + cred->ncerts++; + + /* Check whether the key pair matches. + * After this point we do not deinitialize anything on failure to avoid + * double freeing. We intentionally keep everything as the credentials state + * is documented to be in undefined state. */ + if ((ret = _gnutls_check_key_cert_match(cred)) < 0) { + return gnutls_assert_val(ret); + } + + CRED_RET_SUCCESS(cred); +} + + +/** + * gnutls_certificate_set_rawpk_key_file: + * @cred: is a #gnutls_certificate_credentials_t type. + * @rawpkfile: contains a raw public key in + * PKIX.SubjectPublicKeyInfo format. + * @privkeyfile: contains a file path to a private key. + * @format: encoding of the keys. DER or PEM. + * @pass: an optional password to unlock the private key privkeyfile. + * @key_usage: an ORed sequence of %GNUTLS_KEY_* flags. + * @names: is an array of DNS names belonging to the public-key (NULL if none). + * @names_length: holds the length of the names list. + * @privkey_flags: an ORed sequence of #gnutls_pkcs_encrypt_flags_t. + * These apply to the private key pkey. + * @pkcs11_flags: one of gnutls_pkcs11_obj_flags. These apply to URLs. + * + * This function sets a public/private keypair read from file in the + * #gnutls_certificate_credentials_t type to be used for authentication + * and/or encryption. @spki and @privkey should match otherwise set + * signatures cannot be validated. In case of no match this function + * returns %GNUTLS_E_CERTIFICATE_KEY_MISMATCH. This function should + * be called once for the client because there is currently no mechanism + * to determine which raw public-key to select for the peer when there + * are multiple present. Multiple raw public keys for the server can be + * distinghuished by setting the @names. + * + * Note here that @spki is a raw public-key as defined + * in RFC7250. It means that there is no surrounding certificate that + * holds the public key and that there is therefore no direct mechanism + * to prove the authenticity of this key. The keypair can be used during + * a TLS handshake but its authenticity should be established via a + * different mechanism (e.g. TOFU or known fingerprint). + * + * The supported formats are basic unencrypted key, PKCS8, PKCS12, + * and the openssl format and will be autodetected. + * + * If the raw public-key and the private key are given in PEM encoding + * then the strings that hold their values must be null terminated. + * + * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly + * set because there is no certificate structure around the key to define + * this value. See for more info gnutls_x509_crt_get_key_usage(). + * + * Note that, this function by default returns zero on success and a + * negative value on error. Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 + * is set using gnutls_certificate_set_flags() it returns an index + * (greater or equal to zero). That index can be used in other functions + * to refer to the added key-pair. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, in case the + * key pair does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH is returned, + * in other erroneous cases a different negative error code is returned. + * + * Since: 3.6.6 + */ +int gnutls_certificate_set_rawpk_key_file(gnutls_certificate_credentials_t cred, + const char* rawpkfile, + const char* privkeyfile, + gnutls_x509_crt_fmt_t format, + const char *pass, + unsigned int key_usage, + const char **names, + unsigned int names_length, + unsigned int privkey_flags, + unsigned int pkcs11_flags) +{ + int ret; + gnutls_privkey_t privkey; + gnutls_pubkey_t pubkey; + gnutls_pcert_st* pcert; + gnutls_datum_t rawpubkey = { NULL, 0 }; // to hold rawpk data from file + size_t key_size; + gnutls_str_array_t str_names; + unsigned int i; + + if (rawpkfile == NULL || privkeyfile == NULL) { + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + } + + /* Import our private key. This function does all the necessary + * inits, checks and imports. */ + ret = _gnutls_read_key_file(cred, privkeyfile, format, pass, privkey_flags, &privkey); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + pcert = gnutls_calloc(1, sizeof(*pcert)); + if (pcert == NULL) { + gnutls_privkey_deinit(privkey); + + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + /* Check whether we are importing our raw public-key from a URL + * or from a regular file. + */ + if (gnutls_url_is_supported(rawpkfile)) { + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) { + gnutls_privkey_deinit(privkey); + + return gnutls_assert_val(ret); + } + + ret = gnutls_pubkey_import_url(pubkey, rawpkfile, pkcs11_flags); + if (ret < 0) { + gnutls_privkey_deinit(privkey); + gnutls_pubkey_deinit(pubkey); + + return gnutls_assert_val(ret); + } + + ret = gnutls_pcert_import_rawpk(pcert, pubkey, 0); + if (ret < 0) { + gnutls_privkey_deinit(privkey); + gnutls_pubkey_deinit(pubkey); + + return gnutls_assert_val(ret); + } + + } else { + /* Read our raw public-key into memory from file */ + rawpubkey.data = (void*) read_binary_file(rawpkfile, &key_size); + if (rawpubkey.data == NULL) { + gnutls_privkey_deinit(privkey); + + return gnutls_assert_val(GNUTLS_E_FILE_ERROR); + } + rawpubkey.size = key_size; // Implicit type casting + + /* We now convert our raw public key that we've loaded into memory to + * a parsed certificate (pcert) structure. Note that rawpubkey will + * be copied into pcert. Therefore we can directly cleanup rawpubkey. + */ + ret = gnutls_pcert_import_rawpk_raw(pcert, &rawpubkey, + format, key_usage, 0); + + _gnutls_free_datum(&rawpubkey); + + if (ret < 0) { + gnutls_privkey_deinit(privkey); + + return gnutls_assert_val(ret); + } + + } + + /* Process the names, if any */ + _gnutls_str_array_init(&str_names); + + if (names != NULL && names_length > 0) { + for (i = 0; i < names_length; i++) { + ret = + _gnutls_str_array_append_idna(&str_names, names[i], + strlen(names[i])); + if (ret < 0) { + gnutls_privkey_deinit(privkey); + _gnutls_str_array_clear(&str_names); + + return gnutls_assert_val(ret); + } + } + } + + /* Now that we have converted the key material to our internal structures + * we can now add them to the credentials structure */ + ret = _gnutls_certificate_credential_append_keypair(cred, privkey, str_names, pcert, 1); + if (ret < 0) { + gnutls_privkey_deinit(privkey); + gnutls_pcert_deinit(pcert); + gnutls_free(pcert); + + return gnutls_assert_val(ret); + } + // Successfully added a certificate + cred->ncerts++; + + /* Check whether the key pair matches. + * After this point we do not deinitialize anything on failure to avoid + * double freeing. We intentionally keep everything as the credentials state + * is documented to be in undefined state. */ + if ((ret = _gnutls_check_key_cert_match(cred)) < 0) { + return gnutls_assert_val(ret); + } + + CRED_RET_SUCCESS(cred); +} + diff --git a/lib/cert-cred-x509.c b/lib/cert-cred-x509.c index 42a6bd5ba8..257f1b989a 100644 --- a/lib/cert-cred-x509.c +++ b/lib/cert-cred-x509.c @@ -46,6 +46,7 @@ #include "read-file.h" #include "system-keys.h" #include "urls.h" +#include "cert-cred.h" #ifdef _WIN32 #include <wincrypt.h> #endif @@ -55,42 +56,10 @@ * related objects in a certificate credentials structure. */ -static int -certificate_credential_append_keypair(gnutls_certificate_credentials_t res, - gnutls_privkey_t key, - gnutls_str_array_t names, - gnutls_pcert_st * crt, int nr); - -#define CRED_RET_SUCCESS(cred) \ - if (cred->flags & GNUTLS_CERTIFICATE_API_V2) { \ - return cred->ncerts-1; \ - } else { \ - return 0; \ - } - - -static int str_array_append_idna(gnutls_str_array_t * head, const char *name, size_t size) -{ - int ret; - gnutls_datum_t ahost; - - /* convert the provided hostname to ACE-Labels domain. */ - ret = gnutls_idna_map(name, size, &ahost, 0); - if (ret < 0) { - _gnutls_debug_log("unable to convert hostname %s to IDNA format\n", name); - /* insert the raw name */ - return _gnutls_str_array_append(head, name, size); - } - - ret = _gnutls_str_array_append(head, (char*)ahost.data, ahost.size); - gnutls_free(ahost.data); - - return ret; -} /* Returns the name of the certificate of a null name */ -static int get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names) +int _gnutls_get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names) { size_t max_size; int i, ret = 0, ret2; @@ -107,7 +76,7 @@ static int get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names) have_dns_name = 1; ret2 = - str_array_append_idna(names, name, + _gnutls_str_array_append_idna(names, name, max_size); if (ret2 < 0) { _gnutls_str_array_clear(names); @@ -122,7 +91,7 @@ static int get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names) gnutls_x509_crt_get_dn_by_oid(crt, OID_X520_COMMON_NAME, 0, 0, name, &max_size); if (ret >= 0) { - ret = str_array_append_idna(names, name, max_size); + ret = _gnutls_str_array_append_idna(names, name, max_size); if (ret < 0) { _gnutls_str_array_clear(names); return gnutls_assert_val(ret); @@ -171,7 +140,7 @@ parse_der_cert_mem(gnutls_certificate_credentials_t res, goto cleanup; } - ret = get_x509_name(crt, &names); + ret = _gnutls_get_x509_name(crt, &names); if (ret < 0) { gnutls_assert(); gnutls_x509_crt_deinit(crt); @@ -186,7 +155,7 @@ parse_der_cert_mem(gnutls_certificate_credentials_t res, goto cleanup; } - ret = certificate_credential_append_keypair(res, key, names, ccert, 1); + ret = _gnutls_certificate_credential_append_keypair(res, key, names, ccert, 1); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -279,7 +248,7 @@ parse_pem_cert_mem(gnutls_certificate_credentials_t res, while (ptr != NULL && count < DEFAULT_MAX_VERIFY_DEPTH); ret = - get_x509_name(unsorted[0], &names); + _gnutls_get_x509_name(unsorted[0], &names); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -302,8 +271,7 @@ parse_pem_cert_mem(gnutls_certificate_credentials_t res, } ret = - certificate_credential_append_keypair(res, key, names, pcerts, - ncerts); + _gnutls_certificate_credential_append_keypair(res, key, names, pcerts, ncerts); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -370,8 +338,8 @@ static int tmp_pin_cb(void *userdata, int attempt, const char *token_url, * * It returns the private key read in @rkey. */ -static int -read_key_mem(gnutls_certificate_credentials_t res, +int +_gnutls_read_key_mem(gnutls_certificate_credentials_t res, const void *key, int key_size, gnutls_x509_crt_fmt_t type, const char *pass, unsigned int flags, gnutls_privkey_t *rkey) @@ -501,7 +469,7 @@ read_cert_url(gnutls_certificate_credentials_t res, gnutls_privkey_t key, const goto cleanup; } - ret = get_x509_name(crt, &names); + ret = _gnutls_get_x509_name(crt, &names); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -543,7 +511,7 @@ read_cert_url(gnutls_certificate_credentials_t res, gnutls_privkey_t key, const t.data = NULL; } - ret = certificate_credential_append_keypair(res, key, names, ccert, count); + ret = _gnutls_certificate_credential_append_keypair(res, key, names, ccert, count); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -596,8 +564,8 @@ read_cert_file(gnutls_certificate_credentials_t res, /* Reads PKCS-1 RSA private key file or a DSA file (in the format openssl * stores it). */ -static int -read_key_file(gnutls_certificate_credentials_t res, +int +_gnutls_read_key_file(gnutls_certificate_credentials_t res, const char *keyfile, gnutls_x509_crt_fmt_t type, const char *pass, unsigned int flags, gnutls_privkey_t *rkey) @@ -629,7 +597,7 @@ read_key_file(gnutls_certificate_credentials_t res, return GNUTLS_E_FILE_ERROR; } - ret = read_key_mem(res, data, size, type, pass, flags, rkey); + ret = _gnutls_read_key_mem(res, data, size, type, pass, flags, rkey); free(data); return ret; @@ -716,7 +684,7 @@ gnutls_certificate_set_x509_key_mem2(gnutls_certificate_credentials_t res, /* this should be first */ - if ((ret = read_key_mem(res, key ? key->data : NULL, + if ((ret = _gnutls_read_key_mem(res, key ? key->data : NULL, key ? key->size : 0, type, pass, flags, &rkey)) < 0) return ret; @@ -736,57 +704,6 @@ gnutls_certificate_set_x509_key_mem2(gnutls_certificate_credentials_t res, CRED_RET_SUCCESS(res); } -static int -certificate_credential_append_keypair(gnutls_certificate_credentials_t res, - gnutls_privkey_t key, - gnutls_str_array_t names, - gnutls_pcert_st * crt, int nr) -{ - res->sorted_cert_idx = gnutls_realloc_fast(res->sorted_cert_idx, - (1 + res->ncerts) * - sizeof(unsigned int)); - if (res->sorted_cert_idx == NULL) - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - - res->certs = gnutls_realloc_fast(res->certs, - (1 + res->ncerts) * - sizeof(certs_st)); - if (res->certs == NULL) - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - - memset(&res->certs[res->ncerts], 0, sizeof(res->certs[0])); - - res->certs[res->ncerts].cert_list = crt; - res->certs[res->ncerts].cert_list_length = nr; - res->certs[res->ncerts].names = names; - res->certs[res->ncerts].pkey = key; - - /* move RSA-PSS certificates before any RSA key. - * Note that we cannot assume that any previous pointers - * to sorted list are ok, due to the realloc in res->certs. */ - if (crt->pubkey->params.algo == GNUTLS_PK_RSA_PSS) { - unsigned i,ridx; - unsigned tmp; - - for (i=0;i<res->ncerts;i++) { - ridx = res->sorted_cert_idx[i]; - - if (res->certs[ridx].cert_list->pubkey->params.algo == GNUTLS_PK_RSA) { - tmp = ridx; - res->sorted_cert_idx[i] = res->ncerts; - res->sorted_cert_idx[res->ncerts] = tmp; - goto finish; - } - } - } - - /* otherwise append it normally on the end */ - res->sorted_cert_idx[res->ncerts] = res->ncerts; - - finish: - return 0; - -} /** * gnutls_certificate_set_x509_key: @@ -856,7 +773,7 @@ gnutls_certificate_set_x509_key(gnutls_certificate_credentials_t res, return GNUTLS_E_MEMORY_ERROR; } - ret = get_x509_name(cert_list[0], &names); + ret = _gnutls_get_x509_name(cert_list[0], &names); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -871,7 +788,7 @@ gnutls_certificate_set_x509_key(gnutls_certificate_credentials_t res, } ret = - certificate_credential_append_keypair(res, pkey, names, pcerts, + _gnutls_certificate_credential_append_keypair(res, pkey, names, pcerts, cert_list_size); if (ret < 0) { gnutls_assert(); @@ -1001,126 +918,6 @@ gnutls_certificate_get_x509_crt(gnutls_certificate_credentials_t res, } /** - * gnutls_certificate_set_key: - * @res: is a #gnutls_certificate_credentials_t type. - * @names: is an array of DNS name of the certificate (NULL if none) - * @names_size: holds the size of the names list - * @pcert_list: contains a certificate list (path) for the specified private key - * @pcert_list_size: holds the size of the certificate list - * @key: is a #gnutls_privkey_t key - * - * This function sets a certificate/private key pair in the - * gnutls_certificate_credentials_t type. This function may be - * called more than once, in case multiple keys/certificates exist for - * the server. For clients that want to send more than their own end- - * entity certificate (e.g., also an intermediate CA cert), the full - * certificate chain must be provided in @pcert_list. - * - * Note that the @key and the elements of @pcert_list will become part of the credentials - * structure and must not be deallocated. They will be automatically deallocated - * when the @res structure is deinitialized. - * - * If that function fails to load the @res structure is at an undefined state, it must - * not be reused to load other keys or certificates. - * - * Note that, this function by default returns zero on success and a negative value on error. - * Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 is set using gnutls_certificate_set_flags() - * it returns an index (greater or equal to zero). That index can be used to other functions to refer to the added key-pair. - * - * Returns: On success this functions returns zero, and otherwise a negative value on error (see above for modifying that behavior). - * - * Since: 3.0 - **/ -int -gnutls_certificate_set_key(gnutls_certificate_credentials_t res, - const char **names, - int names_size, - gnutls_pcert_st * pcert_list, - int pcert_list_size, gnutls_privkey_t key) -{ - int ret, i; - gnutls_str_array_t str_names; - gnutls_pcert_st *new_pcert_list; - - _gnutls_str_array_init(&str_names); - - if (names != NULL && names_size > 0) { - for (i = 0; i < names_size; i++) { - ret = - str_array_append_idna(&str_names, names[i], - strlen(names[i])); - if (ret < 0) { - ret = gnutls_assert_val(ret); - goto cleanup; - } - } - } else if (names == NULL && pcert_list[0].type == GNUTLS_CRT_X509) { - gnutls_x509_crt_t crt; - - ret = gnutls_x509_crt_init(&crt); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } - - ret = gnutls_x509_crt_import(crt, &pcert_list[0].cert, GNUTLS_X509_FMT_DER); - if (ret < 0) { - gnutls_assert(); - gnutls_x509_crt_deinit(crt); - goto cleanup; - } - - ret = get_x509_name(crt, &str_names); - gnutls_x509_crt_deinit(crt); - - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } - } - - if (res->pin.cb) - gnutls_privkey_set_pin_function(key, res->pin.cb, - res->pin.data); - - new_pcert_list = gnutls_malloc(sizeof(gnutls_pcert_st) * pcert_list_size); - if (new_pcert_list == NULL) { - gnutls_assert(); - return GNUTLS_E_MEMORY_ERROR; - } - memcpy(new_pcert_list, pcert_list, sizeof(gnutls_pcert_st) * pcert_list_size); - - ret = - certificate_credential_append_keypair(res, key, str_names, - new_pcert_list, - pcert_list_size); - if (ret < 0) { - gnutls_assert(); - gnutls_free(new_pcert_list); - goto cleanup; - } - - res->ncerts++; - - /* Unlike gnutls_certificate_set_x509_key, we deinitialize everything - * local after a failure. That is because the caller is responsible for - * freeing these values after a failure, and if we keep references we - * lead to double freeing */ - if ((ret = _gnutls_check_key_cert_match(res)) < 0) { - gnutls_assert(); - gnutls_free(new_pcert_list); - res->ncerts--; - goto cleanup; - } - - CRED_RET_SUCCESS(res); - - cleanup: - _gnutls_str_array_clear(&str_names); - return ret; -} - -/** * gnutls_certificate_set_trust_list: * @res: is a #gnutls_certificate_credentials_t type. * @tlist: is a #gnutls_x509_trust_list_t type @@ -1265,7 +1062,7 @@ gnutls_certificate_set_x509_key_file2(gnutls_certificate_credentials_t res, /* this should be first */ - if ((ret = read_key_file(res, keyfile, type, pass, flags, &rkey)) < 0) + if ((ret = _gnutls_read_key_file(res, keyfile, type, pass, flags, &rkey)) < 0) return ret; if ((ret = read_cert_file(res, rkey, certfile, type)) < 0) { diff --git a/lib/cert-cred.c b/lib/cert-cred.c index 2d7009b2e5..f04ded4c04 100644 --- a/lib/cert-cred.c +++ b/lib/cert-cred.c @@ -40,7 +40,202 @@ #include <str_array.h> #include <x509/verify-high.h> #include "x509/x509_int.h" +#include "x509/common.h" #include "dh.h" +#include "cert-cred.h" + + +/* + * Adds a public/private key pair to a certificate credential + */ +int +_gnutls_certificate_credential_append_keypair(gnutls_certificate_credentials_t res, + gnutls_privkey_t key, + gnutls_str_array_t names, + gnutls_pcert_st * crt, + int nr) +{ + res->sorted_cert_idx = gnutls_realloc_fast(res->sorted_cert_idx, + (1 + res->ncerts) * + sizeof(unsigned int)); + if (res->sorted_cert_idx == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + res->certs = gnutls_realloc_fast(res->certs, + (1 + res->ncerts) * + sizeof(certs_st)); + if (res->certs == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memset(&res->certs[res->ncerts], 0, sizeof(res->certs[0])); + + res->certs[res->ncerts].cert_list = crt; + res->certs[res->ncerts].cert_list_length = nr; + res->certs[res->ncerts].names = names; + res->certs[res->ncerts].pkey = key; + + /* move RSA-PSS certificates before any RSA key. + * Note that we cannot assume that any previous pointers + * to sorted list are ok, due to the realloc in res->certs. */ + if (crt->pubkey->params.algo == GNUTLS_PK_RSA_PSS) { + unsigned i,ridx; + unsigned tmp; + + for (i=0;i<res->ncerts;i++) { + ridx = res->sorted_cert_idx[i]; + + if (res->certs[ridx].cert_list->pubkey->params.algo == GNUTLS_PK_RSA) { + tmp = ridx; + res->sorted_cert_idx[i] = res->ncerts; + res->sorted_cert_idx[res->ncerts] = tmp; + goto finish; + } + } + } + + /* otherwise append it normally on the end */ + res->sorted_cert_idx[res->ncerts] = res->ncerts; + + finish: + return 0; + +} + + +/** + * gnutls_certificate_set_key: + * @res: is a #gnutls_certificate_credentials_t type. + * @names: is an array of DNS names belonging to the public-key (NULL if none) + * @names_size: holds the size of the names list + * @pcert_list: contains a certificate list (chain) or raw public-key + * @pcert_list_size: holds the size of the certificate list + * @key: is a #gnutls_privkey_t key corresponding to the first public-key in pcert_list + * + * This function sets a public/private key pair in the + * gnutls_certificate_credentials_t type. The given public key may be encapsulated + * in a certificate or can be given as a raw key. This function may be + * called more than once, in case multiple key pairs exist for + * the server. For clients that want to send more than their own end- + * entity certificate (e.g., also an intermediate CA cert), the full + * certificate chain must be provided in @pcert_list. + * + * Note that the @key will become part of the credentials structure and must + * not be deallocated. It will be automatically deallocated when the @res structure + * is deinitialized. + * + * If this function fails, the @res structure is at an undefined state and it must + * not be reused to load other keys or certificates. + * + * Note that, this function by default returns zero on success and a negative value on error. + * Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 is set using gnutls_certificate_set_flags() + * it returns an index (greater or equal to zero). That index can be used for other functions to refer to the added key-pair. + * + * Since GnuTLS 3.6.6 this function also handles raw public keys. + * + * Returns: On success this functions returns zero, and otherwise a negative value on error (see above for modifying that behavior). + * + * Since: 3.0 + **/ +int +gnutls_certificate_set_key(gnutls_certificate_credentials_t res, + const char **names, + int names_size, + gnutls_pcert_st * pcert_list, + int pcert_list_size, + gnutls_privkey_t key) +{ + int ret, i; + gnutls_str_array_t str_names; + gnutls_pcert_st *new_pcert_list; + + /* Sanity checks */ + // Check for a valid credential struct + if (res == NULL) { + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + } + + // A complete key pair must be given + if (pcert_list == NULL || key == NULL) { + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + } + + /* Process the names, if any */ + _gnutls_str_array_init(&str_names); + + if (names != NULL && names_size > 0) { + for (i = 0; i < names_size; i++) { + ret = + _gnutls_str_array_append_idna(&str_names, names[i], + strlen(names[i])); + if (ret < 0) { + ret = gnutls_assert_val(ret); + goto cleanup; + } + } + } else if (names == NULL && pcert_list[0].type == GNUTLS_CRT_X509) { + gnutls_x509_crt_t crt; + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crt_import(crt, &pcert_list[0].cert, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + gnutls_x509_crt_deinit(crt); + goto cleanup; + } + + ret = _gnutls_get_x509_name(crt, &str_names); + gnutls_x509_crt_deinit(crt); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + if (res->pin.cb) + gnutls_privkey_set_pin_function(key, res->pin.cb, + res->pin.data); + + new_pcert_list = gnutls_malloc(sizeof(gnutls_pcert_st) * pcert_list_size); + if (new_pcert_list == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + memcpy(new_pcert_list, pcert_list, sizeof(gnutls_pcert_st) * pcert_list_size); + + ret = + _gnutls_certificate_credential_append_keypair(res, key, str_names, + new_pcert_list, + pcert_list_size); + if (ret < 0) { + gnutls_assert(); + gnutls_free(new_pcert_list); + goto cleanup; + } + + res->ncerts++; + + /* Unlike gnutls_certificate_set_x509_key, we deinitialize everything + * local after a failure. That is because the caller is responsible for + * freeing these values after a failure, and if we keep references we + * lead to double freeing */ + if ((ret = _gnutls_check_key_cert_match(res)) < 0) { + gnutls_assert(); + gnutls_free(new_pcert_list); + res->ncerts--; + goto cleanup; + } + + CRED_RET_SUCCESS(res); + + cleanup: + _gnutls_str_array_clear(&str_names); + return ret; +} /** * gnutls_certificate_free_keys: @@ -199,7 +394,7 @@ gnutls_certificate_free_credentials(gnutls_certificate_credentials_t sc) // Check for valid pointer and otherwise do nothing if (sc == NULL) return; - + gnutls_x509_trust_list_deinit(sc->tlist, 1); gnutls_certificate_free_keys(sc); memset(sc->pin_tmp, 0, sizeof(sc->pin_tmp)); diff --git a/lib/cert-cred.h b/lib/cert-cred.h new file mode 100644 index 0000000000..06cba4dd58 --- /dev/null +++ b/lib/cert-cred.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include <gnutls/abstract.h> +#include "str_array.h" + + +int +_gnutls_certificate_credential_append_keypair(gnutls_certificate_credentials_t res, + gnutls_privkey_t key, + gnutls_str_array_t names, + gnutls_pcert_st * crt, int nr); + +int +_gnutls_read_key_mem(gnutls_certificate_credentials_t res, + const void *key, int key_size, gnutls_x509_crt_fmt_t type, + const char *pass, unsigned int flags, + gnutls_privkey_t *rkey); + +int +_gnutls_read_key_file(gnutls_certificate_credentials_t res, + const char *keyfile, gnutls_x509_crt_fmt_t type, + const char *pass, unsigned int flags, + gnutls_privkey_t *rkey); + +int +_gnutls_get_x509_name(gnutls_x509_crt_t crt, gnutls_str_array_t * names); + +#define CRED_RET_SUCCESS(cred) \ + if (cred->flags & GNUTLS_CERTIFICATE_API_V2) { \ + return cred->ncerts-1; \ + } else { \ + return 0; \ + } diff --git a/lib/cert-session.c b/lib/cert-session.c index 2726512f5d..9a25eb57a4 100644 --- a/lib/cert-session.c +++ b/lib/cert-session.c @@ -599,6 +599,10 @@ _gnutls_x509_cert_verify_peers(gnutls_session_t session, * default upper limits regarding the certificate key size and chain * size are set. To override them use gnutls_certificate_set_verify_limits(). * + * Note that when using raw public-keys verification will not work because there is + * no corresponding certificate body belonging to the raw key that can be verified. In that + * case this function will return %GNUTLS_E_INVALID_REQUEST. + * * Returns: %GNUTLS_E_SUCCESS (0) when the validation is performed, or a negative error code otherwise. * A successful error code means that the @status parameter must be checked to obtain the validation status. **/ @@ -640,6 +644,10 @@ gnutls_certificate_verify_peers2(gnutls_session_t session, * default upper limits regarding the certificate key size and chain * size are set. To override them use gnutls_certificate_set_verify_limits(). * + * Note that when using raw public-keys verification will not work because there is + * no corresponding certificate body belonging to the raw key that can be verified. In that + * case this function will return %GNUTLS_E_INVALID_REQUEST. + * * Returns: %GNUTLS_E_SUCCESS (0) when the validation is performed, or a negative error code otherwise. * A successful error code means that the @status parameter must be checked to obtain the validation status. * @@ -695,6 +703,10 @@ gnutls_typed_vdata_st data; * default upper limits regarding the certificate key size and chain * size are set. To override them use gnutls_certificate_set_verify_limits(). * + * Note that when using raw public-keys verification will not work because there is + * no corresponding certificate body belonging to the raw key that can be verified. In that + * case this function will return %GNUTLS_E_INVALID_REQUEST. + * * Returns: %GNUTLS_E_SUCCESS (0) when the validation is performed, or a negative error code otherwise. * A successful error code means that the @status parameter must be checked to obtain the validation status. * @@ -719,7 +731,7 @@ gnutls_certificate_verify_peers(gnutls_session_t session, return GNUTLS_E_NO_CERTIFICATE_FOUND; - switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) { + switch (get_certificate_type(session, GNUTLS_CTYPE_PEERS)) { case GNUTLS_CRT_X509: return _gnutls_x509_cert_verify_peers(session, data, elements, status); @@ -820,7 +832,7 @@ time_t gnutls_certificate_expiration_time_peers(gnutls_session_t session) return (time_t) - 1; } - switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) { + switch (get_certificate_type(session, GNUTLS_CTYPE_PEERS)) { case GNUTLS_CRT_X509: return _gnutls_x509_get_raw_crt_expiration_time(&info-> @@ -856,7 +868,7 @@ time_t gnutls_certificate_activation_time_peers(gnutls_session_t session) return (time_t) - 1; } - switch (gnutls_certificate_type_get2(session, GNUTLS_CTYPE_PEERS)) { + switch (get_certificate_type(session, GNUTLS_CTYPE_PEERS)) { case GNUTLS_CRT_X509: return _gnutls_x509_get_raw_crt_activation_time(&info-> diff --git a/lib/ext/cert_types.h b/lib/ext/cert_types.h index c54e0f2bfe..04e024d5db 100644 --- a/lib/ext/cert_types.h +++ b/lib/ext/cert_types.h @@ -26,11 +26,13 @@ /* Maps IANA TLS Certificate Types identifiers to internal * certificate type representation. */ -static inline gnutls_certificate_type_t _gnutls_IANA2cert_type(int num) +static inline gnutls_certificate_type_t IANA2cert_type(int num) { switch (num) { case 0: return GNUTLS_CRT_X509; + case 2: + return GNUTLS_CRT_RAWPK; default: return GNUTLS_CRT_UNKNOWN; } @@ -39,12 +41,43 @@ static inline gnutls_certificate_type_t _gnutls_IANA2cert_type(int num) /* Maps internal certificate type representation to * IANA TLS Certificate Types identifiers. */ -static inline int _gnutls_cert_type2IANA(gnutls_certificate_type_t cert_type) +static inline int cert_type2IANA(gnutls_certificate_type_t cert_type) { switch (cert_type) { case GNUTLS_CRT_X509: return 0; + case GNUTLS_CRT_RAWPK: + return 2; default: return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; } } + +/* Checks whether the given cert type is enabled in the application + */ +static inline bool is_cert_type_enabled(gnutls_session_t session, gnutls_certificate_type_t cert_type) +{ + switch(cert_type) { + case GNUTLS_CRT_X509: + // Default cert type, always enabled + return true; + case GNUTLS_CRT_RAWPK: + return session->internals.flags & GNUTLS_ENABLE_RAWPK; + default: + // When not explicitly supported here disable it + return false; + } +} + +/* Checks whether alternative cert types (i.e. other than X.509) + * are enabled in the application + */ +static inline bool are_alternative_cert_types_allowed(gnutls_session_t session) +{ + // OR-ed list of defined cert type init flags + #define CERT_TYPES_FLAGS GNUTLS_ENABLE_RAWPK + + return session->internals.flags & CERT_TYPES_FLAGS; + + #undef CERT_TYPES_FLAGS +} diff --git a/lib/ext/client_cert_type.c b/lib/ext/client_cert_type.c index 5449eae678..534c407b3a 100644 --- a/lib/ext/client_cert_type.c +++ b/lib/ext/client_cert_type.c @@ -33,8 +33,8 @@ #include "hello_ext.h" #include "hello_ext_lib.h" #include "errors.h" -#include <state.h> -#include <datum.h> +#include "state.h" +#include "datum.h" static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, @@ -76,10 +76,10 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, 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) + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) return 0; if (!IS_SERVER(session)) { // client mode @@ -97,7 +97,7 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, * 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]); + cert_type = IANA2cert_type(pdata[0]); // Check validity of cert type if (cert_type == GNUTLS_CRT_UNKNOWN) { @@ -119,7 +119,7 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, // 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) + if (IANA2cert_type(sent_cert_types.data[i]) == cert_type) found = 1; } @@ -160,7 +160,7 @@ static int _gnutls_client_cert_type_recv_params(gnutls_session_t session, */ for (i = 0; i < cert_types.size; i++) { // Convert to internal representation - cert_type = _gnutls_IANA2cert_type(cert_types.data[i]); + cert_type = IANA2cert_type(cert_types.data[i]); // If we have an invalid cert id then continue to the next if (cert_type == GNUTLS_CRT_UNKNOWN) @@ -201,13 +201,13 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session, uint8_t i = 0, num_cert_types = 0; priority_st* cert_priorities; gnutls_datum_t tmp_cert_types; // For type conversion - uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types + uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types. Inv: 0 <= cert type Id < 256 const version_entry_st* vers = get_version(session); - /* Only activate this extension if 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) + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) return 0; if (!IS_SERVER(session)) { // Client mode @@ -255,7 +255,13 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session, return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); // Convert to IANA representation - cert_type = _gnutls_cert_type2IANA(cert_priorities->priorities[i]); + ret = cert_type2IANA(cert_priorities->priorities[i]); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type = ret; // For readability + // Add this cert type to our list with supported types cert_types[num_cert_types] = cert_type; num_cert_types++; @@ -282,7 +288,7 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session, return 0; } else if (num_cert_types == 1 && - _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) { + 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", @@ -342,9 +348,13 @@ static int _gnutls_client_cert_type_send_params(gnutls_session_t session, * 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 = cert_type2IANA(get_certificate_type( + session, GNUTLS_CTYPE_CLIENT)); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type = ret; // For readability ret = gnutls_buffer_append_data(data, &cert_type, 1); diff --git a/lib/ext/server_cert_type.c b/lib/ext/server_cert_type.c index a00a0376c9..35c6d751db 100644 --- a/lib/ext/server_cert_type.c +++ b/lib/ext/server_cert_type.c @@ -33,8 +33,8 @@ #include "hello_ext.h" #include "hello_ext_lib.h" #include "errors.h" -#include <state.h> -#include <datum.h> +#include "state.h" +#include "datum.h" static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, @@ -76,10 +76,10 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, 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) + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) return 0; if (!IS_SERVER(session)) { // client mode @@ -96,7 +96,7 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, * 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]); + cert_type = IANA2cert_type(pdata[0]); // Check validity of cert type if (cert_type == GNUTLS_CRT_UNKNOWN) { @@ -118,7 +118,7 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, // 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) + if (IANA2cert_type(sent_cert_types.data[i]) == cert_type) found = 1; } @@ -159,7 +159,7 @@ static int _gnutls_server_cert_type_recv_params(gnutls_session_t session, */ for (i = 0; i < cert_types.size; i++) { // Convert to internal representation - cert_type = _gnutls_IANA2cert_type(cert_types.data[i]); + cert_type = IANA2cert_type(cert_types.data[i]); // If we have an invalid cert id then continue to the next if (cert_type == GNUTLS_CRT_UNKNOWN) @@ -197,12 +197,12 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session, uint8_t i = 0, num_cert_types = 0; priority_st* cert_priorities; gnutls_datum_t tmp_cert_types; // For type conversion - uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types + uint8_t cert_types[GNUTLS_CRT_MAX]; // The list with supported cert types. Inv: 0 <= cert type Id < 256 - /* 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) + /* Only activate this extension if we have cert credentials set + * and alternative cert types are allowed */ + if (!are_alternative_cert_types_allowed(session) || + (_gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE) == NULL)) return 0; if (!IS_SERVER(session)) { // Client mode @@ -255,7 +255,13 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session, return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); // Convert to IANA representation - cert_type = _gnutls_cert_type2IANA(cert_priorities->priorities[i]); + ret = cert_type2IANA(cert_priorities->priorities[i]); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type = ret; // For readability + // Add this cert type to our list with supported types cert_types[num_cert_types] = cert_type; num_cert_types++; @@ -281,7 +287,7 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session, return 0; } else if (num_cert_types == 1 && - _gnutls_IANA2cert_type(cert_types[0]) == DEFAULT_CERT_TYPE) { + 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", @@ -320,9 +326,13 @@ static int _gnutls_server_cert_type_send_params(gnutls_session_t session, } } else { // Server mode // Retrieve negotiated server certificate type and send it - cert_type = - _gnutls_cert_type2IANA(session->security_parameters. - server_ctype); + ret = cert_type2IANA(get_certificate_type( + session, GNUTLS_CTYPE_SERVER)); + + if (ret < 0) + return gnutls_assert_val(ret); + + cert_type = ret; // For readability ret = gnutls_buffer_append_data(data, &cert_type, 1); diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index cccc3d1a97..8baa8815e7 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1561,9 +1561,39 @@ 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) +/* Returns the during the handshake negotiated certificate type(s). + * See state.c for the full function documentation. + * + * This function is made static inline for optimization reasons. + */ +static inline gnutls_certificate_type_t +get_certificate_type(gnutls_session_t session, + gnutls_ctype_target_t target) { - return session->internals.flags & GNUTLS_ENABLE_CERT_TYPE_NEG; + 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_CRT_UNKNOWN; + } } /* Macros to aide constant time/mem checks */ diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index fcff0a562f..223fb2ed1f 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -693,6 +693,14 @@ int gnutls_pcert_export_openpgp(gnutls_pcert_st * pcert, void gnutls_pcert_deinit(gnutls_pcert_st * pcert); +int gnutls_pcert_import_rawpk(gnutls_pcert_st* pcert, + gnutls_pubkey_t key, unsigned int flags); + +int gnutls_pcert_import_rawpk_raw(gnutls_pcert_st* pcert, + const gnutls_datum_t* rawpubkey, + gnutls_x509_crt_fmt_t format, + unsigned int key_usage, unsigned int flags); + /* For certificate credentials */ /* This is the same as gnutls_certificate_retrieve_function() * but retrieves a gnutls_pcert_st which requires much less processing diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 73141a3a3b..1c3455c0cf 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -421,7 +421,6 @@ 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). * @GNUTLS_AUTO_REAUTH: Enable transparent re-authentication in client side when the server * requests to. That is, reauthentication is handled within gnutls_record_recv(), and * the %GNUTLS_E_REHANDSHAKE or %GNUTLS_E_REAUTH_REQUEST are not returned. This must be @@ -430,6 +429,10 @@ typedef enum { * since gnutls_record_recv() could be interrupted when sending when this flag is enabled. * Note this flag may not be used if you are using the same session for sending and receiving * in different threads. + * @GNUTLS_ENABLE_EARLY_DATA: Under TLS1.3 allow the server to receive early data sent as part of the initial ClientHello (0-RTT). + * This is not enabled by default as early data has weaker security properties than other data. Since 3.6.5. + * + * @GNUTLS_ENABLE_RAWPK: Allows raw public-keys to be negotiated during the handshake. Since 3.6.6. * * Enumeration of different flags for gnutls_init() function. All the flags * can be combined except @GNUTLS_SERVER and @GNUTLS_CLIENT which are mutually @@ -458,7 +461,7 @@ typedef enum { GNUTLS_NO_AUTO_REKEY = (1<<15), GNUTLS_SAFE_PADDING_CHECK = (1<<16), GNUTLS_ENABLE_EARLY_START = (1<<17), - GNUTLS_ENABLE_CERT_TYPE_NEG = (1<<18), + GNUTLS_ENABLE_RAWPK = (1<<18), GNUTLS_AUTO_REAUTH = (1<<19), GNUTLS_ENABLE_EARLY_DATA = (1<<20) } gnutls_init_flags_t; @@ -476,6 +479,8 @@ typedef enum { #define GNUTLS_ENABLE_FALSE_START (1<<8) #define GNUTLS_FORCE_CLIENT_CERT (1<<9) #define GNUTLS_NO_TICKETS (1<<10) +#define GNUTLS_ENABLE_CERT_TYPE_NEG 0 + // Here for compatibility reasons /** * gnutls_alert_level_t: @@ -630,7 +635,7 @@ const char * @GNUTLS_CERT_SIGNER_NOT_FOUND: The certificate's issuer is not known. * This is the case if the issuer is not included in the trusted certificate list. * @GNUTLS_CERT_SIGNER_NOT_CA: The certificate's signer was not a CA. This - * may happen if this was a version 1 certificate, which is common with + * may happen if this was a version 1 certificate, which is common with * some CAs, or a version 3 certificate without the basic constrains extension. * @GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE: The certificate's signer constraints were * violated. @@ -1405,7 +1410,7 @@ ssize_t gnutls_record_recv(gnutls_session_t session, void *data, typedef struct mbuffer_st *gnutls_packet_t; ssize_t -gnutls_record_recv_packet(gnutls_session_t session, +gnutls_record_recv_packet(gnutls_session_t session, gnutls_packet_t *packet); void gnutls_packet_get(gnutls_packet_t packet, gnutls_datum_t *data, unsigned char *sequence); @@ -1685,7 +1690,7 @@ const char * gnutls_protocol_get_name(gnutls_protocol_t version) __GNUTLS_CONST__; -/* get/set session +/* get/set session */ int gnutls_session_set_data(gnutls_session_t session, const void *session_data, @@ -1776,7 +1781,7 @@ int gnutls_session_channel_binding(gnutls_session_t session, gnutls_channel_binding_t cbtype, gnutls_datum_t * cb); -/* checks if this session is a resumed one +/* checks if this session is a resumed one */ int gnutls_session_is_resumed(gnutls_session_t session); int gnutls_session_resumption_requested(gnutls_session_t session); @@ -2130,6 +2135,29 @@ gnutls_ocsp_status_request_get2(gnutls_session_t session, unsigned idx, gnutls_datum_t * response); +/* RAW public key functions (RFC7250) */ +int gnutls_certificate_set_rawpk_key_mem(gnutls_certificate_credentials_t cred, + const gnutls_datum_t* spki, + const gnutls_datum_t* pkey, + gnutls_x509_crt_fmt_t format, + const char* pass, + unsigned int key_usage, + const char **names, + unsigned int names_length, + unsigned int flags); + +int gnutls_certificate_set_rawpk_key_file(gnutls_certificate_credentials_t cred, + const char* rawpkfile, + const char* privkeyfile, + gnutls_x509_crt_fmt_t format, + const char *pass, + unsigned int key_usage, + const char **names, + unsigned int names_length, + unsigned int privkey_flags, + unsigned int pkcs11_flags); + + /* global state functions */ int gnutls_global_init(void); @@ -2272,7 +2300,7 @@ void gnutls_transport_set_errno_function(gnutls_session_t session, void gnutls_transport_set_errno(gnutls_session_t session, int err); -/* session specific +/* session specific */ void gnutls_session_set_ptr(gnutls_session_t session, void *ptr); void *gnutls_session_get_ptr(gnutls_session_t session); @@ -2305,7 +2333,7 @@ int gnutls_random_art(gnutls_random_art_t type, int gnutls_idna_map(const char * input, unsigned ilen, gnutls_datum_t *out, unsigned flags); int gnutls_idna_reverse_map(const char *input, unsigned ilen, gnutls_datum_t *out, unsigned flags); -/* SRP +/* SRP */ typedef struct gnutls_srp_server_credentials_st @@ -2974,13 +3002,13 @@ typedef int (*gnutls_supp_recv_func) (gnutls_session_t session, typedef int (*gnutls_supp_send_func) (gnutls_session_t session, gnutls_buffer_t buf); -int gnutls_supplemental_register(const char *name, - gnutls_supplemental_data_format_type_t type, +int gnutls_supplemental_register(const char *name, + gnutls_supplemental_data_format_type_t type, gnutls_supp_recv_func supp_recv_func, gnutls_supp_send_func supp_send_func); int gnutls_session_supplemental_register(gnutls_session_t session, const char *name, - gnutls_supplemental_data_format_type_t type, + gnutls_supplemental_data_format_type_t type, gnutls_supp_recv_func supp_recv_func, gnutls_supp_send_func supp_send_func, unsigned int flags); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index bfb447ccfd..197644ea43 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1264,6 +1264,15 @@ GNUTLS_3_6_5 gnutls_privkey_decrypt_data2; } GNUTLS_3_6_4; +GNUTLS_3_6_6 +{ + global: + gnutls_certificate_set_rawpk_key_mem; + gnutls_certificate_set_rawpk_key_file; + gnutls_pcert_import_rawpk; + gnutls_pcert_import_rawpk_raw; +} GNUTLS_3_6_5; + GNUTLS_FIPS140_3_4 { global: gnutls_cipher_self_test; diff --git a/lib/pcert.c b/lib/pcert.c index b800e91fc4..816a748b05 100644 --- a/lib/pcert.c +++ b/lib/pcert.c @@ -27,6 +27,7 @@ #include <x509.h> #include "x509/x509_int.h" #include <gnutls/x509.h> +#include "x509_b64.h" /** * gnutls_pcert_import_x509: @@ -215,7 +216,7 @@ gnutls_pcert_list_import_x509_raw(gnutls_pcert_st *pcert_list, cleanup: for (i = 0; i < *pcert_list_size; i++) gnutls_x509_crt_deinit(crt[i]); - + cleanup_crt: gnutls_free(crt); return ret; @@ -356,6 +357,133 @@ int gnutls_pcert_import_x509_raw(gnutls_pcert_st * pcert, } /** + * gnutls_pcert_import_rawpk: + * @pcert: The pcert structure to import the data into. + * @pubkey: The raw public-key in #gnutls_pubkey_t format to be imported + * @flags: zero for now + * + * This convenience function will import (i.e. convert) the given raw + * public key @pubkey into a #gnutls_pcert_st structure. The structure + * must be deinitialized afterwards using gnutls_pcert_deinit(). The + * given @pubkey must not be deinitialized because it will be associated + * with the given @pcert structure and will be deinitialized with it. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.6 + **/ +int gnutls_pcert_import_rawpk(gnutls_pcert_st* pcert, + gnutls_pubkey_t pubkey, unsigned int flags) +{ + int ret; + + if (pubkey == NULL) { + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + } + + memset(pcert, 0, sizeof(*pcert)); + + /* A pcert struct holds a raw copy of the certificate data. + * Therefore we convert our gnutls_pubkey_t to its raw DER + * representation and copy it into our pcert. It is this raw data + * that will be transfered to the peer via a Certificate msg. + * According to the spec (RFC7250) a DER representation must be used. + */ + ret = gnutls_pubkey_export2(pubkey, GNUTLS_X509_FMT_DER, &pcert->cert); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + pcert->pubkey = pubkey; + + pcert->type = GNUTLS_CRT_RAWPK; + + return GNUTLS_E_SUCCESS; +} + +/** + * gnutls_pcert_import_rawpk_raw: + * @pcert: The pcert structure to import the data into. + * @rawpubkey: The raw public-key in #gnutls_datum_t format to be imported. + * @format: The format of the raw public-key. DER or PEM. + * @key_usage: An ORed sequence of %GNUTLS_KEY_* flags. + * @flags: zero for now + * + * This convenience function will import (i.e. convert) the given raw + * public key @rawpubkey into a #gnutls_pcert_st structure. The structure + * must be deinitialized afterwards using gnutls_pcert_deinit(). + * Note that the caller is responsible for freeing @rawpubkey. All necessary + * values will be copied into @pcert. + * + * Key usage (as defined by X.509 extension (2.5.29.15)) can be explicitly + * set because there is no certificate structure around the key to define + * this value. See for more info gnutls_x509_crt_get_key_usage(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.6 + **/ +int gnutls_pcert_import_rawpk_raw(gnutls_pcert_st* pcert, + const gnutls_datum_t* rawpubkey, + gnutls_x509_crt_fmt_t format, + unsigned int key_usage, unsigned int flags) +{ + int ret; + + if (rawpubkey == NULL) { + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + } + + memset(pcert, 0, sizeof(*pcert)); + + ret = gnutls_pubkey_init(&pcert->pubkey); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + // Convert our raw public-key to a gnutls_pubkey_t structure + ret = gnutls_pubkey_import(pcert->pubkey, rawpubkey, format); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + pcert->pubkey->key_usage = key_usage; + + /* A pcert struct holds a raw copy of the certificate data. + * It is this raw data that will be transfered to the peer via a + * Certificate message. According to the spec (RFC7250) a DER + * representation must be used. Therefore we check the format and + * convert if necessary. + */ + if (format == GNUTLS_X509_FMT_PEM) { + ret = _gnutls_fbase64_decode(PEM_PK, + rawpubkey->data, rawpubkey->size, + &pcert->cert); + + if (ret < 0) { + gnutls_pubkey_deinit(pcert->pubkey); + + return gnutls_assert_val(ret); + } + } else { + // Directly copy the raw DER data to our pcert + ret = _gnutls_set_datum(&pcert->cert, rawpubkey->data, rawpubkey->size); + + if (ret < 0) { + gnutls_pubkey_deinit(pcert->pubkey); + + return gnutls_assert_val(ret); + } + } + + pcert->type = GNUTLS_CRT_RAWPK; + + return GNUTLS_E_SUCCESS; +} + +/** * gnutls_pcert_export_x509: * @pcert: The pcert structure. * @crt: An initialized #gnutls_x509_crt_t. @@ -420,15 +548,17 @@ _gnutls_get_auth_info_pcert(gnutls_pcert_st * pcert, cert_auth_info_t info) { switch (type) { - case GNUTLS_CRT_X509: - return gnutls_pcert_import_x509_raw(pcert, - &info-> - raw_certificate_list - [0], - GNUTLS_X509_FMT_DER, - 0); - default: - gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + case GNUTLS_CRT_X509: + return gnutls_pcert_import_x509_raw(pcert, + &info->raw_certificate_list[0], + GNUTLS_X509_FMT_DER, + 0); + case GNUTLS_CRT_RAWPK: + return gnutls_pcert_import_rawpk_raw(pcert, + &info->raw_certificate_list[0], + GNUTLS_X509_FMT_DER, + 0, 0); + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); } } diff --git a/lib/pkcs11x.c b/lib/pkcs11x.c index fc428e17a4..c11f12cb8d 100644 --- a/lib/pkcs11x.c +++ b/lib/pkcs11x.c @@ -277,7 +277,7 @@ gnutls_pkcs11_obj_get_exts(gnutls_pkcs11_obj_t obj, spki.data = obj->raw.data; spki.size = obj->raw.size; } else { - ret = x509_raw_crt_to_raw_pubkey(&obj->raw, &spki); + ret = _gnutls_x509_raw_crt_to_raw_pubkey(&obj->raw, &spki); if (ret < 0) return gnutls_assert_val(ret); deinit_spki = 1; diff --git a/lib/session.c b/lib/session.c index a7ac943153..a5c9c93d53 100644 --- a/lib/session.c +++ b/lib/session.c @@ -27,6 +27,7 @@ #include <datum.h> #include "buffers.h" #include "state.h" +#include "ext/cert_types.h" /** * gnutls_session_get_data: @@ -423,11 +424,10 @@ char *gnutls_session_get_desc(gnutls_session_t session) } } - // Check whether we have negotiated certificate types - if (_gnutls_has_negotiate_ctypes(session)) { + if (are_alternative_cert_types_allowed(session)) { // Get certificate types - ctype_client = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_CLIENT); - ctype_server = gnutls_certificate_type_get2(session, GNUTLS_CTYPE_SERVER); + ctype_client = get_certificate_type(session, GNUTLS_CTYPE_CLIENT); + ctype_server = get_certificate_type(session, GNUTLS_CTYPE_SERVER); if (ctype_client == ctype_server) { // print proto version, client/server cert type @@ -442,9 +442,8 @@ char *gnutls_session_get_desc(gnutls_session_t session) 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))); + snprintf(proto_name, sizeof(proto_name), "%s", + gnutls_protocol_get_name(get_num_version(session))); } desc = gnutls_malloc(DESC_SIZE); diff --git a/lib/state.c b/lib/state.c index 392183c95f..540a83c7b8 100644 --- a/lib/state.c +++ b/lib/state.c @@ -52,6 +52,7 @@ #include <gnutls/dtls.h> #include "dtls.h" #include "tls13/session_ticket.h" +#include "ext/cert_types.h" /* to be used by supplemental data support to disable TLS1.3 * when supplemental data have been globally registered */ @@ -140,30 +141,9 @@ 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); - } + /* We want to inline this function so therefore + * we've defined it in gnutls_int.h */ + return get_certificate_type(session, target); } /** @@ -309,6 +289,10 @@ _gnutls_session_cert_type_supported(gnutls_session_t session, unsigned i; priority_st* ctype_priorities; + // Check whether this cert type is enabled by the application + if (!is_cert_type_enabled(session, cert_type)) + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); + // Perform a credentials check if requested if (check_credentials) { if (!_gnutls_has_cert_credentials(session, cert_type)) @@ -336,14 +320,14 @@ _gnutls_session_cert_type_supported(gnutls_session_t session, // No explicit priorities set, and default ctype is asked if (ctype_priorities->num_priorities == 0 && cert_type == DEFAULT_CERT_TYPE) - return 0; // ok + return 0; /* 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->num_priorities; i++) { if (ctype_priorities->priorities[i] == cert_type) - return 0; /* ok */ + return 0; } return GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; diff --git a/lib/str_array.h b/lib/str_array.h index ef68783905..abec702488 100644 --- a/lib/str_array.h +++ b/lib/str_array.h @@ -112,4 +112,24 @@ inline static int _gnutls_str_array_append(gnutls_str_array_t * head, return 0; } +inline static int _gnutls_str_array_append_idna(gnutls_str_array_t * head, + const char *name, size_t size) +{ + int ret; + gnutls_datum_t ahost; + + /* convert the provided hostname to ACE-Labels domain. */ + ret = gnutls_idna_map(name, size, &ahost, 0); + if (ret < 0) { + _gnutls_debug_log("unable to convert hostname %s to IDNA format\n", name); + /* insert the raw name */ + return _gnutls_str_array_append(head, name, size); + } + + ret = _gnutls_str_array_append(head, (char*)ahost.data, ahost.size); + gnutls_free(ahost.data); + + return ret; +} + #endif diff --git a/lib/tls13/certificate_verify.c b/lib/tls13/certificate_verify.c index 01966b14d1..26c93d2c1d 100644 --- a/lib/tls13/certificate_verify.c +++ b/lib/tls13/certificate_verify.c @@ -113,7 +113,7 @@ int _gnutls13_recv_certificate_verify(gnutls_session_t session) /* 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); + cert_type = get_certificate_type(session, GNUTLS_CTYPE_PEERS); /* Verify the signature */ ret = _gnutls_get_auth_info_pcert(&peer_cert, cert_type, info); diff --git a/lib/verify-tofu.c b/lib/verify-tofu.c index 310778629b..66a583ded7 100644 --- a/lib/verify-tofu.c +++ b/lib/verify-tofu.c @@ -77,11 +77,11 @@ struct gnutls_tdb_int default_tdb = { * @cert: The raw (der) data of the certificate * @flags: should be 0. * - * This function will try to verify the provided (raw or DER-encoded) certificate - * using a list of stored public keys. The @service field if non-NULL should - * be a port number. + * This function will try to verify a raw public-key or a public-key provided via + * a raw (DER-encoded) certificate using a list of stored public keys. + * The @service field if non-NULL should be a port number. * - * The @retrieve variable if non-null specifies a custom backend for + * The @db_name variable if non-null specifies a custom backend for * the retrieval of entries. If it is NULL then the * default file backend will be used. In POSIX-like systems the * file backend uses the $HOME/.gnutls/known_hosts file. @@ -93,10 +93,12 @@ struct gnutls_tdb_int default_tdb = { * the given key is found, and 0 if it was found. The storage * function should return 0 on success. * + * As of GnuTLS 3.6.6 this function also verifies raw public keys. + * * Returns: If no associated public key is found * then %GNUTLS_E_NO_CERTIFICATE_FOUND will be returned. If a key * is found but does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH - * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned, + * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned, * or a negative error value on other errors. * * Since: 3.0.13 @@ -110,14 +112,11 @@ gnutls_verify_stored_pubkey(const char *db_name, const gnutls_datum_t * cert, unsigned int flags) { - gnutls_datum_t pubkey = { NULL, 0 }; + gnutls_datum_t pubkey = { NULL, 0 }; // Holds the pubkey in subjectPublicKeyInfo format (DER encoded) int ret; char local_file[MAX_FILENAME]; + bool need_free; - if (cert_type != GNUTLS_CRT_X509) - return - gnutls_assert_val - (GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); if (db_name == NULL && tdb == NULL) { ret = find_config_file(local_file, sizeof(local_file)); @@ -129,18 +128,38 @@ gnutls_verify_stored_pubkey(const char *db_name, if (tdb == NULL) tdb = &default_tdb; - ret = x509_raw_crt_to_raw_pubkey(cert, &pubkey); - if (ret < 0) { - gnutls_assert(); - goto cleanup; + /* Import the public key depending on the provided certificate type */ + switch (cert_type) { + case GNUTLS_CRT_X509: + /* Extract the pubkey from the cert. This function does a malloc + * deep down the call chain. We are responsible for freeing. */ + ret = _gnutls_x509_raw_crt_to_raw_pubkey(cert, &pubkey); + + if (ret < 0) { + _gnutls_free_datum(&pubkey); + return gnutls_assert_val(ret); + } + + need_free = true; + break; + case GNUTLS_CRT_RAWPK: + pubkey.data = cert->data; + pubkey.size = cert->size; + need_free = false; + break; + default: + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); } + // Verify our pubkey against the database ret = tdb->verify(db_name, host, service, &pubkey); if (ret < 0 && ret != GNUTLS_E_CERTIFICATE_KEY_MISMATCH) ret = gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND); - cleanup: - gnutls_free(pubkey.data); + if (need_free) { + _gnutls_free_datum(&pubkey); + } + return ret; } @@ -203,7 +222,7 @@ static int parse_commitment_line(char *line, /* hash and hex encode */ ret = - _gnutls_hash_fast((gnutls_digest_algorithm_t)hash_algo->id, + _gnutls_hash_fast((gnutls_digest_algorithm_t)hash_algo->id, skey->data, skey->size, phash); if (ret < 0) return gnutls_assert_val(ret); @@ -301,7 +320,7 @@ static int parse_line(char *line, return 0; } -/* Returns the base64 key if found +/* Returns the base64 key if found */ static int verify_pubkey(const char *file, const char *host, const char *service, @@ -460,11 +479,11 @@ int store_commitment(const char *db_name, const char *host, * @expiration: The expiration time (use 0 to disable expiration) * @flags: should be 0. * - * This function will store the provided (raw or DER-encoded) certificate to - * the list of stored public keys. The key will be considered valid until - * the provided expiration time. + * This function will store a raw public-key or a public-key provided via + * a raw (DER-encoded) certificate to the list of stored public keys. The key + * will be considered valid until the provided expiration time. * - * The @store variable if non-null specifies a custom backend for + * The @tdb variable if non-null specifies a custom backend for * the storage of entries. If it is NULL then the * default file backend will be used. * @@ -475,6 +494,8 @@ int store_commitment(const char *db_name, const char *host, * time in seconds since the epoch (0 for no expiration), and a base64 * encoding of the raw (DER) public key information (SPKI) of the peer. * + * As of GnuTLS 3.6.6 this function also accepts raw public keys. + * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a * negative error value. * @@ -489,14 +510,11 @@ gnutls_store_pubkey(const char *db_name, const gnutls_datum_t * cert, time_t expiration, unsigned int flags) { - gnutls_datum_t pubkey = { NULL, 0 }; + gnutls_datum_t pubkey = { NULL, 0 }; // Holds the pubkey in subjectPublicKeyInfo format (DER encoded) int ret; char local_file[MAX_FILENAME]; + bool need_free; - if (cert_type != GNUTLS_CRT_X509) - return - gnutls_assert_val - (GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); if (db_name == NULL && tdb == NULL) { ret = @@ -517,22 +535,38 @@ gnutls_store_pubkey(const char *db_name, if (tdb == NULL) tdb = &default_tdb; - ret = x509_raw_crt_to_raw_pubkey(cert, &pubkey); - if (ret < 0) { - gnutls_assert(); - goto cleanup; + /* Import the public key depending on the provided certificate type */ + switch (cert_type) { + case GNUTLS_CRT_X509: + /* Extract the pubkey from the cert. This function does a malloc + * deep down the call chain. We are responsible for freeing. */ + ret = _gnutls_x509_raw_crt_to_raw_pubkey(cert, &pubkey); + + if (ret < 0) { + _gnutls_free_datum(&pubkey); + return gnutls_assert_val(ret); + } + + need_free = true; + break; + case GNUTLS_CRT_RAWPK: + pubkey.data = cert->data; + pubkey.size = cert->size; + need_free = false; + break; + default: + return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE); } _gnutls_debug_log("Configuration file: %s\n", db_name); tdb->store(db_name, host, service, expiration, &pubkey); - ret = 0; - - cleanup: - gnutls_free(pubkey.data); + if (need_free) { + _gnutls_free_datum(&pubkey); + } - return ret; + return GNUTLS_E_SUCCESS; } /** @@ -546,11 +580,11 @@ gnutls_store_pubkey(const char *db_name, * @expiration: The expiration time (use 0 to disable expiration) * @flags: should be 0 or %GNUTLS_SCOMMIT_FLAG_ALLOW_BROKEN. * - * This function will store the provided hash commitment to + * This function will store the provided hash commitment to * the list of stored public keys. The key with the given * hash will be considered valid until the provided expiration time. * - * The @store variable if non-null specifies a custom backend for + * The @tdb variable if non-null specifies a custom backend for * the storage of entries. If it is NULL then the * default file backend will be used. * @@ -604,12 +638,10 @@ gnutls_store_commitment(const char *db_name, _gnutls_debug_log("Configuration file: %s\n", db_name); - tdb->cstore(db_name, host, service, expiration, + tdb->cstore(db_name, host, service, expiration, (gnutls_digest_algorithm_t)me->id, hash); - ret = 0; - - return ret; + return 0; } #define CONFIG_FILE "known_hosts" diff --git a/lib/x509.h b/lib/x509.h index 859824056a..67eb957d78 100644 --- a/lib/x509.h +++ b/lib/x509.h @@ -21,7 +21,7 @@ */ #include <libtasn1.h> -#include <gnutls/abstract.h> + int _gnutls_x509_cert_verify_peers(gnutls_session_t session, gnutls_typed_vdata_st * data, @@ -35,6 +35,7 @@ int _gnutls_x509_cert_verify_peers(gnutls_session_t session, #define PEM_CRL_SEP "-----BEGIN X509 CRL" + int _gnutls_x509_raw_privkey_to_gkey(gnutls_privkey_t * privkey, const gnutls_datum_t * raw_key, gnutls_x509_crt_fmt_t type); diff --git a/lib/x509/common.c b/lib/x509/common.c index 060c2aede1..9ce4275229 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -1653,7 +1653,7 @@ int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt, } /* Converts an X.509 certificate to subjectPublicKeyInfo */ -int x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert, +int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t * rpubkey) { gnutls_x509_crt_t crt = NULL; diff --git a/lib/x509/common.h b/lib/x509/common.h index 2ff979380f..878da42045 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -260,7 +260,7 @@ int _gnutls_copy_string(gnutls_datum_t* str, uint8_t *out, size_t *out_size); int _gnutls_copy_data(gnutls_datum_t* str, uint8_t *out, size_t *out_size); int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out); -int x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert, +int _gnutls_x509_raw_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t * rpubkey); int x509_crt_to_raw_pubkey(gnutls_x509_crt_t crt, |