From e476e4b7e0de11820123da06ca9fcd0cbb94b8ff Mon Sep 17 00:00:00 2001 From: Sahana Prasad Date: Fri, 22 May 2020 09:42:47 +0200 Subject: Implements a callback function gnutls_x509_trust_list_set_getissuer_function() Signed-off-by: Sahana Prasad --- lib/cert-cred.c | 32 +++++++++++ lib/includes/gnutls/x509.h | 6 +++ lib/libgnutls.map | 8 +++ lib/x509/verify-high.c | 37 ++++++------- lib/x509/verify-high.h | 6 ++- lib/x509/verify.c | 129 ++++++++++++++++++++++++++++----------------- lib/x509/x509_int.h | 33 +++++++----- 7 files changed, 169 insertions(+), 82 deletions(-) diff --git a/lib/cert-cred.c b/lib/cert-cred.c index 7311737298..8d3214dcbb 100644 --- a/lib/cert-cred.c +++ b/lib/cert-cred.c @@ -882,6 +882,38 @@ void cred->verify_callback = func; } +/** + * gnutls_x509_trust_list_set_getissuer_function: + * @tlist: is a #gnutls_x509_trust_list_t type. + * @func: is the callback function + * + * This function sets a callback to be called when the peer's certificate + * chain is incomplete due a missing intermediate certificate/certificates. + * + * The callback's function prototype is defined in `abstract.h': + * int (*callback)( + * gnutls_x509_trust_list_t tlist, + * const gnutls_x509_crt_t crt); + * + * If the callback function is provided then gnutls will call it, in the + * certificate verification procedure. + * To verify or obtain the certificate the verification functions such as + * gnutls_x509_trust_list_verify_crt() and gnutls_x509_trust_list_verify_crt2() + * can be used. + * + * The callback function should return 0 if the missing issuer certificate + * for 'crt' was properly polulated and added to the 'tlist' using + * gnutls_x509_trust_list_add_cas() or non-zero to continue the certificate list + * verification but with issuer as %NULL. + * + * Since: 3.7.0 + **/ +void gnutls_x509_trust_list_set_getissuer_function(gnutls_x509_trust_list_t tlist, + gnutls_x509_trust_list_getissuer_function * func) +{ + tlist->issuer_callback = func; +} + #define TEST_TEXT "test text" /* returns error if the certificate has different algorithm than * the given key parameters. diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 6807271b2a..bcb687ce27 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -1698,6 +1698,12 @@ gnutls_x509_trust_list_add_system_trust(gnutls_x509_trust_list_t unsigned int tl_flags, unsigned int tl_vflags); +typedef int gnutls_x509_trust_list_getissuer_function(gnutls_x509_trust_list_t tlist, + const gnutls_x509_crt_t crt); + +void gnutls_x509_trust_list_set_getissuer_function(gnutls_x509_trust_list_t tlist, + gnutls_x509_trust_list_getissuer_function *func); + void gnutls_certificate_set_trust_list (gnutls_certificate_credentials_t res, gnutls_x509_trust_list_t tlist, unsigned flags); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index ac6be479f1..e29f064a30 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1331,6 +1331,14 @@ GNUTLS_3_6_14 gnutls_pkcs7_print_signature_info; } GNUTLS_3_6_13; +GNUTLS_3_7_0 +{ + global: + gnutls_x509_trust_list_set_getissuer_function; + local: + *; +} GNUTLS_3_4; + GNUTLS_FIPS140_3_4 { global: gnutls_cipher_self_test; diff --git a/lib/x509/verify-high.c b/lib/x509/verify-high.c index 40638ad3aa..763c527a59 100644 --- a/lib/x509/verify-high.c +++ b/lib/x509/verify-high.c @@ -851,11 +851,10 @@ static int shorten_clist(gnutls_x509_trust_list_t list, return clist_size; } -static -int trust_list_get_issuer(gnutls_x509_trust_list_t list, - gnutls_x509_crt_t cert, - gnutls_x509_crt_t * issuer, - unsigned int flags) +int _gnutls_trust_list_get_issuer(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t * issuer, + unsigned int flags) { int ret; unsigned int i; @@ -968,7 +967,7 @@ int gnutls_x509_trust_list_get_issuer(gnutls_x509_trust_list_t list, { int ret; - ret = trust_list_get_issuer(list, cert, issuer, flags); + ret = _gnutls_trust_list_get_issuer(list, cert, issuer, flags); if (ret == 0) { return 0; } @@ -1335,11 +1334,10 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, } *voutput = - _gnutls_verify_crt_status(cert_list, cert_list_size, - list->node[hash].trusted_cas, - list-> - node[hash].trusted_ca_size, - flags, purpose, func); + _gnutls_verify_crt_status(list, cert_list, cert_list_size, + list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size, + flags, purpose, func); saved_output = *voutput; if (SIGNER_OLD_OR_UNKNOWN(*voutput) && @@ -1357,11 +1355,10 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, _gnutls_debug_log("issuer in verification was not found or insecure; trying against trust list\n"); *voutput = - _gnutls_verify_crt_status(cert_list, cert_list_size, - list->node[hash].trusted_cas, - list-> - node[hash].trusted_ca_size, - flags, purpose, func); + _gnutls_verify_crt_status(list, cert_list, cert_list_size, + list->node[hash].trusted_cas, + list->node[hash].trusted_ca_size, + flags, purpose, func); if (*voutput != 0) { if (SIGNER_WAS_KNOWN(saved_output)) *voutput = saved_output; @@ -1375,10 +1372,10 @@ gnutls_x509_trust_list_verify_crt2(gnutls_x509_trust_list_t list, if (SIGNER_OLD_OR_UNKNOWN(*voutput) && list->pkcs11_token) { /* use the token for verification */ - *voutput = _gnutls_pkcs11_verify_crt_status(list->pkcs11_token, - cert_list, cert_list_size, - purpose, - flags, func); + *voutput = _gnutls_pkcs11_verify_crt_status(list, list->pkcs11_token, + cert_list, cert_list_size, + purpose, + flags, func); if (*voutput != 0) { if (SIGNER_WAS_KNOWN(saved_output)) *voutput = saved_output; diff --git a/lib/x509/verify-high.h b/lib/x509/verify-high.h index ca1f98b831..6ce5f958ae 100644 --- a/lib/x509/verify-high.h +++ b/lib/x509/verify-high.h @@ -39,8 +39,12 @@ struct gnutls_x509_trust_list_st { * will be deinitialized */ gnutls_x509_crt_t *keep_certs; unsigned int keep_certs_size; - + char* pkcs11_token; + + /* set this callback if the issuer in the certificate + * chain is missing. */ + gnutls_x509_trust_list_getissuer_function *issuer_callback; }; int _gnutls_trustlist_inlist(gnutls_x509_trust_list_t list, diff --git a/lib/x509/verify.c b/lib/x509/verify.c index fd7c6a1642..4363e818b1 100644 --- a/lib/x509/verify.c +++ b/lib/x509/verify.c @@ -38,6 +38,7 @@ #include #include #include +#include #include "supported_exts.h" #include "profiles.h" @@ -604,11 +605,11 @@ static int _gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, gnutls_x509_crt_t issuer, unsigned vflags); -/* +/* * Verifies the given certificate against a certificate list of * trusted CAs. * - * Returns only 0 or 1. If 1 it means that the certificate + * Returns only 0 or 1. If 1 it means that the certificate * was successfully verified. * * 'flags': an OR of the gnutls_certificate_verify_flags enumeration. @@ -616,13 +617,13 @@ static int _gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, * Output will hold some extra information about the verification * procedure. */ -static unsigned -verify_crt(gnutls_x509_crt_t cert, - const gnutls_x509_crt_t * trusted_cas, - int tcas_size, unsigned int flags, - unsigned int *output, - verify_state_st *vparams, - unsigned end_cert) +static unsigned verify_crt(gnutls_x509_trust_list_t tlist, + gnutls_x509_crt_t cert, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, unsigned int flags, + unsigned int *output, + verify_state_st *vparams, + unsigned end_cert) { gnutls_datum_t cert_signed_data = { NULL, 0 }; gnutls_datum_t cert_signature = { NULL, 0 }; @@ -646,6 +647,25 @@ verify_crt(gnutls_x509_crt_t cert, if (tcas_size >= 1) issuer = find_issuer(cert, trusted_cas, tcas_size); + if (issuer == NULL && tlist != NULL && tlist->issuer_callback != NULL) { + _gnutls_debug_log("Missing issuer callback set.\n"); + + /* missing issuer is populated by the callback */ + ret = tlist->issuer_callback(tlist, cert); + if (ret < 0) { + /* if the callback fails, continue as though the callback + * wasn't invoked i.e issuer remains NULL */ + gnutls_assert(); + issuer = NULL; + } + + ret = _gnutls_trust_list_get_issuer(tlist, cert, &issuer, 0); + if (ret < 0) { + gnutls_assert(); + issuer = NULL; + } + } + ret = _gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate", &cert_signed_data); @@ -680,7 +700,7 @@ verify_crt(gnutls_x509_crt_t cert, } else { if (vparams->nc != NULL) { /* append the issuer's constraints */ - ret = gnutls_x509_crt_get_name_constraints(issuer, vparams->nc, + ret = gnutls_x509_crt_get_name_constraints(issuer, vparams->nc, GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL); if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { MARK_INVALID(GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE); @@ -909,13 +929,14 @@ unsigned check_ca_sanity(const gnutls_x509_crt_t issuer, * list should lead to a trusted certificate in order to be trusted. */ unsigned int -_gnutls_verify_crt_status(const gnutls_x509_crt_t * certificate_list, - int clist_size, - const gnutls_x509_crt_t * trusted_cas, - int tcas_size, - unsigned int flags, - const char *purpose, - gnutls_verify_output_function func) +_gnutls_verify_crt_status(gnutls_x509_trust_list_t tlist, + const gnutls_x509_crt_t * certificate_list, + int clist_size, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, + const char *purpose, + gnutls_verify_output_function func) { int i = 0, ret; unsigned int status = 0, output; @@ -1010,11 +1031,12 @@ _gnutls_verify_crt_status(const gnutls_x509_crt_t * certificate_list, */ output = 0; - ret = verify_crt(certificate_list[clist_size - 1], - trusted_cas, tcas_size, flags, - &output, - &vparams, - clist_size==1?1:0); + ret = verify_crt(tlist, + certificate_list[clist_size - 1], + trusted_cas, tcas_size, flags, + &output, + &vparams, + clist_size==1?1:0); if (ret != 1) { /* if the last certificate in the certificate * list is invalid, then the certificate is not @@ -1053,11 +1075,12 @@ _gnutls_verify_crt_status(const gnutls_x509_crt_t * certificate_list, } if ((ret = - verify_crt(certificate_list[i - 1], - &certificate_list[i], 1, - flags, &output, - &vparams, - i==1?1:0)) != 1) { + verify_crt(tlist, + certificate_list[i - 1], + &certificate_list[i], 1, + flags, &output, + &vparams, + i==1?1:0)) != 1) { gnutls_assert(); status |= output; status |= GNUTLS_CERT_INVALID; @@ -1147,12 +1170,13 @@ unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose, * list should lead to a trusted certificate in order to be trusted. */ unsigned int -_gnutls_pkcs11_verify_crt_status(const char* url, - const gnutls_x509_crt_t * certificate_list, - unsigned clist_size, - const char *purpose, - unsigned int flags, - gnutls_verify_output_function func) +_gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist, + const char* url, + const gnutls_x509_crt_t * certificate_list, + unsigned clist_size, + const char *purpose, + unsigned int flags, + gnutls_verify_output_function func) { int ret; unsigned int status = 0, i; @@ -1249,9 +1273,10 @@ _gnutls_pkcs11_verify_crt_status(const char* url, ret = gnutls_pkcs11_crt_is_known(url, certificate_list[clist_size - 1], GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED|GNUTLS_PKCS11_OBJ_FLAG_COMPARE); if (ret != 0) { - return _gnutls_verify_crt_status(certificate_list, clist_size, - &certificate_list[clist_size - 1], 1, flags, - purpose, func); + return _gnutls_verify_crt_status(tlist, + certificate_list, clist_size, + &certificate_list[clist_size - 1], + 1, flags, purpose, func); } } @@ -1260,7 +1285,7 @@ _gnutls_pkcs11_verify_crt_status(const char* url, /* verify the certificate list against 0 trusted CAs in order * to get, any additional flags from the certificate list (e.g., * insecure algorithms or expired */ - status |= _gnutls_verify_crt_status(certificate_list, clist_size, + status |= _gnutls_verify_crt_status(tlist, certificate_list, clist_size, NULL, 0, flags, purpose, func); goto cleanup; } @@ -1303,8 +1328,8 @@ _gnutls_pkcs11_verify_crt_status(const char* url, goto cleanup; } - status = _gnutls_verify_crt_status(certificate_list, clist_size, - &issuer, 1, flags, purpose, func); + status = _gnutls_verify_crt_status(tlist, certificate_list, clist_size, + &issuer, 1, flags, purpose, func); cleanup: gnutls_free(raw_issuer.data); @@ -1468,18 +1493,20 @@ gnutls_x509_crt_list_verify(const gnutls_x509_crt_t * cert_list, { unsigned i; int ret; + gnutls_x509_trust_list_t tlist; if (cert_list == NULL || cert_list_length == 0) return GNUTLS_E_NO_CERTIFICATE_FOUND; - /* Verify certificate + gnutls_x509_trust_list_init(&tlist, 0); + + /* Verify certificate */ - *verify = - _gnutls_verify_crt_status(cert_list, cert_list_length, + *verify = _gnutls_verify_crt_status(tlist, cert_list, cert_list_length, CA_list, CA_list_length, flags, NULL, NULL); - /* Check for revoked certificates in the chain. + /* Check for revoked certificates in the chain. */ for (i = 0; i < cert_list_length; i++) { ret = gnutls_x509_crt_check_revocation(cert_list[i], @@ -1491,6 +1518,7 @@ gnutls_x509_crt_list_verify(const gnutls_x509_crt_t * cert_list, } } + gnutls_x509_trust_list_deinit(tlist, 0); return 0; } @@ -1518,12 +1546,17 @@ gnutls_x509_crt_verify(gnutls_x509_crt_t cert, unsigned CA_list_length, unsigned int flags, unsigned int *verify) { - /* Verify certificate + gnutls_x509_trust_list_t tlist; + + gnutls_x509_trust_list_init(&tlist, 0); + + /* Verify certificate */ - *verify = - _gnutls_verify_crt_status(&cert, 1, + *verify = _gnutls_verify_crt_status(tlist, &cert, 1, CA_list, CA_list_length, flags, NULL, NULL); + + gnutls_x509_trust_list_deinit(tlist, 0); return 0; } @@ -1533,9 +1566,9 @@ gnutls_x509_crt_verify(gnutls_x509_crt_t cert, * @issuer: is the certificate of a possible issuer * * This function will check if the given CRL was issued by the given - * issuer certificate. + * issuer certificate. * - * Returns: true (1) if the given CRL was issued by the given issuer, + * Returns: true (1) if the given CRL was issued by the given issuer, * and false (0) if not. **/ unsigned diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 050e95059e..a41cc5827a 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -495,23 +495,30 @@ gnutls_x509_crt_verify_data3(gnutls_x509_crt_t crt, const gnutls_datum_t *signature, unsigned int flags); +int _gnutls_trust_list_get_issuer(gnutls_x509_trust_list_t list, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t * issuer, + unsigned int flags); + unsigned int -_gnutls_verify_crt_status(const gnutls_x509_crt_t * certificate_list, - int clist_size, - const gnutls_x509_crt_t * trusted_cas, - int tcas_size, - unsigned int flags, - const char *purpose, - gnutls_verify_output_function func); +_gnutls_verify_crt_status(gnutls_x509_trust_list_t tlist, + const gnutls_x509_crt_t * certificate_list, + int clist_size, + const gnutls_x509_crt_t * trusted_cas, + int tcas_size, + unsigned int flags, + const char *purpose, + gnutls_verify_output_function func); #ifdef ENABLE_PKCS11 unsigned int -_gnutls_pkcs11_verify_crt_status(const char* url, - const gnutls_x509_crt_t * certificate_list, - unsigned clist_size, - const char *purpose, - unsigned int flags, - gnutls_verify_output_function func); +_gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist, + const char* url, + const gnutls_x509_crt_t * certificate_list, + unsigned clist_size, + const char *purpose, + unsigned int flags, + gnutls_verify_output_function func); #endif int _gnutls_check_cert_sanity(gnutls_x509_crt_t cert); -- cgit v1.2.1