]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Add pkcs11 provider
authorZoltan Fridrich <zfridric@redhat.com>
Thu, 16 Jan 2025 12:32:16 +0000 (13:32 +0100)
committerZoltan Fridrich <zfridric@redhat.com>
Tue, 10 Jun 2025 06:50:04 +0000 (08:50 +0200)
Signed-off-by: Zoltan Fridrich <zfridric@redhat.com>
.gitignore
configure.ac
lib/Makefile.am
lib/global.c
lib/gnutls_int.h
lib/pkcs11/Makefile.am [new file with mode: 0644]
lib/pkcs11/p11_provider.c [new file with mode: 0644]
lib/pkcs11/p11_provider.h [new file with mode: 0644]
lib/priority.c

index 963d847eff64e46ffb74e398797aa71b01db4a84..3bb2580494b87dd972f0d5dba4081af8db722a54 100644 (file)
@@ -235,6 +235,9 @@ lib/openpgp/libgnutls_openpgp.la
 lib/openpgp/Makefile
 lib/openpgp/Makefile.in
 lib/openpgp/pgp-api.texi
+lib/pkcs11/libgnutls_pkcs11.la
+lib/pkcs11/Makefile
+lib/pkcs11/Makefile.in
 lib/priority_options.h
 .libs
 libtool
