]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
openssl: add helper to load key from provider/engine
authorLuca Boccassi <bluca@debian.org>
Thu, 12 Oct 2023 09:22:20 +0000 (10:22 +0100)
committerLuca Boccassi <bluca@debian.org>
Fri, 9 Feb 2024 14:36:10 +0000 (14:36 +0000)
It's not the literal private key, but EVP_PKEY becomes a reference
to the engine/provider that OpenSSL knows how to use later

docs/ENVIRONMENT.md
src/shared/openssl-util.c
src/shared/openssl-util.h

index 7f59dcae71e56d2d937d6a04c72bfbba0c46aecb..6fa82d7177a7b2447e91f4992b34fb5093ae1763 100644 (file)
@@ -129,6 +129,14 @@ All tools:
 * `$SYSTEMD_VERITY_SHARING=0` — if set, sharing dm-verity devices by
   using a stable `<ROOTHASH>-verity` device mapper name will be disabled.
 
+* `$SYSTEMD_OPENSSL_KEY_LOADER`— when using OpenSSL to load a key via an engine
+  or a provider, can be used to force the usage of one or the other interface.
+  Set to 'engine' to force the usage of the old engine API, and to 'provider'
+  force the usage of the new provider API. If unset, the provider will be tried
+  first and the engine as a fallback if that fails. Providers are the new OpenSSL
+  3 API, but there are very few if any in a production-ready state, so engines
+  are still needed.
+
 `systemctl`:
 
 * `$SYSTEMCTL_FORCE_BUS=1` — if set, do not connect to PID 1's private D-Bus
index 6cd521623e9f8e7c2b08462ad4d3aff674bbac01..e2386d0513d0fc2805327cf95d449eccb1e5b0ed 100644 (file)
@@ -1298,6 +1298,107 @@ int pkey_generate_volume_keys(
                 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported public key type: %s", OBJ_nid2sn(type));
         }
 }
