diff options
author | Emanuele Giuseppe Esposito <eesposit@redhat.com> | 2023-05-04 11:48:47 -0400 |
---|---|---|
committer | Emanuele Giuseppe Esposito <eesposit@redhat.com> | 2023-05-10 09:18:27 -0400 |
commit | c1e8d1727b64cc38821140312c7c3348300d81a0 (patch) | |
tree | d450622294663899ef76cf464275ba8137f81236 /src/ukify | |
parent | e673c5c2d904d821719b2d21746ef91482acf8b4 (diff) | |
download | systemd-c1e8d1727b64cc38821140312c7c3348300d81a0.tar.gz |
ukify: support pesign as alternative to sbsign
sbsign is not available everywhere, for example RHEL does not have it.
Add pesign as alternative to it.
pesign will use options "--secureboot-certificate-name" (mandatory) and
"--secureboot-certificate-dir" (optional), while sbsign will use
"--secureboot-private-key" and "--secureboot-certificate".
By default, use sbsign. If no key/cert is provided or sbsign is not found,
try pesign.
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
Diffstat (limited to 'src/ukify')
-rwxr-xr-x | src/ukify/ukify.py | 154 |
1 files changed, 108 insertions, 46 deletions
diff --git a/src/ukify/ukify.py b/src/ukify/ukify.py index 9db84afdbb..d87670eb24 100755 --- a/src/ukify/ukify.py +++ b/src/ukify/ukify.py @@ -550,50 +550,93 @@ def pe_add_sections(uki: UKI, output: str): pe.write(output) +def signer_sign(cmd): + print('+', shell_join(cmd)) + subprocess.check_call(cmd) -def make_uki(opts): - # kernel payload signing +def find_sbsign(opts=None): + return find_tool('sbsign', opts=opts) - sbsign_tool = find_tool('sbsign', opts=opts) - sbsign_invocation = [ +def sbsign_sign(sbsign_tool, input_f, output_f, opts=None): + sign_invocation = [ sbsign_tool, '--key', opts.sb_key, '--cert', opts.sb_cert, + input_f, + '--output', output_f, ] - if opts.signing_engine is not None: - sbsign_invocation += ['--engine', opts.signing_engine] + sign_invocation += ['--engine', opts.signing_engine] + signer_sign(sign_invocation) + +def find_pesign(opts=None): + return find_tool('pesign', opts=opts) + +def pesign_sign(pesign_tool, input_f, output_f, opts=None): + sign_invocation = [ + pesign_tool, '-s', '--force', + '-n', opts.sb_certdir, + '-c', opts.sb_cert_name, + '-i', input_f, + '-o', output_f, + ] + signer_sign(sign_invocation) - sign_kernel = opts.sign_kernel - if sign_kernel is None and opts.linux is not None and opts.sb_key: - # figure out if we should sign the kernel - sbverify_tool = find_tool('sbverify', opts=opts) +SBVERIFY = { + 'name': 'sbverify', + 'option': '--list', + 'output': 'No signature table present', +} - cmd = [ - sbverify_tool, - '--list', - opts.linux, - ] +PESIGCHECK = { + 'name': 'pesign', + 'option': '-i', + 'output': 'No signatures found.', + 'flags': '-S' +} - print('+', shell_join(cmd)) - info = subprocess.check_output(cmd, text=True) +def verify(tool, opts): + verify_tool = find_tool(tool['name'], opts=opts) + cmd = [ + verify_tool, + tool['option'], + opts.linux, + ] + if 'flags' in tool: + cmd.append(tool['flags']) + + print('+', shell_join(cmd)) + info = subprocess.check_output(cmd, text=True) + + return tool['output'] in info + +def make_uki(opts): + # kernel payload signing + + sign_tool = None + if opts.signtool == 'sbsign': + sign_tool = find_sbsign(opts=opts) + sign = sbsign_sign + verify_tool = SBVERIFY + else: + sign_tool = find_pesign(opts=opts) + sign = pesign_sign + verify_tool = PESIGCHECK + + sign_args_present = opts.sb_key or opts.sb_cert_name - # sbverify has wonderful API - if 'No signature table present' in info: - sign_kernel = True + if sign_tool is None and sign_args_present: + raise ValueError(f'{opts.signtool}, required for signing, is not installed') + + sign_kernel = opts.sign_kernel + if sign_kernel is None and opts.linux is not None and sign_args_present: + # figure out if we should sign the kernel + sign_kernel = verify(verify_tool, opts) if sign_kernel: linux_signed = tempfile.NamedTemporaryFile(prefix='linux-signed') linux = linux_signed.name - - cmd = [ - *sbsign_invocation, - opts.linux, - '--output', linux, - ] - - print('+', shell_join(cmd)) - subprocess.check_call(cmd) + sign(sign_tool, opts.linux, linux, opts=opts) else: linux = opts.linux @@ -641,7 +684,7 @@ def make_uki(opts): if linux is not None: uki.add_section(Section.create('.linux', linux, measure=True)) - if opts.sb_key: + if sign_args_present: unsigned = tempfile.NamedTemporaryFile(prefix='uki') output = unsigned.name else: @@ -651,20 +694,14 @@ def make_uki(opts): # UKI signing - if opts.sb_key: - cmd = [ - *sbsign_invocation, - unsigned.name, - '--output', opts.output, - ] - print('+', shell_join(cmd)) - subprocess.check_call(cmd) + if sign_args_present: + sign(sign_tool, unsigned.name, opts.output, opts=opts) # We end up with no executable bits, let's reapply them os.umask(umask := os.umask(0)) os.chmod(opts.output, 0o777 & ~umask) - print(f"Wrote {'signed' if opts.sb_key else 'unsigned'} {opts.output}") + print(f"Wrote {'signed' if sign_args_present else 'unsigned'} {opts.output}") @dataclasses.dataclass(frozen=True) @@ -914,17 +951,38 @@ CONFIG_ITEMS = [ config_key = 'UKI/SigningEngine', ), ConfigItem( + '--signtool', + choices = ('sbsign', 'pesign'), + dest = 'signtool', + default = 'sbsign', + help = 'whether to use sbsign or pesign. Default is sbsign.', + config_key = 'UKI/SecureBootSigningTool', + ), + ConfigItem( '--secureboot-private-key', dest = 'sb_key', - help = 'path to key file or engine-specific designation for SB signing', + help = 'required by --signtool=sbsign. Path to key file or engine-specific designation for SB signing', config_key = 'UKI/SecureBootPrivateKey', ), ConfigItem( '--secureboot-certificate', dest = 'sb_cert', - help = 'path to certificate file or engine-specific designation for SB signing', + help = 'required by --signtool=sbsign. sbsign needs a path to certificate file or engine-specific designation for SB signing', config_key = 'UKI/SecureBootCertificate', ), + ConfigItem( + '--secureboot-certificate-dir', + dest = 'sb_certdir', + default = '/etc/pki/pesign', + help = 'required by --signtool=pesign. Path to nss certificate database directory for PE signing. Default is /etc/pki/pesign', + config_key = 'UKI/SecureBootCertificateDir', + ), + ConfigItem( + '--secureboot-certificate-name', + dest = 'sb_cert_name', + help = 'required by --signtool=pesign. pesign needs a certificate nickname of nss certificate database entry to use for PE signing', + config_key = 'UKI/SecureBootCertificateName', + ), ConfigItem( '--sign-kernel', @@ -1091,16 +1149,20 @@ def finalize_options(opts): if opts.sb_cert: opts.sb_cert = pathlib.Path(opts.sb_cert) - if bool(opts.sb_key) ^ bool(opts.sb_cert): - raise ValueError('--secureboot-private-key= and --secureboot-certificate= must be specified together') + if opts.signtool == 'sbsign': + if bool(opts.sb_key) ^ bool(opts.sb_cert): + raise ValueError('--secureboot-private-key= and --secureboot-certificate= must be specified together when using --signtool=sbsign') + else: + if not bool(opts.sb_cert_name): + raise ValueError('--certificate-name must be specified when using --signtool=pesign') - if opts.sign_kernel and not opts.sb_key: - raise ValueError('--sign-kernel requires --secureboot-private-key= and --secureboot-certificate= to be specified') + if opts.sign_kernel and not opts.sb_key and not opts.sb_cert_name: + raise ValueError('--sign-kernel requires either --secureboot-private-key= and --secureboot-certificate= (for sbsign) or --secureboot-certificate-name= (for pesign) to be specified') if opts.output is None: if opts.linux is None: raise ValueError('--output= must be specified when building a PE addon') - suffix = '.efi' if opts.sb_key else '.unsigned.efi' + suffix = '.efi' if opts.sb_key or opts.sb_cert_name else '.unsigned.efi' opts.output = opts.linux.name + suffix for section in opts.sections: |