]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
liboqs: defer loading of liboqs at run-time
authorDaiki Ueno <ueno@gnu.org>
Tue, 23 Jul 2024 11:48:26 +0000 (20:48 +0900)
committerDaiki Ueno <ueno@gnu.org>
Wed, 24 Jul 2024 08:03:35 +0000 (17:03 +0900)
Instead of loading liboqs at startup, this defers it until the liboqs
functions are actually used.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
14 files changed:
lib/dlwrap/brotlidec.c
lib/dlwrap/brotlidec.h
lib/dlwrap/brotlienc.c
lib/dlwrap/brotlienc.h
lib/dlwrap/oqs.c
lib/dlwrap/oqs.h
lib/dlwrap/zlib.c
lib/dlwrap/zlib.h
lib/dlwrap/zstd.c
lib/dlwrap/zstd.h
lib/global.c
lib/liboqs/liboqs.c
lib/liboqs/liboqs.h
lib/nettle/pk.c

index 15cee63bd67567b1227caf83e25c350cac0cc8ec..7e4546a8e7ddcb904d166c8800656cb8015b8436 100644 (file)
@@ -128,10 +128,13 @@ gnutls_brotlidec_ensure_library (const char *soname, int flags)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-macros"
 
-#define FUNC(ret, name, args, cargs)   \
-  err = ENSURE_SYMBOL(name);           \
-  if (err < 0)                         \
-    return err;
+#define FUNC(ret, name, args, cargs)           \
+  err = ENSURE_SYMBOL(name);                   \
+  if (err < 0)                                 \
+    {                                          \
+      gnutls_brotlidec_dlhandle = NULL;                \
+      return err;                              \
+    }
 #define VOID_FUNC FUNC
 #include "brotlidecfuncs.h"
 #undef VOID_FUNC
@@ -165,6 +168,12 @@ gnutls_brotlidec_unload_library (void)
 #pragma GCC diagnostic pop
 }
 
+unsigned
+gnutls_brotlidec_is_usable (void)
+{
+  return gnutls_brotlidec_dlhandle != NULL;
+}
+
 #else /* GNUTLS_BROTLIDEC_ENABLE_DLOPEN */
 
 int
@@ -180,4 +189,11 @@ gnutls_brotlidec_unload_library (void)
 {
 }
 
+unsigned
+gnutls_brotlidec_is_usable (void)
+{
+  /* The library is linked at build time, thus always usable */
+  return 1;
+}
+
 #endif /* !GNUTLS_BROTLIDEC_ENABLE_DLOPEN */
index 31397f24cfc950489b8a5c36c732d335d4e7bdad..f7d97eed355e8a0f19032fccf4339b8a6b9fc078 100644 (file)
@@ -44,4 +44,11 @@ int gnutls_brotlidec_ensure_library (const char *soname, int flags);
  */
 void gnutls_brotlidec_unload_library (void);
 
+/* Return 1 if the library is loaded and usable.
+ *
+ * Note that this function is NOT thread-safe; when calling it from
+ * multi-threaded programs, protect it with a locking mechanism.
+ */
+unsigned gnutls_brotlidec_is_usable (void);
+
 #endif /* GNUTLS_LIB_DLWRAP_BROTLIDEC_H_ */
index 1deff747e212362daae440fc48a351f88a97fa75..fae13ed313e4421be9e5d4f6c3bbaeaec953c3b7 100644 (file)
@@ -128,10 +128,13 @@ gnutls_brotlienc_ensure_library (const char *soname, int flags)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-macros"
 
-#define FUNC(ret, name, args, cargs)   \
-  err = ENSURE_SYMBOL(name);           \
-  if (err < 0)                         \
-    return err;
+#define FUNC(ret, name, args, cargs)           \
+  err = ENSURE_SYMBOL(name);                   \
+  if (err < 0)                                 \
+    {                                          \
+      gnutls_brotlienc_dlhandle = NULL;                \
+      return err;                              \
+    }
 #define VOID_FUNC FUNC
 #include "brotliencfuncs.h"
 #undef VOID_FUNC
@@ -165,6 +168,12 @@ gnutls_brotlienc_unload_library (void)
 #pragma GCC diagnostic pop
 }
 
+unsigned
+gnutls_brotlienc_is_usable (void)
+{
+  return gnutls_brotlienc_dlhandle != NULL;
+}
+
 #else /* GNUTLS_BROTLIENC_ENABLE_DLOPEN */
 
 int
@@ -180,4 +189,11 @@ gnutls_brotlienc_unload_library (void)
 {
 }
 
+unsigned
+gnutls_brotlienc_is_usable (void)
+{
+  /* The library is linked at build time, thus always usable */
+  return 1;
+}
+
 #endif /* !GNUTLS_BROTLIENC_ENABLE_DLOPEN */