index 27db8f2ee8030df214dd5db4b1246e4449c8388b..afd70573e1bb0ff3d7934f8d9364cae0dde2f56d 100644 (file)
@@ -1555,6 +1555,7 @@ AC_CONFIG_FILES([
   lib/includes/gnutls/gnutls.h
   lib/minitasn1/Makefile
   lib/nettle/Makefile
+  lib/pkcs11/Makefile
   lib/x509/Makefile
   lib/unistring/Makefile
   po/Makefile.in
index 56576b65f19ee37b5b0abeefddac4c0a9559994a..b28c623c112958e976ddecf9e1e563494be1d14b 100644 (file)
@@ -25,6 +25,10 @@ BUILT_SOURCES = pkix_asn1_tab.c gnutls_asn1_tab.c priority_options.h
 
 SUBDIRS = includes x509 auth ext algorithms extras accelerated
 
+if ENABLE_PKCS11
+SUBDIRS += pkcs11
+endif
+
 if ENABLE_MINITASN1
 SUBDIRS += minitasn1
 endif
@@ -218,6 +222,10 @@ libgnutls_la_LIBADD = ../gl/libgnu.la x509/libgnutls_x509.la \
 thirdparty_libadd += $(LTLIBINTL) $(LIBSOCKET) $(LTLIBNSL) \
        $(P11_KIT_LIBS) $(LIB_SELECT) $(GNUTLS_LIBS_PRIVATE)
 
+if ENABLE_PKCS11
+libgnutls_la_LIBADD += pkcs11/libgnutls_pkcs11.la
+endif
+
 if HAVE_LIBIDN2
 thirdparty_libadd += $(LIBIDN2_LIBS)
 endif
index 9aa95dd7a5611b1c48a80bdfbd6612a827276044..0ff60c24d97aae5ee3e3dc2949e43aa1b6682da4 100644 (file)
 #include <leancrypto.h>
 #endif
 
+#ifdef ENABLE_PKCS11
+#include "pkcs11/p11_provider.h"
+#endif
+
 /* Minimum library versions we accept. */
 #define GNUTLS_MIN_LIBTASN1_VERSION "0.3.4"
 
@@ -244,6 +248,10 @@ static int _gnutls_global_init(unsigned constructor)
        int ret = 0, res;
        int level;
        const char *e;
+#if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140)
+       const char *p11_provider_path = NULL;
+       const char *p11_provider_pin = NULL;
+#endif
 
        if (!constructor) {
                ret = gnutls_static_mutex_lock(&global_init_mutex);
@@ -391,7 +399,24 @@ static int _gnutls_global_init(unsigned constructor)
                _gnutls_fips_mode_reset_zombie();
        }
 #endif
+
        _gnutls_prepare_to_load_system_priorities();
+
+#if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140)
+       p11_provider_path = _gnutls_config_get_p11_provider_path();
+       p11_provider_pin = _gnutls_config_get_p11_provider_pin();
+
+       if (res == 1 && p11_provider_path != NULL) {
+               ret = _p11_provider_init(p11_provider_path,
+                                        (const uint8_t *)p11_provider_pin,
+                                        strlen(p11_provider_pin));
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto out;
+               }
+       }
+#endif
+
        _gnutls_switch_lib_state(LIB_STATE_OPERATIONAL);
        ret = 0;
 
@@ -435,6 +460,10 @@ static void _gnutls_global_deinit(unsigned destructor)
                _gnutls_supplemental_deinit();
                _gnutls_unload_system_priorities();
 
+#if defined(ENABLE_PKCS11) && defined(ENABLE_FIPS140)
+               _p11_provider_deinit();
+#endif
+
 #ifdef ENABLE_PKCS11
                /* Do not try to deinitialize the PKCS #11 libraries
                 * from the destructor. If we do and the PKCS #11 modules
index b14d3e80da8e4bb2f64f854e3684c91059029de1..539486bc7d90cc1f3c8e9d440b285908d6496d34 100644 (file)
@@ -1811,5 +1811,7 @@ extern unsigned int _gnutls_global_version;
 bool _gnutls_config_is_ktls_enabled(void);
 bool _gnutls_config_is_rsa_pkcs1_encrypt_allowed(void);
 int _gnutls_config_set_certificate_compression_methods(gnutls_session_t session);
+const char *_gnutls_config_get_p11_provider_path(void);
+const char *_gnutls_config_get_p11_provider_pin(void);
 
 #endif /* GNUTLS_LIB_GNUTLS_INT_H */
diff --git a/lib/pkcs11/Makefile.am b/lib/pkcs11/Makefile.am
new file mode 100644 (file)
index 0000000..ad6c783
--- /dev/null
@@ -0,0 +1,47 @@
+## Process this file with automake to produce Makefile.in
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Author: Zoltan Fridrich
+#
+# 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 <https://www.gnu.org/licenses/>
+
+include $(top_srcdir)/lib/common.mk
+
+AM_CPPFLAGS += \
+       -I$(srcdir)/../../gl            \
+       -I$(builddir)/../../gl          \
+       -I$(srcdir)/../includes         \
+       -I$(builddir)/../includes       \
+       -I$(srcdir)/..                  \
+       $(LIBTASN1_CFLAGS)              \
+       $(P11_KIT_CFLAGS)
+
+if ENABLE_MINITASN1
+AM_CPPFLAGS += -I$(srcdir)/../minitasn1
+endif
+
+noinst_LTLIBRARIES = libgnutls_pkcs11.la
+
+libgnutls_pkcs11_la_SOURCES = \
+       p11_provider.c p11_provider.h
+
+libgnutls_pkcs11_la_LIBADD = $(P11_KIT_LIBS) $(LIBPMULTITHREAD)
+
+if ENABLE_MINITASN1
+libgnutls_pkcs11_la_LIBADD += ../minitasn1/libminitasn1.la
+else
+libgnutls_pkcs11_la_LIBADD += $(LIBTASN1_LIBS)
+endif
diff --git a/lib/pkcs11/p11_provider.c b/lib/pkcs11/p11_provider.c
new file mode 100644 (file)
index 0000000..2c8e1ba
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "errors.h"
+#include "gnutls_int.h"
+#include "cipher_int.h"
+#include "p11_provider.h"
+
+#define P11_KIT_FUTURE_UNSTABLE_API
+#include <p11-kit/iter.h>
+#include <p11-kit/pkcs11.h>
+
+static struct {
+       CK_FUNCTION_LIST *module;
+       CK_SLOT_ID slot;
+       uint8_t *pin;
+       size_t pin_size;
+       bool initialized;
+} p11_provider;
+
+int _p11_provider_init(const char *module_path, const uint8_t *pin,
+                      size_t pin_size)
+{
+       int ret;
+       CK_RV rv;
+       P11KitIter *iter = NULL;
+       CK_FUNCTION_LIST *modules[2] = { 0 };
+       CK_SLOT_ID slot = 0;
+       uint8_t *_pin = NULL;
+
+       if (p11_provider.initialized)
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+       modules[0] = p11_kit_module_load(module_path, 0);
+       if (modules[0] == NULL)
+               return gnutls_assert_val(GNUTLS_E_PKCS11_LOAD_ERROR);
+
+       rv = p11_kit_module_initialize(modules[0]);
+       if (rv != CKR_OK) {
+               p11_kit_module_release(modules[0]);
+               return gnutls_assert_val(GNUTLS_E_PKCS11_ERROR);
+       }
+
+       iter = p11_kit_iter_new(NULL, P11_KIT_ITER_WITH_TOKENS |
+                                             P11_KIT_ITER_WITHOUT_OBJECTS);
+       if (iter == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto error;
+       }
+
+       p11_kit_iter_begin(iter, modules);
+       rv = p11_kit_iter_next(iter);
+       if (rv != CKR_OK) {
+               ret = gnutls_assert_val(GNUTLS_E_PKCS11_ERROR);
+               goto error;
+       }
+
+       slot = p11_kit_iter_get_slot(iter);
+       p11_kit_iter_free(iter);
+
+       _pin = gnutls_malloc(pin_size);
+       if (_pin == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto error;
+       }
+       memcpy(_pin, pin, pin_size);
+
+       p11_provider.module = modules[0];
+       p11_provider.slot = slot;
+       p11_provider.pin = _pin;
+       p11_provider.pin_size = pin_size;
+       p11_provider.initialized = true;
+       return 0;
+
+error:
+       if (iter != NULL)
+               p11_kit_iter_free(iter);
+       gnutls_free(_pin);
+       p11_kit_module_finalize(modules[0]);
+       p11_kit_module_release(modules[0]);
+       return ret;
+}
+
+void _p11_provider_deinit(void)
+{
+       if (!p11_provider.initialized)
+               return;
+
+       gnutls_free(p11_provider.pin);
+       p11_kit_module_finalize(p11_provider.module);
+       p11_kit_module_release(p11_provider.module);
+       memset(&p11_provider, 0, sizeof(p11_provider));
+}
+
+bool _p11_provider_is_initialized(void)
+{
+       return p11_provider.initialized;
+}
+
+CK_SESSION_HANDLE _p11_provider_open_session(void)
+{
+       CK_RV rv;
+       CK_SESSION_HANDLE session = CK_INVALID_HANDLE;
+
+       rv = p11_provider.module->C_OpenSession(
+               p11_provider.slot, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL,
+               NULL, &session);
+       if (rv != CKR_OK)
+               return CK_INVALID_HANDLE;
+
+       rv = p11_provider.module->C_Login(session, CKU_USER, p11_provider.pin,
+                                         p11_provider.pin_size);
+       if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) {
+               p11_provider.module->C_CloseSession(session);
+               return CK_INVALID_HANDLE;
+       }
+
+       return session;
+}
+
+void _p11_provider_close_session(CK_SESSION_HANDLE session)
+{
+       if (session == CK_INVALID_HANDLE)
+               return;
+
+       p11_provider.module->C_Logout(session);
+       p11_provider.module->C_CloseSession(session);
+}
+
+CK_FUNCTION_LIST *_p11_provider_get_module(void)
+{
+       return p11_provider.module;
+}
+
+CK_SLOT_ID _p11_provider_get_slot(void)
+{
+       return p11_provider.slot;
+}
diff --git a/lib/pkcs11/p11_provider.h b/lib/pkcs11/p11_provider.h
new file mode 100644 (file)
index 0000000..661dd43
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * 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 <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef GNUTLS_LIB_P11_PROVIDER_H
+#define GNUTLS_LIB_P11_PROVIDER_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <p11-kit/pkcs11.h>
+
+int _p11_provider_init(const char *module_path, const uint8_t *pin,
+                      size_t pin_size);
+void _p11_provider_deinit(void);
+bool _p11_provider_is_initialized(void);
+CK_SESSION_HANDLE _p11_provider_open_session(void);
+void _p11_provider_close_session(CK_SESSION_HANDLE session);
+CK_FUNCTION_LIST *_p11_provider_get_module(void);
+CK_SLOT_ID _p11_provider_get_slot(void);
+
+#endif /* GNUTLS_LIB_P11_PROVIDER_H */
index 7a841374019a0179037f8fa5cf73655255d6143b..25a2de95a80c5def6a6cf66c8e4c8fa40fd6d2be 100644 (file)
@@ -1022,6 +1022,9 @@ struct cfg {
        gnutls_compression_method_t
                cert_comp_algs[MAX_COMPRESS_CERTIFICATE_METHODS + 1];
 
+       char *p11_provider_path;
+       char *p11_provider_pin;
+
        ext_master_secret_t force_ext_master_secret;
        bool force_ext_master_secret_set;
 };