+
+static int load_key_from_provider(const char *provider, const char *private_key_uri, EVP_PKEY **ret) {
+
+        assert(provider);
+        assert(private_key_uri);
+        assert(ret);
+
+#if OPENSSL_VERSION_MAJOR >= 3
+        /* Load the provider so that this can work without any custom written configuration in /etc/.
+         * Also load the 'default' as that seems to be the recommendation. */
+        if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, provider, /* retain_fallbacks= */ true))
+                return log_openssl_errors("Failed to load OpenSSL provider '%s'", provider);
+        if (!OSSL_PROVIDER_try_load(/* ctx= */ NULL, "default", /* retain_fallbacks= */ true))
+                return log_openssl_errors("Failed to load OpenSSL provider 'default'");
+
+        _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open(
+                        private_key_uri,
+                        /* ui_method= */ NULL,
+                        /* ui_data= */ NULL,
+                        /* post_process= */ NULL,
+                        /* post_process_data= */ NULL);
+        if (!store)
+                return log_openssl_errors("Failed to open OpenSSL store via '%s'", private_key_uri);
+
+        _cleanup_(OSSL_STORE_INFO_freep) OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+        if (!info)
+                return log_openssl_errors("Failed to load OpenSSL store via '%s'", private_key_uri);
+
+        _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = OSSL_STORE_INFO_get1_PKEY(info);
+        if (!private_key)
+                return log_openssl_errors("Failed to load private key via '%s'", private_key_uri);
+
+        *ret = TAKE_PTR(private_key);
+
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
+}
+
+static int load_key_from_engine(const char *engine, const char *private_key_uri, EVP_PKEY **ret) {
+
+        assert(engine);
+        assert(private_key_uri);
+        assert(ret);
+
+        DISABLE_WARNING_DEPRECATED_DECLARATIONS;
+        _cleanup_(ENGINE_freep) ENGINE *e = ENGINE_by_id(engine);
+        if (!e)
+                return log_openssl_errors("Failed to load signing engine '%s'", engine);
+
+        if (ENGINE_init(e) == 0)
+                return log_openssl_errors("Failed to initialize signing engine '%s'", engine);
+
+        _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = ENGINE_load_private_key(
+                        e,
+                        private_key_uri,
+                        /* ui_method= */ NULL,
+                        /* callback_data= */ NULL);
+        if (!private_key)
+                return log_openssl_errors("Failed to load private key from '%s'", private_key_uri);
+        REENABLE_WARNING;
+
+        *ret = TAKE_PTR(private_key);
+
+        return 0;
+}
+
+int openssl_load_key_from_token(const char *private_key_uri, EVP_PKEY **ret) {
+        _cleanup_free_ char *provider = NULL;
+        const char *colon, *e;
+        int r;
+
+        assert(private_key_uri);
+
+        colon = strchr(private_key_uri, ':');
+        if (!colon)
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid URI '%s'", private_key_uri);
+
+        provider = strndup(private_key_uri, colon - private_key_uri);
+        if (!provider)
+                return log_oom_debug();
+
+        e = secure_getenv("SYSTEMD_OPENSSL_KEY_LOADER");
+        if (e) {
+                if (streq(e, "provider"))
+                        r = load_key_from_provider(provider, private_key_uri, ret);
+                else if (streq(e, "engine"))
+                        r = load_key_from_engine(provider, private_key_uri, ret);
+                else
+                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid value for SYSTEMD_OPENSSL_KEY_LOADER: %s", e);
+        } else {
+                r = load_key_from_provider(provider, private_key_uri, ret);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to load key from provider '%s', falling back to engine", provider);
+                        r = load_key_from_engine(provider, private_key_uri, ret);
+                }
+        }
+
+        return r;
+}
 #endif
 
 int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) {
index 1a9055af017232c869c15d08c14d018604a33f27..8d7d4f8c9fbccf1ef8f32e10ec42f239131a9dd5 100644 (file)
@@ -11,6 +11,7 @@
 #  include <openssl/bio.h>
 #  include <openssl/bn.h>
 #  include <openssl/crypto.h>
+#  include <openssl/engine.h>
 #  include <openssl/err.h>
 #  include <openssl/evp.h>
 #  include <openssl/opensslv.h>
@@ -25,6 +26,8 @@
 #    include <openssl/core_names.h>
 #    include <openssl/kdf.h>
 #    include <openssl/param_build.h>
+#    include <openssl/provider.h>
+#    include <openssl/store.h>
 #  endif
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_MACRO(void*, OPENSSL_free, NULL);
@@ -41,6 +44,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(SSL*, SSL_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(BIO*, BIO_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD_CTX*, EVP_MD_CTX_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ASN1_OCTET_STRING*, ASN1_OCTET_STRING_free, NULL);
+DISABLE_WARNING_DEPRECATED_DECLARATIONS;
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ENGINE*, ENGINE_free, NULL);
+REENABLE_WARNING;
 #if OPENSSL_VERSION_MAJOR >= 3
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_CIPHER*, EVP_CIPHER_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_KDF*, EVP_KDF_free, NULL);
@@ -50,6 +56,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MAC_CTX*, EVP_MAC_CTX_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_MD*, EVP_MD_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM*, OSSL_PARAM_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_PARAM_BLD*, OSSL_PARAM_BLD_free, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_STORE_CTX*, OSSL_STORE_close, NULL);
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OSSL_STORE_INFO*, OSSL_STORE_INFO_free, NULL);
 #else
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL);
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(HMAC_CTX*, HMAC_CTX_free, NULL);
@@ -115,6 +123,8 @@ int pubkey_fingerprint(EVP_PKEY *pk, const EVP_MD *md, void **ret, size_t *ret_s
 
 int digest_and_sign(const EVP_MD *md, EVP_PKEY *privkey, const void *data, size_t size, void **ret, size_t *ret_size);
 
+int openssl_load_key_from_token(const char *private_key_uri, EVP_PKEY **ret);
+
 #else
 
 typedef struct X509 X509;
@@ -130,6 +140,10 @@ static inline void *EVP_PKEY_free(EVP_PKEY *p) {
         return NULL;
 }
 
+static inline int openssl_load_key_from_token(const char *private_key_uri, EVP_PKEY **ret) {
+        return -EOPNOTSUPP;
+}
+
 #endif
 
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL);