mirror of
https://github.com/systemd/systemd
synced 2024-07-21 18:24:38 +00:00
Merge pull request #31261 from bluca/repart_engine
repart: support engines and providers
This commit is contained in:
commit
769ca9ab55
|
@ -129,6 +129,14 @@ All tools:
|
|||
* `$SYSTEMD_VERITY_SHARING=0` — if set, sharing dm-verity devices by
|
||||
using a stable `<ROOTHASH>-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
|
||||
|
|
|
@ -354,6 +354,16 @@
|
|||
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--private-key-uri=</option></term>
|
||||
|
||||
<listitem><para>Takes a URI-like string referring to a private key, that will be passed to OpenSSL's
|
||||
"engine" or "provider" logic. Configures the signing key to use when creating verity signature
|
||||
partitions with the <varname>Verity=signature</varname> setting in partition files.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--certificate=</option></term>
|
||||
|
||||
|
@ -616,6 +626,27 @@ systemd-confext refresh</programlisting>
|
|||
<citerefentry><refentrytitle>systemd-confext</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
|
||||
</example>
|
||||
|
||||
<example>
|
||||
<title>Generate a system extension image and sign it via PKCS11</title>
|
||||
|
||||
<para>The following creates a system extension DDI (sysext) for an
|
||||
<filename>/usr/foo</filename> update and signs it with a hardware token via PKCS11.</para>
|
||||
|
||||
<programlisting>mkdir tree tree/usr tree/usr/lib/extension-release.d
|
||||
echo "Hello World" > tree/usr/foo
|
||||
cat > tree/usr/lib/extension-release.d/extension-release.my-foo <<EOF
|
||||
ID=fedora
|
||||
VERSION_ID=38
|
||||
IMAGE_ID=my-foo
|
||||
IMAGE_VERSION=7
|
||||
EOF
|
||||
systemd-repart --make-ddi=sysext --private-key-uri="pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=0123456789abcdef;token=Some%20Cert" --certificate=cert.crt -s tree/ /var/lib/extensions/my-foo.sysext.raw
|
||||
systemd-sysext refresh</programlisting>
|
||||
|
||||
<para>The DDI generated that way may be applied to the system with
|
||||
<citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
|
||||
</example>
|
||||
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
|
|
@ -6506,6 +6506,7 @@ static int help(void) {
|
|||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
_cleanup_free_ char *private_key = NULL, *private_key_uri = NULL;
|
||||
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
|
@ -6526,6 +6527,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_JSON,
|
||||
ARG_KEY_FILE,
|
||||
ARG_PRIVATE_KEY,
|
||||
ARG_PRIVATE_KEY_URI,
|
||||
ARG_CERTIFICATE,
|
||||
ARG_TPM2_DEVICE,
|
||||
ARG_TPM2_DEVICE_KEY,
|
||||
|
@ -6566,6 +6568,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "key-file", required_argument, NULL, ARG_KEY_FILE },
|
||||
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
|
||||
{ "private-key-uri", required_argument, NULL, ARG_PRIVATE_KEY_URI },
|
||||
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
|
||||
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
|
||||
{ "tpm2-device-key", required_argument, NULL, ARG_TPM2_DEVICE_KEY },
|
||||
|
@ -6751,20 +6754,14 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
case ARG_PRIVATE_KEY: {
|
||||
_cleanup_(erase_and_freep) char *k = NULL;
|
||||
size_t n = 0;
|
||||
|
||||
r = read_full_file_full(
|
||||
AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX,
|
||||
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
|
||||
NULL,
|
||||
&k, &n);
|
||||
r = free_and_strdup_warn(&private_key, optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
|
||||
return r;
|
||||
break;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(arg_private_key);
|
||||
arg_private_key = NULL;
|
||||
r = parse_private_key(k, n, &arg_private_key);
|
||||
case ARG_PRIVATE_KEY_URI: {
|
||||
r = free_and_strdup_warn(&private_key_uri, optarg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
@ -7101,6 +7098,38 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
*p = gpt_partition_type_override_architecture(*p, arg_architecture);
|
||||
}
|
||||
|
||||
if (private_key && private_key_uri)
|
||||
return log_error_errno(
|
||||
SYNTHETIC_ERRNO(EINVAL),
|
||||
"Cannot specify both --private-key= and --private-key-uri=.");
|
||||
|
||||
if (private_key) {
|
||||
_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 (r < 0)
|
||||
return log_error_errno(r, "Failed to read key file '%s': %m", private_key);
|
||||
|
||||
r = parse_private_key(k, n, &arg_private_key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else if (private_key_uri) {
|
||||
/* 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(private_key_uri, &arg_private_key);
|
||||
if (r < 0)
|
||||
return log_error_errno(
|
||||
r,
|
||||
"Failed to load key '%s' from OpenSSL provider: %m",
|
||||
private_key);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -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]) {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# include <openssl/bio.h>
|
||||
# include <openssl/bn.h>
|
||||
# include <openssl/crypto.h>
|
||||
# include <openssl/engine.h>
|
||||
# include <openssl/err.h>
|
||||
# include <openssl/evp.h>
|
||||
# include <openssl/opensslv.h>
|
||||
|
@ -25,6 +26,8 @@
|
|||
# include <openssl/core_names.h>
|
||||
# include <openssl/kdf.h>
|
||||
# include <openssl/param_build.h>
|
||||
# include <openssl/provider.h>
|
||||
# include <openssl/store.h>
|
||||
# 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);
|
||||
|
|
Loading…
Reference in a new issue