1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-13 00:58:27 +03:00

ukify: add --policy-digest option

Uses the newly added policy-digest verb of systemd-measure, for the same
purpose: build a UKI and get digests for the .pcrsig section out, so
that they can be offline signed and reattached
This commit is contained in:
Luca Boccassi 2025-01-20 00:30:48 +00:00 committed by Daan De Meyer
parent 175cb87a1c
commit 606c5e7580
3 changed files with 35 additions and 3 deletions

View File

@ -209,6 +209,18 @@
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--policy-digest</option></term>
<term><option>--no-policy-digest</option></term>
<listitem><para>Enable or disable a call to
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
to print pre-calculated TPM2 policy digests. Useful for offline signing of PCR policies.
Defaults to false.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--section=<replaceable>NAME</replaceable>:<replaceable>TEXT</replaceable>|<replaceable>@PATH</replaceable></option></term>
<term><option>--section=<replaceable>NAME</replaceable>:text|binary<optional>@<replaceable>PATH</replaceable></optional></option></term>

View File

@ -242,6 +242,8 @@ def test_parse_args_many():
'--output=OUTPUT',
'--measure',
'--no-measure',
'--policy-digest',
'--no-policy-digest',
])
assert opts.linux == pathlib.Path('/ARG1')
assert opts.initrd == [pathlib.Path('/ARG2'), pathlib.Path('/ARG3 WITH SPACE')]
@ -262,6 +264,7 @@ def test_parse_args_many():
assert opts.tools == [pathlib.Path('TOOLZ/')]
assert opts.output == pathlib.Path('OUTPUT')
assert opts.measure is False
assert opts.policy_digest is False
def test_parse_sections():
opts = ukify.parse_args(

View File

@ -255,6 +255,7 @@ class UkifyConfig:
pcr_public_keys: list[str]
pcrpkey: Optional[Path]
phase_path_groups: Optional[list[str]]
policy_digest: bool
profile: Union[str, Path, None]
sb_cert: Union[str, Path, None]
sb_cert_name: Optional[str]
@ -751,7 +752,7 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
unique_to_measure[section.name] = section
if opts.measure:
if opts.measure or opts.policy_digest:
to_measure = unique_to_measure.copy()
for dtbauto in dtbauto_to_measure:
@ -762,7 +763,7 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
cmd = [
measure_tool,
'calculate',
'calculate' if opts.measure else 'policy-digest',
'--json',
opts.json,
*(f'--{s.name.removeprefix(".")}={s.content}' for s in to_measure.values()),
@ -772,6 +773,11 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
*(f'--phase={phase_path}' for phase_path in itertools.chain.from_iterable(pp_groups)),
]
# The JSON object will be used for offline signing, include the public key
# so that the fingerprint is included too.
if opts.policy_digest and opts.pcr_public_keys:
cmd += [f'--public-key={opts.pcr_public_keys[0]}']
print('+', shell_join(cmd), file=sys.stderr)
subprocess.check_call(cmd)
@ -1940,6 +1946,11 @@ CONFIG_ITEMS = [
action=argparse.BooleanOptionalAction,
help='print systemd-measure output for the UKI',
),
ConfigItem(
'--policy-digest',
action=argparse.BooleanOptionalAction,
help='print systemd-measure policy digests for the UKI',
),
ConfigItem(
'--json',
choices=('pretty', 'short', 'off'),
@ -2108,10 +2119,16 @@ def finalize_options(opts: argparse.Namespace) -> None:
# Check that --pcr-public-key=, --pcr-private-key=, and --phases=
# have either the same number of arguments or are not specified at all.
# But allow a single public key, for offline PCR signing, to pre-populate the JSON object
# with the certificate's fingerprint.
n_pcr_pub = None if opts.pcr_public_keys is None else len(opts.pcr_public_keys)
n_pcr_priv = None if opts.pcr_private_keys is None else len(opts.pcr_private_keys)
n_phase_path_groups = None if opts.phase_path_groups is None else len(opts.phase_path_groups)
if n_pcr_pub is not None and n_pcr_pub != n_pcr_priv:
if opts.policy_digest and n_pcr_priv is not None:
raise ValueError('--pcr-private-key= cannot be specified with --policy-digest')
if opts.policy_digest and (n_pcr_pub is None or n_pcr_pub != 1):
raise ValueError('--policy-digest requires exactly one --pcr-public-key=')
if n_pcr_pub is not None and n_pcr_priv is not None and n_pcr_pub != n_pcr_priv:
raise ValueError('--pcr-public-key= specifications must match --pcr-private-key=')
if n_phase_path_groups is not None and n_phase_path_groups != n_pcr_priv:
raise ValueError('--phases= specifications must match --pcr-private-key=')