]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
During PKIX chain verification check the TLSFeatures compliance
authorNikos Mavrogiannopoulos <nmav@redhat.com>
Tue, 31 May 2016 14:07:44 +0000 (16:07 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Tue, 14 Jun 2016 12:42:33 +0000 (14:42 +0200)
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
lib/libgnutls.map
lib/x509.c
lib/x509/Makefile.am
lib/x509/crq.c
lib/x509/name_constraints.c
lib/x509/tls_features.c [new file with mode: 0644]
lib/x509/verify.c
lib/x509/x509.c
lib/x509/x509_ext.c
lib/x509/x509_write.c

index d9f56163ff71cb9429530a60323ecb8db47bb6df..35db44b79658a38444f98074de692491104f0d49 100644 (file)
@@ -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
 
@@ -1348,9 +1356,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,
index c396169101e8fa5e67456be0cddefc4e2367b5b8..d77ff55f1bae00dac1853b3adb2a581fc8e6f64a 100644 (file)
@@ -1098,6 +1098,7 @@ GNUTLS_3_4
        gnutls_x509_crq_set_tlsfeatures;
        gnutls_ext_get_name;
        gnutls_x509_crt_set_crq_extension_by_oid;
+       gnutls_x509_tlsfeatures_check_crt;
  local:
        *;
 };
index 2efd76be05f709d5c840fc8311aedcb2f9c19768..7412557b12400dd97ef1ca27b00cea8e2e46919f 100644 (file)
@@ -224,8 +224,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) {
@@ -234,8 +240,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. */
@@ -243,10 +248,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
 
index 4989896b1cd55f5f62b8bf2f20e0659b7fa2f619..f340a12e74c9f87b69a812a5548de1e298862a98 100644 (file)
@@ -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
index c70f3ce45c6b1f2e0bdc8dda640faec57d289a6f..88f1b7514dc440e131f777b6da1150323febe7b5 100644 (file)
@@ -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;
 }
index 55701922be20879cd651ec6a88880871680e2d44..6d2e1871559399a4cf285b7b6cfb1d2d25d823b1 100644 (file)
@@ -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 (file)
index 0000000..1b0e096
--- /dev/null
@@ -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 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;
+}
index 095a56fcb86dee8c36d6d87843554fc1800334ac..6cd8a87a90dd028aaa2ba9893df66b74ac08183a 100644 (file)
@@ -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;
 }
 
index 160d806a92422b74723ea82b3bd842eb210ec647..910665d085451f2c5d3a1f3efd60ac075b266404 100644 (file)
@@ -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:
index 46354c3848a96ae5e43c99874b69bba89d1ba6e7..3450c8b466bfcfa1babf0143c53eba9d93b22dd2 100644 (file)
@@ -3149,16 +3149,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);
@@ -3173,19 +3178,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;
@@ -3195,19 +3209,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;
@@ -3231,7 +3250,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();
        }
index e9c6259094b3a70f194eded7d67030ea448cac05..8f971071d8816a3ffdf73fa1d400e95426f71f58 100644 (file)
@@ -1907,44 +1907,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;
-}