summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <ueno@gnu.org>2023-03-13 16:58:45 +0900
committerDaiki Ueno <ueno@gnu.org>2023-03-17 06:25:49 +0900
commit1053726cd013cee12b5275973078c017aa24db36 (patch)
treeb22b6681fc19a53e0f02deaf89f21cc003ad2db3
parent157cfaebc098101ad41adbbf67291cd471ec1df2 (diff)
downloadgnutls-1053726cd013cee12b5275973078c017aa24db36.tar.gz
pkcs11: respect Mozilla's time-based distrust upon issuer lookup
This implements the basic logic needed to support time-based distrust of CA, according to [1]. 1. https://wiki.mozilla.org/CA/Additional_Trust_Changes#Distrust_After Signed-off-by: Daiki Ueno <ueno@gnu.org>
-rw-r--r--.gitignore3
-rw-r--r--lib/pkcs11.c181
-rw-r--r--lib/pkcs11_int.h4
-rw-r--r--lib/x509/common.h1
-rw-r--r--lib/x509/time.c2
-rw-r--r--lib/x509/verify.c20
-rw-r--r--tests/Makefile.am10
-rw-r--r--tests/pkcs11/distrust-after.c274
-rw-r--r--tests/pkcs11/pkcs11-mock3.c155
9 files changed, 647 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index 95cb3d89ae..b474c09f41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -456,6 +456,7 @@ tests/keylog-func
tests/ktls_keyupdate
tests/libpkcs11mock1.la
tests/libpkcs11mock2.la
+tests/libpkcs11mock3.la
tests/libutils.la
tests/long-session-id
tests/mini
@@ -570,6 +571,7 @@ tests/pkcs11-privkey-fork-reinit
tests/pkcs11-privkey-raw
tests/pkcs11-privkey-safenet-always-auth
tests/pkcs11-token-raw
+tests/pkcs11/distrust-after
tests/pkcs11/gnutls_pcert_list_import_x509_file
tests/pkcs11/gnutls_x509_crt_list_import_url
tests/pkcs11/list-objects
@@ -734,6 +736,7 @@ tests/slow/hash-large
tests/slow/keygen
tests/slow/mac-override
tests/softhsm-*.db/
+tests/softhsm-distrust-after.config
tests/softhsm-neg-no-key.config
tests/softhsm-post-handshake-with-cert-pkcs11.config
tests/spki
diff --git a/lib/pkcs11.c b/lib/pkcs11.c
index 2fce456212..1db41ad9f6 100644
--- a/lib/pkcs11.c
+++ b/lib/pkcs11.c
@@ -46,6 +46,14 @@
#define MAX_SLOTS 48
+#ifndef CKA_NSS_SERVER_DISTRUST_AFTER
+# define CKA_NSS_SERVER_DISTRUST_AFTER 0xce534373UL
+#endif
+
+#ifndef CKA_NSS_EMAIL_DISTRUST_AFTER
+# define CKA_NSS_EMAIL_DISTRUST_AFTER 0xce534374UL
+#endif
+
GNUTLS_STATIC_MUTEX(pkcs11_mutex);
struct gnutls_pkcs11_provider_st {
@@ -103,6 +111,12 @@ struct find_pkey_list_st {
size_t key_ids_size;
};
+enum distrust_purpose {
+ PKCS11_DISTRUST_AFTER_NONE = 0,
+ PKCS11_DISTRUST_AFTER_SERVER = 1,
+ PKCS11_DISTRUST_AFTER_EMAIL = 2
+};
+
struct find_cert_st {
gnutls_datum_t dn;
gnutls_datum_t issuer_dn;
@@ -112,6 +126,8 @@ struct find_cert_st {
unsigned need_import;
gnutls_pkcs11_obj_t obj;
gnutls_x509_crt_t crt; /* used when compare flag is specified */
+ enum distrust_purpose distrust_purpose;
+ time_t distrust_after;
unsigned flags;
};
@@ -4074,6 +4090,58 @@ static int get_data_and_attrs(struct pkcs11_session_info *sinfo,
return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE;
}
+static enum distrust_purpose distrust_purpose_from_oid(const char *oid)
+{
+ static const struct {
+ const char *oid;
+ enum distrust_purpose purpose;
+ } map[] = {
+ {GNUTLS_KP_TLS_WWW_SERVER, PKCS11_DISTRUST_AFTER_SERVER},
+ {GNUTLS_KP_EMAIL_PROTECTION, PKCS11_DISTRUST_AFTER_EMAIL},
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(map) / sizeof(map[0]); i++) {
+ if (strcmp(map[i].oid, oid) == 0) {
+ return map[i].purpose;
+ }
+ }
+
+ return PKCS11_DISTRUST_AFTER_NONE;
+}
+
+static time_t
+get_distrust_after(struct pkcs11_session_info *sinfo,
+ ck_object_handle_t object, enum distrust_purpose purpose)
+{
+ /* the attribute is in a fixed format: utcTime with seconds */
+ char buf[14];
+ struct ck_attribute a[1];
+
+ switch (purpose) {
+ case PKCS11_DISTRUST_AFTER_SERVER:
+ a[0].type = CKA_NSS_SERVER_DISTRUST_AFTER;
+ break;
+ case PKCS11_DISTRUST_AFTER_EMAIL:
+ a[0].type = CKA_NSS_EMAIL_DISTRUST_AFTER;
+ break;
+ default:
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+
+ a[0].value = buf;
+ a[0].value_len = sizeof(buf) - 1;
+
+ if (pkcs11_get_attribute_value(sinfo->module, sinfo->pks, object, a,
+ 1) != CKR_OK) {
+ return (time_t) (-1);
+ }
+
+ buf[a[0].value_len] = '\0';
+ return _gnutls_utcTime2gtime(buf);
+}
+
static int
find_cert_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo,
struct ck_token_info *tinfo, struct ck_info *lib_info, void *input)
@@ -4270,6 +4338,16 @@ find_cert_cb(struct ck_function_list *module, struct pkcs11_session_info *sinfo,
}
}
+ if ((priv->flags &
+ GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED)
+ && priv->distrust_purpose !=
+ PKCS11_DISTRUST_AFTER_NONE) {
+ priv->distrust_after =
+ get_distrust_after(sinfo, ctx,
+ priv->distrust_purpose);
+ continue;
+ }
+
if (priv->need_import != 0) {
ret =
pkcs11_obj_import(class, priv->obj,
@@ -4818,3 +4896,106 @@ char *gnutls_pkcs11_obj_flags_get_str(unsigned int flags)
return NULL;
}
+
+time_t
+_gnutls_pkcs11_get_distrust_after(const char *url, gnutls_x509_crt_t cert,
+ const char *purpose, unsigned int flags)
+{
+ int ret;
+ struct find_cert_st priv;
+ uint8_t serial[128];
+ size_t serial_size;
+ struct p11_kit_uri *info = NULL;
+ enum distrust_purpose distrust_purpose;
+
+ distrust_purpose = distrust_purpose_from_oid(purpose);
+ if (distrust_purpose == PKCS11_DISTRUST_AFTER_NONE) {
+ return (time_t) (-1);
+ }
+
+ PKCS11_CHECK_INIT_FLAGS_RET(flags, 0);
+
+ memset(&priv, 0, sizeof(priv));
+
+ if (url == NULL || url[0] == 0) {
+ url = "pkcs11:";
+ }
+
+ ret = pkcs11_url_to_info(url, &info, 0);
+ if (ret < 0) {
+ gnutls_assert();
+ return (time_t) (-1);
+ }
+
+ /* Attempt searching using the issuer DN + serial number */
+ serial_size = sizeof(serial);
+ ret = gnutls_x509_crt_get_serial(cert, serial, &serial_size);
+ if (ret < 0) {
+ gnutls_assert();
+ ret = (time_t) (-1);
+ goto cleanup;
+ }
+
+ ret = _gnutls_x509_ext_gen_number(serial, serial_size, &priv.serial);
+ if (ret < 0) {
+ gnutls_assert();
+ ret = (time_t) (-1);
+ goto cleanup;
+ }
+
+ priv.crt = cert;
+
+ priv.issuer_dn.data = cert->raw_issuer_dn.data;
+ priv.issuer_dn.size = cert->raw_issuer_dn.size;
+
+ /* assume PKCS11_OBJ_FLAG_COMPARE everywhere but DISTRUST info */
+ if (!(flags & GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_DISTRUSTED)
+ && !(flags & GNUTLS_PKCS11_OBJ_FLAG_COMPARE_KEY)) {
+ flags |= GNUTLS_PKCS11_OBJ_FLAG_COMPARE;
+ }
+
+ priv.flags = flags;
+ priv.distrust_purpose = distrust_purpose;
+
+ ret =
+ _pkcs11_traverse_tokens(find_cert_cb, &priv, info,
+ NULL, pkcs11_obj_flags_to_int(flags));
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ _gnutls_debug_log
+ ("get_distrust_after: did not find cert, using issuer DN + serial, using DN only\n");
+ /* attempt searching with the subject DN only */
+ gnutls_assert();
+ if (priv.obj)
+ gnutls_pkcs11_obj_deinit(priv.obj);
+ gnutls_free(priv.serial.data);
+ memset(&priv, 0, sizeof(priv));
+ priv.crt = cert;
+ priv.flags = flags;
+ priv.distrust_purpose = distrust_purpose;
+
+ priv.dn.data = cert->raw_dn.data;
+ priv.dn.size = cert->raw_dn.size;
+ ret =
+ _pkcs11_traverse_tokens(find_cert_cb, &priv, info,
+ NULL,
+ pkcs11_obj_flags_to_int(flags));
+ }
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_debug_log
+ ("get_distrust_after: did not find any cert\n");
+ ret = (time_t) (-1);
+ goto cleanup;
+ }
+
+ ret = priv.distrust_after;
+
+ cleanup:
+ if (priv.obj)
+ gnutls_pkcs11_obj_deinit(priv.obj);
+ if (info)
+ p11_kit_uri_free(info);
+ gnutls_free(priv.serial.data);
+
+ return ret;
+}
diff --git a/lib/pkcs11_int.h b/lib/pkcs11_int.h
index b93a6591f8..5a26a5f5ca 100644
--- a/lib/pkcs11_int.h
+++ b/lib/pkcs11_int.h
@@ -460,6 +460,10 @@ _gnutls_pkcs11_crt_is_known(const char *url, gnutls_x509_crt_t cert,
unsigned int flags,
gnutls_x509_crt_t * trusted_cert);
+time_t
+_gnutls_pkcs11_get_distrust_after(const char *url, gnutls_x509_crt_t cert,
+ const char *purpose, unsigned int flags);
+
# endif /* ENABLE_PKCS11 */
#endif /* GNUTLS_LIB_PKCS11_INT_H */
diff --git a/lib/x509/common.h b/lib/x509/common.h
index 46b5bc7926..51f8faab19 100644
--- a/lib/x509/common.h
+++ b/lib/x509/common.h
@@ -256,6 +256,7 @@ unsigned _gnutls_check_key_purpose(gnutls_x509_crt_t cert, const char *purpose,
unsigned no_any);
time_t _gnutls_x509_generalTime2gtime(const char *ttime);
+time_t _gnutls_utcTime2gtime(const char *ttime);
int _gnutls_get_extension(asn1_node asn, const char *root,
const char *extension_id, int indx,
diff --git a/lib/x509/time.c b/lib/x509/time.c
index 63db6d60a1..3dc4eaa751 100644
--- a/lib/x509/time.c
+++ b/lib/x509/time.c
@@ -35,8 +35,6 @@
#include <common.h>
#include <c-ctype.h>
-time_t _gnutls_utcTime2gtime(const char *ttime);
-
/* TIME functions
* Conversions between generalized or UTC time to time_t
*
diff --git a/lib/x509/verify.c b/lib/x509/verify.c
index f4384c4aa5..52ccedbe31 100644
--- a/lib/x509/verify.c
+++ b/lib/x509/verify.c
@@ -1230,6 +1230,7 @@ _gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist,
gnutls_x509_crt_t issuer = NULL;
gnutls_datum_t raw_issuer = { NULL, 0 };
time_t now = gnutls_time(0);
+ time_t distrust_after;
if (clist_size > 1) {
/* Check if the last certificate in the path is self signed.
@@ -1376,6 +1377,25 @@ _gnutls_pkcs11_verify_crt_status(gnutls_x509_trust_list_t tlist,
goto cleanup;
}
+ /* check if the raw issuer is assigned with a time-based
+ * distrust and the certificate is issued after that period
+ */
+ distrust_after =
+ _gnutls_pkcs11_get_distrust_after(url, issuer,
+ purpose == NULL ?
+ GNUTLS_KP_TLS_WWW_SERVER :
+ purpose,
+ GNUTLS_PKCS11_OBJ_FLAG_RETRIEVE_TRUSTED);
+ if (distrust_after != (time_t) - 1
+ && distrust_after <
+ gnutls_x509_crt_get_activation_time(certificate_list
+ [clist_size - 1])) {
+ gnutls_assert();
+ status |= GNUTLS_CERT_INVALID;
+ status |= GNUTLS_CERT_SIGNER_NOT_FOUND;
+ goto cleanup;
+ }
+
/* check if the raw issuer is distrusted (it can happen if
* the issuer is both in the trusted list and the distrusted)
*/
diff --git a/tests/Makefile.am b/tests/Makefile.am
index c263ac7c5c..48c694409f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -342,6 +342,11 @@ libpkcs11mock2_la_SOURCES = pkcs11/pkcs11-mock2.c
libpkcs11mock2_la_LDFLAGS = -shared -rpath $(pkglibdir) -module -no-undefined -avoid-version
libpkcs11mock2_la_LIBADD = ../gl/libgnu.la
+noinst_LTLIBRARIES += libpkcs11mock3.la
+libpkcs11mock3_la_SOURCES = pkcs11/pkcs11-mock3.c
+libpkcs11mock3_la_LDFLAGS = -shared -rpath $(pkglibdir) -module -no-undefined -avoid-version
+libpkcs11mock3_la_LIBADD = ../gl/libgnu.la
+
pkcs11_cert_import_url_exts_SOURCES = pkcs11/pkcs11-cert-import-url-exts.c
pkcs11_cert_import_url_exts_DEPENDENCIES = libpkcs11mock1.la libutils.la
@@ -487,11 +492,13 @@ pathbuf_CPPFLAGS = $(AM_CPPFLAGS) \
if ENABLE_PKCS11
if !WINDOWS
ctests += tls13/post-handshake-with-cert-pkcs11 pkcs11/tls-neg-pkcs11-no-key \
- global-init-override
+ global-init-override pkcs11/distrust-after
tls13_post_handshake_with_cert_pkcs11_DEPENDENCIES = libpkcs11mock2.la libutils.la
tls13_post_handshake_with_cert_pkcs11_LDADD = $(LDADD) $(LIBDL)
pkcs11_tls_neg_pkcs11_no_key_DEPENDENCIES = libpkcs11mock2.la libutils.la
pkcs11_tls_neg_pkcs11_no_key_LDADD = $(LDADD) $(LIBDL)
+pkcs11_distrust_after_DEPENDENCIES = libpkcs11mock3.la libutils.la
+pkcs11_distrust_after_LDADD = $(LDADD) $(LIBDL)
endif
endif
@@ -612,6 +619,7 @@ TESTS_ENVIRONMENT += \
CAFILE=$(srcdir)/cert-tests/data/ca-certs.pem \
P11MOCKLIB1=$(abs_builddir)/.libs/libpkcs11mock1.so \
P11MOCKLIB2=$(abs_builddir)/.libs/libpkcs11mock2.so \
+ P11MOCKLIB3=$(abs_builddir)/.libs/libpkcs11mock3.so \
PKCS12_MANY_CERTS_FILE=$(srcdir)/cert-tests/data/pkcs12_5certs.p12 \
PKCS12FILE=$(srcdir)/cert-tests/data/client.p12 \
PKCS12PASSWORD=foobar \
diff --git a/tests/pkcs11/distrust-after.c b/tests/pkcs11/distrust-after.c
new file mode 100644
index 0000000000..05165baa5a
--- /dev/null
+++ b/tests/pkcs11/distrust-after.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main(void)
+{
+ exit(77);
+}
+
+#else
+
+# include <string.h>
+# include <unistd.h>
+# include <gnutls/gnutls.h>
+# include <assert.h>
+
+# include "cert-common.h"
+# include "pkcs11/softhsm.h"
+# include "utils.h"
+
+/* This program tests that CKA_NSS_SERVER_DISTRUST_AFTER is honored
+ * while validating certificate chain.
+ */
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+# define PIN "1234"
+
+# define CONFIG_NAME "softhsm-distrust-after"
+# define CONFIG CONFIG_NAME".config"
+
+static const unsigned char chain_pem[] =
+ "-----BEGIN CERTIFICATE-----"
+ "MIID5zCCAp+gAwIBAgIUIXzLE8ObVwBGHepbjMWRwW/NpDgwDQYJKoZIhvcNAQEL"
+ "BQAwGTEXMBUGA1UEAxMOR251VExTIHRlc3QgQ0EwIBcNMjMwMzE0MTAwNDAzWhgP"
+ "OTk5OTEyMzEyMzU5NTlaMDcxGzAZBgNVBAoTEkdudVRMUyB0ZXN0IHNlcnZlcjEY"
+ "MBYGA1UEAxMPdGVzdC5nbnV0bHMub3JnMIIBUjANBgkqhkiG9w0BAQEFAAOCAT8A"
+ "MIIBOgKCATEAtGsnmCWvwf8eyrB+9Ni87UOGZ1Rd2rQewpBfgzwCEfwTcoWyiKRl"
+ "QQt2XyO+ip/+eUtzOy7HSzy/FsmXVTUX86FySzDC4CeUEvNWAObOgksRXaQem/r6"
+ "uRsqTRi1uqXmDMeoqKFtqoiE3JYOsmwcNarnx5Q9+dXHwqINS7NuevcIX8UJzRWT"
+ "GveY3ypMZokk7R/QFmOBZaVYO6HNJWKbmYFUCBcY7HwvCKI7KFcynRdHCob7YrFB"
+ "meb73qjqIH7zG+666pohZCmS8q1z5RkFnTdT4hGfGF8iuuKLDQCMni+nhz1Avkqi"
+ "pZIIDC5hwFh8mpnh1qyDOSXPPhvt66NtncvFON7Bx26bNBS+MD6CkB65Spp25O8z"
+ "DEaiMXL2w2EL+KpnifSl5XY3oSmfgHmqdQIDAQABo4GmMIGjMAwGA1UdEwEB/wQC"
+ "MAAwGgYDVR0RBBMwEYIPdGVzdC5nbnV0bHMub3JnMCcGA1UdJQQgMB4GCCsGAQUF"
+ "BwMBBggrBgEFBQcDAwYIKwYBBQUHAwQwDgYDVR0PAQH/BAQDAgWgMB0GA1UdDgQW"
+ "BBRIIzRTCokxOEpa6sq20qbezh0rGDAfBgNVHSMEGDAWgBQedyNtZzEfkQebli/s"
+ "/MhG/ozhAzANBgkqhkiG9w0BAQsFAAOCATEAYbQLlr74D62lPEevV/HWLOMG8taY"
+ "gPld7Z5VApIhsJa913Jya7AOsW+lz48LX3QNTc8Xgj7FVwQeNP1GtBZXCe6U73KB"
+ "Z+qp1rIEwn2cQVmFG+ShxmUA/gxxmWql2BAORNd5ZCVOcZbMh9uwWjhIQN/SImtW"
+ "x3ebFgV5N7GPFbw+5NUITLXoLrD7Bixv3iQS8hWwmAmmPZbHAENRauL6jYSjniru"
+ "SSFYjzJ1trJB6VgpJ2yWfKdcGZmB3osnGshWbayVOaprbH0AWKwOZ/d7sAldjdVw"
+ "ZsaOhA+6NbvpKYZuw6Tdt0+VmUwGC1ATJGpc0dEXRBaFlt/e+gqQ43Mo+YwiMDYq"
+ "LDU5nLC6uTSZLtgQHTqb32xmQ/D/y6NkUTH3f4OcxPGxBRVBHjOTk6MhRA=="
+ "-----END CERTIFICATE-----"
+ "-----BEGIN CERTIFICATE-----"
+ "MIIDjTCCAkWgAwIBAgIUejTcfGbOAc9l4IBW+kpAN6A7Sj4wDQYJKoZIhvcNAQEL"
+ "BQAwGTEXMBUGA1UEAxMOR251VExTIHRlc3QgQ0EwIBcNMjMwMzE0MDk1NzU1WhgP"
+ "OTk5OTEyMzEyMzU5NTlaMBkxFzAVBgNVBAMTDkdudVRMUyB0ZXN0IENBMIIBUjAN"
+ "BgkqhkiG9w0BAQEFAAOCAT8AMIIBOgKCATEAnORCsX1unl//fy2d1054XduIg/3C"
+ "qVBaT3Hca65SEoDwh0KiPtQoOgZLdKY2cobGs/ojYtOjcs0KnlPYdmtjEh6WEhuJ"
+ "U95v4TQdC4OLMiE56eIGq252hZAbHoTL84Q14DxQWGuzQK830iml7fbw2WcIcRQ8"
+ "vFGs8SzfXw63+MI6Fq6iMAQIqP08WzGmRRzL5wvCiPhCVkrPmwbXoABub6AAsYwW"
+ "PJB91M9/lx5gFH5k9/iPfi3s2Kg3F8MOcppqFYjxDSnsfiz6eMh1+bYVIAo367vG"
+ "VYHigXMEZC2FezlwIHaZzpEoFlY3a7LFJ00yrjQ910r8UE+CEMTYzE40D0olCMo7"
+ "FA9RCjeO3bUIoYaIdVTUGWEGHWSeoxGei9Gkm6u+ASj8f+i0jxdD2qXsewIDAQAB"
+ "o2swaTAPBgNVHRMBAf8EBTADAQH/MCcGA1UdJQQgMB4GCCsGAQUFBwMBBggrBgEF"
+ "BQcDAwYIKwYBBQUHAwQwDgYDVR0PAQH/BAQDAgIEMB0GA1UdDgQWBBQedyNtZzEf"
+ "kQebli/s/MhG/ozhAzANBgkqhkiG9w0BAQsFAAOCATEAa37UdOTvdUfRGwjrodhE"
+ "tEnRnfrwfQ61RMK5GY07UAks7CjdeWFDLoQfv9oP9kH122hEGAA683xg/CH5OeN0"
+ "8zrayQKqwcH40SJQDzc748lTgxUIDaf2rrkoF8butpaDaI0fageqjlEvCeZZSuIC"
+ "KCfZK9NPN47DknuerjOTwrWxvXYRepfSo8VVbjRj8R4qsgJsmJZYQfrAg0XrnKf/"
+ "UibNPXRCYABsxH4ZFtivg93LaQ05z4IrPSWGOTDQxNBoEC0DVGfSc8XElP0MkF/K"
+ "BIPsl3Rt2oFNhfViF9Gpzy9Dj1P1kMD6kE7nBDiRBUPNJZBiJSGVTMZTMc2tg42W"
+ "QcUYnUUzOpQWg1tcOZy4s+EuJ0bEWhSkFfSN3ENxsHXNCYYHgeadATcGbzTxD6ib"
+ "eA==" "-----END CERTIFICATE-----";
+
+static const gnutls_datum_t chain = {
+ (unsigned char *)chain_pem, sizeof(chain_pem) - 1
+};
+
+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 test(const char *provider, const char *purpose, bool succeeds)
+{
+ int ret;
+ gnutls_x509_crt_t *certs;
+ unsigned int count;
+ gnutls_x509_trust_list_t tl;
+ gnutls_typed_vdata_st vdata;
+ unsigned int status;
+
+ gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
+
+ success("test with %s for %s\n", provider, purpose);
+
+ if (debug) {
+ gnutls_global_set_log_function(tls_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ /* point to SoftHSM token that libpkcs11mock3.so internally uses */
+ setenv(SOFTHSM_ENV, CONFIG, 1);
+
+ gnutls_pkcs11_set_pin_function(pin_func, NULL);
+
+ ret = gnutls_pkcs11_add_provider(provider, "trusted");
+ if (ret != 0) {
+ fail("gnutls_pkcs11_add_provider: %s\n", gnutls_strerror(ret));
+ }
+
+ /* 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));
+ }
+
+ gnutls_x509_trust_list_init(&tl, 0);
+
+ ret = gnutls_x509_trust_list_add_trust_file(tl, SOFTHSM_URL, NULL,
+ 0, 0, 0);
+ if (ret < 0) {
+ fail("gnutls_x509_trust_list_add_trust_file\n");
+ }
+
+ ret = gnutls_x509_crt_list_import2(&certs, &count,
+ &chain, GNUTLS_X509_FMT_PEM, 0);
+ if (ret < 0) {
+ fail("gnutls_x509_crt_import: %s\n", gnutls_strerror(ret));
+ }
+
+ assert(count == 2);
+
+ /* Use the ICA (instead of the actual root CA) for simplicity. */
+ ret = gnutls_pkcs11_copy_x509_crt(SOFTHSM_URL, certs[1], "ca",
+ GNUTLS_PKCS11_OBJ_FLAG_MARK_TRUSTED |
+ GNUTLS_PKCS11_OBJ_FLAG_MARK_CA |
+ GNUTLS_PKCS11_OBJ_FLAG_LOGIN_SO);
+ if (ret < 0) {
+ fail("gnutls_pkcs11_copy_x509_crt: %s\n", gnutls_strerror(ret));
+ }
+
+ vdata.type = GNUTLS_DT_KEY_PURPOSE_OID;
+ vdata.data = (void *)purpose;
+
+ ret = gnutls_x509_trust_list_verify_crt2(tl, certs, 1, &vdata, 1,
+ 0, &status, NULL);
+ if (ret < 0) {
+ fail("gnutls_x509_trust_list_verify_crt2: %s\n",
+ gnutls_strerror(ret));
+ }
+
+ if (succeeds) {
+ if (status != 0) {
+ fail("verify failed\n");
+ }
+ } else if (!(status & GNUTLS_CERT_SIGNER_NOT_FOUND)) {
+ fail("verify succeeded unexpectedly\n");
+ }
+
+ gnutls_x509_trust_list_deinit(tl, 0);
+ while (count--) {
+ gnutls_x509_crt_deinit(certs[count]);
+ }
+ gnutls_free(certs);
+
+ gnutls_pkcs11_deinit();
+}
+
+void doit(void)
+{
+ const char *bin;
+ const char *lib;
+ char buf[128];
+
+ if (gnutls_fips140_mode_enabled())
+ exit(77);
+
+ /* this must be called once in the program */
+ global_init();
+
+ /* we call gnutls_pkcs11_init manually */
+ gnutls_pkcs11_deinit();
+
+ /* check if softhsm module is loadable */
+ lib = softhsm_lib();
+
+ /* initialize SoftHSM token that libpkcs11mock2.so internally uses */
+ bin = softhsm_bin();
+
+ set_softhsm_conf(CONFIG);
+ snprintf(buf, sizeof(buf),
+ "%s --init-token --slot 0 --label test --so-pin " PIN " --pin "
+ PIN, bin);
+ system(buf);
+
+ test(lib, GNUTLS_KP_TLS_WWW_SERVER, true);
+
+ set_softhsm_conf(CONFIG);
+ snprintf(buf, sizeof(buf),
+ "%s --init-token --slot 0 --label test --so-pin " PIN " --pin "
+ PIN, bin);
+ system(buf);
+
+ test(lib, GNUTLS_KP_EMAIL_PROTECTION, true);
+
+ lib = getenv("P11MOCKLIB3");
+ if (lib == NULL) {
+ fail("P11MOCKLIB3 is not set\n");
+ }
+
+ set_softhsm_conf(CONFIG);
+ snprintf(buf, sizeof(buf),
+ "%s --init-token --slot 0 --label test --so-pin " PIN " --pin "
+ PIN, bin);
+ system(buf);
+
+ test(lib, GNUTLS_KP_TLS_WWW_SERVER, false);
+
+ set_softhsm_conf(CONFIG);
+ snprintf(buf, sizeof(buf),
+ "%s --init-token --slot 0 --label test --so-pin " PIN " --pin "
+ PIN, bin);
+ system(buf);
+
+ test(lib, GNUTLS_KP_EMAIL_PROTECTION, true);
+}
+#endif /* _WIN32 */
diff --git a/tests/pkcs11/pkcs11-mock3.c b/tests/pkcs11/pkcs11-mock3.c
new file mode 100644
index 0000000000..dffe300ee0
--- /dev/null
+++ b/tests/pkcs11/pkcs11-mock3.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2023 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <dlfcn.h>
+#include <p11-kit/pkcs11.h>
+#include <p11-kit/pkcs11x.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include "softhsm.h"
+
+/* This provides a mock PKCS #11 module that delegates all the
+ * operations to SoftHSM except that it returns
+ * CKA_NSS_SERVER_DISTRUST_AFTER upon C_GetAttributeValue.
+ */
+
+static void *dl;
+static CK_C_GetAttributeValue base_C_GetAttributeValue;
+static CK_FUNCTION_LIST override_funcs;
+
+#ifdef __sun
+# pragma fini(mock_deinit)
+# pragma init(mock_init)
+# define _CONSTRUCTOR
+# define _DESTRUCTOR
+#else
+# define _CONSTRUCTOR __attribute__((constructor))
+# define _DESTRUCTOR __attribute__((destructor))
+#endif
+
+/* Should be a date before the activation time of chain[0] in
+ * pkcs11/distrust-after.c: Tue Mar 14 10:04:03 UTC 2023
+ */
+#define DISTRUST_AFTER "230314000000Z"
+
+static CK_RV
+override_C_GetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+ CK_ATTRIBUTE *template;
+ CK_ULONG count = 0, i, offset = ulCount;
+ CK_RV rv;
+
+ template = malloc(ulCount * sizeof(CK_ATTRIBUTE));
+ if (!template) {
+ return CKR_HOST_MEMORY;
+ }
+
+ for (i = 0; i < ulCount; i++) {
+ if (pTemplate[i].type == CKA_NSS_SERVER_DISTRUST_AFTER) {
+ offset = i;
+ } else {
+ template[count++] = pTemplate[i];
+ }
+ }
+
+ rv = base_C_GetAttributeValue(hSession, hObject, template, count);
+
+ for (i = 0; i < offset; i++) {
+ pTemplate[i] = template[i];
+ }
+
+ if (offset < ulCount) {
+ if (!pTemplate[offset].pValue) {
+ pTemplate[offset].ulValueLen =
+ sizeof(DISTRUST_AFTER) - 1;
+ } else if (pTemplate[offset].ulValueLen <
+ sizeof(DISTRUST_AFTER) - 1) {
+ pTemplate[offset].ulValueLen =
+ CK_UNAVAILABLE_INFORMATION;
+ rv = CKR_BUFFER_TOO_SMALL;
+ } else {
+ memcpy(pTemplate[offset].pValue, DISTRUST_AFTER,
+ sizeof(DISTRUST_AFTER) - 1);
+ pTemplate[offset].ulValueLen =
+ sizeof(DISTRUST_AFTER) - 1;
+ }
+ }
+
+ for (i = offset + 1; i < ulCount; i++) {
+ pTemplate[i] = template[i];
+ }
+
+ free(template);
+
+ return rv;
+}
+
+CK_RV C_GetFunctionList(CK_FUNCTION_LIST ** function_list)
+{
+ CK_C_GetFunctionList func;
+ CK_FUNCTION_LIST *funcs;
+
+ assert(dl);
+
+ func = dlsym(dl, "C_GetFunctionList");
+ if (func == NULL) {
+ return CKR_GENERAL_ERROR;
+ }
+
+ func(&funcs);
+
+ base_C_GetAttributeValue = funcs->C_GetAttributeValue;
+
+ memcpy(&override_funcs, funcs, sizeof(CK_FUNCTION_LIST));
+ override_funcs.C_GetAttributeValue = override_C_GetAttributeValue;
+ *function_list = &override_funcs;
+
+ return CKR_OK;
+}
+
+static _CONSTRUCTOR void mock_init(void)
+{
+ const char *lib;
+
+ /* suppress compiler warning */
+ (void)set_softhsm_conf;
+
+ lib = softhsm_lib();
+
+ dl = dlopen(lib, RTLD_NOW);
+ if (dl == NULL)
+ exit(77);
+}
+
+static _DESTRUCTOR void mock_deinit(void)
+{
+ dlclose(dl);
+}