diff options
Diffstat (limited to 'src/shared')
-rw-r--r-- | src/shared/openssl-util.c | 220 | ||||
-rw-r--r-- | src/shared/openssl-util.h | 43 |
2 files changed, 248 insertions, 15 deletions
diff --git a/src/shared/openssl-util.c b/src/shared/openssl-util.c index 2ab89c7639..818f4a5f19 100644 --- a/src/shared/openssl-util.c +++ b/src/shared/openssl-util.c @@ -3,12 +3,15 @@ #include <endian.h> #include "alloc-util.h" +#include "ask-password-api.h" #include "fd-util.h" +#include "fileio.h" #include "hexdecoct.h" #include "memory-util.h" #include "openssl-util.h" #include "random-util.h" #include "string-util.h" +#include "strv.h" #if HAVE_OPENSSL # include <openssl/rsa.h> @@ -1309,10 +1312,15 @@ int pkey_generate_volume_keys( } } -static int load_key_from_provider(const char *provider, const char *private_key_uri, EVP_PKEY **ret) { +static int load_key_from_provider( + const char *provider, + const char *private_key_uri, + OpenSSLAskPasswordUI *ui, + EVP_PKEY **ret) { assert(provider); assert(private_key_uri); + assert(ui); assert(ret); #if OPENSSL_VERSION_MAJOR >= 3 @@ -1325,8 +1333,8 @@ static int load_key_from_provider(const char *provider, const char *private_key_ _cleanup_(OSSL_STORE_closep) OSSL_STORE_CTX *store = OSSL_STORE_open( private_key_uri, - /* ui_method= */ NULL, - /* ui_data= */ NULL, + ui->method, + &ui->request, /* post_process= */ NULL, /* post_process_data= */ NULL); if (!store) @@ -1348,10 +1356,10 @@ static int load_key_from_provider(const char *provider, const char *private_key_ #endif } -static int load_key_from_engine(const char *engine, const char *private_key_uri, EVP_PKEY **ret) { - +static int load_key_from_engine(const char *engine, const char *private_key_uri, OpenSSLAskPasswordUI *ui, EVP_PKEY **ret) { assert(engine); assert(private_key_uri); + assert(ui); assert(ret); #if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0) @@ -1363,11 +1371,13 @@ static int load_key_from_engine(const char *engine, const char *private_key_uri, 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 (ENGINE_ctrl(e, ENGINE_CTRL_SET_USER_INTERFACE, /*i=*/ 0, ui->method, /*f=*/ NULL) <= 0) + return log_openssl_errors("Failed to set engine user interface"); + + if (ENGINE_ctrl(e, ENGINE_CTRL_SET_CALLBACK_DATA, /*i=*/ 0, &ui->request, /*f=*/ NULL) <= 0) + return log_openssl_errors("Failed to set engine user interface data"); + + _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = ENGINE_load_private_key(e, private_key_uri, ui->method, &ui->request); if (!private_key) return log_openssl_errors("Failed to load private key from '%s'", private_key_uri); REENABLE_WARNING; @@ -1384,24 +1394,88 @@ int openssl_load_key_from_token( KeySourceType private_key_source_type, const char *private_key_source, const char *private_key, - EVP_PKEY **ret) { + OpenSSLAskPasswordUI *ui, + EVP_PKEY **ret_private_key) { assert(IN_SET(private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)); assert(private_key_source); + assert(ui); assert(private_key); switch (private_key_source_type) { case OPENSSL_KEY_SOURCE_ENGINE: - return load_key_from_engine(private_key_source, private_key, ret); + return load_key_from_engine(private_key_source, private_key, ui, ret_private_key); case OPENSSL_KEY_SOURCE_PROVIDER: - return load_key_from_provider(private_key_source, private_key, ret); + return load_key_from_provider(private_key_source, private_key, ui, ret_private_key); default: assert_not_reached(); } } + +static int openssl_ask_password_ui_read(UI *ui, UI_STRING *uis) { + int r; + + switch(UI_get_string_type(uis)) { + case UIT_PROMPT: { + /* If no ask password request was configured use the default openssl UI. */ + AskPasswordRequest *req = UI_get0_user_data(ui); + if (!req) + return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); + + req->message = UI_get0_output_string(uis); + + _cleanup_(strv_freep) char **l = NULL; + r = ask_password_auto(req, /*until=*/ 0, ASK_PASSWORD_ACCEPT_CACHED|ASK_PASSWORD_PUSH_CACHE, &l); + if (r < 0) { + log_error_errno(r, "Failed to query for PIN: %m"); + return 0; + } + + if (strv_length(l) != 1) { + log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected only a single password/pin."); + return 0; + } + + if (UI_set_result(ui, uis, *l) != 0) { + log_openssl_errors("Failed to set user interface result"); + return 0; + } + + return 1; + } + default: + return (UI_method_get_reader(UI_OpenSSL()))(ui, uis); + } +} #endif +int openssl_ask_password_ui_new(OpenSSLAskPasswordUI **ret) { +#if HAVE_OPENSSL + assert(ret); + + _cleanup_(UI_destroy_methodp) UI_METHOD *method = UI_create_method("systemd-ask-password"); + if (!method) + return log_openssl_errors("Failed to initialize openssl user interface"); + + if (UI_method_set_reader(method, openssl_ask_password_ui_read) != 0) + return log_openssl_errors("Failed to set openssl user interface reader"); + + OpenSSLAskPasswordUI *ui = new(OpenSSLAskPasswordUI, 1); + if (!ui) + return log_oom_debug(); + + *ui = (OpenSSLAskPasswordUI) { + .method = TAKE_PTR(method), + }; + + *ret = TAKE_PTR(ui); + return 0; +#else + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot create ask-password user interface."); +#endif +} + int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { #if HAVE_OPENSSL _cleanup_free_ uint8_t *der = NULL; @@ -1420,6 +1494,126 @@ int x509_fingerprint(X509 *cert, uint8_t buffer[static SHA256_DIGEST_SIZE]) { #endif } +int openssl_load_x509_certificate(const char *path, X509 **ret) { +#if HAVE_OPENSSL + _cleanup_free_ char *rawcert = NULL; + _cleanup_(X509_freep) X509 *cert = NULL; + _cleanup_(BIO_freep) BIO *cb = NULL; + size_t rawcertsz; + int r; + + assert(path); + assert(ret); + + r = read_full_file_full( + AT_FDCWD, path, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &rawcert, &rawcertsz); + if (r < 0) + return log_debug_errno(r, "Failed to read certificate file '%s': %m", path); + + cb = BIO_new_mem_buf(rawcert, rawcertsz); + if (!cb) + return log_oom_debug(); + + cert = PEM_read_bio_X509(cb, NULL, NULL, NULL); + if (!cert) + return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse X.509 certificate: %s", + ERR_error_string(ERR_get_error(), NULL)); + + if (ret) + *ret = TAKE_PTR(cert); + + return 0; +#else + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot load X509 certificate."); +#endif +} + +static int openssl_load_private_key_from_file(const char *path, EVP_PKEY **ret) { +#if HAVE_OPENSSL + _cleanup_(erase_and_freep) char *rawkey = NULL; + _cleanup_(BIO_freep) BIO *kb = NULL; + _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL; + size_t rawkeysz; + int r; + + assert(path); + assert(ret); + + r = read_full_file_full( + AT_FDCWD, path, UINT64_MAX, SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, + NULL, + &rawkey, &rawkeysz); + if (r < 0) + return log_debug_errno(r, "Failed to read key file '%s': %m", path); + + kb = BIO_new_mem_buf(rawkey, rawkeysz); + if (!kb) + return log_oom_debug(); + + pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL); + if (!pk) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse PEM private key: %s", + ERR_error_string(ERR_get_error(), NULL)); + + if (ret) + *ret = TAKE_PTR(pk); + + return 0; +#else + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot load private key."); +#endif +} + +int openssl_load_private_key( + KeySourceType private_key_source_type, + const char *private_key_source, + const char *private_key, + const AskPasswordRequest *request, + EVP_PKEY **ret_private_key, + OpenSSLAskPasswordUI **ret_user_interface) { + + int r; + + assert(private_key); + assert(request); + + if (private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { + r = openssl_load_private_key_from_file(private_key, ret_private_key); + if (r < 0) + return r; + + *ret_user_interface = NULL; + } else { + _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; + r = openssl_ask_password_ui_new(&ui); + if (r < 0) + return log_debug_errno(r, "Failed to allocate ask-password user interface: %m"); + + ui->request = *request; + + r = openssl_load_key_from_token( + private_key_source_type, + private_key_source, + private_key, + ui, + ret_private_key); + if (r < 0) + return log_debug_errno( + r, + "Failed to load key '%s' from OpenSSL private key source %s: %m", + private_key, + private_key_source); + + *ret_user_interface = TAKE_PTR(ui); + } + + return 0; +} + int parse_openssl_key_source_argument( const char *argument, char **private_key_source, diff --git a/src/shared/openssl-util.h b/src/shared/openssl-util.h index 1a89fcc2bd..c9acd40f22 100644 --- a/src/shared/openssl-util.h +++ b/src/shared/openssl-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include "ask-password-api.h" #include "iovec-util.h" #include "macro.h" #include "sha256.h" @@ -13,6 +14,8 @@ typedef enum KeySourceType { _OPENSSL_KEY_SOURCE_INVALID = -EINVAL, } KeySourceType; +typedef struct OpenSSLAskPasswordUI OpenSSLAskPasswordUI; + int parse_openssl_key_source_argument(const char *argument, char **private_key_source, KeySourceType *private_key_source_type); #define X509_FINGERPRINT_SIZE SHA256_DIGEST_SIZE @@ -26,6 +29,7 @@ int parse_openssl_key_source_argument(const char *argument, char **private_key_s # include <openssl/opensslv.h> # include <openssl/pkcs7.h> # include <openssl/ssl.h> +# include <openssl/ui.h> # include <openssl/x509v3.h> # ifndef OPENSSL_VERSION_MAJOR /* OPENSSL_VERSION_MAJOR macro was added in OpenSSL 3. Thus, if it doesn't exist, we must be before OpenSSL 3. */ @@ -130,12 +134,13 @@ 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(KeySourceType private_key_source_type, const char *private_key_source, const char *private_key, EVP_PKEY **ret); +int openssl_load_key_from_token(KeySourceType private_key_source_type, const char *private_key_source, const char *private_key, OpenSSLAskPasswordUI *ui, EVP_PKEY **ret_private_key); #else typedef struct X509 X509; typedef struct EVP_PKEY EVP_PKEY; +typedef struct UI_METHOD UI_METHOD; static inline void *X509_free(X509 *p) { assert(p == NULL); @@ -147,11 +152,17 @@ static inline void *EVP_PKEY_free(EVP_PKEY *p) { return NULL; } +static inline void* UI_destroy_method(UI_METHOD *p) { + assert(p == NULL); + return NULL; +} + static inline int openssl_load_key_from_token( KeySourceType private_key_source_type, const char *private_key_source, const char *private_key, - EVP_PKEY **ret) { + OpenSSLAskPasswordUI *ui, + EVP_PKEY **ret_private_key) { return -EOPNOTSUPP; } @@ -160,9 +171,37 @@ static inline int openssl_load_key_from_token( DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(X509*, X509_free, NULL); DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EVP_PKEY*, EVP_PKEY_free, NULL); +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(UI_METHOD*, UI_destroy_method, NULL); + +struct OpenSSLAskPasswordUI { + AskPasswordRequest request; + UI_METHOD *method; +}; + +int openssl_ask_password_ui_new(OpenSSLAskPasswordUI **ret); + +static inline OpenSSLAskPasswordUI* openssl_ask_password_ui_free(OpenSSLAskPasswordUI *ui) { + if (!ui) + return NULL; + + UI_destroy_method(ui->method); + return mfree(ui); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(OpenSSLAskPasswordUI*, openssl_ask_password_ui_free, NULL); int x509_fingerprint(X509 *cert, uint8_t buffer[static X509_FINGERPRINT_SIZE]); +int openssl_load_x509_certificate(const char *path, X509 **ret); + +int openssl_load_private_key( + KeySourceType private_key_source_type, + const char *private_key_source, + const char *private_key, + const AskPasswordRequest *request, + EVP_PKEY **ret_private_key, + OpenSSLAskPasswordUI **ret_user_interface); + #if PREFER_OPENSSL /* The openssl definition */ typedef const EVP_MD* hash_md_t; |