mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-22 13:33:56 +03:00
Merge pull request #16142 from poettering/random-seed-cmdline
pid1: add support for allowing to pass in random seed via kernel cmdline
This commit is contained in:
commit
0e31a6c2ad
6
TODO
6
TODO
@ -20,12 +20,6 @@ Features:
|
||||
* add --copy-from and --copy-to command to systemd-dissect which copies stuff
|
||||
in and out of a disk image
|
||||
|
||||
* add systemd.random_seed= on the kernel cmdline, taking some hex or base64
|
||||
encoded data. During earliest boot, credit it to entropy. This is not useful
|
||||
for general purpose systems, but certainly for testing environments in VMs
|
||||
and such, as it allows us to boot up instantly with fully initialized entropy
|
||||
pool even if RNG pass-thru is not available.
|
||||
|
||||
* Support ProtectProc= or so, using: https://patchwork.kernel.org/cover/11310197/
|
||||
|
||||
* if /usr/bin/swapoff fails due to OOM, log a friendly explanatory message about it
|
||||
|
@ -257,7 +257,16 @@ boot, in order to ensure the entropy pool is filled up quickly.
|
||||
file. If done, `systemd-boot` will use the random seed file even if no
|
||||
system token is found in EFI variables.
|
||||
|
||||
With the three mechanisms described above it should be possible to provide
|
||||
4. A kernel command line option `systemd.random_seed=` may be used to pass in a
|
||||
base64 encoded seed to initialize the kernel's entropy pool from during
|
||||
early service manager initialization. This option is only safe in testing
|
||||
environments, as the random seed passed this way is accessible to
|
||||
unprivileged programs via `/proc/cmdline`. Using this option outside of
|
||||
testing environments is a security problem since cryptographic key material
|
||||
derived from the entropy pool initialized with a seed accessible to
|
||||
unprivileged programs should not be considered secret.
|
||||
|
||||
With the four mechanisms described above it should be possible to provide
|
||||
early-boot entropy in most cases. Specifically:
|
||||
|
||||
1. On EFI systems, `systemd-boot`'s random seed logic should make sure good
|
||||
@ -267,7 +276,8 @@ early-boot entropy in most cases. Specifically:
|
||||
2. On virtualized systems, the early `virtio-rng` hookup should ensure entropy
|
||||
is available early on — as long as the VM environment provides virtualized
|
||||
RNG devices, which they really should all do in 2019. Complain to your
|
||||
hosting provider if they don't.
|
||||
hosting provider if they don't. For VMs used in testing environments,
|
||||
`systemd.random_seed=` may be used as an alternative to a virtualized RNG.
|
||||
|
||||
3. On Intel/AMD systems systemd's own reliance on the kernel entropy pool is
|
||||
minimal (as RDRAND is used on those for UUID generation). This only works if
|
||||
@ -286,8 +296,9 @@ This primarily leaves two kind of systems in the cold:
|
||||
boot. Alternatively, consider implementing a solution similar to
|
||||
systemd-boot's random seed concept in your platform's boot loader.
|
||||
|
||||
2. Virtualized environments that lack both virtio-rng and RDRAND. Tough
|
||||
luck. Talk to your hosting provider, and ask them to fix this.
|
||||
2. Virtualized environments that lack both virtio-rng and RDRAND, outside of
|
||||
test environments. Tough luck. Talk to your hosting provider, and ask them
|
||||
to fix this.
|
||||
|
||||
3. Also note: if you deploy an image without any random seed and/or without
|
||||
installing any 'system token' in an EFI variable, as described above, this
|
||||
@ -410,6 +421,10 @@ This primarily leaves two kind of systems in the cold:
|
||||
information to possibly gain too much information about the current state
|
||||
of the kernel's entropy pool.
|
||||
|
||||
That said, we actually do implement this with the `systemd.random_seed=`
|
||||
kernel command line option. Don't use this outside of testing environments,
|
||||
however, for the aforementioned reasons.
|
||||
|
||||
12. *Why doesn't `systemd-boot` rewrite the 'system token' too each time
|
||||
when updating the random seed file stored in the ESP?*
|
||||
|
||||
|
@ -468,8 +468,32 @@
|
||||
<term><varname>systemd.clock-usec=</varname></term>
|
||||
|
||||
<listitem><para>Takes a decimal, numeric timestamp in µs since January 1st 1970, 00:00am, to set the
|
||||
system clock to. The system time is set to the specified timestamp early during
|
||||
boot. It is not propagated to the hardware clock (RTC).</para></listitem>
|
||||
system clock to. The system time is set to the specified timestamp early during boot. It is not
|
||||
propagated to the hardware clock (RTC).</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>systemd.random-seed=</varname></term>
|
||||
|
||||
<listitem><para>Takes a base64 encoded random seed value to credit with full entropy to the kernel's
|
||||
random pool during early service manager initialization. This option is useful in testing
|
||||
environments where delays due to random pool initialization in entropy starved virtual machines shall
|
||||
be avoided.</para>
|
||||
|
||||
<para>Note that if this option is used the seed is accessible to unprivileged programs from
|
||||
<filename>/proc/cmdline</filename>. This option is hence a security risk when used outside of test
|
||||
systems, since the (possibly) only seed used for initialization of the kernel's entropy pool might be
|
||||
easily acquired by unprivileged programs.</para>
|
||||
|
||||
<para>It is recommended to pass 512 bytes of randomized data (as that matches the Linux kernel pool
|
||||
size), which may be generated with a command like the following:</para>
|
||||
|
||||
<programlisting>dd if=/dev/urandom bs=512 count=1 status=none | base64 -w 0</programlisting>
|
||||
|
||||
<para>Again: do not use this option outside of testing environments, it's a security risk elsewhere,
|
||||
as secret key material derived from the entropy pool can possibly be reconstructed by unprivileged
|
||||
programs.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -7,11 +7,13 @@
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/random.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#if HAVE_SYS_AUXV_H
|
||||
@ -438,3 +440,36 @@ size_t random_pool_size(void) {
|
||||
/* Use the minimum as default, if we can't retrieve the correct value */
|
||||
return RANDOM_POOL_SIZE_MIN;
|
||||
}
|
||||
|
||||
int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(seed && size > 0);
|
||||
|
||||
if (credit) {
|
||||
_cleanup_free_ struct rand_pool_info *info = NULL;
|
||||
|
||||
/* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any
|
||||
* chance for confusion here. */
|
||||
if (size > INT_MAX / 8)
|
||||
return -EOVERFLOW;
|
||||
|
||||
info = malloc(offsetof(struct rand_pool_info, buf) + size);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
info->entropy_count = size * 8;
|
||||
info->buf_size = size;
|
||||
memcpy(info->buf, seed, size);
|
||||
|
||||
if (ioctl(fd, RNDADDENTROPY, info) < 0)
|
||||
return -errno;
|
||||
} else {
|
||||
r = loop_write(fd, seed, size, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -38,3 +38,5 @@ int rdrand(unsigned long *ret);
|
||||
#define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
|
||||
|
||||
size_t random_pool_size(void);
|
||||
|
||||
int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
|
||||
|
@ -1,8 +1,6 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/random.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
@ -11,6 +9,7 @@
|
||||
#include "efivars.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "random-util.h"
|
||||
#include "strv.h"
|
||||
|
||||
/* If a random seed was passed by the boot loader in the LoaderRandomSeed EFI variable, let's credit it to
|
||||
@ -43,7 +42,6 @@ static void lock_down_efi_variables(void) {
|
||||
}
|
||||
|
||||
int efi_take_random_seed(void) {
|
||||
_cleanup_free_ struct rand_pool_info *info = NULL;
|
||||
_cleanup_free_ void *value = NULL;
|
||||
_cleanup_close_ int random_fd = -1;
|
||||
size_t size;
|
||||
@ -79,11 +77,6 @@ int efi_take_random_seed(void) {
|
||||
if (size == 0)
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring.");
|
||||
|
||||
/* The kernel API only accepts "int" as entropy count (which is in bits), let's avoid any chance for
|
||||
* confusion here. */
|
||||
if (size > INT_MAX / 8)
|
||||
size = INT_MAX / 8;
|
||||
|
||||
random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (random_fd < 0)
|
||||
return log_warning_errno(errno, "Failed to open /dev/urandom for writing, ignoring: %m");
|
||||
@ -94,15 +87,8 @@ int efi_take_random_seed(void) {
|
||||
if (r < 0)
|
||||
return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m");
|
||||
|
||||
info = malloc(offsetof(struct rand_pool_info, buf) + size);
|
||||
if (!info)
|
||||
return log_oom();
|
||||
|
||||
info->entropy_count = size * 8;
|
||||
info->buf_size = size;
|
||||
memcpy(info->buf, value, size);
|
||||
|
||||
if (ioctl(random_fd, RNDADDENTROPY, info) < 0)
|
||||
r = random_write_entropy(random_fd, value, size, true);
|
||||
if (r < 0)
|
||||
return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
|
||||
|
||||
log_info("Successfully credited entropy passed from boot loader.");
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "hexdecoct.h"
|
||||
#include "hostname-setup.h"
|
||||
#include "ima-setup.h"
|
||||
#include "killall.h"
|
||||
@ -60,6 +61,7 @@
|
||||
#include "pretty-print.h"
|
||||
#include "proc-cmdline.h"
|
||||
#include "process-util.h"
|
||||
#include "random-util.h"
|
||||
#include "raw-clone.h"
|
||||
#include "rlimit-util.h"
|
||||
#if HAVE_SECCOMP
|
||||
@ -100,8 +102,8 @@ static enum {
|
||||
|
||||
static const char *arg_bus_introspect = NULL;
|
||||
|
||||
/* Those variables are initialized to 0 automatically, so we avoid uninitialized memory access.
|
||||
* Real defaults are assigned in reset_arguments() below. */
|
||||
/* Those variables are initialized to 0 automatically, so we avoid uninitialized memory access. Real
|
||||
* defaults are assigned in reset_arguments() below. */
|
||||
static char *arg_default_unit;
|
||||
static bool arg_system;
|
||||
static bool arg_dump_core;
|
||||
@ -149,6 +151,8 @@ static OOMPolicy arg_default_oom_policy;
|
||||
static CPUSet arg_cpu_affinity;
|
||||
static NUMAPolicy arg_numa_policy;
|
||||
static usec_t arg_clock_usec;
|
||||
static void *arg_random_seed;
|
||||
static size_t arg_random_seed_size;
|
||||
|
||||
/* A copy of the original environment block */
|
||||
static char **saved_env = NULL;
|
||||
@ -503,6 +507,21 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse systemd.clock_usec= argument, ignoring: %s", value);
|
||||
|
||||
} else if (proc_cmdline_key_streq(key, "systemd.random_seed")) {
|
||||
void *p;
|
||||
size_t sz;
|
||||
|
||||
if (proc_cmdline_value_missing(key, value))
|
||||
return 0;
|
||||
|
||||
r = unbase64mem(value, (size_t) -1, &p, &sz);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to parse systemd.random_seed= argument, ignoring: %s", value);
|
||||
|
||||
free(arg_random_seed);
|
||||
arg_random_seed = sz > 0 ? p : mfree(p);
|
||||
arg_random_seed_size = sz;
|
||||
|
||||
} else if (streq(key, "quiet") && !value) {
|
||||
|
||||
if (arg_show_status == _SHOW_STATUS_INVALID)
|
||||
@ -1574,6 +1593,9 @@ static void apply_clock_update(void) {
|
||||
if (arg_clock_usec == 0)
|
||||
return;
|
||||
|
||||
if (getpid_cached() != 1)
|
||||
return;
|
||||
|
||||
if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, arg_clock_usec)) < 0)
|
||||
log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m");
|
||||
else {
|
||||
@ -1584,6 +1606,40 @@ static void apply_clock_update(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void cmdline_take_random_seed(void) {
|
||||
_cleanup_close_ int random_fd = -1;
|
||||
size_t suggested;
|
||||
int r;
|
||||
|
||||
if (arg_random_seed_size == 0)
|
||||
return;
|
||||
|
||||
if (getpid_cached() != 1)
|
||||
return;
|
||||
|
||||
assert(arg_random_seed);
|
||||
suggested = random_pool_size();
|
||||
|
||||
if (arg_random_seed_size < suggested)
|
||||
log_warning("Random seed specified on kernel command line has size %zu, but %zu bytes required to fill entropy pool.",
|
||||
arg_random_seed_size, suggested);
|
||||
|
||||
random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY);
|
||||
if (random_fd < 0) {
|
||||
log_warning_errno(errno, "Failed to open /dev/urandom for writing, ignoring: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
r = random_write_entropy(random_fd, arg_random_seed, arg_random_seed_size, true);
|
||||
if (r < 0) {
|
||||
log_warning_errno(r, "Failed to credit entropy specified on kernel command line, ignoring: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
log_notice("Successfully credited entropy passed on kernel command line.\n"
|
||||
"Note that the seed provided this way is accessible to unprivileged programs. This functionality should not be used outside of testing environments.");
|
||||
}
|
||||
|
||||
static void initialize_coredump(bool skip_setup) {
|
||||
#if ENABLE_COREDUMP
|
||||
if (getpid_cached() != 1)
|
||||
@ -2261,6 +2317,9 @@ static void reset_arguments(void) {
|
||||
|
||||
cpu_set_reset(&arg_cpu_affinity);
|
||||
numa_policy_reset(&arg_numa_policy);
|
||||
|
||||
arg_random_seed = mfree(arg_random_seed);
|
||||
arg_random_seed_size = 0;
|
||||
}
|
||||
|
||||
static int parse_configuration(const struct rlimit *saved_rlimit_nofile,
|
||||
@ -2580,8 +2639,7 @@ int main(int argc, char *argv[]) {
|
||||
/* For later on, see above... */
|
||||
log_set_target(LOG_TARGET_JOURNAL);
|
||||
|
||||
/* clear the kernel timestamp,
|
||||
* because we are in a container */
|
||||
/* clear the kernel timestamp, because we are in a container */
|
||||
kernel_timestamp = DUAL_TIMESTAMP_NULL;
|
||||
}
|
||||
|
||||
@ -2600,8 +2658,7 @@ int main(int argc, char *argv[]) {
|
||||
log_set_target(LOG_TARGET_AUTO);
|
||||
log_open();
|
||||
|
||||
/* clear the kernel timestamp,
|
||||
* because we are not PID 1 */
|
||||
/* clear the kernel timestamp, because we are not PID 1 */
|
||||
kernel_timestamp = DUAL_TIMESTAMP_NULL;
|
||||
|
||||
if (mac_selinux_init() < 0) {
|
||||
@ -2620,8 +2677,7 @@ int main(int argc, char *argv[]) {
|
||||
log_warning_errno(r, "Failed to redirect standard streams to /dev/null, ignoring: %m");
|
||||
}
|
||||
|
||||
/* Mount /proc, /sys and friends, so that /proc/cmdline and
|
||||
* /proc/$PID/fd is available. */
|
||||
/* Mount /proc, /sys and friends, so that /proc/cmdline and /proc/$PID/fd is available. */
|
||||
if (getpid_cached() == 1) {
|
||||
|
||||
/* Load the kernel modules early. */
|
||||
@ -2694,8 +2750,13 @@ int main(int argc, char *argv[]) {
|
||||
assert_se(chdir("/") == 0);
|
||||
|
||||
if (arg_action == ACTION_RUN) {
|
||||
/* Apply the systemd.clock_usec= kernel command line switch */
|
||||
apply_clock_update();
|
||||
if (!skip_setup) {
|
||||
/* Apply the systemd.clock_usec= kernel command line switch */
|
||||
apply_clock_update();
|
||||
|
||||
/* Apply random seed from kernel command line */
|
||||
cmdline_take_random_seed();
|
||||
}
|
||||
|
||||
/* A core pattern might have been specified via the cmdline. */
|
||||
initialize_core_pattern(skip_setup);
|
||||
|
@ -236,24 +236,10 @@ static int run(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED)) {
|
||||
_cleanup_free_ struct rand_pool_info *info = NULL;
|
||||
|
||||
info = malloc(offsetof(struct rand_pool_info, buf) + k);
|
||||
if (!info)
|
||||
return log_oom();
|
||||
|
||||
info->entropy_count = k * 8;
|
||||
info->buf_size = k;
|
||||
memcpy(info->buf, buf, k);
|
||||
|
||||
if (ioctl(random_fd, RNDADDENTROPY, info) < 0)
|
||||
return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
|
||||
} else {
|
||||
r = loop_write(random_fd, buf, (size_t) k, false);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
|
||||
}
|
||||
r = random_write_entropy(random_fd, buf, k,
|
||||
IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED));
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,7 +291,7 @@ static int run(int argc, char *argv[]) {
|
||||
* entropy later on. Let's keep that in mind by setting an extended attribute. on the file */
|
||||
if (getrandom_worked)
|
||||
if (fsetxattr(seed_fd, "user.random-seed-creditable", "1", 1, 0) < 0)
|
||||
log_full_errno(IN_SET(errno, ENOSYS, EOPNOTSUPP) ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno,
|
||||
"Failed to mark seed file as creditable, ignoring: %m");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user