mirror of
https://github.com/systemd/systemd
synced 2025-09-21 12:54:44 +02:00
Compare commits
19 Commits
c6e6c85f83
...
0c25f8fd0b
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0c25f8fd0b | ||
![]() |
6f1bc004e9 | ||
![]() |
46869e32cd | ||
![]() |
773a7d9a4e | ||
![]() |
f405165065 | ||
![]() |
f871c20bad | ||
![]() |
794f12c778 | ||
![]() |
600c757b5f | ||
![]() |
6c135f7ace | ||
![]() |
b3f3ce28f8 | ||
![]() |
0ce83b8a57 | ||
![]() |
4f35d74998 | ||
![]() |
419e4dc450 | ||
![]() |
4125e0c3db | ||
![]() |
4b97478979 | ||
![]() |
9db9d6806e | ||
![]() |
eb44fa4d19 | ||
![]() |
49dcc89ddc | ||
![]() |
d439799932 |
@ -480,6 +480,28 @@ sbvarsign --attr "${attr}" --key KEK.key --cert KEK.pem --output db.auth db db.e
|
|||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>log-level</term>
|
||||||
|
|
||||||
|
<listitem><para>Controls the log level used by
|
||||||
|
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>Valid values are <literal>emerg</literal>, <literal>alert</literal>, <literal>crit</literal>,
|
||||||
|
<literal>err</literal>, <literal>warning</literal>, <literal>notice</literal>,
|
||||||
|
<literal>info</literal>, and <literal>debug</literal>.</para>
|
||||||
|
|
||||||
|
<para>If unspecified, <literal>info</literal> will be used, unless one has already been configured
|
||||||
|
via an SMBIOS Type 11 string, see
|
||||||
|
<citerefentry><refentrytitle>smbios-type-11</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>Note that the configured level will only be used after <filename>loader.conf</filename> has
|
||||||
|
been parsed, so log messages generated before that point may be unaffected by this setting.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -869,6 +869,31 @@
|
|||||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>TPM2PCRs=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Configures the list of PCRs to use for LUKS2 volumes configured with
|
||||||
|
the <varname>Encrypt=tpm2</varname> setting in partition files.
|
||||||
|
This option take the same parameters as the similary named options to
|
||||||
|
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||||
|
and have the same effect on partitions where TPM2 enrollment is requested.
|
||||||
|
This option will be overridden by the global <varname>--tpm2-pcrs=</varname> option.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>KeyFile=</varname></term>
|
||||||
|
|
||||||
|
<listitem><para>Takes a file system path. This path must be absolute, otherwise the option is ignored.
|
||||||
|
Configures the encryption key to use when setting up LUKS2 volumes configured with the
|
||||||
|
<varname>Encrypt=key-file</varname> setting in partition files. Please refer to the documentation of
|
||||||
|
<varname>--key-file=</varname> for more details. This option will be overridden by the global
|
||||||
|
<varname>--key-file=</varname> option.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><varname>Compression=</varname></term>
|
<term><varname>Compression=</varname></term>
|
||||||
|
|
||||||
|
@ -80,10 +80,20 @@
|
|||||||
|
|
||||||
<listitem><para>This allows inserting additional entries into the <command>systemd-boot</command>
|
<listitem><para>This allows inserting additional entries into the <command>systemd-boot</command>
|
||||||
menu. For details see
|
menu. For details see
|
||||||
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry></para>
|
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>io.systemd.boot.loglevel=</varname><replaceable>LEVEL</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>This allows configuration of the log level, and is read by <command>systemd-boot</command>.
|
||||||
|
For details see
|
||||||
|
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -640,6 +640,16 @@ uki-url http://example.com/somedir/fooos.efi</programlisting>
|
|||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><varname>io.systemd.boot.loglevel</varname></term>
|
||||||
|
<listitem><para>If set, the value of this string is used as log level. Valid values (from most to
|
||||||
|
least critical) are <literal>emerg</literal>, <literal>alert</literal>, <literal>crit</literal>,
|
||||||
|
<literal>err</literal>, <literal>warning</literal>, <literal>notice</literal>, <literal>info</literal>,
|
||||||
|
and <literal>debug</literal>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -338,9 +338,9 @@
|
|||||||
volumes configured with the <varname>Encrypt=key-file</varname> setting in partition files. Should
|
volumes configured with the <varname>Encrypt=key-file</varname> setting in partition files. Should
|
||||||
refer to a regular file containing the key, or an <constant>AF_UNIX</constant> stream socket in the
|
refer to a regular file containing the key, or an <constant>AF_UNIX</constant> stream socket in the
|
||||||
file system. In the latter case, a connection is made to it and the key read from it. If this switch
|
file system. In the latter case, a connection is made to it and the key read from it. If this switch
|
||||||
is not specified, the empty key (i.e. zero length key) is used. This behaviour is useful for setting
|
is not specified, and no <varname>KeyFile=</varname> is specified in the partition file, the empty
|
||||||
up encrypted partitions during early first boot that receive their user-supplied password only in a
|
key (i.e. zero length key) is used. This behaviour is useful for setting up encrypted partitions during
|
||||||
later setup step.</para>
|
early first boot that receive their user-supplied password only in a later setup step.</para>
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -31,104 +32,111 @@ int efi_get_variable(
|
|||||||
void **ret_value,
|
void **ret_value,
|
||||||
size_t *ret_size) {
|
size_t *ret_size) {
|
||||||
|
|
||||||
_cleanup_close_ int fd = -EBADF;
|
|
||||||
_cleanup_free_ void *buf = NULL;
|
|
||||||
struct stat st;
|
|
||||||
usec_t begin = 0; /* Unnecessary initialization to appease gcc */
|
usec_t begin = 0; /* Unnecessary initialization to appease gcc */
|
||||||
uint32_t a;
|
|
||||||
ssize_t n;
|
|
||||||
|
|
||||||
assert(variable);
|
assert(variable);
|
||||||
|
|
||||||
const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
|
const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
|
||||||
|
|
||||||
if (!ret_value && !ret_size && !ret_attribute) {
|
|
||||||
/* If caller is not interested in anything, just check if the variable exists and is
|
|
||||||
* readable. */
|
|
||||||
if (access(p, R_OK) < 0)
|
|
||||||
return -errno;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DEBUG_LOGGING) {
|
if (DEBUG_LOGGING) {
|
||||||
log_debug("Reading EFI variable %s.", p);
|
log_debug("Reading EFI variable %s.", p);
|
||||||
begin = now(CLOCK_MONOTONIC);
|
begin = now(CLOCK_MONOTONIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
_cleanup_close_ int fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
|
return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
|
||||||
|
|
||||||
if (fstat(fd, &st) < 0)
|
uint32_t attr;
|
||||||
return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
|
_cleanup_free_ char *buf = NULL;
|
||||||
if (st.st_size < 4)
|
ssize_t n;
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
|
|
||||||
if (st.st_size > 4*1024*1024 + 4)
|
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
|
|
||||||
|
|
||||||
if (ret_value || ret_attribute) {
|
/* The kernel ratelimits reads from the efivarfs because EFI is inefficient, and we'll occasionally
|
||||||
/* The kernel ratelimits reads from the efivarfs because EFI is inefficient, and we'll
|
* fail with EINTR here. A slowdown is better than a failure for us, so retry a few times and
|
||||||
* occasionally fail with EINTR here. A slowdown is better than a failure for us, so
|
* eventually fail with -EBUSY.
|
||||||
* retry a few times and eventually fail with -EBUSY.
|
*
|
||||||
*
|
* See https://github.com/torvalds/linux/blob/master/fs/efivarfs/file.c#L75 and
|
||||||
* See https://github.com/torvalds/linux/blob/master/fs/efivarfs/file.c#L75
|
* https://github.com/torvalds/linux/commit/bef3efbeb897b56867e271cdbc5f8adaacaeb9cd.
|
||||||
* and
|
*
|
||||||
* https://github.com/torvalds/linux/commit/bef3efbeb897b56867e271cdbc5f8adaacaeb9cd.
|
* The variable may also be overwritten between the stat and read. If we find out that the new
|
||||||
*/
|
* contents are longer, try again.
|
||||||
for (unsigned try = 0;; try++) {
|
*/
|
||||||
n = read(fd, &a, sizeof(a));
|
for (unsigned try = 0;; try++) {
|
||||||
if (n >= 0)
|
struct stat st;
|
||||||
break;
|
|
||||||
log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
|
|
||||||
if (errno != EINTR)
|
|
||||||
return -errno;
|
|
||||||
if (try >= EFI_N_RETRIES_TOTAL)
|
|
||||||
return -EBUSY;
|
|
||||||
|
|
||||||
if (try >= EFI_N_RETRIES_NO_DELAY)
|
if (fstat(fd, &st) < 0)
|
||||||
(void) usleep_safe(EFI_RETRY_DELAY);
|
return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
|
||||||
}
|
if (st.st_size == 0)
|
||||||
|
|
||||||
/* Unfortunately kernel reports EOF if there's an inconsistency between efivarfs var list
|
|
||||||
* and what's actually stored in firmware, c.f. #34304. A zero size env var is not allowed in
|
|
||||||
* efi and hence the variable doesn't really exist in the backing store as long as it is zero
|
|
||||||
* sized, and the kernel calls this "uncommitted". Hence we translate EOF back to ENOENT here,
|
|
||||||
* as with kernel behavior before
|
|
||||||
* https://github.com/torvalds/linux/commit/3fab70c165795431f00ddf9be8b84ddd07bd1f8f
|
|
||||||
*
|
|
||||||
* If the kernel changes behaviour (to flush dentries on resume), we can drop
|
|
||||||
* this at some point in the future. But note that the commit is 11
|
|
||||||
* years old at this point so we'll need to deal with the current behaviour for
|
|
||||||
* a long time.
|
|
||||||
*/
|
|
||||||
if (n == 0)
|
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
|
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
|
||||||
"EFI variable %s is uncommitted", p);
|
"EFI variable %s is uncommitted", p);
|
||||||
|
if (st.st_size < 4)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
|
||||||
|
if (st.st_size > 4*1024*1024 + 4)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
|
||||||
|
|
||||||
if (n != sizeof(a))
|
if (!ret_attribute && !ret_value) {
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
|
/* No need to read anything, return the reported size. */
|
||||||
"Read %zi bytes from EFI variable %s, expected %zu.", n, p, sizeof(a));
|
n = st.st_size;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We want +1 for the read call, and +3 for the additional terminating bytes added below. */
|
||||||
|
char *t = realloc(buf, (size_t) st.st_size + MAX(1, 3));
|
||||||
|
if (!t)
|
||||||
|
return -ENOMEM;
|
||||||
|
buf = t;
|
||||||
|
|
||||||
|
const struct iovec iov[] = {
|
||||||
|
{ &attr, sizeof(attr) },
|
||||||
|
{ buf, (size_t) st.st_size + 1 },
|
||||||
|
};
|
||||||
|
|
||||||
|
n = readv(fd, iov, 2);
|
||||||
|
assert(n <= st.st_size + 1);
|
||||||
|
if (n == st.st_size + 1)
|
||||||
|
/* We need to try again with a bigger buffer. */
|
||||||
|
continue;
|
||||||
|
if (n >= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
|
||||||
|
if (errno != EINTR)
|
||||||
|
return -errno;
|
||||||
|
if (try >= EFI_N_RETRIES_TOTAL)
|
||||||
|
return -EBUSY;
|
||||||
|
if (try >= EFI_N_RETRIES_NO_DELAY)
|
||||||
|
(void) usleep_safe(EFI_RETRY_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unfortunately kernel reports EOF if there's an inconsistency between efivarfs var list and
|
||||||
|
* what's actually stored in firmware, c.f. #34304. A zero size env var is not allowed in EFI
|
||||||
|
* and hence the variable doesn't really exist in the backing store as long as it is zero
|
||||||
|
* sized, and the kernel calls this "uncommitted". Hence we translate EOF back to ENOENT
|
||||||
|
* here, as with kernel behavior before
|
||||||
|
* https://github.com/torvalds/linux/commit/3fab70c165795431f00ddf9be8b84ddd07bd1f8f.
|
||||||
|
*
|
||||||
|
* If the kernel changes behaviour (to flush dentries on resume), we can drop this at some
|
||||||
|
* point in the future. But note that the commit is 11 years old at this point so we'll need
|
||||||
|
* to deal with the current behaviour for a long time.
|
||||||
|
*/
|
||||||
|
if (n == 0)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
|
||||||
|
"EFI variable %s is uncommitted", p);
|
||||||
|
if (n < 4)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
|
||||||
|
"Read %zi bytes from EFI variable %s, expected >= 4", n, p);
|
||||||
|
|
||||||
|
if (ret_attribute)
|
||||||
|
*ret_attribute = attr;
|
||||||
if (ret_value) {
|
if (ret_value) {
|
||||||
buf = malloc(st.st_size - 4 + 3);
|
assert(buf);
|
||||||
if (!buf)
|
/* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in
|
||||||
return -ENOMEM;
|
* the middle of a character) */
|
||||||
|
buf[n - 4] = 0;
|
||||||
n = read(fd, buf, (size_t) st.st_size - 4);
|
buf[n - 4 + 1] = 0;
|
||||||
if (n < 0)
|
buf[n - 4 + 2] = 0;
|
||||||
return log_debug_errno(errno, "Failed to read value of EFI variable %s: %m", p);
|
*ret_value = TAKE_PTR(buf);
|
||||||
assert(n <= st.st_size - 4);
|
}
|
||||||
|
|
||||||
/* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in the middle
|
|
||||||
* of a character) */
|
|
||||||
((char*) buf)[n] = 0;
|
|
||||||
((char*) buf)[n + 1] = 0;
|
|
||||||
((char*) buf)[n + 2] = 0;
|
|
||||||
} else
|
|
||||||
/* Assume that the reported size is accurate */
|
|
||||||
n = st.st_size - 4;
|
|
||||||
|
|
||||||
if (DEBUG_LOGGING) {
|
if (DEBUG_LOGGING) {
|
||||||
usec_t end = now(CLOCK_MONOTONIC);
|
usec_t end = now(CLOCK_MONOTONIC);
|
||||||
@ -140,14 +148,8 @@ int efi_get_variable(
|
|||||||
/* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
|
/* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
|
||||||
* with a smaller value. */
|
* with a smaller value. */
|
||||||
|
|
||||||
if (ret_attribute)
|
|
||||||
*ret_attribute = a;
|
|
||||||
|
|
||||||
if (ret_value)
|
|
||||||
*ret_value = TAKE_PTR(buf);
|
|
||||||
|
|
||||||
if (ret_size)
|
if (ret_size)
|
||||||
*ret_size = n;
|
*ret_size = n - 4;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -208,10 +210,10 @@ static int efi_verify_variable(const char *variable, uint32_t attr, const void *
|
|||||||
int efi_set_variable(const char *variable, const void *value, size_t size) {
|
int efi_set_variable(const char *variable, const void *value, size_t size) {
|
||||||
static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
|
static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
|
||||||
|
|
||||||
struct var {
|
_cleanup_free_ struct var {
|
||||||
uint32_t attr;
|
uint32_t attr;
|
||||||
char buf[];
|
char buf[];
|
||||||
} _packed_ * _cleanup_free_ buf = NULL;
|
} _packed_ *buf = NULL;
|
||||||
_cleanup_close_ int fd = -EBADF;
|
_cleanup_close_ int fd = -EBADF;
|
||||||
bool saved_flags_valid = false;
|
bool saved_flags_valid = false;
|
||||||
unsigned saved_flags;
|
unsigned saved_flags;
|
||||||
|
@ -361,6 +361,8 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
|
|||||||
if (config->console_mode_efivar != CONSOLE_MODE_KEEP)
|
if (config->console_mode_efivar != CONSOLE_MODE_KEEP)
|
||||||
printf(" console-mode (EFI var): %" PRIi64 "\n", config->console_mode_efivar);
|
printf(" console-mode (EFI var): %" PRIi64 "\n", config->console_mode_efivar);
|
||||||
|
|
||||||
|
printf(" log-level: %s\n", log_level_to_string(log_get_max_level()));
|
||||||
|
|
||||||
if (!ps_continue())
|
if (!ps_continue())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1111,6 +1113,9 @@ static void config_defaults_load_from_file(Config *config, char *content) {
|
|||||||
}
|
}
|
||||||
config->console_mode = u;
|
config->console_mode = u;
|
||||||
}
|
}
|
||||||
|
} else if (streq8(key, "log-level")) {
|
||||||
|
if (log_set_max_level_from_string(value) < 0)
|
||||||
|
log_error("Error parsing 'log-level' config option, ignoring: %s", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2989,6 +2994,9 @@ static EFI_STATUS run(EFI_HANDLE image) {
|
|||||||
uint64_t init_usec;
|
uint64_t init_usec;
|
||||||
bool menu = false;
|
bool menu = false;
|
||||||
|
|
||||||
|
/* set loglevel early to simplify debugging before loader.conf is loaded */
|
||||||
|
log_set_max_level_from_smbios();
|
||||||
|
|
||||||
init_usec = time_usec();
|
init_usec = time_usec();
|
||||||
|
|
||||||
err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
|
err = BS->HandleProtocol(image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &loaded_image);
|
||||||
|
@ -35,7 +35,7 @@ static EFI_STATUS load_one_driver(
|
|||||||
|
|
||||||
if (loaded_image->ImageCodeType != EfiBootServicesCode &&
|
if (loaded_image->ImageCodeType != EfiBootServicesCode &&
|
||||||
loaded_image->ImageCodeType != EfiRuntimeServicesCode)
|
loaded_image->ImageCodeType != EfiRuntimeServicesCode)
|
||||||
return log_error("Image %ls is not a driver, refusing.", fname);
|
return log_error_status(EFI_INVALID_PARAMETER, "Image %ls is not a driver, refusing.", fname);
|
||||||
|
|
||||||
err = BS->StartImage(image, NULL, NULL);
|
err = BS->StartImage(image, NULL, NULL);
|
||||||
if (err != EFI_SUCCESS) {
|
if (err != EFI_SUCCESS) {
|
||||||
|
@ -1,10 +1,78 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
#include "efi-log.h"
|
#include "efi-log.h"
|
||||||
|
#include "efi-string-table.h"
|
||||||
#include "proto/rng.h"
|
#include "proto/rng.h"
|
||||||
|
#include "smbios.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "vmm.h"
|
||||||
|
|
||||||
static unsigned log_count = 0;
|
static unsigned log_count = 0;
|
||||||
|
static LogLevel log_max_level = LOG_INFO;
|
||||||
|
|
||||||
|
static const uint8_t log_level_color[_LOG_MAX] = {
|
||||||
|
[LOG_EMERG] = EFI_LIGHTRED,
|
||||||
|
[LOG_ALERT] = EFI_LIGHTRED,
|
||||||
|
[LOG_CRIT] = EFI_LIGHTRED,
|
||||||
|
[LOG_ERR] = EFI_LIGHTRED,
|
||||||
|
[LOG_WARNING] = EFI_YELLOW,
|
||||||
|
[LOG_NOTICE] = EFI_WHITE,
|
||||||
|
[LOG_INFO] = EFI_WHITE,
|
||||||
|
[LOG_DEBUG] = EFI_LIGHTGRAY,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *const log_level_table[_LOG_MAX] = {
|
||||||
|
[LOG_EMERG] = "emerg",
|
||||||
|
[LOG_ALERT] = "alert",
|
||||||
|
[LOG_CRIT] = "crit",
|
||||||
|
[LOG_ERR] = "err",
|
||||||
|
[LOG_WARNING] = "warning",
|
||||||
|
[LOG_NOTICE] = "notice",
|
||||||
|
[LOG_INFO] = "info",
|
||||||
|
[LOG_DEBUG] = "debug",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP(log_level, LogLevel);
|
||||||
|
|
||||||
|
LogLevel log_get_max_level(void) {
|
||||||
|
return log_max_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_set_max_level(LogLevel level) {
|
||||||
|
assert(level >= 0 && level < _LOG_MAX);
|
||||||
|
|
||||||
|
int old = log_max_level;
|
||||||
|
log_max_level = level;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
int log_set_max_level_from_string(const char *e) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(e);
|
||||||
|
|
||||||
|
r = log_level_from_string(e);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
log_set_max_level(r);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_set_max_level_from_smbios(void) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (is_confidential_vm())
|
||||||
|
return; /* Don't consume SMBIOS in Confidential Computing contexts */
|
||||||
|
|
||||||
|
const char *level_str = smbios_find_oem_string("io.systemd.boot.loglevel=", /* after= */ NULL);
|
||||||
|
if (!level_str)
|
||||||
|
return;
|
||||||
|
|
||||||
|
r = log_set_max_level_from_string(level_str);
|
||||||
|
if (r < 0)
|
||||||
|
log_warning("Failed to parse log level '%s', ignoring.", level_str);
|
||||||
|
}
|
||||||
|
|
||||||
void freeze(void) {
|
void freeze(void) {
|
||||||
for (;;)
|
for (;;)
|
||||||
@ -31,14 +99,18 @@ void efi_assert(const char *expr, const char *file, unsigned line, const char *f
|
|||||||
freeze();
|
freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
EFI_STATUS log_internal(EFI_STATUS status, uint8_t text_color, const char *format, ...) {
|
EFI_STATUS log_internal(EFI_STATUS status, LogLevel log_level, const char *format, ...) {
|
||||||
assert(format);
|
assert(format);
|
||||||
|
assert(log_level >= 0 && log_level < _LOG_MAX);
|
||||||
|
|
||||||
|
if (log_level > log_max_level)
|
||||||
|
return status;
|
||||||
|
|
||||||
int32_t attr = ST->ConOut->Mode->Attribute;
|
int32_t attr = ST->ConOut->Mode->Attribute;
|
||||||
|
|
||||||
if (ST->ConOut->Mode->CursorColumn > 0)
|
if (ST->ConOut->Mode->CursorColumn > 0)
|
||||||
ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n");
|
ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n");
|
||||||
ST->ConOut->SetAttribute(ST->ConOut, EFI_TEXT_ATTR(text_color, EFI_BLACK));
|
ST->ConOut->SetAttribute(ST->ConOut, EFI_TEXT_ATTR(log_level_color[log_level], EFI_BLACK));
|
||||||
|
|
||||||
va_list ap;
|
va_list ap;
|
||||||
va_start(ap, format);
|
va_start(ap, format);
|
||||||
|
@ -21,17 +21,49 @@ __attribute__((no_stack_protector, noinline)) void __stack_chk_guard_init(void);
|
|||||||
# define __stack_chk_guard_init()
|
# define __stack_chk_guard_init()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
typedef enum LogLevel {
|
||||||
|
LOG_EMERG,
|
||||||
|
LOG_ALERT,
|
||||||
|
LOG_CRIT,
|
||||||
|
LOG_ERR,
|
||||||
|
LOG_WARNING,
|
||||||
|
LOG_NOTICE,
|
||||||
|
LOG_INFO,
|
||||||
|
LOG_DEBUG,
|
||||||
|
_LOG_MAX,
|
||||||
|
_LOG_INVALID = -1,
|
||||||
|
} LogLevel;
|
||||||
|
|
||||||
|
LogLevel log_level_from_string(const char *s) _pure_;
|
||||||
|
const char* log_level_to_string(LogLevel l) _const_;
|
||||||
|
|
||||||
|
LogLevel log_get_max_level(void) _pure_;
|
||||||
|
int log_set_max_level(LogLevel level);
|
||||||
|
int log_set_max_level_from_string(const char *e);
|
||||||
|
void log_set_max_level_from_smbios(void);
|
||||||
|
|
||||||
_noreturn_ void freeze(void);
|
_noreturn_ void freeze(void);
|
||||||
void log_wait(void);
|
void log_wait(void);
|
||||||
_gnu_printf_(3, 4) EFI_STATUS log_internal(EFI_STATUS status, uint8_t text_color, const char *format, ...);
|
_gnu_printf_(3, 4) EFI_STATUS log_internal(EFI_STATUS status, LogLevel log_level, const char *format, ...);
|
||||||
#define log_full(status, text_color, format, ...) \
|
|
||||||
log_internal(status, text_color, "%s:%i@%s: " format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
#define log_full(status, log_level, format, ...) \
|
||||||
#define log_debug(...) log_full(EFI_SUCCESS, EFI_LIGHTGRAY, __VA_ARGS__)
|
log_internal(status, log_level, "%s:%i@%s: " format, __FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
||||||
#define log_info(...) log_full(EFI_SUCCESS, EFI_WHITE, __VA_ARGS__)
|
|
||||||
#define log_warning_status(status, ...) log_full(status, EFI_YELLOW, __VA_ARGS__)
|
#define log_debug(...) log_full(EFI_SUCCESS, LOG_DEBUG, __VA_ARGS__)
|
||||||
#define log_error_status(status, ...) log_full(status, EFI_LIGHTRED, __VA_ARGS__)
|
#define log_info(...) log_full(EFI_SUCCESS, LOG_INFO, __VA_ARGS__)
|
||||||
#define log_error(...) log_full(EFI_INVALID_PARAMETER, EFI_LIGHTRED, __VA_ARGS__)
|
#define log_notice(...) log_full(EFI_SUCCESS, LOG_NOTICE, __VA_ARGS__)
|
||||||
#define log_oom() log_full(EFI_OUT_OF_RESOURCES, EFI_LIGHTRED, "Out of memory.")
|
#define log_warning(...) log_full(EFI_SUCCESS, LOG_WARNING, __VA_ARGS__)
|
||||||
|
#define log_error(...) log_full(EFI_SUCCESS, LOG_ERR, __VA_ARGS__)
|
||||||
|
#define log_emergency(...) log_full(EFI_SUCCESS, LOG_EMERG, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define log_debug_status(status, ...) log_full(status, LOG_DEBUG, __VA_ARGS__)
|
||||||
|
#define log_info_status(status, ...) log_full(status, LOG_INFO, __VA_ARGS__)
|
||||||
|
#define log_notice_status(status, ...) log_full(status, LOG_NOTICE, __VA_ARGS__)
|
||||||
|
#define log_warning_status(status, ...) log_full(status, LOG_WARNING, __VA_ARGS__)
|
||||||
|
#define log_error_status(status, ...) log_full(status, LOG_ERR, __VA_ARGS__)
|
||||||
|
#define log_emergency_status(status, ...) log_full(status, LOG_EMERG, __VA_ARGS__)
|
||||||
|
|
||||||
|
#define log_oom() log_full(EFI_OUT_OF_RESOURCES, LOG_ERR, "Out of memory.")
|
||||||
|
|
||||||
/* Debugging helper — please keep this around, even if not used */
|
/* Debugging helper — please keep this around, even if not used */
|
||||||
#define log_hexdump(prefix, data, size) \
|
#define log_hexdump(prefix, data, size) \
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "efi-string.h"
|
||||||
#include "macro-fundamental.h"
|
#include "macro-fundamental.h"
|
||||||
|
|
||||||
#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
|
#define _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,scope) \
|
||||||
@ -9,5 +10,23 @@
|
|||||||
return name##_table[i]; \
|
return name##_table[i]; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, scope) \
|
||||||
|
scope type name##_from_string(const char *s) { \
|
||||||
|
if (!s) \
|
||||||
|
return (type) -1; \
|
||||||
|
for (size_t i = 0; i < ELEMENTSOF(name##_table); ++i) \
|
||||||
|
if (streq8(name##_table[i], s)) \
|
||||||
|
return (type) i; \
|
||||||
|
return (type) -1; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define _DEFINE_STRING_TABLE_LOOKUP(name, type, scope) \
|
||||||
|
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name, type, scope) \
|
||||||
|
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, scope)
|
||||||
|
|
||||||
|
#define DEFINE_STRING_TABLE_LOOKUP(name, type) _DEFINE_STRING_TABLE_LOOKUP(name, type,)
|
||||||
#define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,)
|
#define DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,)
|
||||||
|
#define DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type,)
|
||||||
|
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP(name, type) _DEFINE_STRING_TABLE_LOOKUP(name, type, static)
|
||||||
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
|
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(name,type) _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(name,type,static)
|
||||||
|
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(name, type) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING(name, type, static)
|
||||||
|
@ -20,9 +20,6 @@
|
|||||||
#include "shim.h"
|
#include "shim.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define STUB_PAYLOAD_GUID \
|
|
||||||
{ 0x55c5d1f8, 0x04cd, 0x46b5, { 0x8a, 0x20, 0xe5, 0x6c, 0xbb, 0x30, 0x52, 0xd0 } }
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MEMMAP_DEVICE_PATH memmap_path;
|
MEMMAP_DEVICE_PATH memmap_path;
|
||||||
EFI_DEVICE_PATH end_path;
|
EFI_DEVICE_PATH end_path;
|
||||||
@ -56,22 +53,12 @@ static EFI_STATUS load_via_boot_services(
|
|||||||
uint32_t compat_entry_point,
|
uint32_t compat_entry_point,
|
||||||
const char16_t *cmdline,
|
const char16_t *cmdline,
|
||||||
const struct iovec *kernel,
|
const struct iovec *kernel,
|
||||||
const struct iovec *initrd) {
|
const struct iovec *initrd,
|
||||||
|
KERNEL_FILE_PATH *kernel_file_path) {
|
||||||
_cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
|
_cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
|
||||||
EFI_LOADED_IMAGE_PROTOCOL* loaded_image = NULL;
|
EFI_LOADED_IMAGE_PROTOCOL* loaded_image = NULL;
|
||||||
EFI_STATUS err;
|
EFI_STATUS err;
|
||||||
|
|
||||||
VENDOR_DEVICE_PATH device_node = {
|
|
||||||
.Header = {
|
|
||||||
.Type = MEDIA_DEVICE_PATH,
|
|
||||||
.SubType = MEDIA_VENDOR_DP,
|
|
||||||
.Length = sizeof(device_node),
|
|
||||||
},
|
|
||||||
.Guid = STUB_PAYLOAD_GUID,
|
|
||||||
};
|
|
||||||
|
|
||||||
_cleanup_free_ EFI_DEVICE_PATH* file_path = device_path_replace_node(parent_loaded_image->FilePath, NULL, &device_node.Header);
|
|
||||||
|
|
||||||
/* When running with shim < v16 and booting a UKI directly from it, without a second stage loader,
|
/* When running with shim < v16 and booting a UKI directly from it, without a second stage loader,
|
||||||
* the shim verify protocol needs to be called or it will raise a security violation when starting
|
* the shim verify protocol needs to be called or it will raise a security violation when starting
|
||||||
* the image (e.g.: Fedora Cloud Base UKI). TODO: drop once support for shim < v16 is not needed. */
|
* the image (e.g.: Fedora Cloud Base UKI). TODO: drop once support for shim < v16 is not needed. */
|
||||||
@ -81,13 +68,13 @@ static EFI_STATUS load_via_boot_services(
|
|||||||
&(ValidationContext) {
|
&(ValidationContext) {
|
||||||
.addr = kernel->iov_base,
|
.addr = kernel->iov_base,
|
||||||
.len = kernel->iov_len,
|
.len = kernel->iov_len,
|
||||||
.device_path = file_path,
|
.device_path = &kernel_file_path->memmap_path.Header,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
err = BS->LoadImage(/* BootPolicy= */false,
|
err = BS->LoadImage(/* BootPolicy= */false,
|
||||||
parent,
|
parent,
|
||||||
file_path,
|
&kernel_file_path->memmap_path.Header,
|
||||||
kernel->iov_base,
|
kernel->iov_base,
|
||||||
kernel->iov_len,
|
kernel->iov_len,
|
||||||
&kernel_image);
|
&kernel_image);
|
||||||
@ -204,17 +191,27 @@ EFI_STATUS linux_exec(
|
|||||||
if (err != EFI_SUCCESS)
|
if (err != EFI_SUCCESS)
|
||||||
return log_error_status(err, "Bad kernel image: %m");
|
return log_error_status(err, "Bad kernel image: %m");
|
||||||
|
|
||||||
/* Re-use the parent_image(_handle) and parent_loaded_image for the kernel image we are about to execute.
|
|
||||||
* We have to do this, because if kernel stub code passes its own handle to certain firmware functions,
|
|
||||||
* the firmware could cast EFI_LOADED_IMAGE_PROTOCOL * to a larger struct to access its own private data,
|
|
||||||
* and if we allocated a smaller struct, that could cause problems.
|
|
||||||
* This is modeled exactly after GRUB behaviour, which has proven to be functional. */
|
|
||||||
EFI_LOADED_IMAGE_PROTOCOL *parent_loaded_image;
|
EFI_LOADED_IMAGE_PROTOCOL *parent_loaded_image;
|
||||||
err = BS->HandleProtocol(
|
err = BS->HandleProtocol(
|
||||||
parent_image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &parent_loaded_image);
|
parent_image, MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL), (void **) &parent_loaded_image);
|
||||||
if (err != EFI_SUCCESS)
|
if (err != EFI_SUCCESS)
|
||||||
return log_error_status(err, "Cannot get parent loaded image: %m");
|
return log_error_status(err, "Cannot get parent loaded image: %m");
|
||||||
|
|
||||||
|
_cleanup_free_ KERNEL_FILE_PATH *kernel_file_path = xnew(KERNEL_FILE_PATH, 1);
|
||||||
|
*kernel_file_path = (KERNEL_FILE_PATH) {
|
||||||
|
.memmap_path = {
|
||||||
|
.Header = {
|
||||||
|
.Type = HARDWARE_DEVICE_PATH,
|
||||||
|
.SubType = HW_MEMMAP_DP,
|
||||||
|
.Length = sizeof(MEMMAP_DEVICE_PATH),
|
||||||
|
},
|
||||||
|
.MemoryType = EfiLoaderData,
|
||||||
|
.StartingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base),
|
||||||
|
.EndingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len,
|
||||||
|
},
|
||||||
|
.end_path = DEVICE_PATH_END_NODE,
|
||||||
|
};
|
||||||
|
|
||||||
/* If shim provides LoadImage, it comes from the new SHIM_IMAGE_LOADER interface added in shim 16,
|
/* If shim provides LoadImage, it comes from the new SHIM_IMAGE_LOADER interface added in shim 16,
|
||||||
* and implements the following:
|
* and implements the following:
|
||||||
* - shim hashes PE sections of PE binaries it authenticates and stores the hashes in a global
|
* - shim hashes PE sections of PE binaries it authenticates and stores the hashes in a global
|
||||||
@ -241,7 +238,8 @@ EFI_STATUS linux_exec(
|
|||||||
compat_entry_point,
|
compat_entry_point,
|
||||||
cmdline,
|
cmdline,
|
||||||
kernel,
|
kernel,
|
||||||
initrd);
|
initrd,
|
||||||
|
kernel_file_path);
|
||||||
|
|
||||||
err = pe_kernel_check_no_relocation(kernel->iov_base);
|
err = pe_kernel_check_no_relocation(kernel->iov_base);
|
||||||
if (err != EFI_SUCCESS)
|
if (err != EFI_SUCCESS)
|
||||||
@ -300,26 +298,13 @@ EFI_STATUS linux_exec(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_cleanup_free_ KERNEL_FILE_PATH *kernel_file_path = xnew(KERNEL_FILE_PATH, 1);
|
/* Patch the parent_image(_handle) and parent_loaded_image for the kernel image we are about to execute.
|
||||||
|
* We have to do this, because if kernel stub code passes its own handle to certain firmware functions,
|
||||||
*kernel_file_path = (KERNEL_FILE_PATH) {
|
* the firmware could cast EFI_LOADED_IMAGE_PROTOCOL * to a larger struct to access its own private data,
|
||||||
.memmap_path = {
|
* and if we allocated a smaller struct, that could cause problems.
|
||||||
.Header = {
|
* This is modeled exactly after GRUB behaviour, which has proven to be functional. */
|
||||||
.Type = HARDWARE_DEVICE_PATH,
|
EFI_LOADED_IMAGE_PROTOCOL original_parent_loaded_image = *parent_loaded_image;
|
||||||
.SubType = HW_MEMMAP_DP,
|
parent_loaded_image->FilePath = &kernel_file_path->memmap_path.Header;
|
||||||
.Length = sizeof(MEMMAP_DEVICE_PATH),
|
|
||||||
},
|
|
||||||
.MemoryType = EfiLoaderData,
|
|
||||||
.StartingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base),
|
|
||||||
.EndingAddress = POINTER_TO_PHYSICAL_ADDRESS(kernel->iov_base) + kernel->iov_len,
|
|
||||||
},
|
|
||||||
.end_path = {
|
|
||||||
.Type = END_DEVICE_PATH_TYPE,
|
|
||||||
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
|
|
||||||
.Length = sizeof(EFI_DEVICE_PATH),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
parent_loaded_image->ImageBase = loaded_kernel;
|
parent_loaded_image->ImageBase = loaded_kernel;
|
||||||
parent_loaded_image->ImageSize = kernel_size_in_memory;
|
parent_loaded_image->ImageSize = kernel_size_in_memory;
|
||||||
|
|
||||||
@ -346,6 +331,9 @@ EFI_STATUS linux_exec(
|
|||||||
err = compat_entry(parent_image, ST);
|
err = compat_entry(parent_image, ST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Restore */
|
||||||
|
*parent_loaded_image = original_parent_loaded_image;
|
||||||
|
|
||||||
/* On failure we'll free the buffers. EDK2 requires the memory buffers to be writable and
|
/* On failure we'll free the buffers. EDK2 requires the memory buffers to be writable and
|
||||||
* non-executable, as in some configurations it will overwrite them with a fixed pattern, so if the
|
* non-executable, as in some configurations it will overwrite them with a fixed pattern, so if the
|
||||||
* attributes are not restored FreePages() will crash. */
|
* attributes are not restored FreePages() will crash. */
|
||||||
|
@ -211,10 +211,10 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
|
|||||||
|
|
||||||
size = info->FileSize;
|
size = info->FileSize;
|
||||||
if (size < RANDOM_MAX_SIZE_MIN)
|
if (size < RANDOM_MAX_SIZE_MIN)
|
||||||
return log_error("Random seed file is too short.");
|
return log_error_status(EFI_INVALID_PARAMETER, "Random seed file is too short.");
|
||||||
|
|
||||||
if (size > RANDOM_MAX_SIZE_MAX)
|
if (size > RANDOM_MAX_SIZE_MAX)
|
||||||
return log_error("Random seed file is too large.");
|
return log_error_status(EFI_INVALID_PARAMETER, "Random seed file is too large.");
|
||||||
|
|
||||||
seed = xmalloc(size);
|
seed = xmalloc(size);
|
||||||
rsize = size;
|
rsize = size;
|
||||||
|
@ -169,8 +169,7 @@ static bool arg_size_auto = false;
|
|||||||
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF;
|
||||||
static PagerFlags arg_pager_flags = 0;
|
static PagerFlags arg_pager_flags = 0;
|
||||||
static bool arg_legend = true;
|
static bool arg_legend = true;
|
||||||
static void *arg_key = NULL;
|
static struct iovec arg_key = {};
|
||||||
static size_t arg_key_size = 0;
|
|
||||||
static char *arg_private_key = NULL;
|
static char *arg_private_key = NULL;
|
||||||
static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
|
static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE;
|
||||||
static char *arg_private_key_source = NULL;
|
static char *arg_private_key_source = NULL;
|
||||||
@ -208,7 +207,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_node, freep);
|
|||||||
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_key, iovec_done_erase);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep);
|
||||||
@ -413,6 +412,9 @@ typedef struct Partition {
|
|||||||
OrderedHashmap *subvolumes;
|
OrderedHashmap *subvolumes;
|
||||||
char *default_subvolume;
|
char *default_subvolume;
|
||||||
EncryptMode encrypt;
|
EncryptMode encrypt;
|
||||||
|
struct iovec key;
|
||||||
|
Tpm2PCRValue *tpm2_hash_pcr_values;
|
||||||
|
size_t tpm2_n_hash_pcr_values;
|
||||||
VerityMode verity;
|
VerityMode verity;
|
||||||
char *verity_match_key;
|
char *verity_match_key;
|
||||||
MinimizeMode minimize;
|
MinimizeMode minimize;
|
||||||
@ -676,10 +678,13 @@ static Partition* partition_free(Partition *p) {
|
|||||||
strv_free(p->make_symlinks);
|
strv_free(p->make_symlinks);
|
||||||
ordered_hashmap_free(p->subvolumes);
|
ordered_hashmap_free(p->subvolumes);
|
||||||
free(p->default_subvolume);
|
free(p->default_subvolume);
|
||||||
|
free(p->tpm2_hash_pcr_values);
|
||||||
free(p->verity_match_key);
|
free(p->verity_match_key);
|
||||||
free(p->compression);
|
free(p->compression);
|
||||||
free(p->compression_level);
|
free(p->compression_level);
|
||||||
|
|
||||||
|
iovec_done_erase(&p->key);
|
||||||
|
|
||||||
copy_files_free_many(p->copy_files, p->n_copy_files);
|
copy_files_free_many(p->copy_files, p->n_copy_files);
|
||||||
|
|
||||||
iovec_done(&p->roothash);
|
iovec_done(&p->roothash);
|
||||||
@ -717,10 +722,13 @@ static void partition_foreignize(Partition *p) {
|
|||||||
p->make_symlinks = strv_free(p->make_symlinks);
|
p->make_symlinks = strv_free(p->make_symlinks);
|
||||||
p->subvolumes = ordered_hashmap_free(p->subvolumes);
|
p->subvolumes = ordered_hashmap_free(p->subvolumes);
|
||||||
p->default_subvolume = mfree(p->default_subvolume);
|
p->default_subvolume = mfree(p->default_subvolume);
|
||||||
|
p->tpm2_hash_pcr_values = mfree(p->tpm2_hash_pcr_values);
|
||||||
p->verity_match_key = mfree(p->verity_match_key);
|
p->verity_match_key = mfree(p->verity_match_key);
|
||||||
p->compression = mfree(p->compression);
|
p->compression = mfree(p->compression);
|
||||||
p->compression_level = mfree(p->compression_level);
|
p->compression_level = mfree(p->compression_level);
|
||||||
|
|
||||||
|
iovec_done_erase(&p->key);
|
||||||
|
|
||||||
copy_files_free_many(p->copy_files, p->n_copy_files);
|
copy_files_free_many(p->copy_files, p->n_copy_files);
|
||||||
p->copy_files = NULL;
|
p->copy_files = NULL;
|
||||||
p->n_copy_files = 0;
|
p->n_copy_files = 0;
|
||||||
@ -2467,6 +2475,78 @@ static int config_parse_encrypted_volume(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int config_parse_tpm2_pcrs(
|
||||||
|
const char *unit,
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
unsigned section_line,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
Partition *partition = ASSERT_PTR(data);
|
||||||
|
|
||||||
|
assert(rvalue);
|
||||||
|
|
||||||
|
if (isempty(rvalue)) {
|
||||||
|
/* Clear existing PCR values if empty */
|
||||||
|
partition->tpm2_hash_pcr_values = mfree(partition->tpm2_hash_pcr_values);
|
||||||
|
partition->tpm2_n_hash_pcr_values = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tpm2_parse_pcr_argument_append(rvalue, &partition->tpm2_hash_pcr_values,
|
||||||
|
&partition->tpm2_n_hash_pcr_values);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_key_file(const char *filename, struct iovec *key) {
|
||||||
|
_cleanup_(erase_and_freep) char *k = NULL;
|
||||||
|
size_t n = 0;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = read_full_file_full(
|
||||||
|
AT_FDCWD, filename,
|
||||||
|
/* offset= */ UINT64_MAX,
|
||||||
|
/* size= */ SIZE_MAX,
|
||||||
|
READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET,
|
||||||
|
/* bind_name= */ NULL,
|
||||||
|
&k, &n);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to read key file '%s': %m", filename);
|
||||||
|
|
||||||
|
iovec_done_erase(key);
|
||||||
|
*key = IOVEC_MAKE(TAKE_PTR(k), n);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int config_parse_key_file(
|
||||||
|
const char *unit,
|
||||||
|
const char *filename,
|
||||||
|
unsigned line,
|
||||||
|
const char *section,
|
||||||
|
unsigned section_line,
|
||||||
|
const char *lvalue,
|
||||||
|
int ltype,
|
||||||
|
const char *rvalue,
|
||||||
|
void *data,
|
||||||
|
void *userdata) {
|
||||||
|
|
||||||
|
Partition *partition = ASSERT_PTR(userdata);
|
||||||
|
|
||||||
|
assert(rvalue);
|
||||||
|
|
||||||
|
if (isempty(rvalue)) {
|
||||||
|
iovec_done_erase(&partition->key);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parse_key_file(rvalue, &partition->key);
|
||||||
|
}
|
||||||
|
|
||||||
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF);
|
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF);
|
||||||
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF);
|
static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF);
|
||||||
|
|
||||||
@ -2572,6 +2652,8 @@ static int partition_read_definition(Partition *p, const char *path, const char
|
|||||||
{ "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
|
{ "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size },
|
||||||
{ "Partition", "MountPoint", config_parse_mountpoint, 0, p },
|
{ "Partition", "MountPoint", config_parse_mountpoint, 0, p },
|
||||||
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
{ "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p },
|
||||||
|
{ "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p },
|
||||||
|
{ "Partition", "KeyFile", config_parse_key_file, 0, p },
|
||||||
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
|
{ "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression },
|
||||||
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
|
{ "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level },
|
||||||
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
|
{ "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name },
|
||||||
@ -4793,18 +4875,21 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
|||||||
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
|
return log_error_errno(r, "Failed to LUKS2 format future partition: %m");
|
||||||
|
|
||||||
if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
|
if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) {
|
||||||
|
/* Use partition-specific key if available, otherwise fall back to global key */
|
||||||
|
struct iovec *iovec_key = arg_key.iov_base ? &arg_key : &p->key;
|
||||||
|
|
||||||
r = sym_crypt_keyslot_add_by_volume_key(
|
r = sym_crypt_keyslot_add_by_volume_key(
|
||||||
cd,
|
cd,
|
||||||
CRYPT_ANY_SLOT,
|
CRYPT_ANY_SLOT,
|
||||||
NULL,
|
NULL,
|
||||||
VOLUME_KEY_SIZE,
|
VOLUME_KEY_SIZE,
|
||||||
strempty(arg_key),
|
strempty(iovec_key->iov_base),
|
||||||
arg_key_size);
|
iovec_key->iov_len);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to add LUKS2 key: %m");
|
return log_error_errno(r, "Failed to add LUKS2 key: %m");
|
||||||
|
|
||||||
passphrase = strempty(arg_key);
|
passphrase = strempty(iovec_key->iov_base);
|
||||||
passphrase_size = arg_key_size;
|
passphrase_size = iovec_key->iov_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
|
if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) {
|
||||||
@ -4815,8 +4900,10 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
|||||||
ssize_t base64_encoded_size;
|
ssize_t base64_encoded_size;
|
||||||
int keyslot;
|
int keyslot;
|
||||||
TPM2Flags flags = 0;
|
TPM2Flags flags = 0;
|
||||||
|
Tpm2PCRValue *pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_hash_pcr_values : p->tpm2_hash_pcr_values;
|
||||||
|
size_t n_pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_n_hash_pcr_values : p->tpm2_n_hash_pcr_values;
|
||||||
|
|
||||||
if (arg_tpm2_n_hash_pcr_values == 0 &&
|
if (n_pcr_values == 0 &&
|
||||||
arg_tpm2_public_key_pcr_mask == 0 &&
|
arg_tpm2_public_key_pcr_mask == 0 &&
|
||||||
!arg_tpm2_pcrlock)
|
!arg_tpm2_pcrlock)
|
||||||
log_notice("Notice: encrypting future partition %" PRIu64 ", locking against TPM2 with an empty policy, i.e. without any state or access restrictions.\n"
|
log_notice("Notice: encrypting future partition %" PRIu64 ", locking against TPM2 with an empty policy, i.e. without any state or access restrictions.\n"
|
||||||
@ -4856,7 +4943,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values))
|
if (!tpm2_pcr_values_has_all_values(pcr_values, n_pcr_values))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||||
"Must provide all PCR values when using TPM2 device key.");
|
"Must provide all PCR values when using TPM2 device key.");
|
||||||
} else {
|
} else {
|
||||||
@ -4864,8 +4951,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) {
|
if (!tpm2_pcr_values_has_all_values(pcr_values, n_pcr_values)) {
|
||||||
r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values);
|
r = tpm2_pcr_read_missing_values(tpm2_context, pcr_values, n_pcr_values);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Could not read pcr values: %m");
|
return log_error_errno(r, "Could not read pcr values: %m");
|
||||||
}
|
}
|
||||||
@ -4873,17 +4960,17 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
|||||||
|
|
||||||
uint16_t hash_pcr_bank = 0;
|
uint16_t hash_pcr_bank = 0;
|
||||||
uint32_t hash_pcr_mask = 0;
|
uint32_t hash_pcr_mask = 0;
|
||||||
if (arg_tpm2_n_hash_pcr_values > 0) {
|
if (n_pcr_values > 0) {
|
||||||
size_t hash_count;
|
size_t hash_count;
|
||||||
r = tpm2_pcr_values_hash_count(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &hash_count);
|
r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Could not get hash count: %m");
|
return log_error_errno(r, "Could not get hash count: %m");
|
||||||
|
|
||||||
if (hash_count > 1)
|
if (hash_count > 1)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected.");
|
||||||
|
|
||||||
hash_pcr_bank = arg_tpm2_hash_pcr_values[0].hash;
|
hash_pcr_bank = pcr_values[0].hash;
|
||||||
r = tpm2_pcr_values_to_mask(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask);
|
r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, hash_pcr_bank, &hash_pcr_mask);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Could not get hash mask: %m");
|
return log_error_errno(r, "Could not get hash mask: %m");
|
||||||
}
|
}
|
||||||
@ -4896,8 +4983,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
|||||||
|
|
||||||
/* If both PCR public key unlock and pcrlock unlock is selected, then shard the encryption key. */
|
/* If both PCR public key unlock and pcrlock unlock is selected, then shard the encryption key. */
|
||||||
r = tpm2_calculate_sealing_policy(
|
r = tpm2_calculate_sealing_policy(
|
||||||
arg_tpm2_hash_pcr_values,
|
pcr_values,
|
||||||
arg_tpm2_n_hash_pcr_values,
|
n_pcr_values,
|
||||||
iovec_is_set(&pubkey) ? &public : NULL,
|
iovec_is_set(&pubkey) ? &public : NULL,
|
||||||
/* use_pin= */ false,
|
/* use_pin= */ false,
|
||||||
arg_tpm2_pcrlock && !iovec_is_set(&pubkey) ? &pcrlock_policy : NULL,
|
arg_tpm2_pcrlock && !iovec_is_set(&pubkey) ? &pcrlock_policy : NULL,
|
||||||
@ -4907,8 +4994,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta
|
|||||||
|
|
||||||
if (arg_tpm2_pcrlock && iovec_is_set(&pubkey)) {
|
if (arg_tpm2_pcrlock && iovec_is_set(&pubkey)) {
|
||||||
r = tpm2_calculate_sealing_policy(
|
r = tpm2_calculate_sealing_policy(
|
||||||
arg_tpm2_hash_pcr_values,
|
pcr_values,
|
||||||
arg_tpm2_n_hash_pcr_values,
|
n_pcr_values,
|
||||||
/* public= */ NULL, /* Turn this one off for the 2nd shard */
|
/* public= */ NULL, /* Turn this one off for the 2nd shard */
|
||||||
/* use_pin= */ false,
|
/* use_pin= */ false,
|
||||||
&pcrlock_policy, /* But turn this one on */
|
&pcrlock_policy, /* But turn this one on */
|
||||||
@ -8829,20 +8916,9 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ARG_KEY_FILE: {
|
case ARG_KEY_FILE: {
|
||||||
_cleanup_(erase_and_freep) char *k = NULL;
|
r = parse_key_file(optarg, &arg_key);
|
||||||
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);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to read key file '%s': %m", optarg);
|
return r;
|
||||||
|
|
||||||
erase_and_free(arg_key);
|
|
||||||
arg_key = TAKE_PTR(k);
|
|
||||||
arg_key_size = n;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
#include "glyph-util.h"
|
#include "glyph-util.h"
|
||||||
#include "in-addr-util.h"
|
#include "in-addr-util.h"
|
||||||
|
#include "iovec-util.h"
|
||||||
#include "json-util.h"
|
#include "json-util.h"
|
||||||
#include "resolved-dns-answer.h"
|
#include "resolved-dns-answer.h"
|
||||||
#include "resolved-dns-browse-services.h"
|
#include "resolved-dns-browse-services.h"
|
||||||
@ -37,9 +38,8 @@ typedef struct LookupParameters {
|
|||||||
int ifindex;
|
int ifindex;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
int family;
|
int family;
|
||||||
union in_addr_union address;
|
struct iovec address;
|
||||||
size_t address_size;
|
const char *name;
|
||||||
char *name;
|
|
||||||
uint16_t class;
|
uint16_t class;
|
||||||
uint16_t type;
|
uint16_t type;
|
||||||
} LookupParameters;
|
} LookupParameters;
|
||||||
@ -62,7 +62,8 @@ typedef struct LookupParamatersBrowseServices {
|
|||||||
|
|
||||||
static void lookup_parameters_destroy(LookupParameters *p) {
|
static void lookup_parameters_destroy(LookupParameters *p) {
|
||||||
assert(p);
|
assert(p);
|
||||||
free(p->name);
|
|
||||||
|
iovec_done(&p->address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dns_query_new_for_varlink(
|
static int dns_query_new_for_varlink(
|
||||||
@ -341,10 +342,10 @@ static int parse_as_address(sd_varlink *link, LookupParameters *p) {
|
|||||||
|
|
||||||
static int vl_method_resolve_hostname(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
static int vl_method_resolve_hostname(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||||
static const sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX },
|
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX },
|
||||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(LookupParameters, name), SD_JSON_MANDATORY },
|
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, name), SD_JSON_MANDATORY },
|
||||||
{ "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParameters, family), 0 },
|
{ "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParameters, family), 0 },
|
||||||
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 },
|
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -409,42 +410,6 @@ static int vl_method_resolve_hostname(sd_varlink *link, sd_json_variant *paramet
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int json_dispatch_address(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
|
|
||||||
LookupParameters *p = ASSERT_PTR(userdata);
|
|
||||||
union in_addr_union buf = {};
|
|
||||||
sd_json_variant *i;
|
|
||||||
size_t n, k = 0;
|
|
||||||
|
|
||||||
assert(variant);
|
|
||||||
|
|
||||||
if (!sd_json_variant_is_array(variant))
|
|
||||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name));
|
|
||||||
|
|
||||||
n = sd_json_variant_elements(variant);
|
|
||||||
if (!IN_SET(n, 4, 16))
|
|
||||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name));
|
|
||||||
|
|
||||||
JSON_VARIANT_ARRAY_FOREACH(i, variant) {
|
|
||||||
int64_t b;
|
|
||||||
|
|
||||||
if (!sd_json_variant_is_integer(i))
|
|
||||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an integer.", k, strna(name));
|
|
||||||
|
|
||||||
b = sd_json_variant_integer(i);
|
|
||||||
if (b < 0 || b > 0xff)
|
|
||||||
return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL),
|
|
||||||
"Element %zu of JSON field '%s' is out of range 0%s255.",
|
|
||||||
k, strna(name), glyph(GLYPH_ELLIPSIS));
|
|
||||||
|
|
||||||
buf.bytes[k++] = (uint8_t) b;
|
|
||||||
}
|
|
||||||
|
|
||||||
p->address = buf;
|
|
||||||
p->address_size = k;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void vl_method_resolve_address_complete(DnsQuery *query) {
|
static void vl_method_resolve_address_complete(DnsQuery *query) {
|
||||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *array = NULL;
|
||||||
_cleanup_(dns_query_freep) DnsQuery *q = query;
|
_cleanup_(dns_query_freep) DnsQuery *q = query;
|
||||||
@ -507,10 +472,10 @@ finish:
|
|||||||
|
|
||||||
static int vl_method_resolve_address(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
static int vl_method_resolve_address(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||||
static const sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX },
|
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX },
|
||||||
{ "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParameters, family), SD_JSON_MANDATORY },
|
{ "family", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_int, offsetof(LookupParameters, family), SD_JSON_MANDATORY },
|
||||||
{ "address", SD_JSON_VARIANT_ARRAY, json_dispatch_address, 0, SD_JSON_MANDATORY },
|
{ "address", SD_JSON_VARIANT_ARRAY, json_dispatch_byte_array_iovec, offsetof(LookupParameters, address), SD_JSON_MANDATORY },
|
||||||
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 },
|
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -537,13 +502,15 @@ static int vl_method_resolve_address(sd_varlink *link, sd_json_variant *paramete
|
|||||||
if (!IN_SET(p.family, AF_INET, AF_INET6))
|
if (!IN_SET(p.family, AF_INET, AF_INET6))
|
||||||
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family"));
|
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("family"));
|
||||||
|
|
||||||
if (FAMILY_ADDRESS_SIZE(p.family) != p.address_size)
|
if (FAMILY_ADDRESS_SIZE(p.family) != p.address.iov_len)
|
||||||
return sd_varlink_error(link, "io.systemd.Resolve.BadAddressSize", NULL);
|
return sd_varlink_error(link, "io.systemd.Resolve.BadAddressSize", NULL);
|
||||||
|
|
||||||
if (validate_and_mangle_query_flags(m, &p.flags, /* name = */ NULL, /* ok = */ 0) < 0)
|
if (validate_and_mangle_query_flags(m, &p.flags, /* name = */ NULL, /* ok = */ 0) < 0)
|
||||||
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags"));
|
return sd_varlink_error_invalid_parameter(link, JSON_VARIANT_STRING_CONST("flags"));
|
||||||
|
|
||||||
r = dns_question_new_reverse(&question, p.family, &p.address);
|
union in_addr_union a = IN_ADDR_NULL;
|
||||||
|
memcpy(&a, p.address.iov_base, p.address.iov_len);
|
||||||
|
r = dns_question_new_reverse(&question, p.family, &a);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -555,7 +522,7 @@ static int vl_method_resolve_address(sd_varlink *link, sd_json_variant *paramete
|
|||||||
sd_varlink_set_userdata(link, q);
|
sd_varlink_set_userdata(link, q);
|
||||||
|
|
||||||
q->request_family = p.family;
|
q->request_family = p.family;
|
||||||
q->request_address = p.address;
|
q->request_address = a;
|
||||||
q->complete = vl_method_resolve_address_complete;
|
q->complete = vl_method_resolve_address_complete;
|
||||||
|
|
||||||
r = dns_query_go(q);
|
r = dns_query_go(q);
|
||||||
@ -1137,11 +1104,11 @@ finish:
|
|||||||
|
|
||||||
static int vl_method_resolve_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
static int vl_method_resolve_record(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||||
static const sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX },
|
{ "ifindex", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_ifindex, offsetof(LookupParameters, ifindex), SD_JSON_RELAX },
|
||||||
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(LookupParameters, name), SD_JSON_MANDATORY },
|
{ "name", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(LookupParameters, name), SD_JSON_MANDATORY },
|
||||||
{ "class", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(LookupParameters, class), 0 },
|
{ "class", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(LookupParameters, class), 0 },
|
||||||
{ "type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(LookupParameters, type), SD_JSON_MANDATORY },
|
{ "type", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint16, offsetof(LookupParameters, type), SD_JSON_MANDATORY },
|
||||||
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 },
|
{ "flags", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, offsetof(LookupParameters, flags), 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ int umount_recursive_full(const char *prefix, int flags, char **keep) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MS_CONVERTIBLE_FLAGS (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_NOSYMFOLLOW)
|
#define MS_CONVERTIBLE_FLAGS (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_NOSYMFOLLOW|MS_RELATIME|MS_NOATIME|MS_STRICTATIME|MS_NODIRATIME)
|
||||||
|
|
||||||
static uint64_t ms_flags_to_mount_attr(unsigned long a) {
|
static uint64_t ms_flags_to_mount_attr(unsigned long a) {
|
||||||
uint64_t f = 0;
|
uint64_t f = 0;
|
||||||
@ -130,6 +130,35 @@ static uint64_t ms_flags_to_mount_attr(unsigned long a) {
|
|||||||
if (FLAGS_SET(a, MS_NOSYMFOLLOW))
|
if (FLAGS_SET(a, MS_NOSYMFOLLOW))
|
||||||
f |= MOUNT_ATTR_NOSYMFOLLOW;
|
f |= MOUNT_ATTR_NOSYMFOLLOW;
|
||||||
|
|
||||||
|
if (FLAGS_SET(a, MS_RELATIME))
|
||||||
|
f |= MOUNT_ATTR_RELATIME;
|
||||||
|
|
||||||
|
if (FLAGS_SET(a, MS_NOATIME))
|
||||||
|
f |= MOUNT_ATTR_NOATIME;
|
||||||
|
|
||||||
|
if (FLAGS_SET(a, MS_STRICTATIME))
|
||||||
|
f |= MOUNT_ATTR_STRICTATIME;
|
||||||
|
|
||||||
|
if (FLAGS_SET(a, MS_NODIRATIME))
|
||||||
|
f |= MOUNT_ATTR_NODIRATIME;
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t ms_flags_to_mount_attr_clr(unsigned long a) {
|
||||||
|
uint64_t f = 0;
|
||||||
|
|
||||||
|
/* As per documentation, if relatime/noatime/strictatime are set, we need to clear the atime flag
|
||||||
|
* too, otherwise -EINVAL will be returned by the kernel. */
|
||||||
|
if (FLAGS_SET(a, MS_RELATIME))
|
||||||
|
f |= MOUNT_ATTR__ATIME;
|
||||||
|
|
||||||
|
if (FLAGS_SET(a, MS_NOATIME))
|
||||||
|
f |= MOUNT_ATTR__ATIME;
|
||||||
|
|
||||||
|
if (FLAGS_SET(a, MS_STRICTATIME))
|
||||||
|
f |= MOUNT_ATTR__ATIME;
|
||||||
|
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1881,6 +1910,7 @@ int make_fsmount(
|
|||||||
return log_full_errno(error_log_level, errno, "Failed to create mount fd for \"%s\" (\"%s\"): %m", what, type);
|
return log_full_errno(error_log_level, errno, "Failed to create mount fd for \"%s\" (\"%s\"): %m", what, type);
|
||||||
|
|
||||||
struct mount_attr ma = {
|
struct mount_attr ma = {
|
||||||
|
.attr_clr = ms_flags_to_mount_attr_clr(f),
|
||||||
.attr_set = ms_flags_to_mount_attr(f) | (userns_fd >= 0 ? MOUNT_ATTR_IDMAP : 0),
|
.attr_set = ms_flags_to_mount_attr(f) | (userns_fd >= 0 ? MOUNT_ATTR_IDMAP : 0),
|
||||||
.userns_fd = userns_fd,
|
.userns_fd = userns_fd,
|
||||||
};
|
};
|
||||||
|
@ -2107,7 +2107,8 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
_cleanup_free_ char *image_fn = NULL;
|
_cleanup_free_ char *image_fn = NULL;
|
||||||
if (path_extract_filename(arg_image, &image_fn) < 0)
|
r = path_extract_filename(arg_image, &image_fn);
|
||||||
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to extract filename from path '%s': %m", image_fn);
|
return log_error_errno(r, "Failed to extract filename from path '%s': %m", image_fn);
|
||||||
|
|
||||||
_cleanup_free_ char *escaped_image_fn = escape_qemu_value(image_fn);
|
_cleanup_free_ char *escaped_image_fn = escape_qemu_value(image_fn);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user