Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new pcrlock tool for generating signed PCR policies for PCR 0, 1, 4, … #28891

Merged
merged 21 commits into from Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
981f762
efivars: add UEFI 'database' variable uuid
poettering Aug 14, 2023
a63b260
tpm2-util: add various uefi event log definitions
poettering Jul 13, 2023
f88f929
tpm2-util: add helper for returning path to EFI event log blob
poettering Aug 31, 2023
199d758
tpm2-util: pick up a few new symbols from tpm2-tss
poettering Oct 19, 2023
8f3f9c2
tpm2-util: export a couple of functions we'd like to use for pcrlock
poettering Oct 19, 2023
9fe3b63
tpm2-util: add helpers for marshalling public/private keys
poettering Oct 19, 2023
3600620
tpm2-util: add helpers for marshalling NV index public areas
poettering Oct 25, 2023
f7be7a2
tpm2-util: add helper for setting TPM2B_AUTH in binary
poettering Oct 19, 2023
34657b1
tpm2-util: add helper that calculates name of NV index
poettering Oct 19, 2023
2cd8f75
tpm2-util: add calls for calculating/submitting PolicyAuthorizeNV + P…
poettering Oct 19, 2023
48d0605
tpm2-util: add helper for creating/removing/updating NV index with st…
poettering Oct 23, 2023
ce80da0
tpm2-util: add generic helpers for sealing/unsealing data
poettering Oct 23, 2023
40ce732
tpm2-util: make various marshalling/unmarshalling calls static, as we…
poettering Oct 21, 2023
b52e950
tpm2-util: add common array for TPM2 hash algorithms
poettering Oct 23, 2023
a434270
pcrlock: add new pcrlock tool
poettering Aug 16, 2023
404aea7
tree-wide: hook everything up with pcrlock policy
poettering Oct 24, 2023
8e35338
pcrlock: add pre-defined pcrlock files
poettering Aug 16, 2023
809def1
units: add units that put together and install a TPM2 PCR policy at boot
poettering Oct 23, 2023
e206210
man: document pcrlock
poettering Oct 25, 2023
3e6a25a
test: add pcrlock integration test
poettering Oct 26, 2023
e43f87a
update TODO
poettering Oct 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
95 changes: 17 additions & 78 deletions TODO
Expand Up @@ -144,6 +144,23 @@ Features:
root=nvme:<trtype>:<traddr>:<trsvcid>:<nqn>:<partition> to boot directly from
nvme-oF

* pcrlock:
- make signed PCR work together with pcrlock
- add kernel-install plugin that automatically creates UKI .pcrlock file when
UKI is installed, and removes it when it is removed again
- automatically install PE measurement of sd-boot on "bootctl install"
- write generated pcrlock signature files to the ESP as credential, one for
each installed OS & pick up generated pcrlock signature file in sd-stub,
pass it via initrd to OS
- pre-calc sysext + kernel cmdline measurements
- pre-calc cryptsetup root key measurement
- maybe make systemd-repart generate .pcrlock for old and new GPT header in
/run?
- Add support for more than 8 branches per PCR OR
- add "systemd-pcrlock lock-kernel-current" or so which synthesizes .pcrlock
policy from currently booted kernel/event log, to close gap for first boot
for pre-built images

* add a new systemd-project@.service that is very similar to user@.service but
uses DynamicUser=1 and no PAMName= to invoke an unprivileged somewhat
light-weight service manager. Use HOME=/var/lib/systemd/projects/%i as home
Expand Down Expand Up @@ -296,54 +313,6 @@ Features:
* systemd-mount should only consider modern file systems when mounting, similar
to systemd-dissect

