mirror of
https://github.com/systemd/systemd.git
synced 2024-10-27 01:55:22 +03:00
bootctl: add new verb for initializing a random seed in the ESP
This commit is contained in:
parent
3e155eba43
commit
e44c3229f2
@ -25,6 +25,7 @@
|
||||
#include "copy.h"
|
||||
#include "dirent-util.h"
|
||||
#include "efivars.h"
|
||||
#include "env-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
@ -34,6 +35,7 @@
|
||||
#include "pager.h"
|
||||
#include "parse-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "random-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "stat-util.h"
|
||||
#include "stdio-util.h"
|
||||
@ -580,21 +582,29 @@ static int mkdir_one(const char *prefix, const char *suffix) {
|
||||
}
|
||||
|
||||
static const char *const esp_subdirs[] = {
|
||||
/* The directories to place in the ESP */
|
||||
"EFI",
|
||||
"EFI/systemd",
|
||||
"EFI/BOOT",
|
||||
"loader",
|
||||
/* Note that "/loader/entries" is not listed here, since it should be placed in $BOOT, which might
|
||||
* not necessarily be the ESP */
|
||||
NULL
|
||||
};
|
||||
|
||||
static int create_esp_subdirs(const char *esp_path) {
|
||||
static const char *const dollar_boot_subdirs[] = {
|
||||
/* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
|
||||
"loader",
|
||||
"loader/entries", /* Type #1 entries */
|
||||
"EFI",
|
||||
"EFI/Linux", /* Type #2 entries */
|
||||
NULL
|
||||
};
|
||||
|
||||
static int create_subdirs(const char *root, const char * const *subdirs) {
|
||||
const char *const *i;
|
||||
int r;
|
||||
|
||||
STRV_FOREACH(i, esp_subdirs) {
|
||||
r = mkdir_one(esp_path, *i);
|
||||
STRV_FOREACH(i, subdirs) {
|
||||
r = mkdir_one(root, *i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -865,19 +875,27 @@ static int rmdir_one(const char *prefix, const char *suffix) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_esp_subdirs(const char *esp_path) {
|
||||
size_t i;
|
||||
int r = 0;
|
||||
static int remove_subdirs(const char *root, const char *const *subdirs) {
|
||||
int r, q;
|
||||
|
||||
for (i = ELEMENTSOF(esp_subdirs)-1; i > 0; i--) {
|
||||
int q;
|
||||
/* We use recursion here to destroy the directories in reverse order. Which should be safe given how
|
||||
* short the array is. */
|
||||
|
||||
q = rmdir_one(esp_path, esp_subdirs[i-1]);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
}
|
||||
if (!subdirs[0]) /* A the end of the list */
|
||||
return 0;
|
||||
|
||||
return r;
|
||||
r = remove_subdirs(root, subdirs + 1);
|
||||
q = rmdir_one(root, subdirs[0]);
|
||||
|
||||
return r < 0 ? r : q;
|
||||
}
|
||||
|
||||
static int remove_machine_id_directory(const char *root, sd_id128_t machine_id) {
|
||||
char buf[SD_ID128_STRING_MAX];
|
||||
|
||||
assert(root);
|
||||
|
||||
return rmdir_one(root, sd_id128_to_string(machine_id, buf));
|
||||
}
|
||||
|
||||
static int remove_binaries(const char *esp_path) {
|
||||
@ -894,26 +912,22 @@ static int remove_binaries(const char *esp_path) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static int remove_loader_config(const char *esp_path) {
|
||||
static int remove_file(const char *root, const char *file) {
|
||||
const char *p;
|
||||
|
||||
assert(esp_path);
|
||||
assert(root);
|
||||
assert(file);
|
||||
|
||||
p = prefix_roota(esp_path, "/loader/loader.conf");
|
||||
p = prefix_roota(root, file);
|
||||
if (unlink(p) < 0) {
|
||||
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to unlink file \"%s\": %m", p);
|
||||
if (errno != ENOENT)
|
||||
return -errno;
|
||||
} else
|
||||
log_info("Removed \"%s\".", p);
|
||||
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno,
|
||||
"Failed to unlink file \"%s\": %m", p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
return errno == ENOENT ? 0 : -errno;
|
||||
}
|
||||
|
||||
static int remove_entries_directory(const char *dollar_boot_path) {
|
||||
assert(dollar_boot_path);
|
||||
|
||||
return rmdir_one(dollar_boot_path, "/loader/entries");
|
||||
log_info("Removed \"%s\".", p);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
|
||||
@ -937,6 +951,35 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_loader_variables(void) {
|
||||
const char *p;
|
||||
int r = 0;
|
||||
|
||||
/* Remove all persistent loader variables we define */
|
||||
|
||||
FOREACH_STRING(p,
|
||||
"LoaderConfigTimeout",
|
||||
"LoaderConfigTimeoutOneShot",
|
||||
"LoaderEntryDefault",
|
||||
"LoaderEntryOneShot",
|
||||
"LoaderSystemToken") {
|
||||
|
||||
int q;
|
||||
|
||||
q = efi_set_variable(EFI_VENDOR_LOADER, p, NULL, 0);
|
||||
if (q == -ENOENT)
|
||||
continue;
|
||||
if (q < 0) {
|
||||
log_warning_errno(q, "Failed to remove %s variable: %m", p);
|
||||
if (r >= 0)
|
||||
r = q;
|
||||
} else
|
||||
log_info("Removed EFI variable %s.", p);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int install_loader_config(const char *esp_path, sd_id128_t machine_id) {
|
||||
char machine_string[SD_ID128_STRING_MAX];
|
||||
_cleanup_(unlink_and_freep) char *t = NULL;
|
||||
@ -976,21 +1019,12 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int install_entries_directories(const char *dollar_boot_path, sd_id128_t machine_id) {
|
||||
int r;
|
||||
static int install_machine_id_directory(const char *root, sd_id128_t machine_id) {
|
||||
char buf[SD_ID128_STRING_MAX];
|
||||
|
||||
assert(dollar_boot_path);
|
||||
assert(root);
|
||||
|
||||
/* Both /loader/entries and the entry directories themselves should be located on the same
|
||||
* partition. Also create the parent directory for entry directories, so that kernel-install
|
||||
* knows where to put them. */
|
||||
|
||||
r = mkdir_one(dollar_boot_path, "loader/entries");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return mkdir_one(dollar_boot_path, sd_id128_to_string(machine_id, buf));
|
||||
return mkdir_one(root, sd_id128_to_string(machine_id, buf));
|
||||
}
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
@ -1016,6 +1050,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" install Install systemd-boot to the ESP and EFI variables\n"
|
||||
" update Update systemd-boot in the ESP and EFI variables\n"
|
||||
" remove Remove systemd-boot from the ESP and EFI variables\n"
|
||||
" random-seed Initialize random seed in ESP and EFI variables\n"
|
||||
"\nBoot Loader Entries Commands:\n"
|
||||
" list List boot loader entries\n"
|
||||
" set-default ID Set default boot loader entry\n"
|
||||
@ -1286,6 +1321,122 @@ static int verb_list(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int install_random_seed(const char *esp) {
|
||||
_cleanup_(unlink_and_freep) char *tmp = NULL;
|
||||
_cleanup_free_ void *buffer = NULL;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
_cleanup_close_ int fd = -1;
|
||||
size_t sz, token_size;
|
||||
ssize_t n;
|
||||
int r;
|
||||
|
||||
assert(esp);
|
||||
|
||||
path = path_join(esp, "/loader/random-seed");
|
||||
if (!path)
|
||||
return log_oom();
|
||||
|
||||
sz = random_pool_size();
|
||||
|
||||
buffer = malloc(sz);
|
||||
if (!buffer)
|
||||
return log_oom();
|
||||
|
||||
r = genuine_random_bytes(buffer, sz, RANDOM_BLOCK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Faile to acquire random seed: %m");
|
||||
|
||||
r = tempfn_random(path, "bootctl", &tmp);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
|
||||
if (fd < 0) {
|
||||
tmp = mfree(tmp);
|
||||
return log_error_errno(fd, "Failed to open random seed file for writing: %m");
|
||||
}
|
||||
|
||||
n = write(fd, buffer, sz);
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to write random seed file: %m");
|
||||
if ((size_t) n != sz)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing random seed file.");
|
||||
|
||||
if (rename(tmp, path) < 0)
|
||||
return log_error_errno(r, "Failed to move random seed file into place: %m");
|
||||
|
||||
tmp = mfree(tmp);
|
||||
|
||||
log_info("Successfully written random seed file %s with %zu bytes.", path, sz);
|
||||
|
||||
if (!arg_touch_variables)
|
||||
return 0;
|
||||
|
||||
if (!is_efi_boot()) {
|
||||
log_notice("Not booted with EFI, skipping EFI variable setup.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
|
||||
if (r < 0) {
|
||||
if (r != -ENXIO)
|
||||
log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
|
||||
|
||||
if (detect_vm() > 0) {
|
||||
/* Let's not write a system token if we detect we are running in a VM
|
||||
* environment. Why? Our default security model for the random seed uses the system
|
||||
* token as a mechanism to ensure we are not vulnerable to golden master sloppiness
|
||||
* issues, i.e. that people initialize the random seed file, then copy the image to
|
||||
* many systems and end up with the same random seed in each that is assumed to be
|
||||
* valid but in reality is the same for all machines. By storing a system token in
|
||||
* the EFI variable space we can make sure that even though the random seeds on disk
|
||||
* are all the same they will be different on each system under the assumption that
|
||||
* the EFI variable space is maintained separate from the random seed storage. That
|
||||
* is generally the case on physical systems, as the ESP is stored on persistant
|
||||
* storage, and the EFI variables in NVRAM. However in virtualized environments this
|
||||
* is generally not true: the EFI variable set is typically stored along with the
|
||||
* disk image itself. For example, using the OVMF EFI firmware the EFI variables are
|
||||
* stored in a file in the ESP itself. */
|
||||
|
||||
log_notice("Not installing system token, since we are running in a virtualized environment.");
|
||||
return 0;
|
||||
}
|
||||
} else if (r == 0) {
|
||||
log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = efi_get_variable(EFI_VENDOR_LOADER, "LoaderSystemToken", NULL, NULL, &token_size);
|
||||
if (r < 0) {
|
||||
if (r != -ENOENT)
|
||||
return log_error_errno(r, "Failed to test system token validity: %m");
|
||||
} else {
|
||||
if (token_size >= sz) {
|
||||
/* Let's avoid writes if we can, and initialize this only once. */
|
||||
log_debug("System token already written, not updating.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sz);
|
||||
}
|
||||
|
||||
r = genuine_random_bytes(buffer, sz, RANDOM_BLOCK);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to acquire random seed: %m");
|
||||
|
||||
/* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
|
||||
* and possibly get identification information or too much insight into the kernel's entropy pool
|
||||
* state. */
|
||||
RUN_WITH_UMASK(0077) {
|
||||
r = efi_set_variable(EFI_VENDOR_LOADER, "LoaderSystemToken", buffer, sz);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set LoaderSystemToken EFI variable: %m");
|
||||
}
|
||||
|
||||
log_info("Successfully initialized system token in EFI variable with %zu bytes.", sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sync_everything(void) {
|
||||
int ret = 0, k;
|
||||
|
||||
@ -1331,7 +1482,11 @@ static int verb_install(int argc, char *argv[], void *userdata) {
|
||||
/* Don't create any of these directories when we are just updating. When we update
|
||||
* we'll drop-in our files (unless there are newer ones already), but we won't create
|
||||
* the directories for them in the first place. */
|
||||
r = create_esp_subdirs(arg_esp_path);
|
||||
r = create_subdirs(arg_esp_path, esp_subdirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1345,7 +1500,11 @@ static int verb_install(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = install_entries_directories(arg_dollar_boot_path(), machine_id);
|
||||
r = install_machine_id_directory(arg_dollar_boot_path(), machine_id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = install_random_seed(arg_esp_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@ -1363,7 +1522,7 @@ static int verb_install(int argc, char *argv[], void *userdata) {
|
||||
}
|
||||
|
||||
static int verb_remove(int argc, char *argv[], void *userdata) {
|
||||
sd_id128_t uuid = SD_ID128_NULL;
|
||||
sd_id128_t uuid = SD_ID128_NULL, machine_id;
|
||||
int r, q;
|
||||
|
||||
r = acquire_esp(false, NULL, NULL, NULL, &uuid);
|
||||
@ -1374,28 +1533,56 @@ static int verb_remove(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_id128_get_machine(&machine_id);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to get machine id: %m");
|
||||
|
||||
r = remove_binaries(arg_esp_path);
|
||||
|
||||
q = remove_loader_config(arg_esp_path);
|
||||
q = remove_file(arg_esp_path, "/loader/loader.conf");
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = remove_entries_directory(arg_dollar_boot_path());
|
||||
q = remove_file(arg_esp_path, "/loader/random-seed");
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = remove_esp_subdirs(arg_esp_path);
|
||||
q = remove_subdirs(arg_esp_path, esp_subdirs);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
(void) sync_everything();
|
||||
q = remove_subdirs(arg_esp_path, dollar_boot_subdirs);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
if (arg_touch_variables) {
|
||||
q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
|
||||
q = remove_machine_id_directory(arg_esp_path, machine_id);
|
||||
if (q < 0 && r >= 0)
|
||||
r = 1;
|
||||
|
||||
if (arg_xbootldr_path) {
|
||||
/* Remove the latter two also in the XBOOTLDR partition if it exists */
|
||||
q = remove_subdirs(arg_xbootldr_path, dollar_boot_subdirs);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = remove_machine_id_directory(arg_xbootldr_path, machine_id);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
}
|
||||
|
||||
(void) sync_everything();
|
||||
|
||||
if (!arg_touch_variables)
|
||||
return r;
|
||||
|
||||
q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true);
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
q = remove_loader_variables();
|
||||
if (q < 0 && r >= 0)
|
||||
r = q;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -1447,6 +1634,21 @@ static int verb_set_default(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_random_seed(int argc, char *argv[], void *userdata) {
|
||||
int r;
|
||||
|
||||
r = acquire_esp(false, NULL, NULL, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = install_random_seed(arg_esp_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
(void) sync_everything();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bootctl_main(int argc, char *argv[]) {
|
||||
static const Verb verbs[] = {
|
||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||
@ -1454,6 +1656,7 @@ static int bootctl_main(int argc, char *argv[]) {
|
||||
{ "install", VERB_ANY, 1, 0, verb_install },
|
||||
{ "update", VERB_ANY, 1, 0, verb_install },
|
||||
{ "remove", VERB_ANY, 1, 0, verb_remove },
|
||||
{ "random-seed", VERB_ANY, 1, 0, verb_random_seed },
|
||||
{ "list", VERB_ANY, 1, 0, verb_list },
|
||||
{ "set-default", 2, 2, 0, verb_set_default },
|
||||
{ "set-oneshot", 2, 2, 0, verb_set_default },
|
||||
|
Loading…
Reference in New Issue
Block a user