diff options
author | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-05-31 16:07:44 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2016-05-31 16:11:49 +0200 |
commit | 5423a49bebea5c94474e3406232d9a65b2350b26 (patch) | |
tree | 17e87933a3674ab810feaa7ad1304e4349cad8b1 /lib/x509 | |
parent | 7f9957b5c3610086751453edb46c4766b89758a9 (diff) | |
download | gnutls-tlsfeat-chain.tar.gz |
During PKIX chain verification check the TLSFeatures compliancetlsfeat-chain
This verifies whether a chain complies with RFC7366 p.4.2.2 requirements.
That is whether the issuer's features are a superset of the certificate
under verification.
This enhances gnutls_x509_crt_get_tlsfeatures() to allow appending
of TLSFeatures, and introduces gnutls_x509_tlsfeatures_check_crt().
Diffstat (limited to 'lib/x509')
-rw-r--r-- | lib/x509/Makefile.am | 1 | ||||
-rw-r--r-- | lib/x509/crq.c | 30 | ||||
-rw-r--r-- | lib/x509/name_constraints.c | 4 | ||||
-rw-r--r-- | lib/x509/tls_features.c | 266 | ||||
-rw-r--r-- | lib/x509/verify.c | 29 | ||||
-rw-r--r-- | lib/x509/x509.c | 124 | ||||
-rw-r--r-- | lib/x509/x509_ext.c | 49 | ||||
-rw-r--r-- | lib/x509/x509_write.c | 41 |
8 files changed, 347 insertions, 197 deletions
diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index 4989896b1c..f340a12e74 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -68,6 +68,7 @@ libgnutls_x509_la_SOURCES = \ virt-san.c \ virt-san.h \ x509_ext_int.h \ + tls_features.c \ krb5.c krb5.h if ENABLE_OCSP diff --git a/lib/x509/crq.c b/lib/x509/crq.c index 73ff952f4a..2ecd2fdfee 100644 --- a/lib/x509/crq.c +++ b/lib/x509/crq.c @@ -2921,23 +2921,33 @@ gnutls_x509_crq_set_private_key_usage_period(gnutls_x509_crq_t crq, * @crt: A X.509 certificate request * @features: If the function succeeds, the * features will be stored in this variable. + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND + * @critical: the extension status * * This function will get the X.509 TLS features * extension structure from the certificate request. * The returned structure needs to be freed using * gnutls_x509_tlsfeatures_deinit(). * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @features structure is empty this function will behave + * identically as if the flag was not set. Otherwise if there are elements + * in the @features structure then they will be merged with. + * + * Note that @features must be initialized prior to calling this function. + * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, * otherwise a negative error value. * * Since: 3.5.1 **/ int gnutls_x509_crq_get_tlsfeatures(gnutls_x509_crq_t crq, - gnutls_x509_tlsfeatures_t *features) + gnutls_x509_tlsfeatures_t features, + unsigned int flags, + unsigned int *critical) { int ret; gnutls_datum_t der; - unsigned int critical; if (crq == NULL) { gnutls_assert(); @@ -2946,7 +2956,7 @@ int gnutls_x509_crq_get_tlsfeatures(gnutls_x509_crq_t crq, if ((ret = gnutls_x509_crq_get_extension_by_oid2(crq, GNUTLS_X509EXT_OID_TLSFEATURES, 0, - &der, &critical)) < 0) + &der, critical)) < 0) { return ret; } @@ -2956,24 +2966,14 @@ int gnutls_x509_crq_get_tlsfeatures(gnutls_x509_crq_t crq, return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } - ret = gnutls_x509_tlsfeatures_init(features); + ret = gnutls_x509_ext_import_tlsfeatures(&der, features, flags); if (ret < 0) { gnutls_assert(); goto cleanup; } - ret = gnutls_x509_ext_import_tlsfeatures(&der, *features, 0); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } - - gnutls_free(der.data); - return ret; - + ret = 0; cleanup: - if (features != NULL) - gnutls_x509_tlsfeatures_deinit(*features); gnutls_free(der.data); return ret; } diff --git a/lib/x509/name_constraints.c b/lib/x509/name_constraints.c index 55701922be..6d2e187155 100644 --- a/lib/x509/name_constraints.c +++ b/lib/x509/name_constraints.c @@ -258,7 +258,7 @@ int _gnutls_name_constraints_append(name_constraints_node_st ** _nc, * gnutls_x509_crt_get_name_constraints: * @crt: should contain a #gnutls_x509_crt_t type * @nc: The nameconstraints intermediate type - * @flags: zero or %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND * @critical: the extension status * * This function will return an intermediate type containing @@ -266,7 +266,7 @@ int _gnutls_name_constraints_append(name_constraints_node_st ** _nc, * structure can be used in combination with gnutls_x509_name_constraints_check() * to verify whether a server's name is in accordance with the constraints. * - * When the @flags is set to %GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, * then if the @nc structure is empty this function will behave * identically as if the flag was not set. * Otherwise if there are elements in the @nc structure then the diff --git a/lib/x509/tls_features.c b/lib/x509/tls_features.c new file mode 100644 index 0000000000..35fe9cdefe --- /dev/null +++ b/lib/x509/tls_features.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2003-2016 Free Software Foundation, Inc. + * + * Authors: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu + * + * 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 <datum.h> +#include <global.h> +#include "errors.h" +#include <common.h> +#include <gnutls/x509-ext.h> +#include <x509.h> +#include <x509_b64.h> +#include <x509_int.h> +#include <libtasn1.h> +#include <pk.h> +#include <pkcs11_int.h> +#include "urls.h" + +/** + * gnutls_x509_tlsfeatures_init: + * @f: The TLS features + * + * This function will initialize a X.509 TLS features extention structure + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_tlsfeatures_init(gnutls_x509_tlsfeatures_t *f) +{ + *f = gnutls_calloc(1, sizeof(struct gnutls_x509_tlsfeatures_st)); + if (*f == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +/** + * gnutls_x509_tlsfeatures_deinit: + * @f: The TLS features + * + * This function will deinitialize a X.509 TLS features extention structure + * + * Since: 3.5.1 + **/ +void gnutls_x509_tlsfeatures_deinit(gnutls_x509_tlsfeatures_t f) +{ + gnutls_free(f->features); + gnutls_free(f); +} + +/** + * gnutls_x509_tlsfeatures_get: + * @f: The TLS features + * @idx: The index of the feature to get + * @feature: If the function succeeds, the feature will be stored in this variable + * + * This function will get a feature from the X.509 TLS features + * extention structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_tlsfeatures_get(gnutls_x509_tlsfeatures_t f, unsigned idx, unsigned int *feature) +{ + if (f == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (idx >= f->size) { + return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); + } + + *feature = f->features[idx].feature; + return 0; +} + +/** + * gnutls_x509_crt_get_tlsfeatures: + * @crt: A X.509 certificate + * @features: If the function succeeds, the + * features will be stored in this variable. + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND + * @critical: the extension status + * + * This function will get the X.509 TLS features + * extension structure from the certificate. The + * returned structure needs to be freed using + * gnutls_x509_tlsfeatures_deinit(). + * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @features structure is empty this function will behave + * identically as if the flag was not set. Otherwise if there are elements + * in the @features structure then they will be merged with. + * + * Note that @features must be initialized prior to calling this function. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crt_get_tlsfeatures(gnutls_x509_crt_t crt, + gnutls_x509_tlsfeatures_t features, + unsigned int flags, + unsigned int *critical) +{ + int ret; + gnutls_datum_t der; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if ((ret = + _gnutls_x509_crt_get_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, 0, + &der, critical)) < 0) + { + return ret; + } + + if (der.size == 0 || der.data == NULL) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + ret = gnutls_x509_ext_import_tlsfeatures(&der, features, flags); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = 0; + cleanup: + gnutls_free(der.data); + return ret; +} + +/** + * gnutls_x509_crt_set_tlsfeatures: + * @crt: A X.509 certificate + * @features: If the function succeeds, the + * features will be added to the certificate. + * + * This function will set the certificates + * X.509 TLS extention from the given structure. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, + * otherwise a negative error value. + * + * Since: 3.5.1 + **/ +int gnutls_x509_crt_set_tlsfeatures(gnutls_x509_crt_t crt, + gnutls_x509_tlsfeatures_t features) +{ + int ret; + gnutls_datum_t der; + + if (crt == NULL || features == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_x509_ext_export_tlsfeatures(features, &der); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_x509_crt_set_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, &der, 0); + + _gnutls_free_datum(&der); + + if (ret < 0) { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_x509_tls_features_check_crt: + * @feat: a set of TLSFeatures + * @cert: the certificate to be checked + * + * This function will check the provided certificate names against the TLSFeatures + * set in @feat using the RFC7633 p.4.2.2 rules. It will check whether the certificate + * complies to the features in @feat. + * + * Returns: non-zero if the provided certificate complies, and zero otherwise. + * + * Since: 3.5.1 + **/ +unsigned gnutls_x509_tlsfeatures_check_crt(gnutls_x509_tlsfeatures_t feat, + gnutls_x509_crt_t cert) +{ + int ret; + gnutls_x509_tlsfeatures_t cfeat; + unsigned i, j, uret, found; + + if (feat->size == 0) + return 1; /* shortcut; no constraints to check */ + + ret = gnutls_x509_tlsfeatures_init(&cfeat); + if (ret < 0) + return gnutls_assert_val(0); + + ret = gnutls_x509_crt_get_tlsfeatures(cert, cfeat, 0, NULL); + if (ret < 0) { + gnutls_assert(); + uret = 0; + goto cleanup; + } + + /* if cert's features cannot be a superset */ + if (feat->size > cfeat->size) { + _gnutls_debug_log("certificate has %d, while issuer has %d tlsfeatures\n", cfeat->size, feat->size); + gnutls_assert(); + uret = 0; + goto cleanup; + } + + for (i=0;i<feat->size;i++) { + found = 0; + for (j=0;j<cfeat->size;j++) { + if (feat->features[i].feature == cfeat->features[j].feature) { + found = 1; + break; + } + } + + if (found == 0) { + _gnutls_debug_log("feature %d was not found in cert\n", (int)feat->features[i].feature); + uret = 0; + goto cleanup; + } + } + + uret = 1; + cleanup: + gnutls_x509_tlsfeatures_deinit(cfeat); + return uret; +} diff --git a/lib/x509/verify.c b/lib/x509/verify.c index 3b001e5db9..028072c53d 100644 --- a/lib/x509/verify.c +++ b/lib/x509/verify.c @@ -515,6 +515,7 @@ typedef struct verify_state_st { time_t now; unsigned int max_path; gnutls_x509_name_constraints_t nc; + gnutls_x509_tlsfeatures_t tls_feat; gnutls_verify_output_function *func; } verify_state_st; @@ -646,6 +647,26 @@ verify_crt(gnutls_x509_crt_t cert, } } } + + if (vparams->tls_feat != NULL) { + /* append the issuer's constraints */ + ret = gnutls_x509_crt_get_tlsfeatures(issuer, vparams->tls_feat, GNUTLS_EXT_FLAG_APPEND, NULL); + if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { + feat_fail: + out |= + GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE | + GNUTLS_CERT_INVALID; + gnutls_assert(); + result = 0; + goto nc_done; + } + + ret = gnutls_x509_tlsfeatures_check_crt(vparams->tls_feat, cert); + if (ret == 0) { + gnutls_assert(); + goto feat_fail; + } + } nc_done: issuer_version = gnutls_x509_crt_get_version(issuer); @@ -895,6 +916,13 @@ _gnutls_verify_crt_status(const gnutls_x509_crt_t * certificate_list, return status; } + ret = gnutls_x509_tlsfeatures_init(&vparams.tls_feat); + if (ret < 0) { + gnutls_assert(); + status |= GNUTLS_CERT_INVALID; + goto cleanup; + } + /* Verify the last certificate in the certificate path * against the trusted CA certificate list. * @@ -962,6 +990,7 @@ _gnutls_verify_crt_status(const gnutls_x509_crt_t * certificate_list, cleanup: gnutls_x509_name_constraints_deinit(vparams.nc); + gnutls_x509_tlsfeatures_deinit(vparams.tls_feat); return status; } diff --git a/lib/x509/x509.c b/lib/x509/x509.c index 160d806a92..910665d085 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -2052,130 +2052,6 @@ gnutls_x509_crt_get_proxy(gnutls_x509_crt_t cert, return 0; } -/** - * gnutls_x509_tlsfeatures_init: - * @f: The TLS features - * - * This function will initialize a X.509 TLS features extention structure - * - * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, - * otherwise a negative error value. - * - * Since: 3.5.1 - **/ -int gnutls_x509_tlsfeatures_init(gnutls_x509_tlsfeatures_t *f) -{ - *f = gnutls_calloc(1, sizeof(struct gnutls_x509_tlsfeatures_st)); - if (*f == NULL) - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - - return 0; -} - -/** - * gnutls_x509_tlsfeatures_deinit: - * @f: The TLS features - * - * This function will deinitialize a X.509 TLS features extention structure - * - * Since: 3.5.1 - **/ -void gnutls_x509_tlsfeatures_deinit(gnutls_x509_tlsfeatures_t f) -{ - gnutls_free(f->features); - gnutls_free(f); -} - -/** - * gnutls_x509_tlsfeatures_get: - * @f: The TLS features - * @idx: The index of the feature to get - * @feature: If the function succeeds, the feature will be stored in this variable - * - * This function will get a feature from the X.509 TLS features - * extention structure. - * - * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, - * otherwise a negative error value. - * - * Since: 3.5.1 - **/ -int gnutls_x509_tlsfeatures_get(gnutls_x509_tlsfeatures_t f, unsigned idx, unsigned int *feature) -{ - if (f == NULL) { - gnutls_assert(); - return GNUTLS_E_INVALID_REQUEST; - } - - if (idx >= f->size) { - return gnutls_assert_val(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE); - } - - *feature = f->features[idx].feature; - return 0; -} - -/** - * gnutls_x509_crt_get_tlsfeatures: - * @crt: A X.509 certificate - * @features: If the function succeeds, the - * features will be stored in this variable. - * - * This function will get the X.509 TLS features - * extension structure from the certificate. The - * returned structure needs to be freed using - * gnutls_x509_tlsfeatures_deinit(). - * - * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, - * otherwise a negative error value. - * - * Since: 3.5.1 - **/ -int gnutls_x509_crt_get_tlsfeatures(gnutls_x509_crt_t crt, - gnutls_x509_tlsfeatures_t *features) -{ - int ret; - gnutls_datum_t der; - unsigned int critical; - - if (crt == NULL) { - gnutls_assert(); - return GNUTLS_E_INVALID_REQUEST; - } - - if ((ret = - _gnutls_x509_crt_get_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, 0, - &der, &critical)) < 0) - { - return ret; - } - - if (der.size == 0 || der.data == NULL) { - gnutls_assert(); - return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; - } - - ret = gnutls_x509_tlsfeatures_init(features); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } - - ret = gnutls_x509_ext_import_tlsfeatures(&der, *features, 0); - if (ret < 0) { - gnutls_assert(); - goto cleanup; - } - - gnutls_free(der.data); - return ret; - - cleanup: - if (features != NULL) - gnutls_x509_tlsfeatures_deinit(*features); - gnutls_free(der.data); - return ret; -} /** * gnutls_x509_policy_release: diff --git a/lib/x509/x509_ext.c b/lib/x509/x509_ext.c index 4c4ca9ef92..1f84e626e2 100644 --- a/lib/x509/x509_ext.c +++ b/lib/x509/x509_ext.c @@ -3151,16 +3151,21 @@ int _gnutls_x509_decode_ext(const gnutls_datum_t *der, gnutls_x509_ext_st *out) } - -static int parse_tlsfeatures(ASN1_TYPE c2, gnutls_x509_tlsfeatures_t f) +/* flags can be zero or GNUTLS_EXT_FLAG_APPEND + */ +static int parse_tlsfeatures(ASN1_TYPE c2, gnutls_x509_tlsfeatures_t f, unsigned flags) { char nptr[ASN1_MAX_NAME_SIZE]; int result; void * tmp; - unsigned i, indx; + unsigned i, indx, j; unsigned int feature; + if (!(flags & GNUTLS_EXT_FLAG_APPEND)) + f->size = 0; + for (i = 1;; i++) { + unsigned skip = 0; snprintf(nptr, sizeof(nptr), "?%u", i); result = _gnutls_x509_read_uint(c2, nptr, &feature); @@ -3175,19 +3180,28 @@ static int parse_tlsfeatures(ASN1_TYPE c2, gnutls_x509_tlsfeatures_t f) if (feature > UINT16_MAX) { gnutls_assert(); - return GNUTLS_E_INTERNAL_ERROR; + return GNUTLS_E_CERTIFICATE_ERROR; } - indx = f->size; - tmp = gnutls_realloc(f->features, (f->size + 1) * sizeof(f->features[0])); - if (tmp == NULL) { - return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + /* skip duplicates */ + for (j=0;j<f->size;j++) { + if (f->features[j].feature == feature) { + skip = 1; + break; + } } - f->features = tmp; - f->features[indx].feature = feature; + if (!skip) { + indx = f->size; + tmp = gnutls_realloc(f->features, (f->size + 1) * sizeof(f->features[0])); + if (tmp == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + f->features = tmp; - f->size++; + f->features[indx].feature = feature; + f->size++; + } } return 0; @@ -3197,19 +3211,24 @@ static int parse_tlsfeatures(ASN1_TYPE c2, gnutls_x509_tlsfeatures_t f) * gnutls_x509_ext_import_tlsfeatures: * @ext: The DER-encoded extension data * @f: The features structure - * @flags: should be zero + * @flags: zero or %GNUTLS_EXT_FLAG_APPEND * * This function will export the features in the provided DER-encoded * TLS Features PKIX extension, to a %gnutls_x509_tlsfeatures_t type. @f * must be initialized. * + * When the @flags is set to %GNUTLS_EXT_FLAG_APPEND, + * then if the @features structure is empty this function will behave + * identically as if the flag was not set. Otherwise if there are elements + * in the @features structure then they will be merged with. + * * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a negative error value. * * Since: 3.5.1 **/ int gnutls_x509_ext_import_tlsfeatures(const gnutls_datum_t * ext, - gnutls_x509_tlsfeatures_t f, - unsigned int flags) + gnutls_x509_tlsfeatures_t f, + unsigned int flags) { int ret; ASN1_TYPE c2 = ASN1_TYPE_EMPTY; @@ -3233,7 +3252,7 @@ int gnutls_x509_ext_import_tlsfeatures(const gnutls_datum_t * ext, goto cleanup; } - ret = parse_tlsfeatures(c2, f); + ret = parse_tlsfeatures(c2, f, flags); if (ret < 0) { gnutls_assert(); } diff --git a/lib/x509/x509_write.c b/lib/x509/x509_write.c index 15815700f3..a1561631ea 100644 --- a/lib/x509/x509_write.c +++ b/lib/x509/x509_write.c @@ -1881,44 +1881,3 @@ gnutls_x509_crt_set_policy(gnutls_x509_crt_t crt, return ret; } -/** - * gnutls_x509_crt_set_tlsfeatures: - * @crt: A X.509 certificate - * @features: If the function succeeds, the - * features will be added to the certificate. - * - * This function will set the certificates - * X.509 TLS extention from the given structure. - * - * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, - * otherwise a negative error value. - * - * Since: 3.5.1 - **/ -int gnutls_x509_crt_set_tlsfeatures(gnutls_x509_crt_t crt, - gnutls_x509_tlsfeatures_t features) -{ - int ret; - gnutls_datum_t der; - - if (crt == NULL || features == NULL) { - gnutls_assert(); - return GNUTLS_E_INVALID_REQUEST; - } - - ret = gnutls_x509_ext_export_tlsfeatures(features, &der); - if (ret < 0) { - gnutls_assert(); - return ret; - } - - ret = _gnutls_x509_crt_set_extension(crt, GNUTLS_X509EXT_OID_TLSFEATURES, &der, 0); - - _gnutls_free_datum(&der); - - if (ret < 0) { - gnutls_assert(); - } - - return ret; -} |