diff options
Diffstat (limited to '')
-rw-r--r-- | src/shared/boot-entry.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/src/shared/boot-entry.c b/src/shared/boot-entry.c new file mode 100644 index 0000000..e726073 --- /dev/null +++ b/src/shared/boot-entry.c @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "boot-entry.h" +#include "chase.h" +#include "fd-util.h" +#include "fileio.h" +#include "id128-util.h" +#include "os-util.h" +#include "path-util.h" +#include "string-table.h" +#include "string-util.h" +#include "utf8.h" + +bool boot_entry_token_valid(const char *p) { + return utf8_is_valid(p) && string_is_safe(p) && filename_is_valid(p); +} + +static int entry_token_load(int rfd, const char *etc_kernel, BootEntryTokenType *type, char **token) { + _cleanup_free_ char *buf = NULL, *p = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(rfd >= 0 || rfd == AT_FDCWD); + assert(type); + assert(*type == BOOT_ENTRY_TOKEN_AUTO); + assert(token); + + if (!etc_kernel) + return 0; + + p = path_join(etc_kernel, "entry-token"); + if (!p) + return log_oom(); + + r = chase_and_fopenat_unlocked(rfd, p, CHASE_AT_RESOLVE_IN_ROOT, "re", NULL, &f); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to chase and open '%s': %m", p); + + r = read_line(f, NAME_MAX, &buf); + if (r < 0) + return log_error_errno(r, "Failed to read %s: %m", p); + + if (isempty(buf)) + return 0; + + if (!boot_entry_token_valid(buf)) { + log_debug("Invalid entry token specified in %s, ignoring.", p); + return 0; + } + + *token = TAKE_PTR(buf); + *type = BOOT_ENTRY_TOKEN_LITERAL; + return 1; +} + +static int entry_token_from_machine_id(sd_id128_t machine_id, BootEntryTokenType *type, char **token) { + char *p; + + assert(type); + assert(IN_SET(*type, BOOT_ENTRY_TOKEN_AUTO, BOOT_ENTRY_TOKEN_MACHINE_ID)); + assert(token); + + if (sd_id128_is_null(machine_id)) + return 0; + + p = strdup(SD_ID128_TO_STRING(machine_id)); + if (!p) + return log_oom(); + + *token = p; + *type = BOOT_ENTRY_TOKEN_MACHINE_ID; + return 1; +} + +static int entry_token_from_os_release(int rfd, BootEntryTokenType *type, char **token) { + _cleanup_free_ char *id = NULL, *image_id = NULL; + int r; + + assert(rfd >= 0 || rfd == AT_FDCWD); + assert(type); + assert(IN_SET(*type, BOOT_ENTRY_TOKEN_AUTO, BOOT_ENTRY_TOKEN_OS_IMAGE_ID, BOOT_ENTRY_TOKEN_OS_ID)); + assert(token); + + switch (*type) { + case BOOT_ENTRY_TOKEN_AUTO: + r = parse_os_release_at(rfd, + "IMAGE_ID", &image_id, + "ID", &id); + break; + + case BOOT_ENTRY_TOKEN_OS_IMAGE_ID: + r = parse_os_release_at(rfd, "IMAGE_ID", &image_id); + break; + + case BOOT_ENTRY_TOKEN_OS_ID: + r = parse_os_release_at(rfd, "ID", &id); + break; + + default: + assert_not_reached(); + } + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to load /etc/os-release: %m"); + + if (!isempty(image_id) && boot_entry_token_valid(image_id)) { + *token = TAKE_PTR(image_id); + *type = BOOT_ENTRY_TOKEN_OS_IMAGE_ID; + return 1; + } + + if (!isempty(id) && boot_entry_token_valid(id)) { + *token = TAKE_PTR(id); + *type = BOOT_ENTRY_TOKEN_OS_ID; + return 1; + } + + return 0; +} + +int boot_entry_token_ensure_at( + int rfd, + const char *etc_kernel, + sd_id128_t machine_id, + bool machine_id_is_random, + BootEntryTokenType *type, + char **token) { + + int r; + + assert(rfd >= 0 || rfd == AT_FDCWD); + assert(type); + assert(token); + + if (*token) + return 0; /* Already set. */ + + switch (*type) { + + case BOOT_ENTRY_TOKEN_AUTO: + r = entry_token_load(rfd, etc_kernel, type, token); + if (r != 0) + return r; + + if (!machine_id_is_random) { + r = entry_token_from_machine_id(machine_id, type, token); + if (r != 0) + return r; + } + + r = entry_token_from_os_release(rfd, type, token); + if (r != 0) + return r; + + if (machine_id_is_random) { + r = entry_token_from_machine_id(machine_id, type, token); + if (r != 0) + return r; + } + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields."); + + case BOOT_ENTRY_TOKEN_MACHINE_ID: + r = entry_token_from_machine_id(machine_id, type, token); + if (r != 0) + return r; + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set."); + + case BOOT_ENTRY_TOKEN_OS_IMAGE_ID: + r = entry_token_from_os_release(rfd, type, token); + if (r != 0) + return r; + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "IMAGE_ID= field not set in /etc/os-release."); + + case BOOT_ENTRY_TOKEN_OS_ID: + r = entry_token_from_os_release(rfd, type, token); + if (r != 0) + return r; + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "ID= field not set in /etc/os-release."); + + case BOOT_ENTRY_TOKEN_LITERAL: + /* In this case, the token should be already set by the user input. */ + return -EINVAL; + + default: + assert_not_reached(); + } +} + +int boot_entry_token_ensure( + const char *root, + const char *etc_kernel, + sd_id128_t machine_id, + bool machine_id_is_random, + BootEntryTokenType *type, + char **token) { + + assert(token); + + if (*token) + return 0; /* Already set. */ + + _cleanup_close_ int rfd = -EBADF; + + rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH); + if (rfd < 0) + return -errno; + + return boot_entry_token_ensure_at(rfd, etc_kernel, machine_id, machine_id_is_random, type, token); +} + +int parse_boot_entry_token_type(const char *s, BootEntryTokenType *type, char **token) { + assert(s); + assert(type); + assert(token); + + /* + * This function is intended to be used in command line parsers, to handle token that are passed in. + * + * NOTE THAT THIS WILL FREE THE PREVIOUS ARGUMENT POINTER ON SUCCESS! + * Hence, do not pass in uninitialized pointers. + */ + + if (streq(s, "machine-id")) { + *type = BOOT_ENTRY_TOKEN_MACHINE_ID; + *token = mfree(*token); + return 0; + } + + if (streq(s, "os-image-id")) { + *type = BOOT_ENTRY_TOKEN_OS_IMAGE_ID; + *token = mfree(*token); + return 0; + } + + if (streq(s, "os-id")) { + *type = BOOT_ENTRY_TOKEN_OS_ID; + *token = mfree(*token); + return 0; + } + + const char *e = startswith(s, "literal:"); + if (e) { + if (!boot_entry_token_valid(e)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid entry token literal is specified for --entry-token=."); + + *type = BOOT_ENTRY_TOKEN_LITERAL; + return free_and_strdup_warn(token, e); + } + + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unexpected parameter for --entry-token=: %s", s); +} + +static const char *const boot_entry_token_type_table[] = { + [BOOT_ENTRY_TOKEN_MACHINE_ID] = "machine-id", + [BOOT_ENTRY_TOKEN_OS_IMAGE_ID] = "os-image-id", + [BOOT_ENTRY_TOKEN_OS_ID] = "os-id", + [BOOT_ENTRY_TOKEN_LITERAL] = "literal", + [BOOT_ENTRY_TOKEN_AUTO] = "auto", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_token_type, BootEntryTokenType); |