dissect-image: Various fixes w.r.t verity signature checking (#44386)

This commit is contained in:
Daan De Meyer authored and GitHub committed 2026-10-26 13:57:58 +01:00
commit 80f70d2af6
3 files changed
+57 -27

No files matched your search

+24 -12
View File
@@ -1062,7 +1062,7 @@ static int dissect_image_from_unpartitioned(
m->verity_ready = verity_settings_data_covers(verity, PARTITION_ROOT);
m->has_verity_sig = false; /* signature not embedded, must be specified */
m->verity_sig_ready = m->verity_ready && iovec_is_set(&verity->root_hash);
m->verity_sig_ready = m->verity_ready && iovec_is_set(&verity->root_hash_sig);
if (uuid)
m->image_uuid = *uuid;
@@ -3549,13 +3549,11 @@ static int do_crypt_activate_verity(
return r < 0 ? r : -EDESTADDRREQ;
log_debug("Activation of signed Verity volume without validating signature is permitted by policy. Continuing.");
} else
measure_signature = false;
} else {
log_debug("Verity activation via userspace signature logic worked, activating by root hash.");
/* Otherwise let's see what signature-less activation results in. */
measure_signature = true;
measure_signature = true;
}
} else if (!FLAGS_SET(policy_flags, PARTITION_POLICY_VERITY))
return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL),
"No-signature activation of Verity volume not allowed by policy, refusing.");
@@ -5487,7 +5485,6 @@ int mountfsd_mount_image_fd(
_cleanup_(dissected_image_unrefp) DissectedImage *di = NULL;
_cleanup_close_ int verity_data_fd = -EBADF;
_cleanup_free_ char *ps = NULL;
const char *error_id;
int r;
assert(image_fd >= 0);
@@ -5567,7 +5564,8 @@ int mountfsd_mount_image_fd(
}
sd_json_variant *reply = NULL;
r = varlink_callbo_and_log(
const char *error_id = NULL;
r = sd_varlink_callbo(
vl,
"io.systemd.MountFileSystem.MountImage",
&reply,
@@ -5585,7 +5583,23 @@ int mountfsd_mount_image_fd(
SD_JSON_BUILD_PAIR_CONDITION(verity && iovec_is_set(&verity->root_hash_sig), "verityRootHashSignature", JSON_BUILD_IOVEC_BASE64(&verity->root_hash_sig)),
SD_JSON_BUILD_PAIR_BOOLEAN("allowInteractiveAuthentication", FLAGS_SET(flags, DISSECT_IMAGE_ALLOW_INTERACTIVE_AUTH)));
if (r < 0)
return r;
return log_error_errno(r, "Failed to issue MountImage() varlink call: %m");
if (error_id) {
/* Return the same errors as dissected_image_decrypt(), so that callers can report them the
* same way, whether they mount the image themselves or through mountfsd. */
if (streq(error_id, "io.systemd.MountFileSystem.KeyNotFound"))
return log_error_errno(SYNTHETIC_ERRNO(EDESTADDRREQ),
"mountfsd refused the image, because no trusted key validates its signature.");
if (streq(error_id, "io.systemd.MountFileSystem.DeniedByImagePolicy"))
return log_error_errno(SYNTHETIC_ERRNO(ERFKILL),
"mountfsd refused the image, because the image policy doesn't permit it.");
r = sd_varlink_error_to_errno(error_id, reply);
if (r != -EBADR)
return log_error_errno(r, "Failed to issue MountImage() varlink call: %m");
return log_error_errno(r, "Failed to issue MountImage() varlink call: %s", error_id);
}
r = sd_json_dispatch(reply, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
if (r < 0)
@@ -5741,12 +5755,10 @@ int mountfsd_mount_directory_fd(
}
sd_json_variant *reply = NULL;
const char *error_id = NULL;
r = varlink_callbo_and_log(
vl,
"io.systemd.MountFileSystem.MountDirectory",
&reply,
&error_id,
SD_JSON_BUILD_PAIR_UNSIGNED("directoryFileDescriptor", 0),
SD_JSON_BUILD_PAIR_CONDITION(userns_fd >= 0, "userNamespaceFileDescriptor", SD_JSON_BUILD_UNSIGNED(1)),
SD_JSON_BUILD_PAIR_BOOLEAN("readOnly", FLAGS_SET(flags, DISSECT_IMAGE_MOUNT_READ_ONLY)),
+2 -2
View File
@@ -75,7 +75,7 @@ int pcrextend_pcr_now(unsigned pcr, const char *word, const struct iovec *secret
log_debug("Measurement of '%s' into PCR %u completed.", word, pcr);
return 1;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring.");
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring.");
#endif
}
@@ -116,7 +116,7 @@ int pcrextend_nvpcr_now(const char *nvpcr, const char *word, const char *event)
log_debug("Measurement of '%s' into NvPCR '%s' completed.", word, nvpcr);
return 1;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring.");
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring.");
#endif
}
+31 -13
View File
@@ -26,7 +26,7 @@ at_exit() {
mv -f /run/systemd/tpm2-pcr-signature.json.bak /run/systemd/tpm2-pcr-signature.json
rm -f /tmp/tpm2-pcr-private-key.pem /tmp/tpm2-pcr-public-key.pem /tmp/tpm2-pcr-signature.json.valid
rm -rf /run/nvpcr /tmp/nvpcr
rm -f /var/tmp/nvpcr.raw /run/verity.d/test-70-nvpcr.crt
rm -f /var/tmp/nvpcr.raw /var/tmp/nvpcr-untrusted.raw /run/verity.d/test-70-nvpcr.crt
rm -f /run/systemd/nvpcr/test.auth /run/systemd/nvpcr/test2.auth /run/systemd/nvpcr/aaa.auth /run/systemd/nvpcr/zzz.auth /run/systemd/nvpcr/unauth.auth /run/systemd/nvpcr/unauth2.auth
rm -f /tmp/test.policy
rm -f /run/systemd/tpm2-nv-space-exhausted-orderly
@@ -368,17 +368,9 @@ fi
mkdir -p /tmp/nvpcr/tree
touch /tmp/nvpcr/tree/file
if machine_supports_verity_keyring; then
SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
systemd-repart -P \
-s /tmp/nvpcr/tree \
--certificate=/usr/share/mkosi.crt \
--private-key=/usr/share/mkosi.key \
/var/tmp/nvpcr.raw
else
OPENSSL_CONFIG="/tmp/nvpcr/opensslconfig"
# Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
cat >"${OPENSSL_CONFIG:?}" <<EOF
OPENSSL_CONFIG="/tmp/nvpcr/opensslconfig"
# Unfortunately OpenSSL insists on reading some config file, hence provide one with mostly placeholder contents
cat >"${OPENSSL_CONFIG:?}" <<EOF
[ req ]
prompt = no
distinguished_name = req_distinguished_name
@@ -393,6 +385,14 @@ CN = Common Name
emailAddress = test@email.com
EOF
if machine_supports_verity_keyring; then
SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
systemd-repart -P \
-s /tmp/nvpcr/tree \
--certificate=/usr/share/mkosi.crt \
--private-key=/usr/share/mkosi.key \
/var/tmp/nvpcr.raw
else
openssl req -config "$OPENSSL_CONFIG" -subj="/CN=waldo" \
-x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
-keyout /tmp/nvpcr/test-70-nvpcr.key -out /tmp/nvpcr/test-70-nvpcr.crt
@@ -412,5 +412,23 @@ cp /run/log/systemd/tpm2-measure.log /tmp/nvpcr/log-before
systemd-dissect --image-policy='root=signed:=absent+unused' --mtree /var/tmp/nvpcr.raw
# The record of a validated signature ends with the serial and the issuer of the signer.
set +o pipefail
diff /tmp/nvpcr/log-before /run/log/systemd/tpm2-measure.log | grep -F '"content":{"nvIndexName":"verity","string":"verity:'
diff /tmp/nvpcr/log-before /run/log/systemd/tpm2-measure.log | grep -E '"content":\{"nvIndexName":"verity","string":"verity:[^"]*:[0-9a-f]+:[0-9a-f]+/[^"]+"'
# If neither the kernel nor userspace can validate the signature, only the root hash is measured, without
# the signer. Neither the kernel keyring nor /run/verity.d/ contains the certificate of the untrusted key.
openssl req -config "$OPENSSL_CONFIG" -subj="/CN=untrusted" \
-x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
-keyout /tmp/nvpcr/untrusted.key -out /tmp/nvpcr/untrusted.crt
SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
systemd-repart -P \
-s /tmp/nvpcr/tree \
--certificate=/tmp/nvpcr/untrusted.crt \
--private-key=/tmp/nvpcr/untrusted.key \
/var/tmp/nvpcr-untrusted.raw
cp /run/log/systemd/tpm2-measure.log /tmp/nvpcr/log-before
systemd-dissect --image-policy='root=verity+signed:=absent+unused' --mtree /var/tmp/nvpcr-untrusted.raw
diff /tmp/nvpcr/log-before /run/log/systemd/tpm2-measure.log | grep -E '"content":\{"nvIndexName":"verity","string":"verity:[^"]*:"'