From: Lennart Poettering Date: Tue, 2 Jul 2024 10:36:21 +0000 (+0200) Subject: ukify: introduce new --measure-base= switch X-Git-Tag: v257-rc1~525^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F34295%2Fhead;p=thirdparty%2Fsystemd.git ukify: introduce new --measure-base= switch --- diff --git a/man/ukify.xml b/man/ukify.xml index 5e7681279b3..983e89c270e 100644 --- a/man/ukify.xml +++ b/man/ukify.xml @@ -240,6 +240,19 @@ + + + + Takes a path to an existing PE file to use as base profile, for measuring + multi-profile UKIs. When calculating the PCR values, this has the effect that the sections + specified on the command line are combined with any sections from the PE file specified here (up to + the first .profile section, and only if not already specified on the command + line). Typically, this is used together with to both import and use as + measurement base an existing UKI. + + + + diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index b3fab755b6e..5057d72d2c9 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -505,6 +505,14 @@ def pe_strip_section_name(name): def call_systemd_measure(uki, linux, opts): + + if not opts.measure and not opts.pcr_private_keys: + return + + measure_sections = ('.linux', '.osrel', '.cmdline', '.initrd', + '.ucode', '.splash', '.dtb', '.uname', + '.sbat', '.pcrpkey', '.profile') + measure_tool = find_tool('systemd-measure', '/usr/lib/systemd/systemd-measure', opts=opts) @@ -513,16 +521,67 @@ def call_systemd_measure(uki, linux, opts): # PCR measurement + to_measure = [] + tflist = [] + + # First, pick up the sections we shall measure now */ + for s in uki.sections: + + print(s) + + if not s.measure: + continue + + if s.content is not None: + assert(s.name != ".linux" or linux is None) + to_measure.append(f"--{s.name.removeprefix('.')}={s.content}") + else: + raise ValueError(f"Don't know how to measure section {s.name}"); + + if linux is not None: + to_measure.append(f'--linux={linux}') + + # And now iterate through the base profile and measure what we haven't measured above + if opts.measure_base is not None: + pe = pefile.PE(opts.measure_base, fast_load=True) + + # Find matching PE section in base image + for base_section in pe.sections: + name = pe_strip_section_name(base_section.Name) + + # If we reach the first .profile section the base is over + if name == ".profile": + break + + # Only some sections are measured + if name not in measure_sections: + continue + + # Check if this is a section we already covered above + already_covered = False + for s in uki.sections: + if s.measure and name == s.name: + already_covered = True + break; + + if already_covered: + continue + + # Split out section and use as base + tf = tempfile.NamedTemporaryFile() + tf.write(base_section.get_data(length=base_section.Misc_VirtualSize)) + tf.flush() + tflist.append(tf) + + to_measure.append(f"--{name.removeprefix('.')}={tf.name}") + if opts.measure: pp_groups = opts.phase_path_groups or [] cmd = [ measure_tool, 'calculate', - f'--linux={linux}', - *(f"--{s.name.removeprefix('.')}={s.content}" - for s in uki.sections - if s.measure), + *to_measure, *(f'--bank={bank}' for bank in banks), # For measurement, the keys are not relevant, so we can lump all the phase paths @@ -542,10 +601,7 @@ def call_systemd_measure(uki, linux, opts): cmd = [ measure_tool, 'sign', - f'--linux={linux}', - *(f"--{s.name.removeprefix('.')}={s.content}" - for s in uki.sections - if s.measure), + *to_measure, *(f'--bank={bank}' for bank in banks), ] @@ -1431,6 +1487,14 @@ CONFIG_ITEMS = [ config_key = 'UKI/Extend', ), + ConfigItem( + '--measure-base', + metavar = 'UKI', + type = pathlib.Path, + help = 'path to existing UKI file whose relevant sections shall be used as base for PCR11 prediction', + config_key = 'UKI/MeasureBase', + ), + ConfigItem( '--pcr-banks', metavar = 'BANK…',