1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-27 01:55:22 +03:00

Merge pull request #33567 from poettering/boot-fixlets

sd-boot,sd-stub: a variety of smaller fixes
This commit is contained in:
Luca Boccassi 2024-07-04 13:00:56 +02:00 committed by GitHub
commit 4ac314a81f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 364 additions and 343 deletions

View File

@ -17,7 +17,7 @@
<refnamediv> <refnamediv>
<refname>systemd-measure</refname> <refname>systemd-measure</refname>
<refpurpose>Pre-calculate and sign expected TPM2 PCR values for booted unified kernel images</refpurpose> <refpurpose>Pre-calculate and sign expected TPM2 PCR 11 values for booted unified kernel images</refpurpose>
</refnamediv> </refnamediv>
<refsynopsisdiv> <refsynopsisdiv>
@ -62,7 +62,7 @@
<term><command>status</command></term> <term><command>status</command></term>
<listitem><para>This is the default command if none is specified. This queries the local system's <listitem><para>This is the default command if none is specified. This queries the local system's
TPM2 PCR 11+12+13 values and displays them. The data is written in a similar format as the TPM2 PCR 11 values and displays them. The data is written in a similar format as the
<command>calculate</command> command below, and may be used to quickly compare expectation with <command>calculate</command> command below, and may be used to quickly compare expectation with
reality.</para> reality.</para>
@ -76,9 +76,9 @@
kernel image consisting of the components specified with <option>--linux=</option>, kernel image consisting of the components specified with <option>--linux=</option>,
<option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>, <option>--osrel=</option>, <option>--cmdline=</option>, <option>--initrd=</option>,
<option>--ucode=</option>, <option>--splash=</option>, <option>--dtb=</option>, <option>--ucode=</option>, <option>--splash=</option>, <option>--dtb=</option>,
<option>--uname=</option>, <option>--sbat=</option>, <option>--pcrpkey=</option> see below. <option>--uname=</option>, <option>--sbat=</option>, <option>--pcrpkey=</option> see below. Only
Only <option>--linux=</option> is mandatory. (Alternatively, specify <option>--current</option> to use the current values of PCR <option>--linux=</option> is mandatory. (Alternatively, specify <option>--current</option> to use the
register 11 instead.)</para> current values of PCR register 11 instead.)</para>
<xi:include href="version-info.xml" xpointer="v252"/> <xi:include href="version-info.xml" xpointer="v252"/>
</listitem> </listitem>

View File

