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().
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,
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
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,
gnutls_x509_crq_set_tlsfeatures;
gnutls_ext_get_name;
gnutls_x509_crt_set_crq_extension_by_oid;
+ gnutls_x509_tlsfeatures_check_crt;
local:
*;
};
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) {
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. */
break;
}
}
- gnutls_x509_tlsfeatures_deinit(tlsfeatures);
}
- return 0;
+ ret = 0;
+ cleanup:
+ gnutls_x509_tlsfeatures_deinit(tlsfeatures);
+ return ret;
}
#endif
virt-san.c \
virt-san.h \
x509_ext_int.h \
+ tls_features.c \
krb5.c krb5.h
if ENABLE_OCSP
* @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();
if ((ret =
gnutls_x509_crq_get_extension_by_oid2(crq, GNUTLS_X509EXT_OID_TLSFEATURES, 0,
- &der, &critical)) < 0)
+ &der, critical)) < 0)
{
return ret;
}
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;
}
* 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
* 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
--- /dev/null
+/*
+ * 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 against the TLSFeatures
+ * set in @feat using the RFC7633 p.4.2.2 rules. It will check whether the certificate
+ * contains the features in @feat or a superset.
+ *
+ * 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;
+}
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;
}
}
}
+
+ 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);
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.
*
cleanup:
gnutls_x509_name_constraints_deinit(vparams.nc);
+ gnutls_x509_tlsfeatures_deinit(vparams.tls_feat);
return status;
}
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:
}
-
-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);
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;
* 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;
goto cleanup;
}
- ret = parse_tlsfeatures(c2, f);
+ ret = parse_tlsfeatures(c2, f, flags);
if (ret < 0) {
gnutls_assert();
}
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;
-}