summaryrefslogtreecommitdiffstats
path: root/src/shared
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/openssl-util.c220
-rw-r--r--src/shared/openssl-util.h43
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;