@ -292,6 +292,29 @@
<xi:include href="version-info.xml" xpointer="v254"/></listitem> <xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>OSRelease=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term>
<term><option>--os-release=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
<listitem><para>The os-release description (the <literal>.osrel</literal> section). The argument
may be a literal string, or <literal>@</literal> followed by a path name. If not specified, the
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file
will be picked up from the host system.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>Cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term>
<term><option>--cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
<listitem><para>The kernel command line (the <literal>.cmdline</literal> section). The argument may
be a literal string, or <literal>@</literal> followed by a path name. If not specified, no command
line will be embedded.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>Initrd=<replaceable>INITRD</replaceable>...</varname></term> <term><varname>Initrd=<replaceable>INITRD</replaceable>...</varname></term>
<term><option>--initrd=<replaceable>LINUX</replaceable></option></term> <term><option>--initrd=<replaceable>LINUX</replaceable></option></term>
@ -314,24 +337,12 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>Cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term> <term><varname>Splash=<replaceable>PATH</replaceable></varname></term>
<term><option>--cmdline=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term> <term><option>--splash=<replaceable>PATH</replaceable></option></term>
<listitem><para>The kernel command line (the <literal>.cmdline</literal> section). The argument may <listitem><para>A picture to display during boot (the <literal>.splash</literal> section). The
be a literal string, or <literal>@</literal> followed by a path name. If not specified, no command argument is a path to a BMP file. If not specified, the section will not be present.
line will be embedded.</para> </para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>OSRelease=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term>
<term><option>--os-release=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
<listitem><para>The os-release description (the <literal>.osrel</literal> section). The argument
may be a literal string, or <literal>@</literal> followed by a path name. If not specified, the
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry> file
will be picked up from the host system.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem> <xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry> </varlistentry>
@ -348,16 +359,35 @@
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
<term><varname>Splash=<replaceable>PATH</replaceable></varname></term> <term><varname>Uname=<replaceable>VERSION</replaceable></varname></term>
<term><option>--splash=<replaceable>PATH</replaceable></option></term> <term><option>--uname=<replaceable>VERSION</replaceable></option></term>
<listitem><para>A picture to display during boot (the <literal>.splash</literal> section). The <listitem><para>Specify the kernel version (as in <command>uname -r</command>, the
argument is a path to a BMP file. If not specified, the section will not be present. <literal>.uname</literal> section). If not specified, an attempt will be made to extract the
</para> version string from the kernel image. It is recommended to pass this explicitly if known, because
the extraction is based on heuristics and not very reliable. If not specified and extraction fails,
the section will not be present.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem> <xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>SBAT=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term>
<term><option>--sbat=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
<listitem><para>SBAT metadata associated with the UKI or addon. SBAT policies are useful to revoke
whole groups of UKIs or addons with a single, static policy update that does not take space in
DBX/MOKX. If not specified manually, a default metadata entry consisting of
<literal>uki,1,UKI,uki,1,https://uapi-group.org/specifications/specs/unified_kernel_image/</literal>
for UKIs and
<literal>uki-addon,1,UKI Addon,addon,1,https://www.freedesktop.org/software/systemd/man/latest/systemd-stub.html</literal>
for addons will be used, to ensure it is always possible to revoke them. For more information on
SBAT see <ulink url="https://github.com/rhboot/shim/blob/main/SBAT.md">Shim documentation</ulink>.
</para>
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>PCRPKey=<replaceable>PATH</replaceable></varname></term> <term><varname>PCRPKey=<replaceable>PATH</replaceable></varname></term>
<term><option>--pcrpkey=<replaceable>PATH</replaceable></option></term> <term><option>--pcrpkey=<replaceable>PATH</replaceable></option></term>
@ -370,19 +400,6 @@
<xi:include href="version-info.xml" xpointer="v253"/></listitem> <xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>Uname=<replaceable>VERSION</replaceable></varname></term>
<term><option>--uname=<replaceable>VERSION</replaceable></option></term>
<listitem><para>Specify the kernel version (as in <command>uname -r</command>, the
<literal>.uname</literal> section). If not specified, an attempt will be made to extract the
version string from the kernel image. It is recommended to pass this explicitly if known, because
the extraction is based on heuristics and not very reliable. If not specified and extraction fails,
the section will not be present.</para>
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><varname>PCRBanks=<replaceable>PATH</replaceable></varname></term> <term><varname>PCRBanks=<replaceable>PATH</replaceable></varname></term>
<term><option>--pcr-banks=<replaceable>PATH</replaceable></option></term> <term><option>--pcr-banks=<replaceable>PATH</replaceable></option></term>
@ -488,23 +505,6 @@
<xi:include href="version-info.xml" xpointer="v253"/></listitem> <xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><varname>SBAT=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></varname></term>
<term><option>--sbat=<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
<listitem><para>SBAT metadata associated with the UKI or addon. SBAT policies are useful to revoke
whole groups of UKIs or addons with a single, static policy update that does not take space in
DBX/MOKX. If not specified manually, a default metadata entry consisting of
<literal>uki,1,UKI,uki,1,https://uapi-group.org/specifications/specs/unified_kernel_image/</literal>
for UKIs and
<literal>uki-addon,1,UKI Addon,addon,1,https://www.freedesktop.org/software/systemd/man/latest/systemd-stub.html</literal>
for addons will be used, to ensure it is always possible to revoke them. For more information on
SBAT see <ulink url="https://github.com/rhboot/shim/blob/main/SBAT.md">Shim documentation</ulink>.
</para>
<xi:include href="version-info.xml" xpointer="v254"/></listitem>
</varlistentry>
</variablelist> </variablelist>
</refsect2> </refsect2>

View File

