mirror of
https://github.com/systemd/systemd.git
synced 2025-01-09 01:18:19 +03:00
openssl-util: Query engine/provider pin via ask-password (#34948)
In mkosi, we want to support signing via a hardware token. We already support this in systemd-repart and systemd-measure. However, if the hardware token is protected by a pin, the pin is asked as many as 20 times when building an image as the pin is not cached and thus requested again for every operation. Let's introduce a custom openssl ui when we use engines and providers and plug systemd-ask-password into the process. With systemd-ask-password, the pin can be cached in the kernel keyring, allowing us to reuse it without querying the user again every time to enter the pin. We use the private key URI as the keyring identifier so that the cached pin can be shared across multiple tools.
This commit is contained in:
commit
c32e54456e
@ -738,9 +738,13 @@ Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as
|
||||
|
||||
`systemd-ask-password`:
|
||||
|
||||
* `$SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC` - takes a timespan or `default`,
|
||||
which controls the expiration time of keys stored in the kernel keyring by
|
||||
`systemd-ask-password`. If unset or set to `default`, the default expiration
|
||||
of 150 seconds is used. If set to `0`, keys are not cached in the kernel
|
||||
keyring. If set to `infinity`, keys are cached without an expiration time in
|
||||
the kernel keyring.
|
||||
* `$SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC` - takes a timespan, which controls
|
||||
the expiration time of keys stored in the kernel keyring by `systemd-ask-password`.
|
||||
If unset, the default expiration of 150 seconds is used. If set to `0`, keys are
|
||||
not cached in the kernel keyring. If set to `infinity`, keys are cached without an
|
||||
expiration time in the kernel keyring.
|
||||
|
||||
* `SYSTEMD_ASK_PASSWORD_KEYRING_TYPE` - takes a keyring ID or one of `thread`,
|
||||
`process`, `session`, `user`, `user-session`, or `group`. Controls the kernel
|
||||
keyring in which `systemd-ask-password` caches the queried password. Defaults
|
||||
to `user`.
|
||||
|
@ -526,6 +526,31 @@
|
||||
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--secure-boot-auto-enroll=yes|no</option></term>
|
||||
<term><option>--private-key=<replaceable>PATH/URI</replaceable></option></term>
|
||||
<term><option>--private-key-source=<replaceable>TYPE[:NAME]</replaceable></option></term>
|
||||
<term><option>--certificate=<replaceable>PATH</replaceable></option></term>
|
||||
|
||||
<listitem><para>Configure the ESP for secure boot auto-enrollment when invoking the
|
||||
<command>install</command> command. Takes a boolean argument. Disabled by default. Enabling this
|
||||
option will make <command>bootctl</command> populate the ESP with signed <literal>PK</literal>,
|
||||
<literal>KEK</literal> and <literal>db</literal> signature databases, each containing the given
|
||||
certificate in <literal>DER</literal> format as their only entry. These secure boot signature
|
||||
databases will be picked up and enrolled by <command>systemd-boot</command> if secure boot is in
|
||||
setup mode and secure boot auto-enrollment is enabled.</para>
|
||||
|
||||
<para>When specifying this option, a certificate and private key have to be provided as well using
|
||||
the <option>--certificate=</option> and <option>--private-key=</option> options. The
|
||||
<option>--certificate=</option> option takes a path to a PEM encoded X.509 certificate. The
|
||||
<option>--private-key=</option> option can take a path or a URI that will be passed to the OpenSSL
|
||||
engine or provider, as specified by <option>--private-key-source=</option> as a
|
||||
<literal>type:name</literal> tuple, such as <literal>engine:pkcs11</literal>. The specified OpenSSL
|
||||
signing engine or provider will be used to sign the EFI signature lists.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager"/>
|
||||
<xi:include href="standard-options.xml" xpointer="json" />
|
||||
<xi:include href="standard-options.xml" xpointer="help"/>
|
||||
|
@ -330,7 +330,7 @@ bool is_efi_secure_boot(void) {
|
||||
int r;
|
||||
|
||||
if (cache < 0) {
|
||||
r = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot));
|
||||
r = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
|
||||
if (r == -ENOENT)
|
||||
cache = false;
|
||||
else if (r < 0)
|
||||
@ -348,7 +348,7 @@ SecureBootMode efi_get_secure_boot_mode(void) {
|
||||
if (cache != _SECURE_BOOT_INVALID)
|
||||
return cache;
|
||||
|
||||
int secure = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot));
|
||||
int secure = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
|
||||
if (secure < 0) {
|
||||
if (secure != -ENOENT)
|
||||
log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
|
||||
@ -358,9 +358,9 @@ SecureBootMode efi_get_secure_boot_mode(void) {
|
||||
|
||||
/* We can assume false for all these if they are abscent (AuditMode and
|
||||
* DeployedMode may not exist on older firmware). */
|
||||
int audit = read_flag(EFI_GLOBAL_VARIABLE(AuditMode));
|
||||
int deployed = read_flag(EFI_GLOBAL_VARIABLE(DeployedMode));
|
||||
int setup = read_flag(EFI_GLOBAL_VARIABLE(SetupMode));
|
||||
int audit = read_flag(EFI_GLOBAL_VARIABLE_STR("AuditMode"));
|
||||
int deployed = read_flag(EFI_GLOBAL_VARIABLE_STR("DeployedMode"));
|
||||
int setup = read_flag(EFI_GLOBAL_VARIABLE_STR("SetupMode"));
|
||||
log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d",
|
||||
secure, audit, deployed, setup);
|
||||
|
||||
@ -381,13 +381,13 @@ static int read_efi_options_variable(char **ret) {
|
||||
/* Let's be helpful with the returned error and check if the variable exists at all. If it
|
||||
* does, let's return a recognizable error (EPERM), and if not ENODATA. */
|
||||
|
||||
if (access(EFIVAR_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), F_OK) < 0)
|
||||
if (access(EFIVAR_PATH(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions")), F_OK) < 0)
|
||||
return errno == ENOENT ? -ENODATA : -errno;
|
||||
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), ret);
|
||||
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions"), ret);
|
||||
if (r == -ENOENT)
|
||||
return -ENODATA;
|
||||
return r;
|
||||
@ -401,7 +401,7 @@ int cache_efi_options_variable(void) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return write_string_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), line,
|
||||
return write_string_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions")), line,
|
||||
WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755);
|
||||
}
|
||||
|
||||
@ -418,7 +418,7 @@ int systemd_efi_options_variable(char **ret) {
|
||||
if (e)
|
||||
return strdup_to(ret, e);
|
||||
|
||||
r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), ret);
|
||||
r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions")), ret);
|
||||
if (r == -ENOENT)
|
||||
return -ENODATA;
|
||||
return r;
|
||||
@ -432,12 +432,12 @@ int systemd_efi_options_efivarfs_if_newer(char **ret) {
|
||||
struct stat a = {}, b;
|
||||
int r;
|
||||
|
||||
if (stat(EFIVAR_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), &a) < 0 && errno != ENOENT)
|
||||
if (stat(EFIVAR_PATH(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions")), &a) < 0 && errno != ENOENT)
|
||||
return log_debug_errno(errno, "Failed to stat EFI variable SystemdOptions: %m");
|
||||
|
||||
if (stat(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), &b) < 0) {
|
||||
if (stat(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions")), &b) < 0) {
|
||||
if (errno != ENOENT)
|
||||
log_debug_errno(errno, "Failed to stat "EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions))": %m");
|
||||
log_debug_errno(errno, "Failed to stat "EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions"))": %m");
|
||||
} else if (compare_stat_mtime(&a, &b) > 0)
|
||||
log_debug("Variable SystemdOptions in evifarfs is newer than in cache.");
|
||||
else {
|
||||
|
@ -23,9 +23,10 @@
|
||||
#define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
|
||||
#define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67)
|
||||
|
||||
#define EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
|
||||
#define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002)
|
||||
#define EFI_VARIABLE_RUNTIME_ACCESS UINT32_C(0x00000004)
|
||||
#define EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001)
|
||||
#define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002)
|
||||
#define EFI_VARIABLE_RUNTIME_ACCESS UINT32_C(0x00000004)
|
||||
#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS UINT32_C(0x00000020)
|
||||
|
||||
/* Note that the <lowercaseuuid>-<varname> naming scheme is an efivarfs convention, i.e. part of the Linux
|
||||
* API file system implementation for EFI. EFI itself processes UIDS in binary form.
|
||||
@ -37,10 +38,6 @@
|
||||
#define EFI_LOADER_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_LOADER_STR, name)
|
||||
#define EFI_SYSTEMD_VARIABLE_STR(name) EFI_VENDOR_VARIABLE_STR(EFI_VENDOR_SYSTEMD_STR, name)
|
||||
|
||||
#define EFI_GLOBAL_VARIABLE(name) EFI_GLOBAL_VARIABLE_STR(STRINGIFY(name))
|
||||
#define EFI_LOADER_VARIABLE(name) EFI_LOADER_VARIABLE_STR(STRINGIFY(name))
|
||||
#define EFI_SYSTEMD_VARIABLE(name) EFI_SYSTEMD_VARIABLE_STR(STRINGIFY(name))
|
||||
|
||||
#define EFIVAR_PATH(variable) "/sys/firmware/efi/efivars/" variable
|
||||
#define EFIVAR_CACHE_PATH(variable) "/run/systemd/efivars/" variable
|
||||
|
||||
|
@ -504,6 +504,10 @@ size_t char16_strlen(const char16_t *s) {
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t char16_strsize(const char16_t *s) {
|
||||
return s ? (char16_strlen(s) + 1) * sizeof(*s) : 0;
|
||||
}
|
||||
|
||||
/* expected size used to encode one unicode char */
|
||||
static int utf8_unichar_to_encoded_len(char32_t unichar) {
|
||||
|
||||
|
@ -42,6 +42,7 @@ char* utf16_to_utf8(const char16_t *s, size_t length /* bytes! */);
|
||||
char16_t *utf8_to_utf16(const char *s, size_t length);
|
||||
|
||||
size_t char16_strlen(const char16_t *s); /* returns the number of 16-bit words in the string (not bytes!) */
|
||||
size_t char16_strsize(const char16_t *s);
|
||||
|
||||
int utf8_encoded_valid_unichar(const char *str, size_t length);
|
||||
int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar);
|
||||
|
@ -33,7 +33,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderBootCountPath)), F_OK) < 0) {
|
||||
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderBootCountPath")), F_OK) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_debug_errno(errno, "Skipping generator, not booted with boot counting in effect.");
|
||||
return 0;
|
||||
|
@ -219,7 +219,7 @@ static int acquire_boot_count_path(
|
||||
uint64_t left, done;
|
||||
int r;
|
||||
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderBootCountPath), &path);
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("LoaderBootCountPath"), &path);
|
||||
if (r == -ENOENT)
|
||||
return -EUNATCH; /* in this case, let the caller print a message */
|
||||
if (r < 0)
|
||||
|
@ -8,14 +8,17 @@
|
||||
#include "copy.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efi-api.h"
|
||||
#include "efi-fundamental.h"
|
||||
#include "env-file.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "glyph-util.h"
|
||||
#include "id128-util.h"
|
||||
#include "io-util.h"
|
||||
#include "kernel-config.h"
|
||||
#include "os-util.h"
|
||||
#include "parse-argument.h"
|
||||
#include "path-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "stat-util.h"
|
||||
@ -295,6 +298,8 @@ static const char *const esp_subdirs[] = {
|
||||
"EFI/systemd",
|
||||
"EFI/BOOT",
|
||||
"loader",
|
||||
"loader/keys",
|
||||
"loader/keys/auto",
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -569,6 +574,164 @@ static int install_entry_token(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if HAVE_OPENSSL
|
||||
static int efi_timestamp(EFI_TIME *ret) {
|
||||
uint64_t epoch = UINT64_MAX;
|
||||
struct tm tm = {};
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = secure_getenv_uint64("SOURCE_DATE_EPOCH", &epoch);
|
||||
if (r != -ENXIO)
|
||||
log_debug_errno(r, "Failed to parse $SOURCE_DATE_EPOCH, ignoring: %m");
|
||||
|
||||
r = localtime_or_gmtime_usec(epoch != UINT64_MAX ? epoch : now(CLOCK_REALTIME), /*utc=*/ true, &tm);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to convert timestamp to calendar time: %m");
|
||||
|
||||
*ret = (EFI_TIME) {
|
||||
.Year = 1900 + tm.tm_year,
|
||||
/* tm_mon starts at 0, EFI_TIME months start at 1. */
|
||||
.Month = tm.tm_mon + 1,
|
||||
.Day = tm.tm_mday,
|
||||
.Hour = tm.tm_hour,
|
||||
.Minute = tm.tm_min,
|
||||
.Second = tm.tm_sec,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int install_secure_boot_auto_enroll(const char *esp, X509 *certificate, EVP_PKEY *private_key) {
|
||||
#if HAVE_OPENSSL
|
||||
int r;
|
||||
|
||||
_cleanup_free_ uint8_t *dercert = NULL;
|
||||
int dercertsz;
|
||||
dercertsz = i2d_X509(certificate, &dercert);
|
||||
if (dercertsz < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert X.509 certificate to DER: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
_cleanup_close_ int keys_fd = chase_and_open("loader/keys/auto", esp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, O_DIRECTORY, NULL);
|
||||
if (keys_fd < 0)
|
||||
return log_error_errno(keys_fd, "Failed to chase loader/keys/auto in the ESP: %m");
|
||||
|
||||
uint32_t siglistsz = offsetof(EFI_SIGNATURE_LIST, Signatures) + offsetof(EFI_SIGNATURE_DATA, SignatureData) + dercertsz;
|
||||
/* We use malloc0() to zero-initialize the SignatureOwner field of Signatures[0]. */
|
||||
_cleanup_free_ EFI_SIGNATURE_LIST *siglist = malloc0(siglistsz);
|
||||
if (!siglist)
|
||||
return log_oom();
|
||||
|
||||
*siglist = (EFI_SIGNATURE_LIST) {
|
||||
.SignatureType = EFI_CERT_X509_GUID,
|
||||
.SignatureListSize = siglistsz,
|
||||
.SignatureSize = offsetof(EFI_SIGNATURE_DATA, SignatureData) + dercertsz,
|
||||
};
|
||||
|
||||
memcpy(siglist->Signatures[0].SignatureData, dercert, dercertsz);
|
||||
|
||||
EFI_TIME timestamp;
|
||||
r = efi_timestamp(×tamp);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
uint32_t attrs =
|
||||
EFI_VARIABLE_NON_VOLATILE|
|
||||
EFI_VARIABLE_BOOTSERVICE_ACCESS|
|
||||
EFI_VARIABLE_RUNTIME_ACCESS|
|
||||
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
|
||||
|
||||
FOREACH_STRING(db, "PK", "KEK", "db") {
|
||||
_cleanup_(BIO_freep) BIO *bio = NULL;
|
||||
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
if (!bio)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char16_t *db16 = utf8_to_utf16(db, SIZE_MAX);
|
||||
if (!db16)
|
||||
return log_oom();
|
||||
|
||||
/* Don't count the trailing NUL terminator. */
|
||||
if (BIO_write(bio, db16, char16_strsize(db16) - sizeof(char16_t)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write variable name to bio");
|
||||
|
||||
EFI_GUID *guid = STR_IN_SET(db, "PK", "KEK") ? &(EFI_GUID) EFI_GLOBAL_VARIABLE : &(EFI_GUID) EFI_IMAGE_SECURITY_DATABASE_GUID;
|
||||
|
||||
if (BIO_write(bio, guid, sizeof(*guid)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write variable GUID to bio");
|
||||
|
||||
if (BIO_write(bio, &attrs, sizeof(attrs)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write variable attributes to bio");
|
||||
|
||||
if (BIO_write(bio, ×tamp, sizeof(timestamp)) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write timestamp to bio");
|
||||
|
||||
if (BIO_write(bio, siglist, siglistsz) < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to write signature list to bio");
|
||||
|
||||
_cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
|
||||
p7 = PKCS7_sign(certificate, private_key, /*certs=*/ NULL, bio, PKCS7_DETACHED|PKCS7_NOATTR|PKCS7_BINARY|PKCS7_NOSMIMECAP);
|
||||
if (!p7)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to calculate PKCS7 signature: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
_cleanup_free_ uint8_t *sig = NULL;
|
||||
int sigsz = i2d_PKCS7_SIGNED(p7->d.sign, &sig);
|
||||
if (sigsz < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
|
||||
size_t authsz = offsetof(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo.CertData) + sigsz;
|
||||
_cleanup_free_ EFI_VARIABLE_AUTHENTICATION_2 *auth = malloc(authsz);
|
||||
if (!auth)
|
||||
return log_oom();
|
||||
|
||||
*auth = (EFI_VARIABLE_AUTHENTICATION_2) {
|
||||
.TimeStamp = timestamp,
|
||||
.AuthInfo = {
|
||||
.Hdr = {
|
||||
.dwLength = offsetof(WIN_CERTIFICATE_UEFI_GUID, CertData) + sigsz,
|
||||
.wRevision = 0x0200,
|
||||
.wCertificateType = 0x0EF1, /* WIN_CERT_TYPE_EFI_GUID */
|
||||
},
|
||||
.CertType = EFI_CERT_TYPE_PKCS7_GUID,
|
||||
}
|
||||
};
|
||||
|
||||
memcpy(auth->AuthInfo.CertData, sig, sigsz);
|
||||
|
||||
_cleanup_free_ char *filename = strjoin(db, ".auth");
|
||||
if (!filename)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_close_ int fd = openat(keys_fd, filename, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
|
||||
if (fd < 0)
|
||||
return log_error_errno(fd, "Failed to open secure boot auto-enrollment file for writing: %m");
|
||||
|
||||
r = loop_write(fd, auth, authsz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write authentication descriptor to secure boot auto-enrollment file: %m");
|
||||
|
||||
r = loop_write(fd, siglist, siglistsz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write signature list to secure boot auto-enrollment file: %m");
|
||||
|
||||
if (fsync(fd) < 0 || fsync(keys_fd) < 0)
|
||||
return log_error_errno(errno, "Failed to sync secure boot auto-enrollment file: %m");
|
||||
|
||||
log_info("Secure boot auto-enrollment file %s/loader/keys/auto/%s successfully written.", esp, filename);
|
||||
}
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL is not supported, cannot set up secure boot auto-enrollment.");
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool same_entry(uint16_t id, sd_id128_t uuid, const char *path) {
|
||||
_cleanup_free_ char *opath = NULL;
|
||||
sd_id128_t ouuid;
|
||||
@ -778,6 +941,9 @@ static int are_we_installed(const char *esp_path) {
|
||||
}
|
||||
|
||||
int verb_install(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(X509_freep) X509 *certificate = NULL;
|
||||
_cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL;
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL;
|
||||
sd_id128_t uuid = SD_ID128_NULL;
|
||||
uint64_t pstart = 0, psize = 0;
|
||||
uint32_t part = 0;
|
||||
@ -789,6 +955,26 @@ int verb_install(int argc, char *argv[], void *userdata) {
|
||||
install = streq(argv[0], "install");
|
||||
graceful = !install && arg_graceful; /* support graceful mode for updates */
|
||||
|
||||
if (arg_secure_boot_auto_enroll) {
|
||||
r = openssl_load_x509_certificate(arg_certificate, &certificate);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load X.509 certificate from %s: %m", arg_certificate);
|
||||
|
||||
r = openssl_load_private_key(
|
||||
arg_private_key_source_type,
|
||||
arg_private_key_source,
|
||||
arg_private_key,
|
||||
&(AskPasswordRequest) {
|
||||
.id = "bootctl-private-key-pin",
|
||||
.keyring = arg_private_key,
|
||||
.credential = "bootctl.private-key-pin",
|
||||
},
|
||||
&private_key,
|
||||
&ui);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load private key from %s: %m", arg_private_key);
|
||||
}
|
||||
|
||||
r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid, NULL);
|
||||
if (graceful && r == -ENOKEY)
|
||||
return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
|
||||
@ -852,6 +1038,12 @@ int verb_install(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_secure_boot_auto_enroll) {
|
||||
r = install_secure_boot_auto_enroll(arg_esp_path, certificate, private_key);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
r = install_loader_specification(arg_dollar_boot_path());
|
||||
@ -1015,13 +1207,13 @@ static int remove_loader_variables(void) {
|
||||
/* Remove all persistent loader variables we define */
|
||||
|
||||
FOREACH_STRING(var,
|
||||
EFI_LOADER_VARIABLE(LoaderConfigConsoleMode),
|
||||
EFI_LOADER_VARIABLE(LoaderConfigTimeout),
|
||||
EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot),
|
||||
EFI_LOADER_VARIABLE(LoaderEntryDefault),
|
||||
EFI_LOADER_VARIABLE(LoaderEntryLastBooted),
|
||||
EFI_LOADER_VARIABLE(LoaderEntryOneShot),
|
||||
EFI_LOADER_VARIABLE(LoaderSystemToken)) {
|
||||
EFI_LOADER_VARIABLE_STR("LoaderConfigConsoleMode"),
|
||||
EFI_LOADER_VARIABLE_STR("LoaderConfigTimeout"),
|
||||
EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"),
|
||||
EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"),
|
||||
EFI_LOADER_VARIABLE_STR("LoaderEntryLastBooted"),
|
||||
EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"),
|
||||
EFI_LOADER_VARIABLE_STR("LoaderSystemToken")) {
|
||||
|
||||
int q;
|
||||
|
||||
@ -1069,6 +1261,16 @@ int verb_remove(int argc, char *argv[], void *userdata) {
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
FOREACH_STRING(db, "PK.auth", "KEK.auth", "db.auth") {
|
||||
_cleanup_free_ char *p = path_join("/loader/keys/auto", db);
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
q = remove_file(arg_esp_path, p);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
q = remove_subdirs(arg_esp_path, esp_subdirs);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
@ -79,7 +79,7 @@ static int set_system_token(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), NULL, NULL, &token_size);
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderSystemToken"), NULL, NULL, &token_size);
|
||||
if (r == -ENODATA)
|
||||
log_debug_errno(r, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
|
||||
else if (r < 0) {
|
||||
@ -103,7 +103,7 @@ static int set_system_token(void) {
|
||||
* and possibly get identification information or too much insight into the kernel's entropy pool
|
||||
* state. */
|
||||
WITH_UMASK(0077) {
|
||||
r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
|
||||
r = efi_set_variable(EFI_LOADER_VARIABLE_STR("LoaderSystemToken"), buffer, sizeof(buffer));
|
||||
if (r < 0) {
|
||||
if (!arg_graceful)
|
||||
return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
|
||||
|
@ -74,17 +74,17 @@ static int parse_loader_entry_target_arg(const char *arg1, char16_t **ret_target
|
||||
assert(ret_target_size);
|
||||
|
||||
if (streq(arg1, "@current")) {
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected), NULL, (void *) ret_target, ret_target_size);
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntrySelected"), NULL, (void *) ret_target, ret_target_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntrySelected': %m");
|
||||
|
||||
} else if (streq(arg1, "@oneshot")) {
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, (void *) ret_target, ret_target_size);
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), NULL, (void *) ret_target, ret_target_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryOneShot': %m");
|
||||
|
||||
} else if (streq(arg1, "@default")) {
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault), NULL, (void *) ret_target, ret_target_size);
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), NULL, (void *) ret_target, ret_target_size);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get EFI variable 'LoaderEntryDefault': %m");
|
||||
|
||||
@ -114,7 +114,7 @@ int verb_set_efivar(int argc, char *argv[], void *userdata) {
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"Not booted with UEFI.");
|
||||
|
||||
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo)), F_OK) < 0) {
|
||||
if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderInfo")), F_OK) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_error_errno(errno, "Not booted with a supported boot loader.");
|
||||
return -EOPNOTSUPP;
|
||||
@ -137,16 +137,16 @@ int verb_set_efivar(int argc, char *argv[], void *userdata) {
|
||||
int (* arg_parser)(const char *, char16_t **, size_t *);
|
||||
|
||||
if (streq(argv[0], "set-default")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderEntryDefault);
|
||||
variable = EFI_LOADER_VARIABLE_STR("LoaderEntryDefault");
|
||||
arg_parser = parse_loader_entry_target_arg;
|
||||
} else if (streq(argv[0], "set-oneshot")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderEntryOneShot);
|
||||
variable = EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot");
|
||||
arg_parser = parse_loader_entry_target_arg;
|
||||
} else if (streq(argv[0], "set-timeout")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderConfigTimeout);
|
||||
variable = EFI_LOADER_VARIABLE_STR("LoaderConfigTimeout");
|
||||
arg_parser = parse_timeout;
|
||||
} else if (streq(argv[0], "set-timeout-oneshot")) {
|
||||
variable = EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot);
|
||||
variable = EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot");
|
||||
arg_parser = parse_timeout;
|
||||
} else
|
||||
assert_not_reached();
|
||||
|
@ -413,17 +413,17 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
uint64_t loader_features = 0, stub_features = 0;
|
||||
int have;
|
||||
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareType), &fw_type);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderFirmwareInfo), &fw_info);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderInfo), &loader);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(StubInfo), &stub);
|
||||
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &loader_path);
|
||||
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE(StubImageIdentifier), &stub_path);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderFirmwareType"), &fw_type);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderFirmwareInfo"), &fw_info);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderInfo"), &loader);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("StubInfo"), &stub);
|
||||
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE_STR("LoaderImageIdentifier"), &loader_path);
|
||||
(void) efi_get_variable_path_and_warn(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &stub_path);
|
||||
(void) efi_loader_get_features(&loader_features);
|
||||
(void) efi_stub_get_features(&stub_features);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntrySelected), ¤t_entry);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &oneshot_entry);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE(LoaderEntryDefault), &default_entry);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntrySelected"), ¤t_entry);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &oneshot_entry);
|
||||
(void) efi_get_variable_string_and_warn(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), &default_entry);
|
||||
|
||||
SecureBootMode secure = efi_get_secure_boot_mode();
|
||||
printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
|
||||
@ -525,7 +525,7 @@ int verb_status(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
|
||||
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
|
||||
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
|
||||
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderSystemToken")), F_OK) >= 0;
|
||||
printf(" System Token: %s\n", have ? "set" : "not set");
|
||||
|
||||
if (arg_esp_path) {
|
||||
|
@ -34,7 +34,7 @@ int verb_systemd_efi_options(int argc, char *argv[], void *userdata) {
|
||||
log_notice("Note: SystemdOptions EFI variable has been modified since boot. New value: %s",
|
||||
new);
|
||||
} else {
|
||||
r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), argv[1]);
|
||||
r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE_STR("SystemdOptions"), argv[1]);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set SystemdOptions EFI variable: %m");
|
||||
}
|
||||
|
@ -62,6 +62,11 @@ char *arg_efi_boot_option_description = NULL;
|
||||
bool arg_dry_run = false;
|
||||
ImagePolicy *arg_image_policy = NULL;
|
||||
bool arg_varlink = false;
|
||||
bool arg_secure_boot_auto_enroll = false;
|
||||
char *arg_certificate = NULL;
|
||||
char *arg_private_key = NULL;
|
||||
KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
|
||||
char *arg_private_key_source = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep);
|
||||
@ -71,6 +76,9 @@ STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
|
||||
|
||||
int acquire_esp(
|
||||
int unprivileged_mode,
|
||||
@ -149,7 +157,7 @@ static int print_loader_or_stub_path(void) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to determine loader partition UUID: %m");
|
||||
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE(LoaderImageIdentifier), &p);
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("LoaderImageIdentifier"), &p);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No loader EFI binary path passed.");
|
||||
if (r < 0)
|
||||
@ -163,7 +171,7 @@ static int print_loader_or_stub_path(void) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to determine stub partition UUID: %m");
|
||||
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE(StubImageIdentifier), &p);
|
||||
r = efi_get_variable_path(EFI_LOADER_VARIABLE_STR("StubImageIdentifier"), &p);
|
||||
if (r == -ENOENT)
|
||||
return log_error_errno(r, "No stub EFI binary path passed.");
|
||||
if (r < 0)
|
||||
@ -277,6 +285,19 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --efi-boot-option-description=DESCRIPTION\n"
|
||||
" Description of the entry in the boot option list\n"
|
||||
" --dry-run Dry run (unlink and cleanup)\n"
|
||||
" --secure-boot-auto-enroll\n"
|
||||
" Set up secure boot auto-enrollment\n"
|
||||
" --private-key=PATH|URI\n"
|
||||
" Private key to use when setting up secure boot\n"
|
||||
" auto-enrollment or an engine or provider specific\n"
|
||||
" designation if --private-key-source= is used\n"
|
||||
" --private-key-source=file|provider:PROVIDER|engine:ENGINE\n"
|
||||
" Specify how to use KEY for --private-key=. Allows\n"
|
||||
" an OpenSSL engine/provider to be used when setting\n"
|
||||
" up secure boot auto-enrollment\n"
|
||||
" --certificate=PATH\n"
|
||||
" PEM certificate to use when setting up secure boot\n"
|
||||
" auto-enrollment\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
@ -309,6 +330,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_DRY_RUN,
|
||||
ARG_PRINT_LOADER_PATH,
|
||||
ARG_PRINT_STUB_PATH,
|
||||
ARG_SECURE_BOOT_AUTO_ENROLL,
|
||||
ARG_CERTIFICATE,
|
||||
ARG_PRIVATE_KEY,
|
||||
ARG_PRIVATE_KEY_SOURCE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -339,6 +364,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "all-architectures", no_argument, NULL, ARG_ARCH_ALL },
|
||||
{ "efi-boot-option-description", required_argument, NULL, ARG_EFI_BOOT_OPTION_DESCRIPTION },
|
||||
{ "dry-run", no_argument, NULL, ARG_DRY_RUN },
|
||||
{ "secure-boot-auto-enroll", required_argument, NULL, ARG_SECURE_BOOT_AUTO_ENROLL },
|
||||
{ "certificate", required_argument, NULL, ARG_CERTIFICATE },
|
||||
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
|
||||
{ "private-key-source", required_argument, NULL, ARG_PRIVATE_KEY_SOURCE },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -491,6 +520,35 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_dry_run = true;
|
||||
break;
|
||||
|
||||
case ARG_SECURE_BOOT_AUTO_ENROLL:
|
||||
r = parse_boolean_argument("--secure-boot-auto-enroll=", optarg, &arg_secure_boot_auto_enroll);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case ARG_CERTIFICATE: {
|
||||
r = parse_path_argument(optarg, /*suppress_root=*/ false, &arg_certificate);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_PRIVATE_KEY: {
|
||||
r = free_and_strdup_warn(&arg_private_key, optarg);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_PRIVATE_KEY_SOURCE:
|
||||
r = parse_openssl_key_source_argument(
|
||||
optarg,
|
||||
&arg_private_key_source,
|
||||
&arg_private_key_source_type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -517,6 +575,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_dry_run && argv[optind] && !STR_IN_SET(argv[optind], "unlink", "cleanup"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--dry is only supported with --unlink or --cleanup");
|
||||
|
||||
if (arg_secure_boot_auto_enroll && !arg_certificate)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no certificate provided");
|
||||
|
||||
if (arg_secure_boot_auto_enroll && !arg_private_key)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Secure boot auto-enrollment requested but no private key provided");
|
||||
|
||||
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "boot-entry.h"
|
||||
#include "image-policy.h"
|
||||
#include "openssl-util.h"
|
||||
#include "pager.h"
|
||||
|
||||
typedef enum InstallSource {
|
||||
@ -38,6 +39,11 @@ extern char *arg_efi_boot_option_description;
|
||||
extern bool arg_dry_run;
|
||||
extern ImagePolicy *arg_image_policy;
|
||||
extern bool arg_varlink;
|
||||
extern bool arg_secure_boot_auto_enroll;
|
||||
extern char *arg_certificate;
|
||||
extern char *arg_private_key;
|
||||
extern KeySourceType arg_private_key_source_type;
|
||||
extern char *arg_private_key_source;
|
||||
|
||||
static inline const char* arg_dollar_boot_path(void) {
|
||||
/* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
|
||||
|
@ -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) {
|
||||
@ -1049,7 +1028,7 @@ static int validate_stub(void) {
|
||||
log_warning("Warning: current kernel image does not support measuring itself, the command line or initrd system extension images.\n"
|
||||
"The PCR measurements seen are unlikely to be valid.");
|
||||
|
||||
r = compare_reported_pcr_nr(TPM2_PCR_KERNEL_BOOT, EFI_LOADER_VARIABLE(StubPcrKernelImage), "kernel image");
|
||||
r = compare_reported_pcr_nr(TPM2_PCR_KERNEL_BOOT, EFI_LOADER_VARIABLE_STR("StubPcrKernelImage"), "kernel image");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -30,7 +30,7 @@ executables += [
|
||||
],
|
||||
'sources' : bootctl_sources,
|
||||
'link_with' : boot_link_with,
|
||||
'dependencies' : libblkid,
|
||||
'dependencies' : [libblkid, libopenssl],
|
||||
},
|
||||
libexec_template + {
|
||||
'name' : 'systemd-bless-boot',
|
||||
|
@ -16,7 +16,7 @@ void lock_down_efi_variables(void) {
|
||||
_cleanup_close_ int fd = -EBADF;
|
||||
int r;
|
||||
|
||||
fd = open(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), O_RDONLY|O_CLOEXEC);
|
||||
fd = open(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderSystemToken")), O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
if (errno != ENOENT)
|
||||
log_warning_errno(errno, "Unable to open LoaderSystemToken EFI variable, ignoring: %m");
|
||||
|
@ -10,4 +10,65 @@ typedef struct {
|
||||
uint16_t Data3;
|
||||
uint8_t Data4[8];
|
||||
} EFI_GUID;
|
||||
|
||||
typedef struct {
|
||||
EFI_GUID SignatureOwner;
|
||||
uint8_t SignatureData[];
|
||||
} EFI_SIGNATURE_DATA;
|
||||
|
||||
typedef struct {
|
||||
EFI_GUID SignatureType;
|
||||
uint32_t SignatureListSize;
|
||||
uint32_t SignatureHeaderSize;
|
||||
uint32_t SignatureSize;
|
||||
EFI_SIGNATURE_DATA Signatures[];
|
||||
} EFI_SIGNATURE_LIST;
|
||||
|
||||
typedef struct {
|
||||
uint32_t dwLength;
|
||||
uint16_t wRevision;
|
||||
uint16_t wCertificateType;
|
||||
uint8_t bCertificate[];
|
||||
} WIN_CERTIFICATE;
|
||||
|
||||
typedef struct {
|
||||
WIN_CERTIFICATE Hdr;
|
||||
EFI_GUID CertType;
|
||||
uint8_t CertData[];
|
||||
} WIN_CERTIFICATE_UEFI_GUID;
|
||||
|
||||
typedef struct {
|
||||
uint16_t Year;
|
||||
uint8_t Month;
|
||||
uint8_t Day;
|
||||
uint8_t Hour;
|
||||
uint8_t Minute;
|
||||
uint8_t Second;
|
||||
uint8_t Pad1;
|
||||
uint32_t Nanosecond;
|
||||
int16_t TimeZone;
|
||||
uint8_t Daylight;
|
||||
uint8_t Pad2;
|
||||
} EFI_TIME;
|
||||
|
||||
typedef struct {
|
||||
EFI_TIME TimeStamp;
|
||||
WIN_CERTIFICATE_UEFI_GUID AuthInfo;
|
||||
} EFI_VARIABLE_AUTHENTICATION_2;
|
||||
|
||||
#define GUID_DEF(d1, d2, d3, d4_1, d4_2, d4_3, d4_4, d4_5, d4_6, d4_7, d4_8) \
|
||||
{ d1, d2, d3, { d4_1, d4_2, d4_3, d4_4, d4_5, d4_6, d4_7, d4_8 } }
|
||||
|
||||
#define MAKE_GUID_PTR(name) ((EFI_GUID *) &(const EFI_GUID) name##_GUID)
|
||||
|
||||
#define EFI_GLOBAL_VARIABLE \
|
||||
GUID_DEF(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)
|
||||
#define EFI_IMAGE_SECURITY_DATABASE_GUID \
|
||||
GUID_DEF(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
|
||||
|
||||
#define EFI_CERT_X509_GUID \
|
||||
GUID_DEF(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
|
||||
#define EFI_CERT_TYPE_PKCS7_GUID \
|
||||
GUID_DEF(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
|
||||
|
||||
#endif
|
||||
|
@ -161,7 +161,7 @@ int get_efi_hibernate_location(EFIHibernateLocation **ret) {
|
||||
if (!is_efi_boot())
|
||||
goto skip;
|
||||
|
||||
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(HibernateLocation), &location_str);
|
||||
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE_STR("HibernateLocation"), &location_str);
|
||||
if (r == -ENOENT) {
|
||||
log_debug_errno(r, "EFI variable HibernateLocation is not set, skipping.");
|
||||
goto skip;
|
||||
|
@ -3286,12 +3286,12 @@ static int method_set_reboot_to_boot_loader_menu(
|
||||
|
||||
if (use_efi) {
|
||||
if (x == UINT64_MAX)
|
||||
r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), NULL, 0);
|
||||
r = efi_set_variable(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"), NULL, 0);
|
||||
else {
|
||||
char buf[DECIMAL_STR_MAX(uint64_t) + 1];
|
||||
xsprintf(buf, "%" PRIu64, DIV_ROUND_UP(x, USEC_PER_SEC)); /* second granularity */
|
||||
|
||||
r = efi_set_variable_string(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), buf);
|
||||
r = efi_set_variable_string(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"), buf);
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -3481,9 +3481,9 @@ static int method_set_reboot_to_boot_loader_entry(
|
||||
if (use_efi) {
|
||||
if (isempty(v))
|
||||
/* Delete item */
|
||||
r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot), NULL, 0);
|
||||
r = efi_set_variable(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), NULL, 0);
|
||||
else
|
||||
r = efi_set_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), v);
|
||||
r = efi_set_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -8587,7 +8535,7 @@ static int parse_efi_variable_factory_reset(void) {
|
||||
if (!in_initrd()) /* Never honour EFI variable factory reset request outside of the initrd */
|
||||
return 0;
|
||||
|
||||
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(FactoryReset), &value);
|
||||
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE_STR("FactoryReset"), &value);
|
||||
if (r < 0) {
|
||||
if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return 0;
|
||||
@ -8608,7 +8556,7 @@ static int parse_efi_variable_factory_reset(void) {
|
||||
static int remove_efi_variable_factory_reset(void) {
|
||||
int r;
|
||||
|
||||
r = efi_set_variable(EFI_SYSTEMD_VARIABLE(FactoryReset), NULL, 0);
|
||||
r = efi_set_variable(EFI_SYSTEMD_VARIABLE_STR("FactoryReset"), NULL, 0);
|
||||
if (r < 0) {
|
||||
if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))
|
||||
return 0;
|
||||
@ -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;
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "random-util.h"
|
||||
#include "signal-util.h"
|
||||
#include "socket-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "terminal-util.h"
|
||||
@ -54,6 +55,17 @@
|
||||
|
||||
#define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2)
|
||||
|
||||
static const char* keyring_table[] = {
|
||||
[-KEY_SPEC_THREAD_KEYRING] = "thread",
|
||||
[-KEY_SPEC_PROCESS_KEYRING] = "process",
|
||||
[-KEY_SPEC_SESSION_KEYRING] = "session",
|
||||
[-KEY_SPEC_USER_KEYRING] = "user",
|
||||
[-KEY_SPEC_USER_SESSION_KEYRING] = "user-session",
|
||||
[-KEY_SPEC_GROUP_KEYRING] = "group",
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(keyring, int);
|
||||
|
||||
static int lookup_key(const char *keyname, key_serial_t *ret) {
|
||||
key_serial_t serial;
|
||||
|
||||
@ -115,7 +127,7 @@ static int touch_ask_password_directory(AskPasswordFlags flags) {
|
||||
}
|
||||
|
||||
static usec_t keyring_cache_timeout(void) {
|
||||
static usec_t saved_timeout = USEC_INFINITY;
|
||||
static usec_t saved_timeout = KEYRING_TIMEOUT_USEC;
|
||||
static bool saved_timeout_set = false;
|
||||
int r;
|
||||
|
||||
@ -123,9 +135,7 @@ static usec_t keyring_cache_timeout(void) {
|
||||
return saved_timeout;
|
||||
|
||||
const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC");
|
||||
if (streq_ptr(e, "default"))
|
||||
saved_timeout = KEYRING_TIMEOUT_USEC;
|
||||
else if (e) {
|
||||
if (e) {
|
||||
r = parse_sec(e, &saved_timeout);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TIMEOUT_SEC, ignoring: %s", e);
|
||||
@ -136,6 +146,38 @@ static usec_t keyring_cache_timeout(void) {
|
||||
return saved_timeout;
|
||||
}
|
||||
|
||||
static key_serial_t keyring_cache_type(void) {
|
||||
static key_serial_t saved_keyring = KEY_SPEC_USER_KEYRING;
|
||||
static bool saved_keyring_set = false;
|
||||
int r;
|
||||
|
||||
if (saved_keyring_set)
|
||||
return saved_keyring;
|
||||
|
||||
const char *e = secure_getenv("SYSTEMD_ASK_PASSWORD_KEYRING_TYPE");
|
||||
if (e) {
|
||||
key_serial_t keyring;
|
||||
|
||||
r = safe_atoi32(e, &keyring);
|
||||
if (r >= 0)
|
||||
if (keyring < 0)
|
||||
log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
|
||||
else
|
||||
saved_keyring = keyring;
|
||||
else {
|
||||
keyring = keyring_from_string(e);
|
||||
if (keyring < 0)
|
||||
log_debug_errno(keyring, "Invalid value in $SYSTEMD_ASK_PASSWORD_KEYRING_TYPE, ignoring: %s", e);
|
||||
else
|
||||
saved_keyring = -keyring;
|
||||
}
|
||||
}
|
||||
|
||||
saved_keyring_set = true;
|
||||
|
||||
return saved_keyring;
|
||||
}
|
||||
|
||||
static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
|
||||
_cleanup_strv_free_erase_ char **l = NULL;
|
||||
_cleanup_(erase_and_freep) char *p = NULL;
|
||||
@ -170,7 +212,7 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
|
||||
* have multiple passwords. */
|
||||
n = LESS_BY(n, (size_t) 1);
|
||||
|
||||
serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
|
||||
serial = add_key("user", keyname, p, n, keyring_cache_type());
|
||||
if (serial == -1)
|
||||
return -errno;
|
||||
|
||||
|
@ -1382,19 +1382,19 @@ static int boot_load_efi_entry_pointers(BootConfig *config, bool skip_efivars) {
|
||||
|
||||
/* Loads the three "pointers" to boot loader entries from their EFI variables */
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &config->entry_oneshot);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &config->entry_oneshot);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
|
||||
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\", ignoring: %m");
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryDefault), &config->entry_default);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryDefault"), &config->entry_default);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
|
||||
log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\", ignoring: %m");
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntrySelected), &config->entry_selected);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntrySelected"), &config->entry_selected);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA))
|
||||
@ -1560,7 +1560,7 @@ int boot_config_augment_from_loader(
|
||||
break;
|
||||
}
|
||||
|
||||
p = strdup(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderEntries)));
|
||||
p = strdup(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderEntries")));
|
||||
if (!p)
|
||||
return log_oom();
|
||||
|
||||
|
@ -79,7 +79,7 @@ int efi_reboot_to_firmware_supported(void) {
|
||||
if (!is_efi_boot())
|
||||
goto not_supported;
|
||||
|
||||
r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndicationsSupported), NULL, &v, &s);
|
||||
r = efi_get_variable(EFI_GLOBAL_VARIABLE_STR("OsIndicationsSupported"), NULL, &v, &s);
|
||||
if (r == -ENOENT)
|
||||
goto not_supported; /* variable doesn't exist? it's not supported then */
|
||||
if (r < 0)
|
||||
@ -115,7 +115,7 @@ static int get_os_indications(uint64_t *ret) {
|
||||
return r;
|
||||
|
||||
/* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
|
||||
if (stat(EFIVAR_PATH(EFI_GLOBAL_VARIABLE(OsIndications)), &new_stat) < 0) {
|
||||
if (stat(EFIVAR_PATH(EFI_GLOBAL_VARIABLE_STR("OsIndications")), &new_stat) < 0) {
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
@ -129,7 +129,7 @@ static int get_os_indications(uint64_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndications), NULL, &v, &s);
|
||||
r = efi_get_variable(EFI_GLOBAL_VARIABLE_STR("OsIndications"), NULL, &v, &s);
|
||||
if (r == -ENOENT) {
|
||||
/* Some firmware implementations that do support OsIndications and report that with
|
||||
* OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's
|
||||
@ -172,7 +172,7 @@ int efi_set_reboot_to_firmware(bool value) {
|
||||
|
||||
/* Avoid writing to efi vars store if we can due to firmware bugs. */
|
||||
if (b != b_new)
|
||||
return efi_set_variable(EFI_GLOBAL_VARIABLE(OsIndications), &b_new, sizeof(uint64_t));
|
||||
return efi_set_variable(EFI_GLOBAL_VARIABLE_STR("OsIndications"), &b_new, sizeof(uint64_t));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -399,7 +399,7 @@ int efi_get_boot_order(uint16_t **ret_order) {
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = efi_get_variable(EFI_GLOBAL_VARIABLE(BootOrder), NULL, &buf, &l);
|
||||
r = efi_get_variable(EFI_GLOBAL_VARIABLE_STR("BootOrder"), NULL, &buf, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -419,7 +419,7 @@ int efi_set_boot_order(const uint16_t *order, size_t n) {
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return efi_set_variable(EFI_GLOBAL_VARIABLE(BootOrder), order, n * sizeof(uint16_t));
|
||||
return efi_set_variable(EFI_GLOBAL_VARIABLE_STR("BootOrder"), order, n * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
static int boot_id_hex(const char s[static 4]) {
|
||||
|
@ -43,11 +43,11 @@ int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = read_usec(EFI_LOADER_VARIABLE(LoaderTimeInitUSec), &x);
|
||||
r = read_usec(EFI_LOADER_VARIABLE_STR("LoaderTimeInitUSec"), &x);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read LoaderTimeInitUSec: %m");
|
||||
|
||||
r = read_usec(EFI_LOADER_VARIABLE(LoaderTimeExecUSec), &y);
|
||||
r = read_usec(EFI_LOADER_VARIABLE_STR("LoaderTimeExecUSec"), &y);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read LoaderTimeExecUSec: %m");
|
||||
|
||||
@ -69,11 +69,11 @@ static int get_device_part_uuid(const char *variable, sd_id128_t *ret) {
|
||||
}
|
||||
|
||||
int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
|
||||
return get_device_part_uuid(EFI_LOADER_VARIABLE(LoaderDevicePartUUID), ret);
|
||||
return get_device_part_uuid(EFI_LOADER_VARIABLE_STR("LoaderDevicePartUUID"), ret);
|
||||
}
|
||||
|
||||
int efi_stub_get_device_part_uuid(sd_id128_t *ret) {
|
||||
return get_device_part_uuid(EFI_LOADER_VARIABLE(StubDevicePartUUID), ret);
|
||||
return get_device_part_uuid(EFI_LOADER_VARIABLE_STR("StubDevicePartUUID"), ret);
|
||||
}
|
||||
|
||||
int efi_loader_get_entries(char ***ret) {
|
||||
@ -87,7 +87,7 @@ int efi_loader_get_entries(char ***ret) {
|
||||
if (!is_efi_boot())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntries), NULL, (void**) &entries, &size);
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntries"), NULL, (void**) &entries, &size);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -148,12 +148,12 @@ int efi_loader_get_features(uint64_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderFeatures), NULL, &v, &s);
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderFeatures"), NULL, &v, &s);
|
||||
if (r == -ENOENT) {
|
||||
_cleanup_free_ char *info = NULL;
|
||||
|
||||
/* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderInfo), &info);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderInfo"), &info);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return r;
|
||||
@ -199,12 +199,12 @@ int efi_stub_get_features(uint64_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE(StubFeatures), NULL, &v, &s);
|
||||
r = efi_get_variable(EFI_LOADER_VARIABLE_STR("StubFeatures"), NULL, &v, &s);
|
||||
if (r == -ENOENT) {
|
||||
_cleanup_free_ char *info = NULL;
|
||||
|
||||
/* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubInfo), &info);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubInfo"), &info);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return r;
|
||||
@ -261,7 +261,7 @@ int efi_measured_uki(int log_level) {
|
||||
if (!efi_has_tpm2())
|
||||
return (cached = 0);
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubPcrKernelImage"), &pcr_string);
|
||||
if (r == -ENOENT)
|
||||
return (cached = 0);
|
||||
if (r < 0)
|
||||
@ -291,7 +291,7 @@ int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
|
||||
assert(ret);
|
||||
|
||||
/* stat() the EFI variable, to see if the mtime changed. If it did, we need to cache again. */
|
||||
if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot)), &new_stat) < 0)
|
||||
if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot")), &new_stat) < 0)
|
||||
return -errno;
|
||||
|
||||
if (stat_inode_unmodified(&new_stat, &cache_stat)) {
|
||||
@ -299,7 +299,7 @@ int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), &v);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"), &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -323,13 +323,13 @@ int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat
|
||||
assert(cache_stat);
|
||||
|
||||
/* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
|
||||
if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderEntryOneShot)), &new_stat) < 0)
|
||||
if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot")), &new_stat) < 0)
|
||||
return -errno;
|
||||
|
||||
if (stat_inode_unmodified(&new_stat, cache_stat))
|
||||
return 0;
|
||||
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &v);
|
||||
r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -527,7 +527,7 @@ int clear_efi_hibernate_location_and_warn(void) {
|
||||
if (!is_efi_boot())
|
||||
return 0;
|
||||
|
||||
r = efi_set_variable(EFI_SYSTEMD_VARIABLE(HibernateLocation), NULL, 0);
|
||||
r = efi_set_variable(EFI_SYSTEMD_VARIABLE_STR("HibernateLocation"), NULL, 0);
|
||||
if (r == -ENOENT)
|
||||
return 0;
|
||||
if (r < 0)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -114,7 +114,7 @@ static int write_efi_hibernate_location(const HibernationDevice *hibernation_dev
|
||||
if (r < 0)
|
||||
return log_full_errno(log_level, r, "Failed to format JSON object: %m");
|
||||
|
||||
r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE(HibernateLocation), formatted);
|
||||
r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE_STR("HibernateLocation"), formatted);
|
||||
if (r < 0)
|
||||
return log_full_errno(log_level, r, "Failed to set EFI variable HibernateLocation: %m");
|
||||
|
||||
|
@ -38,6 +38,7 @@ test_append_files() {
|
||||
instmods vmw_vsock_vmci_transport
|
||||
inst_binary gcc
|
||||
generate_module_dependencies
|
||||
inst_binary openssl
|
||||
}
|
||||
|
||||
do_test "$@"
|
||||
|
@ -276,4 +276,31 @@ testcase_bootctl_varlink() {
|
||||
SYSTEMD_LOG_TARGET=console varlinkctl call --json=short /run/systemd/io.systemd.BootControl io.systemd.BootControl.SetRebootToFirmware '{"state":false}' --graceful=io.systemd.BootControl.RebootToFirmwareNotSupported
|
||||
}
|
||||
|
||||
testcase_bootctl_secure_boot_auto_enroll() {
|
||||
cat >/tmp/openssl.conf <<EOF
|
||||
[ req ]
|
||||
prompt = no
|
||||
distinguished_name = req_distinguished_name
|
||||
|
||||
[ req_distinguished_name ]
|
||||
C = DE
|
||||
ST = Test State
|
||||
L = Test Locality
|
||||
O = Org Name
|
||||
OU = Org Unit Name
|
||||
CN = Common Name
|
||||
emailAddress = test@email.com
|
||||
EOF
|
||||
|
||||
openssl req -config /tmp/openssl.conf -subj="/CN=waldo" \
|
||||
-x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
|
||||
-keyout /tmp/sb.key -out /tmp/sb.crt
|
||||
|
||||
bootctl install --make-entry-directory=yes --secure-boot-auto-enroll=yes --certificate /tmp/sb.crt --private-key /tmp/sb.key
|
||||
for var in PK KEK db; do
|
||||
test -f "$(bootctl --print-esp-path)/loader/keys/auto/$var.auth"
|
||||
done
|
||||
bootctl remove
|
||||
}
|
||||
|
||||
run_testcases
|
||||
|
Loading…
Reference in New Issue
Block a user