index ed1af45e040f2c7714a2c0c4d106d1af03cf7ef9..4dfbf3b838d8b280b18e47199dfe4d8e213498bf 100644 (file)
@@ -44,4 +44,11 @@ int gnutls_brotlienc_ensure_library (const char *soname, int flags);
  */
 void gnutls_brotlienc_unload_library (void);
 
+/* Return 1 if the library is loaded and usable.
+ *
+ * Note that this function is NOT thread-safe; when calling it from
+ * multi-threaded programs, protect it with a locking mechanism.
+ */
+unsigned gnutls_brotlienc_is_usable (void);
+
 #endif /* GNUTLS_LIB_DLWRAP_BROTLIENC_H_ */
index c05f883127231a27706df106bc797701c842e9da..f9ae269faaf5c979fea201ba28d073c1afb2f02f 100644 (file)
@@ -128,10 +128,13 @@ gnutls_oqs_ensure_library (const char *soname, int flags)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-macros"
 
-#define FUNC(ret, name, args, cargs)   \
-  err = ENSURE_SYMBOL(name);           \
-  if (err < 0)                         \
-    return err;
+#define FUNC(ret, name, args, cargs)           \
+  err = ENSURE_SYMBOL(name);                   \
+  if (err < 0)                                 \
+    {                                          \
+      gnutls_oqs_dlhandle = NULL;              \
+      return err;                              \
+    }
 #define VOID_FUNC FUNC
 #include "oqsfuncs.h"
 #undef VOID_FUNC
@@ -165,6 +168,12 @@ gnutls_oqs_unload_library (void)
 #pragma GCC diagnostic pop
 }
 
+unsigned
+gnutls_oqs_is_usable (void)
+{
+  return gnutls_oqs_dlhandle != NULL;
+}
+
 #else /* GNUTLS_OQS_ENABLE_DLOPEN */
 
 int
@@ -180,4 +189,11 @@ gnutls_oqs_unload_library (void)
 {
 }
 
+unsigned
+gnutls_oqs_is_usable (void)
+{
+  /* The library is linked at build time, thus always usable */
+  return 1;
+}
+
 #endif /* !GNUTLS_OQS_ENABLE_DLOPEN */
index 6a1d8e0766f5b938b2513b385a6d5872e9bbdeda..1cf5d015a5e83f9d3e302c9ef3c3ec3fa6116426 100644 (file)
@@ -50,4 +50,11 @@ int gnutls_oqs_ensure_library (const char *soname, int flags);
  */
 void gnutls_oqs_unload_library (void);
 
+/* Return 1 if the library is loaded and usable.
+ *
+ * Note that this function is NOT thread-safe; when calling it from
+ * multi-threaded programs, protect it with a locking mechanism.
+ */
+unsigned gnutls_oqs_is_usable (void);
+
 #endif /* GNUTLS_LIB_DLWRAP_OQS_H_ */
index 878070c0a47cedcb43955b584bb09b5b900432ec..19851513a929e5e3f43be4088498c0a7f710a50d 100644 (file)
@@ -128,10 +128,13 @@ gnutls_zlib_ensure_library (const char *soname, int flags)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-macros"
 
-#define FUNC(ret, name, args, cargs)   \
-  err = ENSURE_SYMBOL(name);           \
-  if (err < 0)                         \
-    return err;
+#define FUNC(ret, name, args, cargs)           \
+  err = ENSURE_SYMBOL(name);                   \
+  if (err < 0)                                 \
+    {                                          \
+      gnutls_zlib_dlhandle = NULL;             \
+      return err;                              \
+    }
 #define VOID_FUNC FUNC
 #include "zlibfuncs.h"
 #undef VOID_FUNC
@@ -165,6 +168,12 @@ gnutls_zlib_unload_library (void)
 #pragma GCC diagnostic pop
 }
 
+unsigned
+gnutls_zlib_is_usable (void)
+{
+  return gnutls_zlib_dlhandle != NULL;
+}
+
 #else /* GNUTLS_ZLIB_ENABLE_DLOPEN */
 
 int
@@ -180,4 +189,11 @@ gnutls_zlib_unload_library (void)
 {
 }
 
+unsigned
+gnutls_zlib_is_usable (void)
+{
+  /* The library is linked at build time, thus always usable */
+  return 1;
+}
+
 #endif /* !GNUTLS_ZLIB_ENABLE_DLOPEN */
index a9666d27f53ac0e63f938aa59ab62c90e7a1d789..0d7113febf64beb553d7d66040309d0a1ffc6956 100644 (file)
@@ -44,4 +44,11 @@ int gnutls_zlib_ensure_library (const char *soname, int flags);
  */
 void gnutls_zlib_unload_library (void);
 
