]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
pcert: added functionality to retrieve lists
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 29 Apr 2018 13:16:35 +0000 (15:16 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 12 May 2018 19:39:52 +0000 (21:39 +0200)
That introduces gnutls_pcert_list_import_x509_file() and
gnutls_x509_crt_list_import_url().

Resolves #373

Signed-off-by: Nikos Mavrogiannopoulos <nmav@gnutls.org>
12 files changed:
NEWS
lib/includes/gnutls/abstract.h
lib/includes/gnutls/x509.h
lib/libgnutls.map
lib/pcert.c
lib/pkcs11.c
lib/pkcs11_write.c
lib/urls.c
lib/x509/x509.c
tests/Makefile.am
tests/pkcs11/gnutls_pcert_list_import_x509_file.c [new file with mode: 0644]
tests/pkcs11/gnutls_x509_crt_list_import_url.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index cbdc91af5de18ee3d4fb8dce06c12b95caf4e2d4..f7b397c57a64f2aa49634ff26edb068e2e51e8ea 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -64,6 +64,8 @@ gnutls_certificate_set_ocsp_status_request_mem: Added
 gnutls_certificate_get_ocsp_expiration: Added
 gnutls_record_send2: Added
 gnutls_ext_raw_parse: Added
+gnutls_pcert_list_import_x509_file: Added
+gnutls_x509_crt_list_import_url: Added
 GNUTLS_PRIVKEY_INFO_PK_ALGO_BITS: Added
 GNUTLS_PKCS11_OBJ_FLAG_MARK_NOT_SENSITIVE: Added
 
index e15bd3a0f52f80d0d8e8d702abca0af343e83210..d3cd91b93c3c19aeb2bf134c01cd2a7dbed9356f 100644 (file)
@@ -620,6 +620,14 @@ gnutls_pcert_list_import_x509_raw(gnutls_pcert_st * pcerts,
                                  gnutls_x509_crt_fmt_t format,
                                  unsigned int flags);
 
+int gnutls_pcert_list_import_x509_file(gnutls_pcert_st *pcert_list,
+                                      unsigned *pcert_list_size,
+                                      const char *file,
+                                      gnutls_x509_crt_fmt_t format,
+                                      gnutls_pin_callback_t pin_fn,
+                                      void *pin_fn_userdata,
+                                      unsigned int flags);
+
 int gnutls_pcert_import_x509_raw(gnutls_pcert_st * pcert,
                                 const gnutls_datum_t * cert,
                                 gnutls_x509_crt_fmt_t format,
index 1246a30eb2cebd56829b6a018264ad40c78b2db2..fef901a10182a27016e7dd318a54d47ce301fa8e 100644 (file)
@@ -169,6 +169,14 @@ int gnutls_x509_crt_import_url(gnutls_x509_crt_t crt,
                                      /* GNUTLS_PKCS11_OBJ_FLAG_* */
     );
 
+int
+gnutls_x509_crt_list_import_url(gnutls_x509_crt_t **certs,
+                               unsigned int *size,
+                               const char *url,
+                               gnutls_pin_callback_t pin_fn,
+                               void *pin_fn_userdata,
+                               unsigned int flags);
+
 int gnutls_x509_crt_export(gnutls_x509_crt_t cert,
                           gnutls_x509_crt_fmt_t format,
                           void *output_data, size_t * output_data_size);
index 45aa9b94312b88c027ed8f46de668543e30494b2..17cd8ff2b3e62d905ab02dcc77f67f373fe66407 100644 (file)
@@ -1215,6 +1215,8 @@ GNUTLS_3_6_3
        gnutls_certificate_get_ocsp_expiration;
        gnutls_record_send2;
        gnutls_ext_raw_parse;
+       gnutls_x509_crt_list_import_url;
+       gnutls_pcert_list_import_x509_file;
 } GNUTLS_3_6_2;
 
 GNUTLS_FIPS140_3_4 {
index e88ddc3fbabaf3eeba8d02e0c3c4326c41c5a4b9..34764050222bf040d100a1675b22c0ffc9d2fc54 100644 (file)
@@ -26,6 +26,7 @@
 #include <x509/common.h>
 #include <x509.h>
 #include "x509/x509_int.h"
+#include <gnutls/x509.h>
 
 /**
  * gnutls_pcert_import_x509:
@@ -84,14 +85,15 @@ int gnutls_pcert_import_x509(gnutls_pcert_st * pcert,
 
 /**
  * gnutls_pcert_import_x509_list:
- * @pcert: The pcert structure
+ * @pcert_list: The structures to store the certificates; must not contain initialized #gnutls_pcert_st structures.
  * @crt: The certificates to be imported
- * @ncrt: The number of certificates
+ * @ncrt: The number of certificates in @crt; will be updated if necessary
  * @flags: zero or %GNUTLS_X509_CRT_LIST_SORT
  *
- * This convenience function will import the given certificate to a
- * #gnutls_pcert_st structure. The structure must be deinitialized
- * afterwards using gnutls_pcert_deinit();
+ * This convenience function will import the given certificates to an
+ * already allocated set of #gnutls_pcert_st structures. The structures must
+ * be deinitialized afterwards using gnutls_pcert_deinit(). @pcert_list
+ * should contain space for at least @ncrt elements.
  *
  * In the case %GNUTLS_X509_CRT_LIST_SORT is specified and that
  * function cannot sort the list, %GNUTLS_E_CERTIFICATE_LIST_UNSORTED
@@ -103,7 +105,7 @@ int gnutls_pcert_import_x509(gnutls_pcert_st * pcert,
  *
  * Since: 3.4.0
  **/
-int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert,
+int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert_list,
                                  gnutls_x509_crt_t *crt, unsigned *ncrt,
                                  unsigned int flags)
 {
@@ -132,7 +134,7 @@ int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert,
        }
 
        for (i=0;i<*ncrt;i++) {
-               ret = gnutls_pcert_import_x509(&pcert[i], s[i], 0);
+               ret = gnutls_pcert_import_x509(&pcert_list[i], s[i], 0);
                if (ret < 0) {
                        current = i;
                        goto cleanup;
@@ -143,7 +145,7 @@ int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert,
 
  cleanup:
        for (i=0;i<current;i++) {
-               gnutls_pcert_deinit(&pcert[i]);
+               gnutls_pcert_deinit(&pcert_list[i]);
        }
        return ret;
 
@@ -151,27 +153,30 @@ int gnutls_pcert_import_x509_list(gnutls_pcert_st * pcert,
 
 /**
  * gnutls_pcert_list_import_x509_raw:
- * @pcerts: The structures to store the parsed certificate. Must not be initialized.
- * @pcert_max: Initially must hold the maximum number of certs. It will be updated with the number of certs available.
+ * @pcert_list: The structures to store the certificates; must not contain initialized #gnutls_pcert_st structures.
+ * @pcert_list_size: Initially must hold the maximum number of certs. It will be updated with the number of certs available.
  * @data: The certificates.
  * @format: One of DER or PEM.
  * @flags: must be (0) or an OR'd sequence of gnutls_certificate_import_flags.
  *
- * This function will convert the given PEM encoded certificate list
- * to the native gnutls_x509_crt_t format. The output will be stored
- * in @certs.  They will be automatically initialized.
+ * This function will import the provided DER or PEM encoded certificates to an
+ * already allocated set of #gnutls_pcert_st structures. The structures must
+ * be deinitialized afterwards using gnutls_pcert_deinit(). @pcert_list
+ * should contain space for at least @pcert_list_size elements.
  *
  * If the Certificate is PEM encoded it should have a header of "X509
  * CERTIFICATE", or "CERTIFICATE".
  *
- * Returns: the number of certificates read or a negative error value.
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value; if the @pcert list doesn't have enough space
+ *   %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned.
  *
  * Since: 3.0
  **/
 int
-gnutls_pcert_list_import_x509_raw(gnutls_pcert_st * pcerts,
-                                 unsigned int *pcert_max,
-                                 const gnutls_datum_t * data,
+gnutls_pcert_list_import_x509_raw(gnutls_pcert_st *pcert_list,
+                                 unsigned int *pcert_list_size,
+                                 const gnutls_datum_t *data,
                                  gnutls_x509_crt_fmt_t format,
                                  unsigned int flags)
 {
@@ -179,21 +184,21 @@ gnutls_pcert_list_import_x509_raw(gnutls_pcert_st * pcerts,
        unsigned int i = 0, j;
        gnutls_x509_crt_t *crt;
 
-       crt = gnutls_malloc((*pcert_max) * sizeof(gnutls_x509_crt_t));
+       crt = gnutls_malloc((*pcert_list_size) * sizeof(gnutls_x509_crt_t));
 
        if (crt == NULL)
                return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
 
        ret =
-           gnutls_x509_crt_list_import(crt, pcert_max, data, format,
+           gnutls_x509_crt_list_import(crt, pcert_list_size, data, format,
                                        flags);
        if (ret < 0) {
                ret = gnutls_assert_val(ret);
                goto cleanup_crt;
        }
 
-       for (i = 0; i < *pcert_max; i++) {
-               ret = gnutls_pcert_import_x509(&pcerts[i], crt[i], flags);
+       for (i = 0; i < *pcert_list_size; i++) {
+               ret = gnutls_pcert_import_x509(&pcert_list[i], crt[i], flags);
                if (ret < 0) {
                        ret = gnutls_assert_val(ret);
                        goto cleanup_pcert;
@@ -205,10 +210,10 @@ gnutls_pcert_list_import_x509_raw(gnutls_pcert_st * pcerts,
 
  cleanup_pcert:
        for (j = 0; j < i; j++)
-               gnutls_pcert_deinit(&pcerts[j]);
+               gnutls_pcert_deinit(&pcert_list[j]);
 
  cleanup:
-       for (i = 0; i < *pcert_max; i++)
+       for (i = 0; i < *pcert_list_size; i++)
                gnutls_x509_crt_deinit(crt[i]);
  
  cleanup_crt:
@@ -217,6 +222,89 @@ gnutls_pcert_list_import_x509_raw(gnutls_pcert_st * pcerts,
 
 }
 
+/**
+ * gnutls_pcert_list_import_x509_url:
+ * @pcert_list: The structures to store the certificates; must not contain initialized #gnutls_pcert_st structures.
+ * @pcert_list_size: Initially must hold the maximum number of certs. It will be updated with the number of certs available.
+ * @file: A file or supported URI with the certificates to load
+ * @format: %GNUTLS_X509_FMT_DER or %GNUTLS_X509_FMT_PEM if a file is given
+ * @pin_fn: a PIN callback if not globally set
+ * @pin_fn_userdata: parameter for the PIN callback
+ * @flags: zero or flags from %gnutls_certificate_import_flags
+ *
+ * This convenience function will import a certificate chain from the given
+ * file or supported URI to #gnutls_pcert_st structures. The structures
+ * must be deinitialized afterwards using gnutls_pcert_deinit().
+ *
+ * This function will always return a sorted certificate chain.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value; if the @pcert list doesn't have enough space
+ *   %GNUTLS_E_SHORT_MEMORY_BUFFER will be returned.
+ *
+ * Since: 3.6.3
+ **/
+int gnutls_pcert_list_import_x509_file(gnutls_pcert_st *pcert_list,
+                                      unsigned *pcert_list_size,
+                                      const char *file,
+                                      gnutls_x509_crt_fmt_t format,
+                                      gnutls_pin_callback_t pin_fn,
+                                      void *pin_fn_userdata,
+                                      unsigned int flags)
+{
+       int ret, ret2;
+       unsigned i;
+       gnutls_x509_crt_t *crts = NULL;
+       unsigned crts_size = 0;
+       gnutls_datum_t data = {NULL, 0};
+
+       if (gnutls_url_is_supported(file) != 0) {
+               ret = gnutls_x509_crt_list_import_url(&crts, &crts_size, file, pin_fn, pin_fn_userdata, 0);
+               if (ret < 0) {
+                       ret2 = gnutls_x509_crt_list_import_url(&crts, &crts_size, file, pin_fn, pin_fn_userdata, GNUTLS_PKCS11_OBJ_FLAG_LOGIN);
+                       if (ret2 >= 0) ret = ret2;
+               }
+
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+
+       } else { /* file */
+               ret = gnutls_load_file(file, &data);
+               if (ret < 0)
+                       return gnutls_assert_val(ret);
+
+               ret = gnutls_x509_crt_list_import2(&crts, &crts_size, &data, format, flags|GNUTLS_X509_CRT_LIST_SORT);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+       }
+
+       if (crts_size > *pcert_list_size) {
+               gnutls_assert();
+               ret = GNUTLS_E_SHORT_MEMORY_BUFFER;
+               goto cleanup;
+       }
+
+       ret = gnutls_pcert_import_x509_list(pcert_list, crts, &crts_size, flags);
+       if (ret < 0) {
+               gnutls_assert();
+               goto cleanup;
+       }
+       *pcert_list_size = crts_size;
+
+       ret = 0;
+cleanup:
+       for (i=0;i<crts_size;i++)
+               gnutls_x509_crt_deinit(crts[i]);
+       gnutls_free(crts);
+       gnutls_free(data.data);
+       return ret;
+}
+
+
 /**
  * gnutls_pcert_import_x509_raw:
  * @pcert: The pcert structure
index 395a7e59aa4e67422713c8aadf08d4d6bf72773e..b31c1a0a14404e1055757290741df01d9230adcf 100644 (file)
@@ -39,6 +39,7 @@
 #include "pkcs11x.h"
 #include <p11-kit/pin.h>
 #include <system-keys.h>
+#include "x509/x509_int.h"
 
 #include <atfork.h>
 
@@ -4045,11 +4046,14 @@ int gnutls_pkcs11_get_raw_issuer(const char *url, gnutls_x509_crt_t cert,
                gnutls_assert();
                goto cleanup;
        }
+
+       gnutls_pkcs11_obj_set_pin_function(priv.obj, cert->pin.cb, cert->pin.data);
+
        priv.need_import = 1;
 
        ret =
            _pkcs11_traverse_tokens(find_cert_cb, &priv, info,
-                                   NULL, pkcs11_obj_flags_to_int(flags));
+                                   &cert->pin, pkcs11_obj_flags_to_int(flags));
        if (ret < 0) {
                gnutls_assert();
                goto cleanup;
index e55bcbeda4cf4793f650e211d863300b3960027f..35207d55431bea0a2f7e2e4246545518ac3e312d 100644 (file)
@@ -205,10 +205,12 @@ gnutls_pkcs11_copy_x509_crt2(const char *token_url,
        a[a_val].value_len = crt->raw_dn.size;
        a_val++;
 
-       a[a_val].type = CKA_ISSUER;
-       a[a_val].value = crt->raw_issuer_dn.data;
-       a[a_val].value_len = crt->raw_issuer_dn.size;
-       a_val++;
+       if (crt->raw_issuer_dn.size > 0) {
+               a[a_val].type = CKA_ISSUER;
+               a[a_val].value = crt->raw_issuer_dn.data;
+               a[a_val].value_len = crt->raw_issuer_dn.size;
+               a_val++;
+       }
 
        serial_size = sizeof(serial);
        if (gnutls_x509_crt_get_serial(crt, serial, &serial_size) >= 0) {
index 2861c31d28a3779670aef82b934a2cf0ef6a71b6..69b6cfb2a2c70e1c8ab89b713798ef3e22fd149e 100644 (file)
@@ -148,7 +148,7 @@ int _gnutls_get_raw_issuer(const char *url, gnutls_x509_crt_t cert,
 
 #ifdef ENABLE_PKCS11
        if (strncmp(url, PKCS11_URL, PKCS11_URL_SIZE) == 0) {
-               return gnutls_pkcs11_get_raw_issuer(url, cert, issuer, GNUTLS_X509_FMT_DER, 0);
+               return gnutls_pkcs11_get_raw_issuer(url, cert, issuer, GNUTLS_X509_FMT_DER, flags);
        }
 #endif
        for (i=0;i<_gnutls_custom_urls_size;i++) {
index 162a49be4e1ad2c55f494cf7feff81d98830af46..74040a4e9d7735679ecdca9cd191b2178df586c6 100644 (file)
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2003-2018 Free Software Foundation, Inc.
+ * Copyright (C) 2018 Red Hat, Inc.
  *
  * Authors: Nikos Mavrogiannopoulos, Simon Josefsson, Howard Chu
  *
@@ -4165,7 +4166,7 @@ void gnutls_x509_crt_set_pin_function(gnutls_x509_crt_t crt,
  **/
 int
 gnutls_x509_crt_import_url(gnutls_x509_crt_t crt,
-                                 const char *url, unsigned int flags)
+                          const char *url, unsigned int flags)
 {
        int ret;
        unsigned i;
@@ -4194,6 +4195,110 @@ gnutls_x509_crt_import_url(gnutls_x509_crt_t crt,
        return ret;
 }
 
+/**
+ * gnutls_x509_crt_list_import_url:
+ * @certs: Will hold the allocated certificate list.
+ * @size: It will contain the size of the list.
+ * @url: A PKCS 11 url
+ * @pin_fn: a PIN callback if not globally set
+ * @pin_fn_userdata: parameter for the PIN callback
+ * @flags: One of GNUTLS_PKCS11_OBJ_* flags for PKCS#11 URLs or zero otherwise
+ *
+ * This function will import a certificate chain present in a PKCS#11 token
+ * or any type of back-end that supports URLs. The certificates
+ * must be deinitialized afterwards using gnutls_x509_crt_deinit()
+ * and the returned pointer must be freed using gnutls_free().
+ *
+ * The URI provided must be the first certificate in the chain; subsequent
+ * certificates will be retrieved using gnutls_pkcs11_get_raw_issuer() or
+ * equivalent functionality for the supported URI.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.6.3
+ **/
+int
+gnutls_x509_crt_list_import_url(gnutls_x509_crt_t **certs,
+                               unsigned int *size,
+                               const char *url,
+                               gnutls_pin_callback_t pin_fn,
+                               void *pin_fn_userdata,
+                               unsigned int flags)
+{
+       int ret;
+       unsigned i;
+       gnutls_x509_crt_t crts[DEFAULT_MAX_VERIFY_DEPTH];
+       gnutls_datum_t issuer = {NULL, 0};
+       unsigned total = 0;
+
+       memset(crts, 0, sizeof(crts));
+
+       ret = gnutls_x509_crt_init(&crts[0]);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       gnutls_x509_crt_set_pin_function(crts[0], pin_fn, pin_fn_userdata);
+
+       total = 1;
+
+       ret = gnutls_x509_crt_import_url(crts[0], url, flags);
+       if (ret < 0) {
+               gnutls_assert();
+               goto cleanup;
+       }
+
+       for (i=1;i<DEFAULT_MAX_VERIFY_DEPTH;i++) {
+               ret = _gnutls_get_raw_issuer(url, crts[i-1], &issuer, flags|GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_ANY);
+               if (ret < 0) {
+                       issuer.data = NULL;
+                       break;
+               }
+
+               if (gnutls_x509_crt_equals2(crts[i-1], &issuer)) {
+                       gnutls_free(issuer.data);
+                       issuer.data = NULL;
+                       break;
+               }
+
+               ret = gnutls_x509_crt_init(&crts[i]);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+
+               total++;
+
+               gnutls_x509_crt_set_pin_function(crts[i], pin_fn, pin_fn_userdata);
+
+               ret = gnutls_x509_crt_import(crts[i], &issuer, GNUTLS_X509_FMT_DER);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
+               }
+
+               gnutls_free(issuer.data);
+               issuer.data = NULL;
+       }
+
+       *certs = gnutls_malloc(total*sizeof(gnutls_x509_crt_t));
+       if (*certs == NULL) {
+               ret = GNUTLS_E_MEMORY_ERROR;
+               goto cleanup;
+       }
+
+       memcpy(*certs, crts, total*sizeof(gnutls_x509_crt_t));
+       *size = total;
+
+       return 0;
+ cleanup:
+       gnutls_free(issuer.data);
+       for (i=0;i<total;i++)
+               gnutls_x509_crt_deinit(crts[i]);
+
+       return ret;
+}
+
 /*-
  * gnutls_x509_crt_verify_data3:
  * @crt: Holds the certificate to verify with
index 99d6f933c8213cf1653b493723b00ed2cb386580..4243c5befeac13fbc13bd74badbe176176e80af9 100644 (file)
@@ -321,7 +321,8 @@ ctests += pkcs11-cert-import-url-exts pkcs11-get-exts pkcs11-get-raw-issuer-exts
        pkcs11-privkey-always-auth pkcs11-privkey-export pkcs11/pkcs11-import-with-pin \
        pkcs11/pkcs11-privkey-pthread pkcs11/pkcs11-pin-func pkcs11/pkcs11-obj-import \
        pkcs11-privkey-fork-reinit pkcs11-mechanisms pkcs11-privkey-safenet-always-auth \
-       pkcs11/pkcs11-rsa-pss-privkey-test pkcs11/tls-neg-pkcs11-key pkcs11/pkcs11-privkey-generate
+       pkcs11/pkcs11-rsa-pss-privkey-test pkcs11/tls-neg-pkcs11-key pkcs11/pkcs11-privkey-generate \
+       pkcs11/gnutls_x509_crt_list_import_url pkcs11/gnutls_pcert_list_import_x509_file
 
 
 endif
diff --git a/tests/pkcs11/gnutls_pcert_list_import_x509_file.c b/tests/pkcs11/gnutls_pcert_list_import_x509_file.c
new file mode 100644 (file)
index 0000000..5cef01b
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2018 Nikos Mavrogiannopoulos
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+
+#include "../utils.h"
+#include "softhsm.h"
+
+/* Tests whether gnutls_x509_crt_list_import_url() will return a well
+ * sorted chain, out of values written to softhsm token.
+ */
+
+#define CONFIG_NAME "softhsm-import-url"
+#define CONFIG CONFIG_NAME".config"
+
+#include "../test-chains.h"
+
+#define PIN "123456"
+
+static void tls_log_func(int level, const char *str)
+{
+       fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+static
+int pin_func(void* userdata, int attempt, const char* url, const char *label,
+               unsigned flags, char *pin, size_t pin_max)
+{
+       if (attempt == 0) {
+               strcpy(pin, PIN);
+               return 0;
+       }
+       return -1;
+}
+
+static int comp_cert(gnutls_pcert_st *pcert, unsigned i)
+{
+       int ret;
+       gnutls_datum_t data;
+       gnutls_x509_crt_t crt2;
+
+       if (debug)
+               success("comparing cert %d\n", i);
+
+       ret = gnutls_x509_crt_init(&crt2);
+       if (ret < 0)
+               return -1;
+
+       data.data = (void*)nc_good2[i];
+       data.size = strlen(nc_good2[i]);
+       ret = gnutls_x509_crt_import(crt2, &data, GNUTLS_X509_FMT_PEM);
+       if (ret < 0)
+               return -1;
+
+       if (!gnutls_x509_crt_equals2(crt2, &pcert->cert)) {
+               return -1;
+       }
+
+       gnutls_x509_crt_deinit(crt2);
+
+       return 0;
+}
+
+static void load_cert(const char *url, unsigned i)
+{
+       int ret;
+       gnutls_datum_t data;
+       gnutls_x509_crt_t crt;
+       char name[64];
+
+       ret = gnutls_x509_crt_init(&crt);
+       if (ret < 0)
+               fail("error: %s\n", gnutls_strerror(ret));
+
+       data.data = (void*)nc_good2[i];
+       data.size = strlen(nc_good2[i]);
+       ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_PEM);
+       if (ret < 0)
+               fail("error[%d]: %s\n", i, gnutls_strerror(ret));
+
+       snprintf(name, sizeof(name), "cert-%d", i);
+       ret = gnutls_pkcs11_copy_x509_crt(url, crt, name, GNUTLS_PKCS11_OBJ_FLAG_LOGIN|GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE);
+       if (ret < 0)
+               fail("error[%d]: %s\n", i, gnutls_strerror(ret));
+
+       success("written cert-%d\n", i);
+
+       gnutls_x509_crt_deinit(crt);
+}
+
+static void load_chain(const char *url)
+{
+       load_cert(url, 1);
+       load_cert(url, 0);
+       load_cert(url, 4);
+       load_cert(url, 2);
+       load_cert(url, 3);
+}
+
+static void write_certs(const char *file)
+{
+       FILE *fp = fopen(file, "w");
+       assert(fp != NULL);
+       fwrite(nc_good2[0], strlen(nc_good2[0]), 1, fp);
+       fwrite(nc_good2[4], strlen(nc_good2[4]), 1, fp);
+       fwrite(nc_good2[1], strlen(nc_good2[1]), 1, fp);
+       fwrite(nc_good2[2], strlen(nc_good2[2]), 1, fp);
+       fwrite(nc_good2[3], strlen(nc_good2[3]), 1, fp);
+       fclose(fp);
+}
+
+void doit(void)
+{
+       char buf[512];
+       int ret;
+       const char *lib, *bin;
+       unsigned int i;
+       gnutls_pcert_st pcerts[16];
+       unsigned int pcerts_size;
+       char file[TMPNAME_SIZE];
+
+       track_temp_files();
+       bin = softhsm_bin();
+
+       lib = softhsm_lib();
+
+       ret = global_init();
+       if (ret != 0) {
+               fail("%d: %s\n", ret, gnutls_strerror(ret));
+               exit(1);
+       }
+
+       gnutls_pkcs11_set_pin_function(pin_func, NULL);
+       gnutls_global_set_log_function(tls_log_func);
+       if (debug)
+               gnutls_global_set_log_level(4711);
+
+       set_softhsm_conf(CONFIG);
+       snprintf(buf, sizeof(buf), "%s --init-token --slot 0 --label test --so-pin "PIN" --pin "PIN, bin);
+       system(buf);
+
+       ret = gnutls_pkcs11_add_provider(lib, NULL);
+       if (ret < 0) {
+               fprintf(stderr, "add_provider: %s\n",
+                       gnutls_strerror(ret));
+               exit(1);
+       }
+
+       /* initialize softhsm token */
+       ret = gnutls_pkcs11_token_init(SOFTHSM_URL, PIN, "test");
+       if (ret < 0) {
+               fail("gnutls_pkcs11_token_init: %s\n", gnutls_strerror(ret));
+       }
+
+       ret = gnutls_pkcs11_token_set_pin(SOFTHSM_URL, NULL, PIN, GNUTLS_PIN_USER);
+       if (ret < 0) {
+               fail("gnutls_pkcs11_token_set_pin: %s\n", gnutls_strerror(ret));
+       }
+
+       load_chain(SOFTHSM_URL);
+       gnutls_pkcs11_set_pin_function(NULL, NULL);
+
+       success("import from URI\n");
+       pcerts_size = 2;
+       ret = gnutls_pcert_list_import_x509_file(pcerts, &pcerts_size, SOFTHSM_URL";object=cert-0",
+                                               GNUTLS_X509_FMT_PEM, pin_func, NULL, 0);
+       assert(ret == GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+       pcerts_size = sizeof(pcerts)/sizeof(pcerts[0]);
+       ret = gnutls_pcert_list_import_x509_file(pcerts, &pcerts_size, SOFTHSM_URL";object=cert-0",
+                                               GNUTLS_X509_FMT_PEM, pin_func, NULL, 0);
+       if (ret < 0)
+               fail("cannot load certs: %s\n", gnutls_strerror(ret));
+
+       assert(pcerts_size == 5);
+
+       for (i=0;i<pcerts_size;i++)
+               assert(comp_cert(&pcerts[i], i)>=0);
+
+       for (i=0;i<pcerts_size;i++)
+               gnutls_pcert_deinit(&pcerts[i]);
+
+       /* Try testing importing from file
+        */
+       success("import from file\n");
+       get_tmpname(file);
+
+       write_certs(file);
+
+       pcerts_size = 2;
+       ret = gnutls_pcert_list_import_x509_file(pcerts, &pcerts_size, file,
+                                               GNUTLS_X509_FMT_PEM, pin_func, NULL, 0);
+       assert(ret == GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+       pcerts_size = sizeof(pcerts)/sizeof(pcerts[0]);
+       ret = gnutls_pcert_list_import_x509_file(pcerts, &pcerts_size, file,
+                                               GNUTLS_X509_FMT_PEM, pin_func, NULL, 0);
+       if (ret < 0)
+               fail("cannot load certs: %s\n", gnutls_strerror(ret));
+
+       assert(pcerts_size == 5);
+
+       for (i=0;i<pcerts_size;i++)
+               assert(comp_cert(&pcerts[i], i)>=0);
+
+       for (i=0;i<pcerts_size;i++)
+               gnutls_pcert_deinit(&pcerts[i]);
+
+       gnutls_global_deinit();
+       delete_temp_files();
+
+       remove(CONFIG);
+}
+
diff --git a/tests/pkcs11/gnutls_x509_crt_list_import_url.c b/tests/pkcs11/gnutls_x509_crt_list_import_url.c
new file mode 100644 (file)
index 0000000..21d31b3
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2018 Nikos Mavrogiannopoulos
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+
+#include "../utils.h"
+#include "softhsm.h"
+
+/* Tests whether gnutls_x509_crt_list_import_url() will return a well
+ * sorted chain, out of values written to softhsm token.
+ */
+
+#define CONFIG_NAME "x509-crt-list-import-url"
+#define CONFIG CONFIG_NAME".config"
+
+#include "../test-chains.h"
+
+#define PIN "123456"
+
+static void tls_log_func(int level, const char *str)
+{
+       fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+static
+int pin_func(void* userdata, int attempt, const char* url, const char *label,
+               unsigned flags, char *pin, size_t pin_max)
+{
+       if (attempt == 0) {
+               strcpy(pin, PIN);
+               return 0;
+       }
+       return -1;
+}
+
+static void comp_cert(gnutls_x509_crt_t crt1, unsigned i)
+{
+       int ret;
+       gnutls_datum_t data;
+       gnutls_x509_crt_t crt2;
+
+       ret = gnutls_x509_crt_init(&crt2);
+       if (ret < 0)
+               fail("error: %s\n", gnutls_strerror(ret));
+
+       data.data = (void*)nc_good2[i];
+       data.size = strlen(nc_good2[i]);
+       ret = gnutls_x509_crt_import(crt2, &data, GNUTLS_X509_FMT_PEM);
+       if (ret < 0)
+               fail("error[%d]: %s\n", i, gnutls_strerror(ret));
+
+       if (!gnutls_x509_crt_equals(crt1, crt2)) {
+               fail("certificate doesn't match chain at %d\n", i);
+       }
+
+       gnutls_x509_crt_deinit(crt2);
+}
+
+static void load_cert(const char *url, unsigned i)
+{
+       int ret;
+       gnutls_datum_t data;
+       gnutls_x509_crt_t crt;
+       char name[64];
+
+       ret = gnutls_x509_crt_init(&crt);
+       if (ret < 0)
+               fail("error: %s\n", gnutls_strerror(ret));
+
+       data.data = (void*)nc_good2[i];
+       data.size = strlen(nc_good2[i]);
+       ret = gnutls_x509_crt_import(crt, &data, GNUTLS_X509_FMT_PEM);
+       if (ret < 0)
+               fail("error[%d]: %s\n", i, gnutls_strerror(ret));
+
+       snprintf(name, sizeof(name), "cert-%d", i);
+       ret = gnutls_pkcs11_copy_x509_crt(url, crt, name, GNUTLS_PKCS11_OBJ_FLAG_LOGIN|GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE);
+       if (ret < 0)
+               fail("error[%d]: %s\n", i, gnutls_strerror(ret));
+
+       success("written cert-%d\n", i);
+
+       gnutls_x509_crt_deinit(crt);
+}
+
+static void load_chain(const char *url)
+{
+       load_cert(url, 1);
+       load_cert(url, 0);
+       load_cert(url, 4);
+       load_cert(url, 2);
+       load_cert(url, 3);
+}
+
+void doit(void)
+{
+       char buf[512];
+       int ret;
+       const char *lib, *bin;
+       gnutls_x509_crt_t *crts;
+       unsigned int crts_size, i;
+
+       bin = softhsm_bin();
+
+       lib = softhsm_lib();
+
+       ret = global_init();
+       if (ret != 0) {
+               fail("%d: %s\n", ret, gnutls_strerror(ret));
+               exit(1);
+       }
+
+       gnutls_pkcs11_set_pin_function(pin_func, NULL);
+       gnutls_global_set_log_function(tls_log_func);
+       if (debug)
+               gnutls_global_set_log_level(4711);
+
+       set_softhsm_conf(CONFIG);
+       snprintf(buf, sizeof(buf), "%s --init-token --slot 0 --label test --so-pin "PIN" --pin "PIN, bin);
+       system(buf);
+
+       ret = gnutls_pkcs11_add_provider(lib, NULL);
+       if (ret < 0) {
+               fprintf(stderr, "add_provider: %s\n",
+                       gnutls_strerror(ret));
+               exit(1);
+       }
+
+       /* initialize softhsm token */
+       ret = gnutls_pkcs11_token_init(SOFTHSM_URL, PIN, "test");
+       if (ret < 0) {
+               fail("gnutls_pkcs11_token_init: %s\n", gnutls_strerror(ret));
+       }
+
+       ret = gnutls_pkcs11_token_set_pin(SOFTHSM_URL, NULL, PIN, GNUTLS_PIN_USER);
+       if (ret < 0) {
+               fail("gnutls_pkcs11_token_set_pin: %s\n", gnutls_strerror(ret));
+       }
+
+       load_chain(SOFTHSM_URL);
+       gnutls_pkcs11_set_pin_function(NULL, NULL);
+
+       /* try importing without login */
+       ret = gnutls_x509_crt_list_import_url(&crts, &crts_size, SOFTHSM_URL";object=cert-0",
+                                             pin_func, NULL, 0);
+       if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+               fail("cannot load certs: %s\n", gnutls_strerror(ret));
+
+       /* try importing with login */
+       ret = gnutls_x509_crt_list_import_url(&crts, &crts_size, SOFTHSM_URL";object=cert-0",
+                                             pin_func, NULL, GNUTLS_PKCS11_OBJ_FLAG_LOGIN);
+       if (ret < 0)
+               fail("cannot load certs: %s\n", gnutls_strerror(ret));
+
+       assert(crts_size == 5);
+
+       for (i=0;i<crts_size;i++)
+               comp_cert(crts[i], i);
+
+       for (i=0;i<crts_size;i++)
+               gnutls_x509_crt_deinit(crts[i]);
+       gnutls_free(crts);
+
+       gnutls_global_deinit();
+       delete_temp_files();
+
+       remove(CONFIG);
+}
+