diff options
Diffstat (limited to 'src/shared/openssl-util.c')
-rw-r--r-- | src/shared/openssl-util.c | 220 |
1 files changed, 207 insertions, 13 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, |