mirror of
https://github.com/systemd/systemd.git
synced 2026-11-06 05:17:24 +00:00
keyring-setup: check certificate usage against the VOA purpose
The specification requires the extended key usage of a verifier to be aligned with its purpose. The kernel has no concept of this. So do it in systemd-keyring-setup. An artifact verifier whose extended key usage permits neither code signing nor any usage is rejected. A trust anchor that is explicitly not a CA is still enrolled and only logged at notice level because self-signed leaf certificates doubling as their own anchor are common. Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
1 parent
b98913f4b8
commit
fb6167ca1f
2 files changed
+58
-6
No files matched your search
@@ -113,8 +113,8 @@
|
||||
<filename><replaceable>OS</replaceable>/kernel-keyring/<replaceable>CONTEXT</replaceable>/x509/<replaceable>NAME</replaceable>-certificate.pem</filename>,
|
||||
and trust anchors from
|
||||
<filename><replaceable>OS</replaceable>/trust-anchor-kernel-keyring/<replaceable>CONTEXT</replaceable>/x509/<replaceable>NAME</replaceable>-certificate.pem</filename>
|
||||
below each load path. Both are enrolled into the same keyring. <replaceable>NAME</replaceable> may consist
|
||||
of lowercase letters, digits,
|
||||
below each load path. Both are enrolled into the same keyring, and only differ in how their usage is
|
||||
checked, see below. <replaceable>NAME</replaceable> may consist of lowercase letters, digits,
|
||||
<literal>.</literal>, <literal>_</literal> and <literal>-</literal>, other files are ignored. Each file must
|
||||
contain a single PEM-encoded certificate. A file that contains more than one certificate is ignored as a
|
||||
whole. CA certificates should be placed in separate files among the trust anchors instead.</para>
|
||||
@@ -152,6 +152,13 @@
|
||||
world-writable, and directories that are world-writable, are ignored. Once a directory owned by an
|
||||
unprivileged user has been entered, the lookup does not continue into files or directories owned by anyone
|
||||
else.</para>
|
||||
|
||||
<para>As required by the VOA specification, the usage of each certificate is checked against its purpose.
|
||||
An artifact verifier is ignored if it has an extended key usage extension that permits neither code signing
|
||||
nor any extended key usage, or a key usage extension that does not permit digital signatures. A trust anchor
|
||||
that is not a CA certificate is enrolled nonetheless, since self-signed certificates that act as their own
|
||||
trust anchor are common, but a message is logged about it. Certificates with invalid extensions are
|
||||
ignored.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
|
||||
@@ -21,7 +21,7 @@ use systemd_shared::recurse_dir::{self, RECURSE_DIR_IGNORE_DOT, RECURSE_DIR_SORT
|
||||
use systemd_shared::sys::COMMAND_EXPERIMENTAL;
|
||||
use systemd_shared::table::{Table, TABLE_ERSATZ_DASH};
|
||||
use systemd_shared::tmpfile::{LinkableTmpfile, LINK_TMPFILE_REPLACE};
|
||||
use systemd_shared::voa::{self, Lookup};
|
||||
use systemd_shared::voa::{self, Lookup, VoaMode};
|
||||
use systemd_shared::x509::{self, Der, X509};
|
||||
use systemd_shared::{creds, fd, json, sys};
|
||||
use systemd_shared::{libcrypto_note, libkmod_note, log_openssl_errors, table_log_add_error};
|
||||
@@ -227,6 +227,50 @@ impl Keyring {
|
||||
}
|
||||
}
|
||||
|
||||
/// As the VOA specification requires, usage of each certificate is checked against its purpose. An artifact
|
||||
/// verifier whose extended key usage permits neither code signing nor any usage is not enrolled. A trust
|
||||
/// anchor that is not a CA certificate is still enrolled and only logged about at notice level, since a
|
||||
/// self-signed leaf certificate doubling as its own anchor is common.
|
||||
fn check_usage(path: &CStr, x: &X509, mode: VoaMode) -> Result<()> {
|
||||
let flags = x.extension_flags();
|
||||
if flags & sys::EXFLAG_INVALID != 0 {
|
||||
return Err(log_warning_errno!(
|
||||
SYNTHETIC_ERRNO(EKEYREJECTED),
|
||||
"'{}' carries an invalid X.509 extension, ignoring.",
|
||||
display(path)
|
||||
));
|
||||
}
|
||||
|
||||
if mode == voa::VOA_MODE_ARTIFACT_VERIFIER {
|
||||
if x.extended_key_usage()
|
||||
.is_some_and(|u| u & (sys::XKU_CODE_SIGN | sys::XKU_ANYEKU) == 0)
|
||||
{
|
||||
return Err(log_warning_errno!(
|
||||
SYNTHETIC_ERRNO(EKEYREJECTED),
|
||||
"'{}' is not a code signing certificate, ignoring.",
|
||||
display(path)
|
||||
));
|
||||
}
|
||||
if x.key_usage().is_some_and(|u| u & sys::KU_DIGITAL_SIGNATURE == 0) {
|
||||
return Err(log_warning_errno!(
|
||||
SYNTHETIC_ERRNO(EKEYREJECTED),
|
||||
"'{}' does not permit digital signatures, ignoring.",
|
||||
display(path)
|
||||
));
|
||||
}
|
||||
} else if (flags & sys::EXFLAG_BCONS != 0 && flags & sys::EXFLAG_CA == 0)
|
||||
|| x.key_usage().is_some_and(|u| u & sys::KU_KEY_CERT_SIGN == 0)
|
||||
{
|
||||
// A trust anchor that is explicitly not a CA is common enough to only log a notice about
|
||||
log_notice!(
|
||||
"'{}' is not a CA certificate, but placed among the trust anchors.",
|
||||
display(path)
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The kernel describes a certificate as "<subject>: <hex>", with the subject key identifier or, lacking one,
|
||||
/// the serial number as hex. Returns the ": <hex>" part, separator included.
|
||||
fn certificate_description_suffix(x: &X509) -> Result<OwnedCStr> {
|
||||
@@ -265,7 +309,7 @@ impl core::fmt::Display for Hex<'_> {
|
||||
}
|
||||
|
||||
/// Returns true if the certificate was added, false if the same certificate was found before.
|
||||
fn load_certificate(k: &mut Keyring, f: &ConfFile) -> Result<bool> {
|
||||
fn load_certificate(k: &mut Keyring, f: &ConfFile, mode: VoaMode) -> Result<bool> {
|
||||
let path = f.original_path();
|
||||
|
||||
let text = f
|
||||
@@ -302,7 +346,8 @@ fn load_certificate(k: &mut Keyring, f: &ConfFile) -> Result<bool> {
|
||||
));
|
||||
}
|
||||
|
||||
// The kernel takes DER
|
||||
check_usage(path, &x, mode)?;
|
||||
|
||||
let Some(der) = x.to_der() else {
|
||||
return Err(log_openssl_errors!(
|
||||
LOG_WARNING,
|
||||
@@ -370,7 +415,7 @@ fn collect_certificates(k: &mut Keyring, root: BorrowedFd<'_>, os: &Strv) -> Res
|
||||
})?;
|
||||
|
||||
for f in files.iter() {
|
||||
match load_certificate(k, f) {
|
||||
match load_certificate(k, f, mode) {
|
||||
Err(e @ Errno::ENOMEM) => return Err(e),
|
||||
Err(_) => k.data_error = true,
|
||||
Ok(_) => {}
|
||||
|
||||
Reference in new issue
Block a user