diff options
-rw-r--r-- | src/basic/os-util.h | 3 | ||||
-rw-r--r-- | src/boot/bootctl-reboot-to-firmware.c | 2 | ||||
-rw-r--r-- | src/boot/bootctl-uki.c | 231 | ||||
-rw-r--r-- | src/boot/bootctl.c | 2 | ||||
-rw-r--r-- | src/shared/meson.build | 1 | ||||
-rw-r--r-- | src/shared/uki-util.c | 294 | ||||
-rw-r--r-- | src/shared/uki-util.h | 23 |
7 files changed, 337 insertions, 219 deletions
diff --git a/src/basic/os-util.h b/src/basic/os-util.h index a6b121ec43..3bafeaeb92 100644 --- a/src/basic/os-util.h +++ b/src/basic/os-util.h @@ -1,8 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include <stdbool.h> #include <stdio.h> +#include "time-util.h" + /* The *_extension_release flavours will look for /usr/lib/extension-release/extension-release.NAME * in accordance with the OS extension specification, rather than for /usr/lib/ or /etc/os-release. */ diff --git a/src/boot/bootctl-reboot-to-firmware.c b/src/boot/bootctl-reboot-to-firmware.c index 77e3ff12a9..91f259768c 100644 --- a/src/boot/bootctl-reboot-to-firmware.c +++ b/src/boot/bootctl-reboot-to-firmware.c @@ -11,7 +11,7 @@ int verb_reboot_to_firmware(int argc, char *argv[], void *userdata) { r = efi_get_reboot_to_firmware(); if (r > 0) { puts("active"); - return EXIT_SUCCESS; /* success */ + return 0; /* success */ } if (r == 0) { puts("supported"); diff --git a/src/boot/bootctl-uki.c b/src/boot/bootctl-uki.c index 261e687e3f..eda79218fc 100644 --- a/src/boot/bootctl-uki.c +++ b/src/boot/bootctl-uki.c @@ -1,240 +1,37 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "bootctl.h" +#include "alloc-util.h" #include "bootctl-uki.h" -#include "env-file.h" -#include "fd-util.h" -#include "os-util.h" -#include "parse-util.h" -#include "pe-header.h" - -#define MAX_SECTIONS 96 - -static const uint8_t dos_file_magic[2] = "MZ"; -static const uint8_t pe_file_magic[4] = "PE\0\0"; - -static const uint8_t name_osrel[8] = ".osrel"; -static const uint8_t name_linux[8] = ".linux"; -static const uint8_t name_initrd[8] = ".initrd"; -static const uint8_t name_cmdline[8] = ".cmdline"; -static const uint8_t name_uname[8] = ".uname"; - -static int pe_sections(FILE *uki, struct PeSectionHeader **ret, size_t *ret_n) { - _cleanup_free_ struct PeSectionHeader *sections = NULL; - struct DosFileHeader dos; - struct PeHeader pe; - size_t scount; - uint64_t soff, items; - - assert(uki); - assert(ret); - assert(ret_n); - - items = fread(&dos, 1, sizeof(dos), uki); - if (items < sizeof(dos.Magic)) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS magic (got %"PRIu64" of %zu bytes)", - items, sizeof(dos.Magic)); - if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0) - goto no_sections; - - if (items != sizeof(dos)) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS header (got %"PRIu64" of %zu bytes)", - items, sizeof(dos)); - - if (fseek(uki, le32toh(dos.ExeHeader), SEEK_SET) < 0) - return log_error_errno(errno, "seek to PE header"); - - items = fread(&pe, 1, sizeof(pe), uki); - if (items != sizeof(pe)) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE header read error"); - if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0) - goto no_sections; - - soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader); - if (fseek(uki, soff, SEEK_SET) < 0) - return log_error_errno(errno, "seek to PE section headers"); - - scount = le16toh(pe.FileHeader.NumberOfSections); - if (scount > MAX_SECTIONS) - goto no_sections; - sections = new(struct PeSectionHeader, scount); - if (!sections) - return log_oom(); - items = fread(sections, sizeof(*sections), scount, uki); - if (items != scount) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section header read error"); - - *ret = TAKE_PTR(sections); - *ret_n = scount; - return 0; - -no_sections: - *ret = NULL; - *ret_n = 0; - return 0; -} - -static bool find_pe_section( - struct PeSectionHeader *sections, - size_t scount, - const uint8_t *name, - size_t namelen, - size_t *ret) { - - assert(sections || scount == 0); - assert(name || namelen == 0); - - for (size_t s = 0; s < scount; s++) - if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name), name, namelen) == 0) { - if (ret) - *ret = s; - return true; - } - - return false; -} - -static bool is_uki(struct PeSectionHeader *sections, size_t scount) { - assert(sections || scount == 0); - - return - find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) && - find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) && - find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL); -} +#include "uki-util.h" int verb_kernel_identify(int argc, char *argv[], void *userdata) { - _cleanup_fclose_ FILE *uki = NULL; - _cleanup_free_ struct PeSectionHeader *sections = NULL; - size_t scount; + KernelType t; int r; - uki = fopen(argv[1], "re"); - if (!uki) - return log_error_errno(errno, "Failed to open UKI file '%s': %m", argv[1]); - - r = pe_sections(uki, §ions, &scount); + r = inspect_kernel(argv[1], &t, NULL, NULL, NULL); if (r < 0) return r; - if (!sections) - puts("unknown"); - else if (is_uki(sections, scount)) - puts("uki"); - else - puts("pe"); - - return EXIT_SUCCESS; -} - -static int read_pe_section( - FILE *uki, - const struct PeSectionHeader *section, - void **ret, - size_t *ret_n) { - - _cleanup_free_ void *data = NULL; - uint32_t size, bytes; - uint64_t soff; - - assert(uki); - assert(section); - assert(ret); - - soff = le32toh(section->PointerToRawData); - size = le32toh(section->VirtualSize); - - if (size > 16 * 1024) - return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "PE section too big"); - - if (fseek(uki, soff, SEEK_SET) < 0) - return log_error_errno(errno, "seek to PE section"); - - data = malloc(size+1); - if (!data) - return log_oom(); - ((uint8_t*) data)[size] = 0; /* safety NUL byte */ - - bytes = fread(data, 1, size, uki); - if (bytes != size) - return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section read error"); - - *ret = TAKE_PTR(data); - if (ret_n) - *ret_n = size; + puts(kernel_type_to_string(t)); return 0; } -static int inspect_osrel(const void *osrel, size_t osrel_size) { - _cleanup_fclose_ FILE *s = NULL; - _cleanup_free_ char *pname = NULL, *name = NULL; +int verb_kernel_inspect(int argc, char *argv[], void *userdata) { + _cleanup_free_ char *cmdline = NULL, *uname = NULL, *pname = NULL; + KernelType t; int r; - assert(osrel || osrel_size == 0); - - if (!osrel) - return 0; - - s = fmemopen((void*) osrel, osrel_size, "r"); - if (!s) - return log_warning_errno(errno, "Failed to open embedded os-release file, ignoring: %m"); - - r = parse_env_file(s, NULL, - "PRETTY_NAME", &pname, - "NAME", &name); + r = inspect_kernel(argv[1], &t, &cmdline, &uname, &pname); if (r < 0) - return log_warning_errno(r, "Failed to parse embedded os-release file, ignoring: %m"); - - printf(" OS: %s\n", os_release_pretty_name(pname, name)); - return 0; -} - -static void inspect_uki(FILE *uki, struct PeSectionHeader *sections, size_t scount) { - _cleanup_free_ char *cmdline = NULL, *uname = NULL; - _cleanup_free_ void *osrel = NULL; - size_t osrel_size = 0, idx; - - assert(uki); - assert(sections || scount == 0); - - if (find_pe_section(sections, scount, name_cmdline, sizeof(name_cmdline), &idx)) - read_pe_section(uki, sections + idx, (void**) &cmdline, NULL); - - if (find_pe_section(sections, scount, name_uname, sizeof(name_uname), &idx)) - read_pe_section(uki, sections + idx, (void**) &uname, NULL); - - if (find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), &idx)) - read_pe_section(uki, sections + idx, &osrel, &osrel_size); + return r; + printf("Kernel Type: %s\n", kernel_type_to_string(t)); if (cmdline) printf(" Cmdline: %s\n", cmdline); if (uname) printf(" Version: %s\n", uname); + if (pname) + printf(" OS: %s\n", pname); - (void) inspect_osrel(osrel, osrel_size); -} - -int verb_kernel_inspect(int argc, char *argv[], void *userdata) { - _cleanup_fclose_ FILE *uki = NULL; - _cleanup_free_ struct PeSectionHeader *sections = NULL; - size_t scount; - int r; - - uki = fopen(argv[1], "re"); - if (!uki) - return log_error_errno(errno, "Failed to open UKI file '%s': %m", argv[1]); - - r = pe_sections(uki, §ions, &scount); - if (r < 0) - return r; - - if (!sections) - puts("Kernel Type: unknown"); - else if (is_uki(sections, scount)) { - puts("Kernel Type: uki"); - inspect_uki(uki, sections, scount); - } else - puts("Kernel Type: pe"); - - return EXIT_SUCCESS; + return 0; } diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index d8de09cab5..f984c3050e 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -489,7 +489,7 @@ static int run(int argc, char *argv[]) { return log_oom(); puts(path); - return EXIT_SUCCESS; + return 0; } /* Open up and mount the image */ diff --git a/src/shared/meson.build b/src/shared/meson.build index 8c107fd161..57bea08101 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -150,6 +150,7 @@ shared_sources = files( 'tpm2-util.c', 'udev-util.c', 'uid-alloc-range.c', + 'uki-util.c', 'user-record-nss.c', 'user-record-show.c', 'user-record.c', diff --git a/src/shared/uki-util.c b/src/shared/uki-util.c new file mode 100644 index 0000000000..218e7950f8 --- /dev/null +++ b/src/shared/uki-util.c @@ -0,0 +1,294 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "fd-util.h" +#include "env-file.h" +#include "os-util.h" +#include "parse-util.h" +#include "pe-header.h" +#include "string-table.h" +#include "uki-util.h" + +#define MAX_SECTIONS 96 + +static const uint8_t dos_file_magic[2] = "MZ"; +static const uint8_t pe_file_magic[4] = "PE\0\0"; + +static const uint8_t name_osrel[8] = ".osrel"; +static const uint8_t name_linux[8] = ".linux"; +static const uint8_t name_initrd[8] = ".initrd"; +static const uint8_t name_cmdline[8] = ".cmdline"; +static const uint8_t name_uname[8] = ".uname"; + +static const char * const kernel_type_table[_KERNEL_TYPE_MAX] = { + [KERNEL_TYPE_UNKNOWN] = "unknown", + [KERNEL_TYPE_UKI] = "uki", + [KERNEL_TYPE_PE] = "pe", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(kernel_type, KernelType); + +static int pe_sections(FILE *uki, struct PeSectionHeader **ret, size_t *ret_n) { + _cleanup_free_ struct PeSectionHeader *sections = NULL; + struct DosFileHeader dos; + struct PeHeader pe; + size_t scount; + uint64_t soff, items; + + assert(uki); + assert(ret); + assert(ret_n); + + items = fread(&dos, 1, sizeof(dos), uki); + if (items < sizeof(dos.Magic)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS magic (got %"PRIu64" of %zu bytes)", + items, sizeof(dos.Magic)); + if (memcmp(dos.Magic, dos_file_magic, sizeof(dos_file_magic)) != 0) + goto no_sections; + + if (items != sizeof(dos)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "File is smaller than DOS header (got %"PRIu64" of %zu bytes)", + items, sizeof(dos)); + + if (fseek(uki, le32toh(dos.ExeHeader), SEEK_SET) < 0) + return log_error_errno(errno, "seek to PE header"); + + items = fread(&pe, 1, sizeof(pe), uki); + if (items != sizeof(pe)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE header read error"); + if (memcmp(pe.Magic, pe_file_magic, sizeof(pe_file_magic)) != 0) + goto no_sections; + + soff = le32toh(dos.ExeHeader) + sizeof(pe) + le16toh(pe.FileHeader.SizeOfOptionalHeader); + if (fseek(uki, soff, SEEK_SET) < 0) + return log_error_errno(errno, "seek to PE section headers"); + + scount = le16toh(pe.FileHeader.NumberOfSections); + if (scount > MAX_SECTIONS) + goto no_sections; + sections = new(struct PeSectionHeader, scount); + if (!sections) + return log_oom(); + items = fread(sections, sizeof(*sections), scount, uki); + if (items != scount) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section header read error"); + + *ret = TAKE_PTR(sections); + *ret_n = scount; + return 0; + +no_sections: + *ret = NULL; + *ret_n = 0; + return 0; +} + +static bool find_pe_section( + struct PeSectionHeader *sections, + size_t scount, + const uint8_t *name, + size_t namelen, + size_t *ret) { + + assert(sections || scount == 0); + assert(name || namelen == 0); + + for (size_t s = 0; s < scount; s++) + if (memcmp_nn(sections[s].Name, sizeof(sections[s].Name), name, namelen) == 0) { + if (ret) + *ret = s; + return true; + } + + return false; +} + +static bool is_uki(struct PeSectionHeader *sections, size_t scount) { + assert(sections || scount == 0); + + return + find_pe_section(sections, scount, name_osrel, sizeof(name_osrel), NULL) && + find_pe_section(sections, scount, name_linux, sizeof(name_linux), NULL) && + find_pe_section(sections, scount, name_initrd, sizeof(name_initrd), NULL); +} + +static int read_pe_section( + FILE *uki, + struct PeSectionHeader *sections, + size_t scount, + const uint8_t *name, + size_t name_len, + void **ret, + size_t *ret_n) { + + struct PeSectionHeader *section; + _cleanup_free_ void *data = NULL; + uint32_t size, bytes; + uint64_t soff; + size_t idx; + + assert(uki); + assert(sections || scount == 0); + assert(ret); + + if (!find_pe_section(sections, scount, name, name_len, &idx)) { + *ret = NULL; + if (ret_n) + *ret_n = 0; + return 0; + } + + section = sections + idx; + soff = le32toh(section->PointerToRawData); + size = le32toh(section->VirtualSize); + + if (size > 16 * 1024) + return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "PE section too big"); + + if (fseek(uki, soff, SEEK_SET) < 0) + return log_error_errno(errno, "seek to PE section"); + + data = malloc(size+1); + if (!data) + return log_oom(); + ((uint8_t*) data)[size] = 0; /* safety NUL byte */ + + bytes = fread(data, 1, size, uki); + if (bytes != size) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "PE section read error"); + + *ret = TAKE_PTR(data); + if (ret_n) + *ret_n = size; + return 1; +} + +static int uki_read_pretty_name( + FILE *uki, + struct PeSectionHeader *sections, + size_t scount, + char **ret) { + + _cleanup_free_ char *pname = NULL, *name = NULL; + _cleanup_fclose_ FILE *s = NULL; + _cleanup_free_ void *osrel = NULL; + size_t osrel_size = 0; + int r; + + assert(uki); + assert(sections || scount == 0); + assert(ret); + + r = read_pe_section(uki, sections, scount, name_osrel, sizeof(name_osrel), &osrel, &osrel_size); + if (r < 0) + return r; + if (r == 0) { + *ret = NULL; + return 0; + } + + s = fmemopen(osrel, osrel_size, "r"); + if (!s) + return log_warning_errno(errno, "Failed to open embedded os-release file, ignoring: %m"); + + r = parse_env_file(s, NULL, + "PRETTY_NAME", &pname, + "NAME", &name); + if (r < 0) + return log_warning_errno(r, "Failed to parse embedded os-release file, ignoring: %m"); + + /* follow the same logic as os_release_pretty_name() */ + if (!isempty(pname)) + *ret = TAKE_PTR(pname); + else if (!isempty(name)) + *ret = TAKE_PTR(name); + else { + char *n = strdup("Linux"); + if (!n) + return log_oom(); + + *ret = n; + } + + return 0; +} + +static int inspect_uki( + FILE *uki, + struct PeSectionHeader *sections, + size_t scount, + char **ret_cmdline, + char **ret_uname, + char **ret_pretty_name) { + + _cleanup_free_ char *cmdline = NULL, *uname = NULL, *pname = NULL; + int r; + + assert(uki); + assert(sections || scount == 0); + + if (ret_cmdline) { + r = read_pe_section(uki, sections, scount, name_cmdline, sizeof(name_cmdline), (void**) &cmdline, NULL); + if (r < 0) + return r; + } + + if (ret_uname) { + r = read_pe_section(uki, sections, scount, name_uname, sizeof(name_uname), (void**) &uname, NULL); + if (r < 0) + return r; + } + + if (ret_pretty_name) { + r = uki_read_pretty_name(uki, sections, scount, &pname); + if (r < 0) + return r; + } + + if (ret_cmdline) + *ret_cmdline = TAKE_PTR(cmdline); + if (ret_uname) + *ret_uname = TAKE_PTR(uname); + if (ret_pretty_name) + *ret_pretty_name = TAKE_PTR(pname); + + return 0; +} + +int inspect_kernel( + const char *filename, + KernelType *ret_type, + char **ret_cmdline, + char **ret_uname, + char **ret_pretty_name) { + + _cleanup_fclose_ FILE *uki = NULL; + _cleanup_free_ struct PeSectionHeader *sections = NULL; + size_t scount; + KernelType t; + int r; + + assert(filename); + + uki = fopen(filename, "re"); + if (!uki) + return log_error_errno(errno, "Failed to open UKI file '%s': %m", filename); + + r = pe_sections(uki, §ions, &scount); + if (r < 0) + return r; + + if (!sections) + t = KERNEL_TYPE_UNKNOWN; + else if (is_uki(sections, scount)) { + t = KERNEL_TYPE_UKI; + r = inspect_uki(uki, sections, scount, ret_cmdline, ret_uname, ret_pretty_name); + if (r < 0) + return r; + } else + t = KERNEL_TYPE_PE; + + if (ret_type) + *ret_type = t; + + return 0; +} diff --git a/src/shared/uki-util.h b/src/shared/uki-util.h new file mode 100644 index 0000000000..e579f46259 --- /dev/null +++ b/src/shared/uki-util.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include <errno.h> + +#include "macro.h" + +typedef enum KernelType { + KERNEL_TYPE_UNKNOWN, + KERNEL_TYPE_UKI, + KERNEL_TYPE_PE, + _KERNEL_TYPE_MAX, + _KERNEL_TYPE_INVALID = -EINVAL, +} KernelType; + +const char* kernel_type_to_string(KernelType t) _const_; + +int inspect_kernel( + const char *filename, + KernelType *ret_type, + char **ret_cmdline, + char **ret_uname, + char **ret_pretty_name); |