diff options
author | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-03-17 14:20:29 +0900 |
---|---|---|
committer | Yu Watanabe <watanabe.yu+github@gmail.com> | 2023-03-17 20:40:34 +0900 |
commit | 16af02cd9787b0c18cf5072aea2458ece0fbe3d8 (patch) | |
tree | 493219a341473719d39464730003fd47882b22f2 | |
parent | c8eced395b8caa26b11b8876393438c2d73a72a8 (diff) | |
download | systemd-16af02cd9787b0c18cf5072aea2458ece0fbe3d8.tar.gz |
uki-util: move functions for inspecting kernel to uki-util.[ch]
-rw-r--r-- | src/boot/bootctl-uki.c | 302 | ||||
-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 |
4 files changed, 320 insertions, 300 deletions
diff --git a/src/boot/bootctl-uki.c b/src/boot/bootctl-uki.c index cfa491f22e..eda79218fc 100644 --- a/src/boot/bootctl-uki.c +++ b/src/boot/bootctl-uki.c @@ -1,306 +1,8 @@ /* 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" -#include "string-table.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"; - -typedef enum KernelType { - KERNEL_TYPE_UNKNOWN, - KERNEL_TYPE_UKI, - KERNEL_TYPE_PE, - _KERNEL_TYPE_MAX, - _KERNEL_TYPE_INVALID = -EINVAL, -} KernelType; - -static const char * const kernel_type_table[_KERNEL_TYPE_MAX] = { - [KERNEL_TYPE_UNKNOWN] = "unknown", - [KERNEL_TYPE_UKI] = "uki", - [KERNEL_TYPE_PE] = "pe", -}; - -DEFINE_PRIVATE_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; -} - -static 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; -} +#include "uki-util.h" int verb_kernel_identify(int argc, char *argv[], void *userdata) { KernelType t; 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); |