* new "systemd-pcrlock" component for dealing with PCR4. Design idea:
1. define /{etc,usr,var/lib}/pcrlock.d/<component>/<version>.pcrlock
2. these files contain list of hashes that will be measured when component is
run, per PCR
3. each component involved in the boot that is deterministically measured can
place one or more of these files in those dirs (shim, sd-boot,
sd-stub/UKI, cryptsetup, pcrphase, pcrfs, …)
4. since each component has its own dir, with multiple files in them, package
such as kernels (of which there can be multiple installed at the same
time) can be grouped together: only one of them is measured at a time.
5. whenever a new component is added or an old one removed, or the PCR lock
shall be relaxed or tightened the systemd-pcrlock tool is invoked.
6. tool iterates through all these files, orders them alphabetically by
component, then matches them up with current measurements (as per uefi
event log), identifying by hash, accepting that the "beginning" of the
measurements might not be recognizable.
7. Then calculates expected PCR values starting with the "unrecognized
head" from the event log, then continuing with all of components
defined via the .pcrlock files (but dropping out the "recognized tail"
from the uefi event log). (This might mean combinatorial explosion, if
there are multiple shims, multiple sd-boot, and so on.)
8. Generates a public/private key pair on the TPM
9. Generates a counter object in the TPM, with a policy that allows only
one-by-one increase with signature policy by the public/private key pair.
10. now signs policies of all expected PCR values with the generated keypair,
using all combinations of components defined in the .pcrlock files
restricting it to the counter + 1.
11. locks down the keypair with a signed policy with its own public key
12. generates JSON file of all these policies with their signatures, drops
them as singleton in ESP
13. increases the counter by one.
14. after boot sd-stub picks JSON up from ESP, passes it to userspace via
.extra
15. JSON contained policies can now be used to unlock disk as well as the
public/key itself for signing further policies, as well as increment for
the counter
16. whenever any of the components above is added/removed new JSON file with
signatures for counter + 1 is generated, dropped in ESP, then counter
increased. (i.e. this means the "recognized tail" of the event log is
deterministically swapped out)
17. when firmware update is expected, relaxed signed policy is generated for
next boot only valid if counter is increased (this means the
"unrecognized head" for the event log can change without losing access)
18. on every boot checks if releaxed policy is in effect, if so, new strict
policy is generated and counter increased.
Net result: Removes downgrade attack surface + Locks OS to firmware + Allows
downgrades within bounds

* add another PE section ".fname" or so that encodes the intended filename for
PE file, and validate that when loading add-ons and similar before using
it. This is particularly relevant when we load multiple add-ons and want to
Expand Down Expand Up @@ -466,30 +435,6 @@ Features:
* SIGRTMIN+18 and memory pressure handling should still be added to: hostnamed,
localed, oomd, timedated.

* in order to make binding to PCR 4 realistic:
- generate one keypair "U" and store it in a tpm2 nvindex.
- Generate another keypair "P" and store it in a second tpm2 nvindex.
- allocate a persistent counter object "C" in the tpm2
- Enroll all user objects (i.e. luks volumes, creds, …) to a tpm2 policy
signed by U.
- Lock both U and P down with a tpm2 policy signed by P (yes, P can only be
used if a signature by P itself can be provided)
- For regular reboots generate a signature for a restrictive PCR4 + counter C
based policy with key P. Place signature in EFI var, so it can be found on
next boot
- For reboots where a firmware update is expected generate a signature with a
more open policy against just counter C. Place signature in same EFI var.
- Increase C whenever switching between these two signature types.
- During early boot, use the signature from the EFI var to unlock U and P.
Use it to generate a signature for unlocking user objects given the current
PCR 4 value, store that away into /run somewhere, for user during the whole
later boot.
- When booting up automatically update the mentioned efi var so that it
contains the restrictive signature. But also generate a signature ahead of
time that could be used in case during the current boot we later detect we might
need to reboot for a firmware update. Store that in /run somewhere, so that
it can be placed in the EFI var, if needed.