+/* Return 1 if the library is loaded and usable.
+ *
+ * Note that this function is NOT thread-safe; when calling it from
+ * multi-threaded programs, protect it with a locking mechanism.
+ */
+unsigned gnutls_zlib_is_usable (void);
+
 #endif /* GNUTLS_LIB_DLWRAP_ZLIB_H_ */
index 532c80b61014043bb65c8e20db8c6a79bf5e4400..bd5153e4648a680aeea50f482b3497529318e801 100644 (file)
@@ -128,10 +128,13 @@ gnutls_zstd_ensure_library (const char *soname, int flags)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wunused-macros"
 
-#define FUNC(ret, name, args, cargs)   \
-  err = ENSURE_SYMBOL(name);           \
-  if (err < 0)                         \
-    return err;
+#define FUNC(ret, name, args, cargs)           \
+  err = ENSURE_SYMBOL(name);                   \
+  if (err < 0)                                 \
+    {                                          \
+      gnutls_zstd_dlhandle = NULL;             \
+      return err;                              \
+    }
 #define VOID_FUNC FUNC
 #include "zstdfuncs.h"
 #undef VOID_FUNC
@@ -165,6 +168,12 @@ gnutls_zstd_unload_library (void)
 #pragma GCC diagnostic pop
 }
 
+unsigned
+gnutls_zstd_is_usable (void)
+{
+  return gnutls_zstd_dlhandle != NULL;
+}
+
 #else /* GNUTLS_ZSTD_ENABLE_DLOPEN */
 
 int
@@ -180,4 +189,11 @@ gnutls_zstd_unload_library (void)
 {
 }
 
+unsigned
+gnutls_zstd_is_usable (void)
+{
+  /* The library is linked at build time, thus always usable */
+  return 1;
+}
+
 #endif /* !GNUTLS_ZSTD_ENABLE_DLOPEN */
index 80ac2fbd4679833d6b74ad60f0f9119f48a5dd61..4a68a45e37384d4e2ed37033e90dc74f9612dc01 100644 (file)
@@ -44,4 +44,11 @@ int gnutls_zstd_ensure_library (const char *soname, int flags);
  */
 void gnutls_zstd_unload_library (void);
 
+/* Return 1 if the library is loaded and usable.
+ *
+ * Note that this function is NOT thread-safe; when calling it from
+ * multi-threaded programs, protect it with a locking mechanism.
+ */
+unsigned gnutls_zstd_is_usable (void);
+
 #endif /* GNUTLS_LIB_DLWRAP_ZSTD_H_ */
index 4aaf79a76830b3705f42cc5db4d8fc95667a30c3..42d90ee9d59ff5ec6909bb3d347c70a43e2c8f8f 100644 (file)
@@ -329,14 +329,6 @@ static int _gnutls_global_init(unsigned constructor)
                goto out;
        }
 
-#ifdef HAVE_LIBOQS
-       ret = _gnutls_liboqs_init();
-       if (ret < 0) {
-               gnutls_assert();
-               goto out;
-       }
-#endif
-
 #ifndef _WIN32
        ret = _gnutls_register_fork_handler();
        if (ret < 0) {
index c5531d479617c7046334b1833e21d76b91ca3bef..3c0df566446a7a43641f65c637410cf1fd901116 100644 (file)
 #endif
 
 #include "errors.h"
+#include "locks.h"
 
 #include "dlwrap/oqs.h"
 #include "liboqs/rand.h"
 #include "liboqs/sha3.h"
 
-int _gnutls_liboqs_init(void)
+/* We can't use GNUTLS_ONCE here, as it wouldn't allow manual unloading */
+GNUTLS_STATIC_MUTEX(liboqs_init_mutex);
+static int _liboqs_init = 0;
+
+int _gnutls_liboqs_ensure(void)
 {
+       int ret;
+
+       if (_liboqs_init)
+               return GNUTLS_E_SUCCESS;
+
+       ret = gnutls_static_mutex_lock(&liboqs_init_mutex);
+       if (unlikely(ret < 0))
+               return gnutls_assert_val(ret);
+
        if (gnutls_oqs_ensure_library(OQS_LIBRARY_SONAME,
-                                     RTLD_NOW | RTLD_GLOBAL) < 0)
-               return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+                                     RTLD_NOW | RTLD_GLOBAL) < 0) {
+               _gnutls_debug_log(
+                       "liboqs: unable to initialize liboqs functions\n");
+               ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+               goto out;
+       }
 
        _gnutls_liboqs_sha3_init();
        GNUTLS_OQS_FUNC(OQS_init)();
        _gnutls_liboqs_rand_init();
-       return 0;
+
+       _liboqs_init = 1;
+       ret = GNUTLS_E_SUCCESS;
+
+out:
+       (void)gnutls_static_mutex_unlock(&liboqs_init_mutex);
+
+       return ret;
 }
 
