diff --git a/man/ukify.xml b/man/ukify.xml index 0a0ee4390c..d6a46bc843 100644 --- a/man/ukify.xml +++ b/man/ukify.xml @@ -258,6 +258,17 @@ + + + + Takes a profile ID for which signed PCR measurements should be generated by ukify. + This option can be used together with when building the final + unified kernel image. If not specified, signed PCR measurements will be added for all profiles. + + + + + diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index 5b4f20aeda..5f821297c1 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -141,6 +141,25 @@ def try_import(modname: str, name: Optional[str] = None) -> ModuleType: raise ValueError(f'Kernel is compressed with {name or modname}, but module unavailable') from e +def read_env_file(text: str) -> dict[str, str]: + result = {} + + for line in text.splitlines(): + line = line.rstrip() + if not line or line.startswith('#'): + continue + if m := re.match(r'([A-Z][A-Z_0-9]+)=(.*)', line): + name, val = m.groups() + if val and val[0] in '"\'': + val = next(shlex.shlex(val, posix=True)) + + result[name] = val + else: + print(f'bad line {line!r}', file=sys.stderr) + + return result + + def get_zboot_kernel(f: IO[bytes]) -> bytes: """Decompress zboot efistub kernel if compressed. Return contents.""" # See linux/drivers/firmware/efi/libstub/Makefile.zboot @@ -244,6 +263,7 @@ class UkifyConfig: hwids: Path initrd: list[Path] join_profiles: list[Path] + sign_profiles: list[str] json: Union[Literal['pretty'], Literal['short'], Literal['off']] linux: Optional[Path] measure: bool @@ -1249,7 +1269,10 @@ def make_uki(opts: UkifyConfig) -> None: # PCR measurement and signing - call_systemd_measure(uki, opts=opts) + if (opts.join_profiles or not opts.profile) and ( + not opts.sign_profiles or opts.profile in opts.sign_profiles + ): + call_systemd_measure(uki, opts=opts) # UKI profiles @@ -1298,6 +1321,13 @@ def make_uki(opts: UkifyConfig) -> None: Section.create(n, pesection.get_data(length=pesection.Misc_VirtualSize), measure=True) ) + if opts.sign_profiles: + pesection = next(s for s in pe.sections if pe_strip_section_name(s.Name) == '.profile') + id = read_env_file(pesection.get_data(length=pesection.Misc_VirtualSize).decode()).get('ID') + if not id or id not in opts.sign_profiles: + print(f'Not signing expected PCR measurements for "{id}" profile') + continue + call_systemd_measure(uki, opts=opts, profile_start=prev_len) # UKI creation @@ -1807,6 +1837,14 @@ CONFIG_ITEMS = [ default=[], help='A PE binary containing an additional profile to add to the UKI', ), + ConfigItem( + '--sign-profile', + dest='sign_profiles', + metavar='ID', + action='append', + default=[], + help='Which profiles to sign expected PCR measurements for', + ), ConfigItem( '--efi-arch', metavar='ARCH',