@ -620,16 +620,21 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
} }
static EFI_STATUS set_reboot_into_firmware(void) { static EFI_STATUS set_reboot_into_firmware(void) {
uint64_t osind = 0;
EFI_STATUS err; EFI_STATUS err;
uint64_t osind = 0;
(void) efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"OsIndications", &osind); (void) efivar_get_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"OsIndications", &osind);
if (FLAGS_SET(osind, EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
return EFI_SUCCESS;
osind |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI; osind |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
err = efivar_set_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"OsIndications", osind, EFI_VARIABLE_NON_VOLATILE); err = efivar_set_uint64_le(MAKE_GUID_PTR(EFI_GLOBAL_VARIABLE), u"OsIndications", osind, EFI_VARIABLE_NON_VOLATILE);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
log_error_status(err, "Error setting OsIndications: %m"); return log_error_status(err, "Error setting OsIndications, ignoring: %m");
return err;
return EFI_SUCCESS;
} }
_noreturn_ static EFI_STATUS poweroff_system(void) { _noreturn_ static EFI_STATUS poweroff_system(void) {
@ -881,6 +886,7 @@ static bool menu_run(
switch (key) { switch (key) {
case KEYPRESS(0, SCAN_UP, 0): case KEYPRESS(0, SCAN_UP, 0):
case KEYPRESS(0, SCAN_VOLUME_UP, 0): /* Handle phones/tablets that only have a volume up/down rocker + power key (and otherwise just touchscreen input) */
case KEYPRESS(0, 0, 'k'): case KEYPRESS(0, 0, 'k'):
case KEYPRESS(0, 0, 'K'): case KEYPRESS(0, 0, 'K'):
if (idx_highlight > 0) if (idx_highlight > 0)
@ -888,6 +894,7 @@ static bool menu_run(
break; break;
case KEYPRESS(0, SCAN_DOWN, 0): case KEYPRESS(0, SCAN_DOWN, 0):
case KEYPRESS(0, SCAN_VOLUME_DOWN, 0):
case KEYPRESS(0, 0, 'j'): case KEYPRESS(0, 0, 'j'):
case KEYPRESS(0, 0, 'J'): case KEYPRESS(0, 0, 'J'):
if (idx_highlight < config->n_entries-1) if (idx_highlight < config->n_entries-1)
@ -925,9 +932,10 @@ static bool menu_run(
case KEYPRESS(0, 0, '\n'): case KEYPRESS(0, 0, '\n'):
case KEYPRESS(0, 0, '\r'): case KEYPRESS(0, 0, '\r'):
case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */ case KEYPRESS(0, SCAN_F3, 0): /* EZpad Mini 4s firmware sends malformed events */
case KEYPRESS(0, SCAN_F3, '\r'): /* Teclast X98+ II firmware sends malformed events */ case KEYPRESS(0, SCAN_F3, '\r'): /* Teclast X98+ II firmware sends malformed events */
case KEYPRESS(0, SCAN_RIGHT, 0): case KEYPRESS(0, SCAN_RIGHT, 0):
case KEYPRESS(0, SCAN_SUSPEND, 0): /* Handle phones/tablets with only a power key + volume up/down rocker (and otherwise just touchscreen input) */
action = ACTION_RUN; action = ACTION_RUN;
break; break;
@ -1658,15 +1666,21 @@ static void config_load_type1_entries(
continue; continue;
if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY)) if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY))
continue; continue;
if (!endswith_no_case(f->FileName, u".conf")) if (!endswith_no_case(f->FileName, u".conf"))
continue; continue;
if (startswith(f->FileName, u"auto-")) if (startswith_no_case(f->FileName, u"auto-"))
continue; continue;
err = file_read(entries_dir, f->FileName, 0, 0, &content, NULL); err = file_read(entries_dir,
if (err == EFI_SUCCESS) f->FileName,
boot_entry_add_type1(config, device, root_dir, u"\\loader\\entries", f->FileName, content, loaded_image_path); /* offset= */ 0,
/* size= */ 0,
&content,
/* ret_size= */ NULL);
if (err != EFI_SUCCESS)
continue;
boot_entry_add_type1(config, device, root_dir, u"\\loader\\entries", f->FileName, content, loaded_image_path);
} }
} }
@ -2081,6 +2095,139 @@ static void config_add_entry_windows(Config *config, EFI_HANDLE *device, EFI_FIL
#endif #endif
} }
static void boot_entry_add_type2(
Config *config,
EFI_HANDLE *device,
EFI_FILE *dir,
const uint16_t *filename) {
enum {
SECTION_CMDLINE,
SECTION_OSREL,
_SECTION_MAX,
};
static const char * const section_names[_SECTION_MAX + 1] = {
[SECTION_CMDLINE] = ".cmdline",
[SECTION_OSREL] = ".osrel",
NULL,
};
EFI_STATUS err;
assert(config);
assert(device);
assert(dir);
assert(filename);
/* Look for .osrel and .cmdline sections in the .efi binary */
PeSectionVector sections[_SECTION_MAX] = {};
err = pe_file_locate_sections(dir, filename, section_names, sections);
if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + SECTION_OSREL))
return;
_cleanup_free_ char *content = NULL;
err = file_read(dir,
filename,
sections[SECTION_OSREL].file_offset,
sections[SECTION_OSREL].size,
&content,
/* ret_size= */ NULL);
if (err != EFI_SUCCESS)
return;
_cleanup_free_ char16_t *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
char *line, *key, *value;
size_t pos = 0;
/* read properties from the embedded os-release file */
while ((line = line_get_key_value(content, "=", &pos, &key, &value)))
if (streq8(key, "PRETTY_NAME")) {
free(os_pretty_name);
os_pretty_name = xstr8_to_16(value);
} else if (streq8(key, "IMAGE_ID")) {
free(os_image_id);
os_image_id = xstr8_to_16(value);
} else if (streq8(key, "NAME")) {
free(os_name);
os_name = xstr8_to_16(value);
} else if (streq8(key, "ID")) {
free(os_id);
os_id = xstr8_to_16(value);
} else if (streq8(key, "IMAGE_VERSION")) {
free(os_image_version);
os_image_version = xstr8_to_16(value);
} else if (streq8(key, "VERSION")) {
free(os_version);
os_version = xstr8_to_16(value);
} else if (streq8(key, "VERSION_ID")) {
free(os_version_id);
os_version_id = xstr8_to_16(value);
} else if (streq8(key, "BUILD_ID")) {
free(os_build_id);
os_build_id = xstr8_to_16(value);
}
const char16_t *good_name, *good_version, *good_sort_key;
if (!bootspec_pick_name_version_sort_key(
os_pretty_name,
os_image_id,
os_name,
os_id,
os_image_version,
os_version,
os_version_id,
os_build_id,
&good_name,
&good_version,
&good_sort_key))
return;
BootEntry *entry = xnew(BootEntry, 1);
*entry = (BootEntry) {
.id = xstrdup16(filename),
.type = LOADER_UNIFIED_LINUX,
.title = xstrdup16(good_name),
.version = xstrdup16(good_version),
.device = device,
.loader = xasprintf("\\EFI\\Linux\\%ls", filename),
.sort_key = xstrdup16(good_sort_key),
.key = 'l',
.tries_done = -1,
.tries_left = -1,
};
strtolower16(entry->id);
config_add_entry(config, entry);
boot_entry_parse_tries(entry, u"\\EFI\\Linux", filename, u".efi");
if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_CMDLINE))
return;
content = mfree(content);
/* read the embedded cmdline file */
size_t cmdline_len;
err = file_read(dir,
filename,
sections[SECTION_CMDLINE].file_offset,
sections[SECTION_CMDLINE].size,
&content,
&cmdline_len);
if (err == EFI_SUCCESS) {
entry->options = xstrn8_to_16(content, cmdline_len);
mangle_stub_cmdline(entry->options);
entry->options_implied = true;
}
}
static void config_load_type2_entries( static void config_load_type2_entries(
Config *config, Config *config,
EFI_HANDLE *device, EFI_HANDLE *device,
@ -2102,26 +2249,6 @@ static void config_load_type2_entries(
return; return;
for (;;) { for (;;) {
enum {
SECTION_CMDLINE,
SECTION_OSREL,
_SECTION_MAX,
};
static const char * const section_names[_SECTION_MAX + 1] = {
[SECTION_CMDLINE] = ".cmdline",
[SECTION_OSREL] = ".osrel",
NULL,
};
_cleanup_free_ char16_t *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
const char16_t *good_name, *good_version, *good_sort_key;
_cleanup_free_ char *content = NULL;
PeSectionVector sections[_SECTION_MAX] = {};
char *line, *key, *value;
size_t pos = 0;
err = readdir(linux_dir, &f, &f_size); err = readdir(linux_dir, &f, &f_size);
if (err != EFI_SUCCESS || !f) if (err != EFI_SUCCESS || !f)
break; break;
@ -2132,108 +2259,10 @@ static void config_load_type2_entries(
continue; continue;
if (!endswith_no_case(f->FileName, u".efi")) if (!endswith_no_case(f->FileName, u".efi"))
continue; continue;
if (startswith(f->FileName, u"auto-")) if (startswith_no_case(f->FileName, u"auto-"))
continue; continue;
/* look for .osrel and .cmdline sections in the .efi binary */ boot_entry_add_type2(config, device, linux_dir, f->FileName);
err = pe_file_locate_sections(linux_dir, f->FileName, section_names, sections);
if (err != EFI_SUCCESS || !PE_SECTION_VECTOR_IS_SET(sections + SECTION_OSREL))
continue;
err = file_read(linux_dir,
f->FileName,
sections[SECTION_OSREL].file_offset,
sections[SECTION_OSREL].size,
&content,
NULL);
if (err != EFI_SUCCESS)
continue;
/* read properties from the embedded os-release file */
while ((line = line_get_key_value(content, "=", &pos, &key, &value)))
if (streq8(key, "PRETTY_NAME")) {
free(os_pretty_name);
os_pretty_name = xstr8_to_16(value);
} else if (streq8(key, "IMAGE_ID")) {
free(os_image_id);
os_image_id = xstr8_to_16(value);
} else if (streq8(key, "NAME")) {
free(os_name);
os_name = xstr8_to_16(value);
} else if (streq8(key, "ID")) {
free(os_id);
os_id = xstr8_to_16(value);
} else if (streq8(key, "IMAGE_VERSION")) {
free(os_image_version);
os_image_version = xstr8_to_16(value);
} else if (streq8(key, "VERSION")) {
free(os_version);
os_version = xstr8_to_16(value);
} else if (streq8(key, "VERSION_ID")) {
free(os_version_id);
os_version_id = xstr8_to_16(value);
} else if (streq8(key, "BUILD_ID")) {
free(os_build_id);
os_build_id = xstr8_to_16(value);
}
if (!bootspec_pick_name_version_sort_key(
os_pretty_name,
os_image_id,
os_name,
os_id,
os_image_version,
os_version,
os_version_id,
os_build_id,
&good_name,
&good_version,
&good_sort_key))
continue;
BootEntry *entry = xnew(BootEntry, 1);
*entry = (BootEntry) {
.id = xstrdup16(f->FileName),
.type = LOADER_UNIFIED_LINUX,
.title = xstrdup16(good_name),
.version = xstrdup16(good_version),
.device = device,
.loader = xasprintf("\\EFI\\Linux\\%ls", f->FileName),
.sort_key = xstrdup16(good_sort_key),
.key = 'l',
.tries_done = -1,
.tries_left = -1,
};
strtolower16(entry->id);
config_add_entry(config, entry);
boot_entry_parse_tries(entry, u"\\EFI\\Linux", f->FileName, u".efi");
if (!PE_SECTION_VECTOR_IS_SET(sections + SECTION_CMDLINE))
continue;
content = mfree(content);
/* read the embedded cmdline file */
size_t cmdline_len;
err = file_read(linux_dir,
f->FileName,
sections[SECTION_CMDLINE].file_offset,
sections[SECTION_CMDLINE].size,
&content,
&cmdline_len);
if (err == EFI_SUCCESS) {
entry->options = xstrn8_to_16(content, cmdline_len);
mangle_stub_cmdline(entry->options);
entry->options_implied = true;
}
} }
} }

View File

@ -178,22 +178,22 @@ static bool pe_section_name_equal(const char *a, const char *b) {
static void pe_locate_sections( static void pe_locate_sections(
const PeSectionHeader section_table[], const PeSectionHeader section_table[],
size_t n_section_table, size_t n_section_table,
const char * const sections[], const char *const section_names[],
size_t validate_base, size_t validate_base,
PeSectionVector *ret_sections) { PeSectionVector sections[]) {
assert(section_table || n_section_table == 0); assert(section_table || n_section_table == 0);
assert(section_names);
assert(sections); assert(sections);
assert(ret_sections);
/* Searches for the sections listed in 'sections[]' within the section table. Validates the resulted /* Searches for the sections listed in 'sections[]' within the section table. Validates the resulted
* data. If 'validate_base' is non-zero also takes base offset when loaded into memory into account for * data. If 'validate_base' is non-zero also takes base offset when loaded into memory into account for
* qchecking for overflows. */ * qchecking for overflows. */
for (size_t i = 0; sections[i]; i++) for (size_t i = 0; section_names[i]; i++)
FOREACH_ARRAY(j, section_table, n_section_table) { FOREACH_ARRAY(j, section_table, n_section_table) {
if (!pe_section_name_equal((const char*) j->Name, sections[i])) if (!pe_section_name_equal((const char*) j->Name, section_names[i]))
continue; continue;
/* Overflow check: ignore sections that are impossibly large, relative to the file /* Overflow check: ignore sections that are impossibly large, relative to the file
@ -220,7 +220,7 @@ static void pe_locate_sections(
} }
/* At this time, the sizes and offsets have been validated. Store them away */ /* At this time, the sizes and offsets have been validated. Store them away */
ret_sections[i] = (PeSectionVector) { sections[i] = (PeSectionVector) {
.size = j->VirtualSize, .size = j->VirtualSize,
.file_offset = j->PointerToRawData, .file_offset = j->PointerToRawData,
.memory_offset = j->VirtualAddress, .memory_offset = j->VirtualAddress,
@ -232,17 +232,19 @@ static void pe_locate_sections(
} }
static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const PeFileHeader *pe) { static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const PeFileHeader *pe) {
static const char *sections[] = { ".compat", NULL };
PeSectionVector vector = {};
/* The kernel may provide alternative PE entry points for different PE architectures. This allows /* The kernel may provide alternative PE entry points for different PE architectures. This allows
* booting a 64-bit kernel on 32-bit EFI that is otherwise running on a 64-bit CPU. The locations of any * booting a 64-bit kernel on 32-bit EFI that is otherwise running on a 64-bit CPU. The locations of any
* such compat entry points are located in a special PE section. */ * such compat entry points are located in a special PE section. */
assert(dos);
assert(pe);
static const char *const section_names[] = { ".compat", NULL };
PeSectionVector vector = {};
pe_locate_sections( pe_locate_sections(
(const PeSectionHeader *) ((const uint8_t *) dos + section_table_offset(dos, pe)), (const PeSectionHeader *) ((const uint8_t *) dos + section_table_offset(dos, pe)),
pe->FileHeader.NumberOfSections, pe->FileHeader.NumberOfSections,
sections, section_names,
PTR_TO_SIZE(dos), PTR_TO_SIZE(dos),
&vector); &vector);
@ -259,7 +261,7 @@ static uint32_t get_compatibility_entry_address(const DosFileHeader *dos, const
size_t addr = vector.memory_offset, size = vector.size; size_t addr = vector.memory_offset, size = vector.size;
while (size >= sizeof(LinuxPeCompat1) && addr % alignof(LinuxPeCompat1) == 0) { while (size >= sizeof(LinuxPeCompat1) && addr % alignof(LinuxPeCompat1) == 0) {
LinuxPeCompat1 *compat = (LinuxPeCompat1 *) ((uint8_t *) dos + addr); const LinuxPeCompat1 *compat = (const LinuxPeCompat1 *) ((const uint8_t *) dos + addr);
if (compat->type == 0 || compat->size == 0 || compat->size > size) if (compat->type == 0 || compat->size == 0 || compat->size > size)
break; break;
@ -308,16 +310,16 @@ EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address) {
EFI_STATUS pe_memory_locate_sections( EFI_STATUS pe_memory_locate_sections(
const void *base, const void *base,
const char* const sections[], const char *const section_names[],
PeSectionVector *ret_sections) { PeSectionVector sections[]) {
const DosFileHeader *dos; const DosFileHeader *dos;
const PeFileHeader *pe; const PeFileHeader *pe;
size_t offset; size_t offset;
assert(base); assert(base);
assert(section_names);
assert(sections); assert(sections);
assert(ret_sections);
dos = (const DosFileHeader *) base; dos = (const DosFileHeader *) base;
if (!verify_dos(dos)) if (!verify_dos(dos))
@ -331,9 +333,9 @@ EFI_STATUS pe_memory_locate_sections(
pe_locate_sections( pe_locate_sections(
(const PeSectionHeader *) ((const uint8_t *) base + offset), (const PeSectionHeader *) ((const uint8_t *) base + offset),
pe->FileHeader.NumberOfSections, pe->FileHeader.NumberOfSections,
sections, section_names,
PTR_TO_SIZE(base), PTR_TO_SIZE(base),
ret_sections); sections);
return EFI_SUCCESS; return EFI_SUCCESS;
} }
@ -341,8 +343,8 @@ EFI_STATUS pe_memory_locate_sections(
EFI_STATUS pe_file_locate_sections( EFI_STATUS pe_file_locate_sections(
EFI_FILE *dir, EFI_FILE *dir,
const char16_t *path, const char16_t *path,
const char * const sections[], const char *const section_names[],
PeSectionVector *ret_sections) { PeSectionVector sections[]) {
_cleanup_free_ PeSectionHeader *section_table = NULL; _cleanup_free_ PeSectionHeader *section_table = NULL;
_cleanup_(file_closep) EFI_FILE *handle = NULL; _cleanup_(file_closep) EFI_FILE *handle = NULL;
DosFileHeader dos; DosFileHeader dos;
@ -352,8 +354,8 @@ EFI_STATUS pe_file_locate_sections(
assert(dir); assert(dir);
assert(path); assert(path);
assert(section_names);
assert(sections); assert(sections);
assert(ret_sections);
err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL); err = dir->Open(dir, &handle, (char16_t *) path, EFI_FILE_MODE_READ, 0ULL);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
@ -402,9 +404,9 @@ EFI_STATUS pe_file_locate_sections(
pe_locate_sections( pe_locate_sections(
section_table, section_table,
pe.FileHeader.NumberOfSections, pe.FileHeader.NumberOfSections,
sections, section_names,
/* validate_base= */ 0, /* don't validate base */ /* validate_base= */ 0, /* don't validate base */
ret_sections); sections);
return EFI_SUCCESS; return EFI_SUCCESS;
} }

View File

@ -17,13 +17,13 @@ static inline bool PE_SECTION_VECTOR_IS_SET(const PeSectionVector *v) {
EFI_STATUS pe_memory_locate_sections( EFI_STATUS pe_memory_locate_sections(
const void *base, const void *base,
const char * const sections[], const char *const section_names[],
PeSectionVector *ret_sections); PeSectionVector sections[]);
EFI_STATUS pe_file_locate_sections( EFI_STATUS pe_file_locate_sections(
EFI_FILE *dir, EFI_FILE *dir,
const char16_t *path, const char16_t *path,
const char * const sections[], const char *const section_names[],
PeSectionVector *ret_sections); PeSectionVector sections[]);
EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address); EFI_STATUS pe_kernel_info(const void *base, uint32_t *ret_compat_address);

View File

@ -9,6 +9,9 @@
#include "version.h" #include "version.h"
#include "efivars.h" #include "efivars.h"
/* Never try to read more than 16G into memory (and on 32bit 1G) */
#define FILE_READ_MAX MIN(SIZE_MAX/4, UINT64_C(16)*1024U*1024U*1024U)
void convert_efi_path(char16_t *path) { void convert_efi_path(char16_t *path) {
assert(path); assert(path);
@ -39,19 +42,17 @@ static bool shall_be_whitespace(char16_t c) {
} }
char16_t* mangle_stub_cmdline(char16_t *cmdline) { char16_t* mangle_stub_cmdline(char16_t *cmdline) {
char16_t *p, *q, *e;
if (!cmdline) if (!cmdline)
return cmdline; return cmdline;
p = q = cmdline;
/* Skip initial whitespace */ /* Skip initial whitespace */
while (shall_be_whitespace(*p)) const char16_t *p = cmdline;
while (*p != 0 && shall_be_whitespace(*p))
p++; p++;
/* Turn inner control characters into proper spaces */ /* Turn inner control characters into proper spaces */
for (e = p; *p != 0; p++) { char16_t *e = cmdline;
for (char16_t *q = cmdline; *p != 0; p++) {
if (shall_be_whitespace(*p)) { if (shall_be_whitespace(*p)) {
*(q++) = ' '; *(q++) = ' ';
continue; continue;
@ -107,7 +108,7 @@ EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf) {
EFI_STATUS file_read( EFI_STATUS file_read(
EFI_FILE *dir, EFI_FILE *dir,
const char16_t *name, const char16_t *name,
uint64_t off, uint64_t offset,
size_t size, size_t size,
char **ret, char **ret,
size_t *ret_size) { size_t *ret_size) {
@ -131,14 +132,17 @@ EFI_STATUS file_read(
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return err;
if (info->FileSize > SIZE_MAX) if (info->FileSize > SIZE_MAX) /* overflow check */
return EFI_BAD_BUFFER_SIZE; return EFI_BAD_BUFFER_SIZE;
size = info->FileSize; size = info->FileSize;
} }
if (off > 0) { if (size > FILE_READ_MAX) /* make sure we don't read unbounded data into RAM */
err = handle->SetPosition(handle, off); return EFI_BAD_BUFFER_SIZE;
if (offset > 0) {
err = handle->SetPosition(handle, offset);
if (err != EFI_SUCCESS) if (err != EFI_SUCCESS)
return err; return err;
} }

View File

@ -86,7 +86,7 @@ char16_t *xstr8_to_path(const char *stra);
char16_t *mangle_stub_cmdline(char16_t *cmdline); char16_t *mangle_stub_cmdline(char16_t *cmdline);
EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf); EFI_STATUS chunked_read(EFI_FILE *file, size_t *size, void *buf);
EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, uint64_t off, size_t size, char **content, size_t *content_size); EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, uint64_t offset, size_t size, char **content, size_t *content_size);
static inline void file_closep(EFI_FILE **handle) { static inline void file_closep(EFI_FILE **handle) {
if (!*handle) if (!*handle)

View File

@ -1017,14 +1017,6 @@ static int validate_stub(void) {
if (r < 0) if (r < 0)
return r; return r;
r = compare_reported_pcr_nr(TPM2_PCR_KERNEL_CONFIG, EFI_LOADER_VARIABLE(StubPcrKernelParameters), "kernel parameters");
if (r < 0)
return r;
r = compare_reported_pcr_nr(TPM2_PCR_SYSEXTS, EFI_LOADER_VARIABLE(StubPcrInitRDSysExts), "initrd system extension images");
if (r < 0)
return r;
STRV_FOREACH(bank, arg_banks) { STRV_FOREACH(bank, arg_banks) {
_cleanup_free_ char *b = NULL, *p = NULL; _cleanup_free_ char *b = NULL, *p = NULL;
@ -1049,12 +1041,6 @@ static int validate_stub(void) {
} }
static int verb_status(int argc, char *argv[], void *userdata) { static int verb_status(int argc, char *argv[], void *userdata) {
static const uint32_t relevant_pcrs[] = {
TPM2_PCR_KERNEL_BOOT,
TPM2_PCR_KERNEL_CONFIG,
TPM2_PCR_SYSEXTS,
};
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
int r; int r;
@ -1062,72 +1048,69 @@ static int verb_status(int argc, char *argv[], void *userdata) {
if (r < 0) if (r < 0)
return r; return r;
for (size_t i = 0; i < ELEMENTSOF(relevant_pcrs); i++) { STRV_FOREACH(bank, arg_banks) {
_cleanup_free_ char *b = NULL, *p = NULL, *s = NULL;
_cleanup_free_ void *h = NULL;
size_t l;
STRV_FOREACH(bank, arg_banks) { b = strdup(*bank);
_cleanup_free_ char *b = NULL, *p = NULL, *s = NULL; if (!b)
_cleanup_free_ void *h = NULL; return log_oom();
size_t l;
b = strdup(*bank); if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, ascii_strlower(b), (uint32_t) TPM2_PCR_KERNEL_BOOT) < 0)
if (!b) return log_oom();
r = read_virtual_file(p, 4096, &s, NULL);
if (r == -ENOENT)
continue;
if (r < 0)
return log_error_errno(r, "Failed to read '%s': %m", p);
r = unhexmem(strstrip(s), &h, &l);
if (r < 0)
return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
_cleanup_free_ char *f = NULL;
f = hexmem(h, l);
if (!h)
return log_oom(); return log_oom();
if (asprintf(&p, "/sys/class/tpm/tpm0/pcr-%s/%" PRIu32, ascii_strlower(b), relevant_pcrs[i]) < 0) if (bank == arg_banks) {
return log_oom(); /* before the first line for each PCR, write a short descriptive text to
* stderr, and leave the primary content on stdout */
r = read_virtual_file(p, 4096, &s, NULL); fflush(stdout);
if (r == -ENOENT) fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
continue; ansi_grey(),
if (r < 0) (uint32_t) TPM2_PCR_KERNEL_BOOT,
return log_error_errno(r, "Failed to read '%s': %m", p); tpm2_pcr_index_to_string(TPM2_PCR_KERNEL_BOOT),
memeqzero(h, l) ? " (NOT SET!)" : "",
r = unhexmem(strstrip(s), &h, &l); ansi_normal());
if (r < 0) fflush(stderr);
return log_error_errno(r, "Failed to decode PCR value '%s': %m", s);
if (arg_json_format_flags & SD_JSON_FORMAT_OFF) {
_cleanup_free_ char *f = NULL;
f = hexmem(h, l);
if (!h)
return log_oom();
if (bank == arg_banks) {
/* before the first line for each PCR, write a short descriptive text to
* stderr, and leave the primary content on stdout */
fflush(stdout);
fprintf(stderr, "%s# PCR[%" PRIu32 "] %s%s%s\n",
ansi_grey(),
relevant_pcrs[i],
tpm2_pcr_index_to_string(relevant_pcrs[i]),
memeqzero(h, l) ? " (NOT SET!)" : "",
ansi_normal());
fflush(stderr);
}
printf("%" PRIu32 ":%s=%s\n", relevant_pcrs[i], b, f);
} else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *bv = NULL, *a = NULL;
r = sd_json_buildo(
&bv,
SD_JSON_BUILD_PAIR("pcr", SD_JSON_BUILD_INTEGER(relevant_pcrs[i])),
SD_JSON_BUILD_PAIR("hash", SD_JSON_BUILD_HEX(h, l)));
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
a = sd_json_variant_ref(sd_json_variant_by_key(v, b));
r = sd_json_variant_append_array(&a, bv);
if (r < 0)
return log_error_errno(r, "Failed to append PCR entry to JSON array: %m");
r = sd_json_variant_set_field(&v, b, a);
if (r < 0)
return log_error_errno(r, "Failed to add bank info to object: %m");
} }
printf("%" PRIu32 ":%s=%s\n", (uint32_t) TPM2_PCR_KERNEL_BOOT, b, f);
} else {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *bv = NULL, *a = NULL;
r = sd_json_buildo(
&bv,
SD_JSON_BUILD_PAIR("pcr", SD_JSON_BUILD_INTEGER(TPM2_PCR_KERNEL_BOOT)),
SD_JSON_BUILD_PAIR("hash", SD_JSON_BUILD_HEX(h, l)));
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
a = sd_json_variant_ref(sd_json_variant_by_key(v, b));
r = sd_json_variant_append_array(&a, bv);
if (r < 0)
return log_error_errno(r, "Failed to append PCR entry to JSON array: %m");
r = sd_json_variant_set_field(&v, b, a);
if (r < 0)
return log_error_errno(r, "Failed to add bank info to object: %m");
} }
} }

View File

@ -1264,6 +1264,13 @@ CONFIG_ITEMS = [
action = 'store_true', action = 'store_true',
), ),
ConfigItem(
('--config', '-c'),
metavar = 'PATH',
type = pathlib.Path,
help = 'configuration file',
),
ConfigItem( ConfigItem(
'--linux', '--linux',
type = pathlib.Path, type = pathlib.Path,
@ -1271,6 +1278,20 @@ CONFIG_ITEMS = [
config_key = 'UKI/Linux', config_key = 'UKI/Linux',
), ),
ConfigItem(
'--os-release',
metavar = 'TEXT|@PATH',
help = 'path to os-release file [.osrel section]',
config_key = 'UKI/OSRelease',
),
ConfigItem(
'--cmdline',
metavar = 'TEXT|@PATH',
help = 'kernel command line [.cmdline section]',
config_key = 'UKI/Cmdline',
),
ConfigItem( ConfigItem(
'--initrd', '--initrd',
metavar = 'INITRD', metavar = 'INITRD',
@ -1290,24 +1311,11 @@ CONFIG_ITEMS = [
), ),
ConfigItem( ConfigItem(
('--config', '-c'), '--splash',
metavar = 'PATH', metavar = 'BMP',
type = pathlib.Path, type = pathlib.Path,
help = 'configuration file', help = 'splash image bitmap file [.splash section]',
), config_key = 'UKI/Splash',
ConfigItem(
'--cmdline',
metavar = 'TEXT|@PATH',
help = 'kernel command line [.cmdline section]',
config_key = 'UKI/Cmdline',
),
ConfigItem(
'--os-release',
metavar = 'TEXT|@PATH',
help = 'path to os-release file [.osrel section]',
config_key = 'UKI/OSRelease',
), ),
ConfigItem( ConfigItem(
@ -1317,13 +1325,23 @@ CONFIG_ITEMS = [
help = 'Device Tree file [.dtb section]', help = 'Device Tree file [.dtb section]',
config_key = 'UKI/DeviceTree', config_key = 'UKI/DeviceTree',
), ),
ConfigItem( ConfigItem(
'--splash', '--uname',
metavar = 'BMP', metavar='VERSION',
type = pathlib.Path, help='"uname -r" information [.uname section]',
help = 'splash image bitmap file [.splash section]', config_key = 'UKI/Uname',
config_key = 'UKI/Splash',
), ),
ConfigItem(
'--sbat',
metavar = 'TEXT|@PATH',
help = 'SBAT policy [.sbat section]',
default = [],
action = 'append',
config_key = 'UKI/SBAT',
),
ConfigItem( ConfigItem(
'--pcrpkey', '--pcrpkey',
metavar = 'KEY', metavar = 'KEY',
@ -1331,11 +1349,14 @@ CONFIG_ITEMS = [
help = 'embedded public key to seal secrets to [.pcrpkey section]', help = 'embedded public key to seal secrets to [.pcrpkey section]',
config_key = 'UKI/PCRPKey', config_key = 'UKI/PCRPKey',
), ),
ConfigItem( ConfigItem(
'--uname', '--section',
metavar='VERSION', dest = 'sections',
help='"uname -r" information [.uname section]', metavar = 'NAME:TEXT|@PATH',
config_key = 'UKI/Uname', action = 'append',
default = [],
help = 'section as name and contents [NAME section] or section to print',
), ),
ConfigItem( ConfigItem(
@ -1353,24 +1374,6 @@ CONFIG_ITEMS = [
config_key = 'UKI/Stub', config_key = 'UKI/Stub',
), ),
ConfigItem(
'--sbat',
metavar = 'TEXT|@PATH',
help = 'SBAT policy [.sbat section]',
default = [],
action = 'append',
config_key = 'UKI/SBAT',
),
ConfigItem(
'--section',
dest = 'sections',
metavar = 'NAME:TEXT|@PATH',
action = 'append',
default = [],
help = 'section as name and contents [NAME section] or section to print',
),
ConfigItem( ConfigItem(
'--pcr-banks', '--pcr-banks',
metavar = 'BANK…', metavar = 'BANK…',
@ -1636,7 +1639,7 @@ def finalize_options(opts):
elif len(opts.positional) == 1 and opts.positional[0] in VERBS: elif len(opts.positional) == 1 and opts.positional[0] in VERBS:
opts.verb = opts.positional[0] opts.verb = opts.positional[0]
elif opts.linux or opts.initrd: elif opts.linux or opts.initrd:
raise ValueError('--linux/--initrd options cannot be used with positional arguments') raise ValueError('--linux=/--initrd= options cannot be used with positional arguments')
else: else:
print("Assuming obsolete command line syntax with no verb. Please use 'build'.") print("Assuming obsolete command line syntax with no verb. Please use 'build'.")
if opts.positional: if opts.positional: