From: Luca Boccassi Date: Thu, 12 Oct 2023 09:22:20 +0000 (+0100) Subject: openssl: add helper to load key from provider/engine X-Git-Tag: v256-rc1~912^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=dba0afa14e6baf308e504a469106e6a0bd798a4e;p=thirdparty%2Fsystemd.git openssl: add helper to load key from provider/engine It's not the literal private key, but EVP_PKEY becomes a reference to the engine/provider that OpenSSL knows how to use later --- diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 7f59dcae71e..6fa82d7177a 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -129,6 +129,14 @@ All tools: * `$SYSTEMD_VERITY_SHARING=0` — if set, sharing dm-verity devices by using a stable `-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 diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 6cd521623e9..e2386d0513d 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -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]) { diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 1a9055af017..8d7d4f8c9fb 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -11,6 +11,7 @@ # include # include # include +# include # include # include # include @@ -25,6 +26,8 @@ # include # include # include +# include +# include # 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);