We already have this nice code in system that determines the block
device backing the root file system, but it's only used internally in
systemd-gpt-generator. Let's make this more accessible and expose it
directly in bootctl.
It doesn't fit immediately into the topic of bootctl, but I think it's
close enough and behaves very similar to the existing "bootctl
--print-boot-path" and "--print-esp-path" tools.
If --print-root-device (or -R) is specified once, will show the block device
backing the root fs, and if specified twice (probably easier: -RR) it
will show the whole block device that block device belongs to in case it
is a partition block device.
Suggested use:
# cfdisk `bootctl -RR`
To get access to the partition table, behind the OS install, for
whatever it might be.
grub (at least on fedora) nowadays implements the boot loader spec, but
not the boot loader interface. Hence let's split out the commands
specific to each in two groups in the --help text. This way the first
group just covers boot lodaer spec stuff (i.e. stuff on disk in the
ESP/XBOOTLDR partition). And the other covers talking to the boot loader
via EFI var.
The unlink command removes an entry from the ESP including
referenced files that are not referenced in other entries. That is
useful eg to have multiple entries that use the same kernel with
different options.
The cleanup command removes all files that are not referenced by any
entry.
RUN_WITH_UMASK was initially conceived for spawning externals progs with the
umask set. But nowadays we use it various syscalls and stuff that doesn't "run"
anything, so the "RUN_" prefix has outlived its usefulness.
sd-stub has an opportunity to handle the seed the same way sd-boot does,
which would have benefits for UKIs when sd-boot is not in use. This
commit wires that up.
It refactors the XBOOTLDR partition discovery to also find the ESP
partition, so that it access the random seed there.
Removing the virtualization check might not be the worst thing in the
world, and would potentially get many, many more systems properly seeded
rather than not seeded. There are a few reasons to consider this:
- In most QEMU setups and most guides on how to setup QEMU, a separate
pflash file is used for nvram variables, and this generally isn't
copied around.
- We're now hashing in a timestamp, which should provide some level of
differentiation, given that EFI_TIME has a nanoseconds field.
- The kernel itself will additionally hash in: a high resolution time
stamp, a cycle counter, RDRAND output, the VMGENID uniquely
identifying the virtual machine, any other seeds from the hypervisor
(like from FDT or setup_data).
- During early boot, the RNG is reseeded quite frequently to account for
the importance of early differentiation.
So maybe the mitigating factors make the actual feared problem
significantly less likely and therefore the pros of having file-based
seeding might outweigh the cons of weird misconfigured setups having a
hypothetical problem on first boot.
Follow-up for #25368.
Let's consider ENOENT an expected error, and just debug log about it
(though, let's suffix it with `, ignoring.`). All other errors will log
loudly, as they are unexpected errors.
Rather than passing seeds up to userspace via EFI variables, pass seeds
directly to the kernel's EFI stub loader, via LINUX_EFI_RANDOM_SEED_TABLE_GUID.
EFI variables can potentially leak and suffer from forward secrecy
issues, and processing these with userspace means that they are
initialized much too late in boot to be useful. In contrast,
LINUX_EFI_RANDOM_SEED_TABLE_GUID uses EFI configuration tables, and so
is hidden from userspace entirely, and is parsed extremely early on by
the kernel, so that every single call to get_random_bytes() by the
kernel is seeded.
In order to do this properly, we use a bit more robust hashing scheme,
and make sure that each input is properly memzeroed out after use. The
scheme is:
key = HASH(LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN)
new_disk_seed = HASH(key || 0)
seed_for_linux = HASH(key || 1)
The various inputs are:
- LINUX_EFI_RANDOM_SEED_TABLE_GUID from prior bootloaders
- 256 bits of seed from EFI's RNG
- The (immutable) system token, from its EFI variable
- The prior on-disk seed
- The UEFI monotonic counter
- A timestamp
This also adjusts the secure boot semantics, so that the operation is
only aborted if it's not possible to get random bytes from EFI's RNG or
a prior boot stage. With the proper hashing scheme, this should make
boot seeds safe even on secure boot.
There is currently a bug in Linux's EFI stub in which if the EFI stub
manages to generate random bytes on its own using EFI's RNG, it will
ignore what the bootloader passes. That's annoying, but it means that
either way, via systemd-boot or via EFI stub's mechanism, the RNG *does*
get initialized in a good safe way. And this bug is now fixed in the
efi.git tree, and will hopefully be backported to older kernels.
As the kernel recommends, the resultant seeds are 256 bits and are
allocated using pool memory of type EfiACPIReclaimMemory, so that it
gets freed at the right moment in boot.
Let's make use of the new flag whenever we access the ESP or XBOOTLDR.
The resources we make use of in these partitions can't possibly use
symlinks (because UEFI knows no symlink concept), and they are untrusted
territory, hence under no circumstances we should be tricked into
following symlinks that shouldn't be there in the first place.
Of course, you might argue thta ESP/XBOOTLDR are VFAT and thus don#t
know symlinks. But the thing is, they don#t have to be. Firmware can
support other file systems too, and people can use efifs to gain access
to arbitrary Linux file systems from EFI. Hence, let's better be safe
than sorry.
entry.
By default an entry named "Linux Boot Manager" is created (which is the
previous behavior). With the flag the name of the entry can be
controlled, which is useful when installing systemd-boot to multiple ESP
partitions and having uniquely named entries.
Fixes#17044.
Previously, the tree output of discovered boot laoders in the ESP would
look like this:
Available Boot Loaders on ESP:
ESP: /efi (/dev/disk/by-partuuid/0c6f41ed-2573-4723-8c84-23681f9d1c28)
File: └─/EFI/systemd/systemd-bootx64.efi (systemd-boot v250.7-1.fc36)
File: └─/EFI/BOOT/BOOTX64.EFI (systemd-boot v250.7-1.fc36)
With this change the tree branches are corrected to look like this:
Available Boot Loaders on ESP:
ESP: /efi (/dev/disk/by-partuuid/0c6f41ed-2573-4723-8c84-23681f9d1c28)
File: ├─/EFI/systemd/systemd-bootx64.efi (systemd-boot v250.7-1.fc36)
└─/EFI/BOOT/BOOTX64.EFI (systemd-boot v250.7-1.fc36)
Ahhh! So much nicer. This incorrect tree drawing has been bugging me for
so long. Finally I can sleep at night again!
Report whether the devicetree + sort-key boot loader spec type #1
fields are supported, and whether the "@saved" pseudo-entry is
supported.
Strictly speaking, thes features have been added in versions that are
already released (250+), so by adding this those version even though
they support the features will be considered not supporting them, but
that should be OK (the opposite would be a problem though, i.e. if we'd
assume a boot loader had a feature it actually does not).
These three features are features relevant to userspace, as it allows
userspace to tweak/genereate BLS entries or set EFI vars correctly.
Other features (i.e. that have no impliciations to userspace) are not
reported.
We allow ESP autodetection to fail, e.g. if it is not mounted, but then we'd
say that the detected one is different than the one reported by the bootloader,
which is rather confusing.
While at it, if we actually detect a mismatch, print the two uuids.
When using --root=/--image= the binaries to install/update will be
picked from the directory/image. Add an option to let the caller
choose.
By default (auto) the image is tried first, and if nothing is found
then the host. The other options allow to strictly try the image
or host and ignore the other.
We currently have a convoluted and complex selection of which random
numbers to use. We can simplify this down to two functions that cover
all of our use cases:
1) Randomness for crypto: this one needs to wait until the RNG is
initialized. So it uses getrandom(0). If that's not available, it
polls on /dev/random, and then reads from /dev/urandom. This function
returns whether or not it was successful, as before.
2) Randomness for other things: this one uses getrandom(GRND_INSECURE).
If it's not available it uses getrandom(GRND_NONBLOCK). And if that
would block, then it falls back to /dev/urandom. And if /dev/urandom
isn't available, it uses the fallback code. It never fails and
doesn't return a value.
These two cases match all the uses of randomness inside of systemd.
I would prefer to make both of these return void, and get rid of the
fallback code, and simply assert in the incredibly unlikely case that
/dev/urandom doesn't exist. But Luca disagrees, so this commit attempts
to instead keep case (1) returning a return value, which all the callers
already check, and fix the fallback code in (2) to be less bad than
before.
For the less bad fallback code for (2), we now use auxval and some
timestamps, together with various counters representing the invocation,
hash it all together and provide the output. Provided that AT_RANDOM is
secure, this construction is probably okay too, though notably it
doesn't have any forward secrecy. Fortunately, it's only used by
random_bytes() and not by crypto_random_bytes().