diff options
author | Daiki Ueno <ueno@gnu.org> | 2023-03-13 16:58:45 +0900 |
---|---|---|
committer | Daiki Ueno <ueno@gnu.org> | 2023-03-17 06:25:49 +0900 |
commit | 1053726cd013cee12b5275973078c017aa24db36 (patch) | |
tree | b22b6681fc19a53e0f02deaf89f21cc003ad2db3 | |
parent | 157cfaebc098101ad41adbbf67291cd471ec1df2 (diff) | |
download | gnutls-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-- | .gitignore | 3 | ||||
-rw-r--r-- | lib/pkcs11.c | 181 | ||||
-rw-r--r-- | lib/pkcs11_int.h | 4 | ||||
-rw-r--r-- | lib/x509/common.h | 1 | ||||
-rw-r--r-- | lib/x509/time.c | 2 | ||||
-rw-r--r-- | lib/x509/verify.c | 20 | ||||
-rw-r--r-- | tests/Makefile.am | 10 | ||||
-rw-r--r-- | tests/pkcs11/distrust-after.c | 274 | ||||
-rw-r--r-- | tests/pkcs11/pkcs11-mock3.c | 155 |
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); +} |