mirror of
https://github.com/systemd/systemd.git
synced 2025-01-10 05:18:17 +03:00
ukify: Introduce --certificate-provider= option
This translates to --certificate-source=provider:<provider> for signing tools invoked by ukify.
This commit is contained in:
parent
c4bc0fd6de
commit
64cc7ba517
@ -527,6 +527,17 @@
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>CertificateProvider=<replaceable>PROVIDER</replaceable></varname></term>
|
||||
<term><option>--certificate-provider=<replaceable>PROVIDER</replaceable></option></term>
|
||||
|
||||
<listitem><para>An OpenSSL provider to be used for loading the certificate used to sign the
|
||||
resulting binary and PCR measurements. This option can only be used when using
|
||||
<command>systemd-sbsign</command> as the signing tool.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v257"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>SignKernel=<replaceable>BOOL</replaceable></varname></term>
|
||||
<term><option>--sign-kernel</option></term>
|
||||
|
@ -138,7 +138,7 @@ def test_apply_config(tmp_path):
|
||||
|
||||
assert ns._groups == ['NAME']
|
||||
assert ns.pcr_private_keys == ['some/path7']
|
||||
assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
|
||||
assert ns.pcr_public_keys == ['some/path8']
|
||||
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
|
||||
|
||||
ukify.finalize_options(ns)
|
||||
@ -156,12 +156,12 @@ def test_apply_config(tmp_path):
|
||||
assert ns.pcr_banks == ['sha512', 'sha1']
|
||||
assert ns.signing_engine == 'engine1'
|
||||
assert ns.sb_key == 'some/path5'
|
||||
assert ns.sb_cert == 'some/path6'
|
||||
assert ns.sb_cert == pathlib.Path('some/path6')
|
||||
assert ns.sign_kernel is False
|
||||
|
||||
assert ns._groups == ['NAME']
|
||||
assert ns.pcr_private_keys == ['some/path7']
|
||||
assert ns.pcr_public_keys == [pathlib.Path('some/path8')]
|
||||
assert ns.pcr_public_keys == ['some/path8']
|
||||
assert ns.phase_path_groups == [['enter-initrd:leave-initrd:sysinit:ready:shutdown:final']]
|
||||
|
||||
def test_parse_args_minimal():
|
||||
@ -207,11 +207,11 @@ def test_parse_args_many_deprecated():
|
||||
assert opts.uname == '1.2.3'
|
||||
assert opts.stub == pathlib.Path('STUBPATH')
|
||||
assert opts.pcr_private_keys == ['PKEY1']
|
||||
assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
|
||||
assert opts.pcr_public_keys == ['PKEY2']
|
||||
assert opts.pcr_banks == ['SHA1', 'SHA256']
|
||||
assert opts.signing_engine == 'ENGINE'
|
||||
assert opts.sb_key == 'SBKEY'
|
||||
assert opts.sb_cert == 'SBCERT'
|
||||
assert opts.sb_cert == pathlib.Path('SBCERT')
|
||||
assert opts.sign_kernel is False
|
||||
assert opts.tools == [pathlib.Path('TOOLZ/')]
|
||||
assert opts.output == pathlib.Path('OUTPUT')
|
||||
@ -253,11 +253,11 @@ def test_parse_args_many():
|
||||
assert opts.uname == '1.2.3'
|
||||
assert opts.stub == pathlib.Path('STUBPATH')
|
||||
assert opts.pcr_private_keys == ['PKEY1']
|
||||
assert opts.pcr_public_keys == [pathlib.Path('PKEY2')]
|
||||
assert opts.pcr_public_keys == ['PKEY2']
|
||||
assert opts.pcr_banks == ['SHA1', 'SHA256']
|
||||
assert opts.signing_engine == 'ENGINE'
|
||||
assert opts.sb_key == 'SBKEY'
|
||||
assert opts.sb_cert == 'SBCERT'
|
||||
assert opts.sb_cert == pathlib.Path('SBCERT')
|
||||
assert opts.sign_kernel is False
|
||||
assert opts.tools == [pathlib.Path('TOOLZ/')]
|
||||
assert opts.output == pathlib.Path('OUTPUT')
|
||||
@ -360,13 +360,12 @@ def test_config_priority(tmp_path):
|
||||
assert opts.uname == '1.2.3'
|
||||
assert opts.stub == pathlib.Path('STUBPATH')
|
||||
assert opts.pcr_private_keys == ['PKEY1', 'some/path7']
|
||||
assert opts.pcr_public_keys == [pathlib.Path('PKEY2'),
|
||||
pathlib.Path('some/path8')]
|
||||
assert opts.pcr_public_keys == ['PKEY2', 'some/path8']
|
||||
assert opts.pcr_banks == ['SHA1', 'SHA256']
|
||||
assert opts.signing_engine == 'ENGINE'
|
||||
assert opts.signtool == ukify.SbSign # from args
|
||||
assert opts.sb_key == 'SBKEY' # from args
|
||||
assert opts.sb_cert == 'SBCERT' # from args
|
||||
assert opts.sb_cert == pathlib.Path('SBCERT') # from args
|
||||
assert opts.sb_certdir == 'some/path5' # from config
|
||||
assert opts.sb_cert_name == 'some/name1' # from config
|
||||
assert opts.sign_kernel is False
|
||||
|
@ -249,21 +249,22 @@ class UkifyConfig:
|
||||
output: Optional[str]
|
||||
pcr_banks: list[str]
|
||||
pcr_private_keys: list[str]
|
||||
pcr_public_keys: list[Path]
|
||||
pcr_public_keys: list[str]
|
||||
pcrpkey: Optional[Path]
|
||||
phase_path_groups: Optional[list[str]]
|
||||
profile: Union[str, Path, None]
|
||||
sb_cert: Path
|
||||
sb_cert: Union[str, Path, None]
|
||||
sb_cert_name: Optional[str]
|
||||
sb_cert_validity: int
|
||||
sb_certdir: Path
|
||||
sb_key: Optional[Path]
|
||||
sb_key: Union[str, Path, None]
|
||||
sbat: Optional[list[str]]
|
||||
sections: list['Section']
|
||||
sections_by_name: dict[str, 'Section']
|
||||
sign_kernel: bool
|
||||
signing_engine: Optional[str]
|
||||
signing_provider: Optional[str]
|
||||
certificate_provider: Optional[str]
|
||||
signtool: Optional[type['SignTool']]
|
||||
splash: Optional[Path]
|
||||
stub: Path
|
||||
@ -554,6 +555,11 @@ class SystemdSbSign(SignTool):
|
||||
if opts.signing_provider is not None
|
||||
else []
|
||||
),
|
||||
*(
|
||||
['--certificate-source', f'provider:{opts.certificate_provider}']
|
||||
if opts.certificate_provider is not None
|
||||
else []
|
||||
),
|
||||
input_f,
|
||||
'--output', output_f,
|
||||
] # fmt: skip
|
||||
@ -666,7 +672,7 @@ def combine_signatures(pcrsigs: list[dict[str, str]]) -> str:
|
||||
return json.dumps(combined)
|
||||
|
||||
|
||||
def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[Path], Optional[str]]]:
|
||||
def key_path_groups(opts: UkifyConfig) -> Iterator[tuple[str, Optional[str], Optional[str]]]:
|
||||
if not opts.pcr_private_keys:
|
||||
return
|
||||
|
||||
@ -757,6 +763,10 @@ def call_systemd_measure(uki: UKI, opts: UkifyConfig, profile_start: int = 0) ->
|
||||
extra += [f'--certificate={pub_key}']
|
||||
elif pub_key:
|
||||
extra += [f'--public-key={pub_key}']
|
||||
|
||||
if opts.certificate_provider is not None:
|
||||
extra += [f'--certificate-source=provider:{opts.certificate_provider}']
|
||||
|
||||
extra += [f'--phase={phase_path}' for phase_path in group or ()]
|
||||
|
||||
print('+', shell_join(cmd + extra)) # type: ignore
|
||||
@ -1007,34 +1017,30 @@ def make_uki(opts: UkifyConfig) -> None:
|
||||
|
||||
pcrpkey: Union[bytes, Path, None] = opts.pcrpkey
|
||||
if pcrpkey is None:
|
||||
measure_tool = find_tool('systemd-measure', '/usr/lib/systemd/systemd-measure')
|
||||
cmd = [measure_tool, 'pcrpkey']
|
||||
|
||||
if opts.pcr_public_keys and len(opts.pcr_public_keys) == 1:
|
||||
pcrpkey = opts.pcr_public_keys[0]
|
||||
# If we are getting a certificate when using an engine or provider, we need to convert it to
|
||||
# public key format.
|
||||
if (opts.signing_engine or opts.signing_provider) and Path(pcrpkey).exists():
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.x509 import load_pem_x509_certificate
|
||||
# If we're using an engine or provider, the public key will be an X.509 certificate.
|
||||
if opts.signing_engine or opts.signing_provider:
|
||||
cmd += ['--certificate', opts.pcr_public_keys[0]]
|
||||
if opts.certificate_provider:
|
||||
cmd += ['--certificate-source', f'provider:{opts.certificate_provider}']
|
||||
else:
|
||||
cmd += ['--public-key', opts.pcr_public_keys[0]]
|
||||
|
||||
try:
|
||||
cert = load_pem_x509_certificate(Path(pcrpkey).read_bytes())
|
||||
except ValueError:
|
||||
raise ValueError(f'{pcrpkey} must be an X.509 certificate when signing with an engine')
|
||||
else:
|
||||
pcrpkey = cert.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
print('+', shell_join(cmd))
|
||||
pcrpkey = subprocess.check_output(cmd)
|
||||
elif opts.pcr_private_keys and len(opts.pcr_private_keys) == 1:
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
cmd += ['--private-key', Path(opts.pcr_private_keys[0])]
|
||||
|
||||
privkey = serialization.load_pem_private_key(
|
||||
Path(opts.pcr_private_keys[0]).read_bytes(),
|
||||
password=None,
|
||||
)
|
||||
pcrpkey = privkey.public_key().public_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PublicFormat.SubjectPublicKeyInfo,
|
||||
)
|
||||
if opts.signing_engine:
|
||||
cmd += ['--private-key-source', f'engine:{opts.signing_engine}']
|
||||
if opts.signing_provider:
|
||||
cmd += ['--private-key-source', f'provider:{opts.signing_provider}']
|
||||
|
||||
print('+', shell_join(cmd))
|
||||
pcrpkey = subprocess.check_output(cmd)
|
||||
|
||||
sections = [
|
||||
# name, content, measure?
|
||||
@ -1270,9 +1276,9 @@ def generate_keys(opts: UkifyConfig) -> None:
|
||||
)
|
||||
print(f'Writing SecureBoot private key to {opts.sb_key}')
|
||||
with temporary_umask(0o077):
|
||||
opts.sb_key.write_bytes(key_pem)
|
||||
Path(opts.sb_key).write_bytes(key_pem)
|
||||
print(f'Writing SecureBoot certificate to {opts.sb_cert}')
|
||||
opts.sb_cert.write_bytes(cert_pem)
|
||||
Path(opts.sb_cert).write_bytes(cert_pem)
|
||||
|
||||
work = True
|
||||
|
||||
@ -1284,7 +1290,7 @@ def generate_keys(opts: UkifyConfig) -> None:
|
||||
Path(priv_key).write_bytes(priv_key_pem)
|
||||
if pub_key:
|
||||
print(f'Writing public key for PCR signing to {pub_key}')
|
||||
pub_key.write_bytes(pub_key_pem)
|
||||
Path(pub_key).write_bytes(pub_key_pem)
|
||||
|
||||
work = True
|
||||
|
||||
@ -1674,6 +1680,12 @@ CONFIG_ITEMS = [
|
||||
help='OpenSSL provider to use for signing',
|
||||
config_key='UKI/SigningProvider',
|
||||
),
|
||||
ConfigItem(
|
||||
'--certificate-provider',
|
||||
metavar='PROVIDER',
|
||||
help='OpenSSL provider to load certificate from',
|
||||
config_key='UKI/CertificateProvider',
|
||||
),
|
||||
ConfigItem(
|
||||
'--signtool',
|
||||
choices=('sbsign', 'pesign', 'systemd-sbsign'),
|
||||
@ -1746,7 +1758,6 @@ CONFIG_ITEMS = [
|
||||
'--pcr-public-key',
|
||||
dest='pcr_public_keys',
|
||||
metavar='PATH',
|
||||
type=Path,
|
||||
action='append',
|
||||
help='public part of the keypair or engine/provider designation for signing PCR signatures',
|
||||
config_key='PCRSignature:/PCRPublicKey',
|
||||
@ -1982,11 +1993,11 @@ def finalize_options(opts: argparse.Namespace) -> None:
|
||||
if opts.signing_engine and opts.signing_provider:
|
||||
raise ValueError('Only one of --signing-engine= and --signing-provider= may be specified')
|
||||
|
||||
if opts.signing_engine is None and opts.signing_provider is None:
|
||||
if opts.sb_key:
|
||||
opts.sb_key = Path(opts.sb_key)
|
||||
if opts.sb_cert:
|
||||
opts.sb_cert = Path(opts.sb_cert)
|
||||
if opts.signing_engine is None and opts.signing_provider is None and opts.sb_key:
|
||||
opts.sb_key = Path(opts.sb_key)
|
||||
|
||||
if opts.certificate_provider is None and opts.sb_cert:
|
||||
opts.sb_cert = Path(opts.sb_cert)
|
||||
|
||||
if bool(opts.sb_key) ^ bool(opts.sb_cert):
|
||||
# one param only given, sbsign needs both
|
||||
@ -2012,6 +2023,9 @@ def finalize_options(opts: argparse.Namespace) -> None:
|
||||
if opts.signing_provider and opts.signtool != SystemdSbSign:
|
||||
raise ValueError('--signing-provider= can only be used with--signtool=systemd-sbsign')
|
||||
|
||||
if opts.certificate_provider and opts.signtool != SystemdSbSign:
|
||||
raise ValueError('--certificate-provider= can only be used with--signtool=systemd-sbsign')
|
||||
|
||||
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' # noqa: E501
|
||||
|
Loading…
Reference in New Issue
Block a user