From 5423a49bebea5c94474e3406232d9a65b2350b26 Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Tue, 31 May 2016 16:07:44 +0200 Subject: During PKIX chain verification check the TLSFeatures compliance 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(). --- lib/includes/gnutls/x509.h | 18 ++- lib/libgnutls.map | 1 + lib/x509.c | 17 ++- lib/x509/Makefile.am | 1 + lib/x509/crq.c | 30 ++--- lib/x509/name_constraints.c | 4 +- lib/x509/tls_features.c | 266 ++++++++++++++++++++++++++++++++++++++++++++ lib/x509/verify.c | 29 +++++ lib/x509/x509.c | 124 --------------------- lib/x509/x509_ext.c | 49 +++++--- lib/x509/x509_write.c | 41 ------- 11 files changed, 374 insertions(+), 206 deletions(-) create mode 100644 lib/x509/tls_features.c diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 5217942abd..41bb1da5e4 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -299,7 +299,9 @@ unsigned gnutls_x509_name_constraints_check_crt(gnutls_x509_name_constraints_t n int gnutls_x509_name_constraints_init(gnutls_x509_name_constraints_t *nc); void gnutls_x509_name_constraints_deinit(gnutls_x509_name_constraints_t nc); -#define GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND 1 +#define GNUTLS_EXT_FLAG_APPEND 1 + +#define GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND GNUTLS_EXT_FLAG_APPEND int gnutls_x509_crt_get_name_constraints(gnutls_x509_crt_t crt, gnutls_x509_name_constraints_t nc, unsigned int flags, @@ -466,7 +468,13 @@ int gnutls_x509_crt_set_tlsfeatures(gnutls_x509_crt_t crt, gnutls_x509_tlsfeatures_t features); int gnutls_x509_crt_get_tlsfeatures(gnutls_x509_crt_t cert, - gnutls_x509_tlsfeatures_t * features); + gnutls_x509_tlsfeatures_t features, + unsigned int flags, + unsigned int *critical); + +unsigned gnutls_x509_tlsfeatures_check_crt(gnutls_x509_tlsfeatures_t feat, + gnutls_x509_crt_t crt); + #define GNUTLS_MAX_QUALIFIERS 8 @@ -1343,9 +1351,11 @@ int gnutls_x509_crq_get_extension_by_oid(gnutls_x509_crq_t crq, unsigned int *critical); int gnutls_x509_crq_get_tlsfeatures(gnutls_x509_crq_t crq, - gnutls_x509_tlsfeatures_t * features); + gnutls_x509_tlsfeatures_t features, + unsigned flags, + unsigned int *critical); int gnutls_x509_crq_set_tlsfeatures(gnutls_x509_crq_t crq, - gnutls_x509_tlsfeatures_t features); + gnutls_x509_tlsfeatures_t features); int gnutls_x509_crt_get_extension_by_oid2(gnutls_x509_crt_t cert, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 1416504936..7ded32dabd 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1097,6 +1097,7 @@ GNUTLS_3_4 gnutls_x509_crq_get_tlsfeatures; gnutls_x509_crq_set_tlsfeatures; gnutls_ext_get_name; + gnutls_x509_tlsfeatures_check_crt; local: *; }; diff --git a/lib/x509.c b/lib/x509.c index f407f74478..02117f41a5 100644 --- a/lib/x509.c +++ b/lib/x509.c @@ -195,8 +195,14 @@ _gnutls_ocsp_verify_mandatory_stapling(gnutls_session_t session, return 0; } + ret = gnutls_x509_tlsfeatures_init(&tlsfeatures); + if (ret < 0) { + gnutls_assert(); + return ret; + } + /* We have requested the status, now check whether the certificate mandates a response */ - if (gnutls_x509_crt_get_tlsfeatures(cert, &tlsfeatures) == 0) { + if (gnutls_x509_crt_get_tlsfeatures(cert, tlsfeatures, 0, NULL) == 0) { for (i = 0;; ++i) { ret = gnutls_x509_tlsfeatures_get(tlsfeatures, i, &feature); if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { @@ -205,8 +211,7 @@ _gnutls_ocsp_verify_mandatory_stapling(gnutls_session_t session, if (ret < 0) { gnutls_assert(); - gnutls_x509_tlsfeatures_deinit(tlsfeatures); - return ret; + goto cleanup; } if (feature == GNUTLS_EXTENSION_STATUS_REQUEST) { /* We sent a status request, the certificate mandates a reply, but we did not get any. */ @@ -214,10 +219,12 @@ _gnutls_ocsp_verify_mandatory_stapling(gnutls_session_t session, break; } } - gnutls_x509_tlsfeatures_deinit(tlsfeatures); } - return 0; + ret = 0; + cleanup: + gnutls_x509_tlsfeatures_deinit(tlsfeatures); + return ret; } #endif 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 + * + */ + +#include "gnutls_int.h" +#include +#include +#include "errors.h" +#include +#include +#include +#include +#include +#include +#include +#include +#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;isize;i++) { + found = 0; + for (j=0;jsize;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;jsize;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; -} -- cgit v1.2.1