@@ -1039,6 +1042,8 @@ static inline void cfg_deinit(struct cfg *cfg)
        }
        gnutls_free(cfg->priority_string);
        gnutls_free(cfg->default_priority_string);
+       gnutls_free(cfg->p11_provider_path);
+       gnutls_free(cfg->p11_provider_pin);
 }
 
 /* Lock for reading and writing system_wide_config */
@@ -1052,6 +1057,7 @@ static unsigned system_priority_file_loaded = 0;
 
 #define GLOBAL_SECTION "global"
 #define CUSTOM_PRIORITY_SECTION "priorities"
+#define PROVIDER_SECTION "provider"
 #define OVERRIDES_SECTION "overrides"
 #define MAX_ALGO_NAME 2048
 
@@ -1138,6 +1144,12 @@ static inline void cfg_steal(struct cfg *dst, struct cfg *src)
        dst->default_priority_string = src->default_priority_string;
        src->default_priority_string = NULL;
 
+       dst->p11_provider_path = src->p11_provider_path;
+       src->p11_provider_path = NULL;
+
+       dst->p11_provider_pin = src->p11_provider_pin;
+       src->p11_provider_pin = NULL;
+
        dst->allowlisting = src->allowlisting;
        dst->ktls_enabled = src->ktls_enabled;
        dst->allow_rsa_pkcs1_encrypt = src->allow_rsa_pkcs1_encrypt;
