summaryrefslogtreecommitdiffstats
path: root/src/shared/boot-entry.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/boot-entry.c')
-rw-r--r--src/shared/boot-entry.c273
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);