+/* This is not thread-safe: call this function only from
+ * gnutls_global_deinit, which has a proper protection.
+ */
 void _gnutls_liboqs_deinit(void)
 {
-       _gnutls_liboqs_rand_deinit();
-       _gnutls_liboqs_sha3_deinit();
-       GNUTLS_OQS_FUNC(OQS_destroy)();
+       if (_liboqs_init) {
+               _gnutls_liboqs_rand_deinit();
+               _gnutls_liboqs_sha3_deinit();
+               GNUTLS_OQS_FUNC(OQS_destroy)();
+       }
+
        gnutls_oqs_unload_library();
+       _liboqs_init = 0;
 }
index 50206fa77ccb02b21dcae9e029e2026860bf5f76..3717454275dc74355911de0a91b71b019dd6f506 100644 (file)
@@ -21,7 +21,7 @@
 #ifndef GNUTLS_LIB_LIBOQS_LIBOQS_H
 #define GNUTLS_LIB_LIBOQS_LIBOQS_H
 
-int _gnutls_liboqs_init(void);
+int _gnutls_liboqs_ensure(void);
 void _gnutls_liboqs_deinit(void);
 
 #endif /* GNUTLS_LIB_LIBOQS_LIBOQS_H */
index 4155a540ed798672c5eb915e25c5928b7f82e80f..79f7988d505c9194e29c9a731045c31edc84b7ce 100644 (file)
@@ -72,6 +72,7 @@
 #include "dh.h"
 #ifdef HAVE_LIBOQS
 #include "dlwrap/oqs.h"
+#include "liboqs/liboqs.h"
 #endif
 
 static inline const struct ecc_curve *get_supported_nist_curve(int curve);
@@ -703,6 +704,9 @@ static int _wrap_nettle_pk_encaps(gnutls_pk_algorithm_t algo,
                OQS_KEM *kem = NULL;
                OQS_STATUS rc;
 
+               if (_gnutls_liboqs_ensure() < 0)
+                       return gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
+
                kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
                if (kem == NULL)
                        return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
@@ -761,6 +765,9 @@ static int _wrap_nettle_pk_decaps(gnutls_pk_algorithm_t algo,
                OQS_KEM *kem = NULL;
                OQS_STATUS rc;
 
+               if (_gnutls_liboqs_ensure() < 0)
+                       return gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
+
                kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
                if (kem == NULL)
                        return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
@@ -2342,9 +2349,6 @@ static int _wrap_nettle_pk_exists(gnutls_pk_algorithm_t pk)
        case GNUTLS_PK_RSA_PSS:
        case GNUTLS_PK_RSA_OAEP:
        case GNUTLS_PK_EDDSA_ED25519:
-#ifdef HAVE_LIBOQS
-       case GNUTLS_PK_EXP_KYBER768:
-#endif
 #if ENABLE_GOST
        case GNUTLS_PK_GOST_01:
        case GNUTLS_PK_GOST_12_256:
@@ -2353,6 +2357,10 @@ static int _wrap_nettle_pk_exists(gnutls_pk_algorithm_t pk)
        case GNUTLS_PK_ECDH_X448:
        case GNUTLS_PK_EDDSA_ED448:
                return 1;
+#ifdef HAVE_LIBOQS
+       case GNUTLS_PK_EXP_KYBER768:
+               return _gnutls_liboqs_ensure() == 0;
+#endif
        default:
                return 0;
        }
@@ -2986,11 +2994,15 @@ static int pct_test(gnutls_pk_algorithm_t algo,
        }
        case GNUTLS_PK_ECDH_X25519:
        case GNUTLS_PK_ECDH_X448:
+               break;
 #ifdef HAVE_LIBOQS
        case GNUTLS_PK_EXP_KYBER768:
+               if (_gnutls_liboqs_ensure() < 0) {
+                       ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
+                       goto cleanup;
+               }
 #endif
-               ret = 0;
-               goto cleanup;
+               break;
        default:
                ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
                goto cleanup;
@@ -3724,6 +3736,13 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo,
                OQS_KEM *kem = NULL;
                OQS_STATUS rc;
 
+#ifdef HAVE_LIBOQS
+               if (_gnutls_liboqs_ensure() < 0) {
+                       ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
+                       goto cleanup;
+               }
+#endif
+
                not_approved = true;
 
                kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
@@ -4017,7 +4036,9 @@ static int wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo,
        }
 #ifdef HAVE_LIBOQS
        case GNUTLS_PK_EXP_KYBER768:
-               ret = 0;
+               ret = _gnutls_liboqs_ensure();
+               if (ret < 0)
+                       ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_PK_ALGORITHM);
                break;
 #endif
 #if ENABLE_GOST