@@ -1607,6 +1619,49 @@ static int cfg_ini_handler(void *_ctx, const char *section, const char *name,
                                             value);
                if (ret < 0)
                        return 0;
+       } else if (c_strcasecmp(section, PROVIDER_SECTION) == 0) {
+               if (c_strcasecmp(name, "path") == 0) {
+                       gnutls_free(cfg->p11_provider_path);
+                       cfg->p11_provider_path = NULL;
+                       p = clear_spaces(value, str);
+                       _gnutls_debug_log(
+                               "cfg: adding pkcs11 provider path %s\n", p);
+                       if (strlen(p) > 0) {
+                               cfg->p11_provider_path = gnutls_strdup(p);
+                               if (cfg->p11_provider_path == NULL) {
+                                       _gnutls_debug_log(
+                                               "cfg: failed setting pkcs11 provider path\n");
+                                       return 0;
+                               }
+                       } else {
+                               _gnutls_debug_log(
+                                       "cfg: empty pkcs11 provider path, using default\n");
+                               if (fail_on_invalid_config)
+                                       return 0;
+                       }
+               } else if (c_strcasecmp(name, "pin") == 0) {
+                       gnutls_free(cfg->p11_provider_pin);
+                       cfg->p11_provider_pin = NULL;
+                       p = clear_spaces(value, str);
+                       _gnutls_debug_log("cfg: adding pkcs11 provider pin\n");
+                       if (strlen(p) > 0) {
+                               cfg->p11_provider_pin = gnutls_strdup(p);
+                               if (cfg->p11_provider_pin == NULL) {
+                                       _gnutls_debug_log(
+                                               "cfg: failed setting pkcs11 provider pin\n");
+                                       return 0;
+                               }
+                       } else {
+                               _gnutls_debug_log(
+                                       "cfg: empty pkcs11 provider pin, using default\n");
+                               if (fail_on_invalid_config)
+                                       return 0;
+                       }
+               } else {
+                       _gnutls_debug_log("unknown parameter %s\n", name);
+                       if (fail_on_invalid_config)
+                               return 0;
+               }
        } else if (c_strcasecmp(section, OVERRIDES_SECTION) == 0) {
                if (!override_allowed(cfg->allowlisting, name)) {
                        _gnutls_debug_log(
@@ -4040,6 +4095,16 @@ int _gnutls_config_set_certificate_compression_methods(gnutls_session_t session)
        return 0;
 }
 
+const char *_gnutls_config_get_p11_provider_path(void)
+{
+       return system_wide_config.p11_provider_path;
+}
+
+const char *_gnutls_config_get_p11_provider_pin(void)
+{
+       return system_wide_config.p11_provider_pin;
+}
+
 /*
  * high-level interface for overriding configuration files
  */