* repart/gpt-auto/DDIs: maybe introduce a concept of "extension" partitions,
that have a new type uuid and can "extend" earlier partitions, to work around
the fact that systemd-repart can only grow the last partition defined. During
Expand Down Expand Up @@ -1028,12 +973,6 @@ Features:
set up the directory so that it can only be accessed if host and app are in
order.

* TPM2: extend unlock policy to protect against version downgrades in signed
policies: policy probably must take some nvram based generation counter into
account that can only monotonically increase and can be used to invalidate
old PCR signatures. Otherwise people could downgrade to old signed PCR sets
whenever they want.

* update HACKING.md to suggest developing systemd with the ideas from:
https://0pointer.net/blog/testing-my-system-code-in-usr-without-modifying-usr.html
https://0pointer.net/blog/running-an-container-off-the-host-usr.html
Expand Down
16 changes: 16 additions & 0 deletions man/crypttab.xml
Expand Up @@ -808,6 +808,22 @@
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>

<varlistentry>
<term><option>tpm2-pcrlock=</option></term>

<listitem><para>Takes an absolute path to a TPM2 pcrlock policy file, as produced by the
<citerefentry><refentrytitle>systemd-pcrlock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
tool. This permits locking LUKS2 volumes to a local policy of allowed PCR values with
variants. See
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details on enrolling TPM2 pcrlock policies. If this option is not specified but it is attempted
to unlock a LUKS2 volume with a TPM2 pcrlock enrollment a suitable signature file
<filename>pcrlock.json</filename> is searched for in <filename>/run/systemd/</filename> and
<filename>/var/lib/systemd/</filename> (in this order).</para>

<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>

<varlistentry>
<term><option>tpm2-measure-pcr=</option></term>

Expand Down
11 changes: 11 additions & 0 deletions man/rules/meson.build
Expand Up @@ -1004,6 +1004,16 @@ manpages = [
['systemd-nspawn', '1', [], ''],
['systemd-oomd.service', '8', ['systemd-oomd'], 'ENABLE_OOMD'],
['systemd-path', '1', [], ''],
['systemd-pcrlock',
'8',
['systemd-pcrlock-file-system.service',
'systemd-pcrlock-firmware-code.service',
'systemd-pcrlock-firmware-config.service',
'systemd-pcrlock-machine-id.service',
'systemd-pcrlock-make-policy.service',
'systemd-pcrlock-secureboot-authority.service',
'systemd-pcrlock-secureboot-policy.service'],
'ENABLE_BOOTLOADER'],
['systemd-pcrphase.service',
'8',
['systemd-pcrextend',
Expand Down Expand Up @@ -1138,6 +1148,7 @@ manpages = [
['systemd.nspawn', '5', [], ''],
['systemd.offline-updates', '7', [], ''],
['systemd.path', '5', [], ''],
['systemd.pcrlock', '5', ['systemd.pcrlock.d'], ''],
['systemd.preset', '5', [], ''],
['systemd.resource-control', '5', [], ''],
['systemd.scope', '5', [], ''],
Expand Down
14 changes: 14 additions & 0 deletions man/systemd-cryptenroll.xml
Expand Up @@ -519,6 +519,20 @@
<xi:include href="version-info.xml" xpointer="v252"/></listitem>
</varlistentry>

<varlistentry>
<term><option>--tpm2-pcrlock=</option><arg>PATH</arg></term>

<listitem><para>Configures a TPM2 pcrlock policy to bind encryption to. Expects a path to a pcrlock
policy file as generated by the
<citerefentry><refentrytitle>systemd-pcrlock</refentrytitle><manvolnum>1</manvolnum></citerefentry>
tool. If a TPM2 device is enrolled and this option is not used but a file
<filename>pcrlock.json</filename> is found in <filename>/run/systemd/</filename> or
<filename>/var/lib/systemd/</filename> it is automatically used. Assign an empty string to turn this
behaviour off.</para>

<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>

<varlistentry>
<term><option>--wipe-slot=</option><arg rep="repeat">SLOT</arg></term>

Expand Down