diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/boot/measure.c | 65 | ||||
-rw-r--r-- | src/partition/repart.c | 174 | ||||
-rw-r--r-- | src/shared/openssl-util.c | 220 | ||||
-rw-r--r-- | src/shared/openssl-util.h | 43 |
4 files changed, 334 insertions, 168 deletions
diff --git a/src/boot/measure.c b/src/boot/measure.c index 557e06b094..3c409f8bd9 100644 --- a/src/boot/measure.c +++ b/src/boot/measure.c @@ -6,6 +6,7 @@ #include "sd-json.h" #include "alloc-util.h" +#include "ask-password-api.h" #include "build.h" #include "efi-loader.h" #include "fd-util.h" @@ -803,6 +804,7 @@ static int verb_calculate(int argc, char *argv[], void *userdata) { static int verb_sign(int argc, char *argv[], void *userdata) { _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; _cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL; + _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; _cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL; _cleanup_(X509_freep) X509 *certificate = NULL; size_t n; @@ -834,54 +836,31 @@ static int verb_sign(int argc, char *argv[], void *userdata) { /* This must be done before openssl_load_key_from_token() otherwise it will get stuck */ if (arg_certificate) { - _cleanup_(BIO_freep) BIO *cb = NULL; - _cleanup_free_ char *crt = NULL; - - r = read_full_file_full( - AT_FDCWD, arg_certificate, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_CONNECT_SOCKET, - /* bind_name= */ NULL, - &crt, &n); + r = openssl_load_x509_certificate(arg_certificate, &certificate); if (r < 0) - return log_error_errno(r, "Failed to read certificate file '%s': %m", arg_certificate); - - cb = BIO_new_mem_buf(crt, n); - if (!cb) - return log_oom(); - - certificate = PEM_read_bio_X509(cb, NULL, NULL, NULL); - if (!certificate) - return log_error_errno( - SYNTHETIC_ERRNO(EBADMSG), - "Failed to parse X.509 certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); + return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate); } - if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { - _cleanup_fclose_ FILE *privkeyf = NULL; - _cleanup_free_ char *resolved_pkey = NULL; + if (arg_private_key) { + if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { + r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &arg_private_key); + if (r < 0) + return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key); + } - r = parse_path_argument(arg_private_key, /* suppress_root= */ false, &resolved_pkey); + r = openssl_load_private_key( + arg_private_key_source_type, + arg_private_key_source, + arg_private_key, + &(AskPasswordRequest) { + .id = "measure-private-key-pin", + .keyring = arg_private_key, + .credential = "measure.private-key-pin", + }, + &privkey, + &ui); if (r < 0) - return log_error_errno(r, "Failed to parse private key path %s: %m", arg_private_key); - - privkeyf = fopen(resolved_pkey, "re"); - if (!privkeyf) - return log_error_errno(errno, "Failed to open private key file '%s': %m", resolved_pkey); - - privkey = PEM_read_PrivateKey(privkeyf, NULL, NULL, NULL); - if (!privkey) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse private key '%s'.", resolved_pkey); - } else if (arg_private_key_source && - IN_SET(arg_private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)) { - r = openssl_load_key_from_token( - arg_private_key_source_type, arg_private_key_source, arg_private_key, &privkey); - if (r < 0) - return log_error_errno( - r, - "Failed to load key '%s' from OpenSSL key source %s: %m", - arg_private_key, - arg_private_key_source); + return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key); } if (arg_public_key) { diff --git a/src/partition/repart.c b/src/partition/repart.c index 8070b02cab..be5171d5e1 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -17,6 +17,7 @@ #include "sd-json.h" #include "alloc-util.h" +#include "ask-password-api.h" #include "blkid-util.h" #include "blockdev-list.h" #include "blockdev-util.h" @@ -149,10 +150,10 @@ static PagerFlags arg_pager_flags = 0; static bool arg_legend = true; static void *arg_key = NULL; static size_t arg_key_size = 0; -static EVP_PKEY *arg_private_key = NULL; +static char *arg_private_key = NULL; static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; static char *arg_private_key_source = NULL; -static X509 *arg_certificate = NULL; +static char *arg_certificate = NULL; static char *arg_tpm2_device = NULL; static uint32_t arg_tpm2_seal_key_handle = 0; static char *arg_tpm2_device_key = NULL; @@ -182,9 +183,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep); -STATIC_DESTRUCTOR_REGISTER(arg_private_key, EVP_PKEY_freep); +STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); -STATIC_DESTRUCTOR_REGISTER(arg_certificate, X509_freep); +STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device_key, freep); STATIC_DESTRUCTOR_REGISTER(arg_tpm2_hash_pcr_values, freep); @@ -429,6 +430,9 @@ typedef struct Context { int backing_fd; bool from_scratch; + + X509 *certificate; + EVP_PKEY *private_key; } Context; static const char *empty_mode_table[_EMPTY_MODE_MAX] = { @@ -709,9 +713,11 @@ static Partition* partition_unlink_and_free(Context *context, Partition *p) { DEFINE_TRIVIAL_CLEANUP_FUNC(Partition*, partition_free); -static Context* context_new(sd_id128_t seed) { +static Context* context_new(sd_id128_t seed, X509 *certificate, EVP_PKEY *private_key) { Context *context; + /* Note: This function takes ownership of the certificate and private_key arguments. */ + context = new(Context, 1); if (!context) return NULL; @@ -721,6 +727,8 @@ static Context* context_new(sd_id128_t seed) { .end = UINT64_MAX, .total = UINT64_MAX, .seed = seed, + .certificate = certificate, + .private_key = private_key, }; return context; @@ -755,6 +763,9 @@ static Context* context_free(Context *context) { else free(context->node); + X509_free(context->certificate); + EVP_PKEY_free(context->private_key); + return mfree(context); } @@ -4979,6 +4990,8 @@ static int partition_format_verity_hash( static int sign_verity_roothash( const struct iovec *roothash, + X509 *certificate, + EVP_PKEY *private_key, struct iovec *ret_signature) { #if HAVE_OPENSSL @@ -4989,6 +5002,7 @@ static int sign_verity_roothash( int sigsz; assert(roothash); + assert(private_key); assert(iovec_is_set(roothash)); assert(ret_signature); @@ -5000,7 +5014,7 @@ static int sign_verity_roothash( if (!rb) return log_oom(); - p7 = PKCS7_sign(arg_certificate, arg_private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY); + p7 = PKCS7_sign(certificate, private_key, NULL, rb, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY); if (!p7) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s", ERR_error_string(ERR_get_error(), NULL)); @@ -5039,15 +5053,13 @@ static int partition_format_verity_sig(Context *context, Partition *p) { assert_se(hp = p->siblings[VERITY_HASH]); assert(!hp->dropped); - assert(arg_certificate); - assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0); - r = sign_verity_roothash(&hp->roothash, &sig); + r = sign_verity_roothash(&hp->roothash, context->certificate, context->private_key, &sig); if (r < 0) return r; - r = x509_fingerprint(arg_certificate, fp); + r = x509_fingerprint(context->certificate, fp); if (r < 0) return log_error_errno(r, "Unable to calculate X509 certificate fingerprint: %m"); @@ -6040,60 +6052,6 @@ static int context_mkfs(Context *context) { return 0; } -static int parse_x509_certificate(const char *certificate, size_t certificate_size, X509 **ret) { -#if HAVE_OPENSSL - _cleanup_(X509_freep) X509 *cert = NULL; - _cleanup_(BIO_freep) BIO *cb = NULL; - - assert(certificate); - assert(certificate_size > 0); - assert(ret); - - cb = BIO_new_mem_buf(certificate, certificate_size); - if (!cb) - return log_oom(); - - cert = PEM_read_bio_X509(cb, NULL, NULL, NULL); - if (!cert) - return log_error_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_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse X509 certificate."); -#endif -} - -static int parse_private_key(const char *key, size_t key_size, EVP_PKEY **ret) { -#if HAVE_OPENSSL - _cleanup_(BIO_freep) BIO *kb = NULL; - _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL; - - assert(key); - assert(key_size > 0); - assert(ret); - - kb = BIO_new_mem_buf(key, key_size); - if (!kb) - return log_oom(); - - pk = PEM_read_bio_PrivateKey(kb, NULL, NULL, NULL); - if (!pk) - return log_error_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_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot parse private key."); -#endif -} - static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *ret) { struct { sd_id128_t type_uuid; @@ -7898,9 +7856,7 @@ static int help(void) { return 0; } -static int parse_argv(int argc, char *argv[]) { - _cleanup_free_ char *private_key = NULL; - +static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY **ret_private_key, OpenSSLAskPasswordUI **ret_ui) { enum { ARG_VERSION = 0x100, ARG_NO_PAGER, @@ -7989,11 +7945,17 @@ static int parse_argv(int argc, char *argv[]) { {} }; + _cleanup_(X509_freep) X509 *certificate = NULL; + _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; + _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL; bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true; int c, r; assert(argc >= 0); assert(argv); + assert(ret_certificate); + assert(ret_private_key); + assert(ret_ui); while ((c = getopt_long(argc, argv, "hs:SCP", options, NULL)) >= 0) @@ -8153,7 +8115,7 @@ static int parse_argv(int argc, char *argv[]) { } case ARG_PRIVATE_KEY: { - r = free_and_strdup_warn(&private_key, optarg); + r = free_and_strdup_warn(&arg_private_key, optarg); if (r < 0) return r; break; @@ -8169,20 +8131,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_CERTIFICATE: { - _cleanup_free_ char *cert = NULL; - size_t n = 0; - - r = read_full_file_full( - AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &cert, &n); - if (r < 0) - return log_error_errno(r, "Failed to read certificate file '%s': %m", optarg); - - X509_free(arg_certificate); - arg_certificate = NULL; - r = parse_x509_certificate(cert, n, &arg_certificate); + r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_certificate); if (r < 0) return r; break; @@ -8518,39 +8467,38 @@ static int parse_argv(int argc, char *argv[]) { *p = gpt_partition_type_override_architecture(*p, arg_architecture); } - if (private_key && arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { - _cleanup_(erase_and_freep) char *k = NULL; - size_t n = 0; - - r = read_full_file_full( - AT_FDCWD, private_key, UINT64_MAX, SIZE_MAX, - READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, - NULL, - &k, &n); + if (arg_certificate) { + r = openssl_load_x509_certificate(arg_certificate, &certificate); if (r < 0) - return log_error_errno(r, "Failed to read key file '%s': %m", private_key); + return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate); + } - r = parse_private_key(k, n, &arg_private_key); - if (r < 0) - return r; - } else if (private_key && - IN_SET(arg_private_key_source_type, OPENSSL_KEY_SOURCE_ENGINE, OPENSSL_KEY_SOURCE_PROVIDER)) { - /* This must happen after parse_x509_certificate() is called above, otherwise - * signing later will get stuck as the parsed private key won't have the - * certificate, so this block cannot be inline in ARG_PRIVATE_KEY. */ - r = openssl_load_key_from_token( + if (arg_private_key) { + if (arg_private_key_source_type == OPENSSL_KEY_SOURCE_FILE) { + r = parse_path_argument(arg_private_key, /*suppress_root=*/ false, &arg_private_key); + if (r < 0) + return r; + } + + r = openssl_load_private_key( arg_private_key_source_type, arg_private_key_source, - private_key, - &arg_private_key); + arg_private_key, + &(AskPasswordRequest) { + .id = "repart-private-key-pin", + .keyring = arg_private_key, + .credential = "repart.private-key-pin", + }, + &private_key, + &ui); if (r < 0) - return log_error_errno( - r, - "Failed to load key '%s' from OpenSSL private key source %s: %m", - private_key, - arg_private_key_source); + return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key); } + *ret_certificate = TAKE_PTR(certificate); + *ret_private_key = TAKE_PTR(private_key); + *ret_ui = TAKE_PTR(ui); + return 1; } @@ -8967,6 +8915,9 @@ static int determine_auto_size(Context *c) { } static int run(int argc, char *argv[]) { + _cleanup_(X509_freep) X509 *certificate = NULL; + _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; + _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL; _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; _cleanup_(umount_and_freep) char *mounted_dir = NULL; _cleanup_(context_freep) Context* context = NULL; @@ -8975,7 +8926,7 @@ static int run(int argc, char *argv[]) { log_setup(); - r = parse_argv(argc, argv); + r = parse_argv(argc, argv, &certificate, &private_key, &ui); if (r <= 0) return r; @@ -9034,10 +8985,13 @@ static int run(int argc, char *argv[]) { return log_oom(); } - context = context_new(arg_seed); + context = context_new(arg_seed, certificate, private_key); if (!context) return log_oom(); + TAKE_PTR(certificate); + TAKE_PTR(private_key); + r = context_read_seed(context, arg_root); if (r < 0) return r; 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; |