mirror of
https://github.com/systemd/systemd
synced 2026-03-07 13:44:46 +01:00
Compare commits
16 Commits
450e0dce02
...
cad60201fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cad60201fc | ||
|
|
10eaca4159 | ||
|
|
d8da141348 | ||
|
|
02cab70acf | ||
|
|
a3af68b84d | ||
|
|
3821e4d8fb | ||
|
|
521a523ce0 | ||
|
|
a450fab9c6 | ||
|
|
85d7fb2247 | ||
|
|
32f405074a | ||
|
|
71ca7532de | ||
|
|
3f31c8ff46 | ||
|
|
8485bba53d | ||
|
|
0a8f393c64 | ||
|
|
be36f69c90 | ||
|
|
d84d17853e |
2
README
2
README
@ -204,6 +204,8 @@ REQUIREMENTS:
|
|||||||
CONFIG_MEMCG
|
CONFIG_MEMCG
|
||||||
|
|
||||||
glibc >= 2.34
|
glibc >= 2.34
|
||||||
|
musl >= 1.2.5 with fde29c04adbab9d5b081bf6717b5458188647f1c
|
||||||
|
(required when building systemd with -Dlibc=musl)
|
||||||
libxcrypt >= 4.4.0 (optional)
|
libxcrypt >= 4.4.0 (optional)
|
||||||
libmount >= 2.30 (from util-linux)
|
libmount >= 2.30 (from util-linux)
|
||||||
(util-linux *must* be built without --enable-libmount-support-mtab)
|
(util-linux *must* be built without --enable-libmount-support-mtab)
|
||||||
|
|||||||
22
TODO
22
TODO
@ -720,10 +720,6 @@ Features:
|
|||||||
deleting entries for rotation, place an event that declares how many items
|
deleting entries for rotation, place an event that declares how many items
|
||||||
have been dropped, and what the hash before and after that.
|
have been dropped, and what the hash before and after that.
|
||||||
|
|
||||||
* measure information about all DDIs as we activate them to an NvPCR. We
|
|
||||||
probably should measure the dm-verity root hash from the kernel side, but
|
|
||||||
DDI meta info from userspace.
|
|
||||||
|
|
||||||
* use name_to_handle_at() with AT_HANDLE_FID instead of .st_ino (inode
|
* use name_to_handle_at() with AT_HANDLE_FID instead of .st_ino (inode
|
||||||
number) for identifying inodes, for example in copy.c when finding hard
|
number) for identifying inodes, for example in copy.c when finding hard
|
||||||
links, or loop-util.c for tracking backing files, and other places.
|
links, or loop-util.c for tracking backing files, and other places.
|
||||||
@ -1299,9 +1295,9 @@ Features:
|
|||||||
- If run on every boot, should it use the sysupdate config from the host on
|
- If run on every boot, should it use the sysupdate config from the host on
|
||||||
subsequent boots?
|
subsequent boots?
|
||||||
|
|
||||||
* To mimic the new tpm2-measure-pcr= crypttab option add the same to veritytab
|
* To mimic the new tpm2-measure-pcr= crypttab option and tpm2-measure-nvpcr=
|
||||||
(measuring the root hash) and integritytab (measuring the HMAC key if one is
|
veritytab option, add the same to integritytab (measuring the HMAC key if one
|
||||||
used)
|
is used)
|
||||||
|
|
||||||
* We should start measuring all services, containers, and system extensions we
|
* We should start measuring all services, containers, and system extensions we
|
||||||
activate. probably into PCR 13. i.e. add --tpm2-measure-pcr= or so to
|
activate. probably into PCR 13. i.e. add --tpm2-measure-pcr= or so to
|
||||||
@ -1720,18 +1716,6 @@ Features:
|
|||||||
keys of /etc/crypttab. That way people can store/provide the roothash
|
keys of /etc/crypttab. That way people can store/provide the roothash
|
||||||
externally and provide to us on demand only.
|
externally and provide to us on demand only.
|
||||||
|
|
||||||
* we probably should extend the root verity hash of the root fs into some PCR
|
|
||||||
on boot. (i.e. maybe add a veritytab option tpm2-measure=12 or so to measure
|
|
||||||
it into PCR 12); Similar: we probably should extend the LUKS volume key of
|
|
||||||
the root fs into some PCR on boot. (i.e. maybe add a crypttab option
|
|
||||||
tpm2-measure=15 or so to measure it into PCR 15); once both are in place
|
|
||||||
update gpt-auto-discovery to generate these by default for the partitions it
|
|
||||||
discovers. Static vendor stuff should probably end up in PCR 12 (i.e. the
|
|
||||||
verity hash), with local keys in PCR 15 (i.e. the encryption volume
|
|
||||||
key). That way, we nicely distinguish resources supplied by the OS vendor
|
|
||||||
(i.e. sysext, root verity) from those inherently local (i.e. encryption key),
|
|
||||||
which is useful if they shall be signed separately.
|
|
||||||
|
|
||||||
* rework recursive read-only remount to use new mount API
|
* rework recursive read-only remount to use new mount API
|
||||||
|
|
||||||
* when mounting disk images: if IMAGE_ID/IMAGE_VERSION is set in os-release
|
* when mounting disk images: if IMAGE_ID/IMAGE_VERSION is set in os-release
|
||||||
|
|||||||
@ -945,12 +945,12 @@
|
|||||||
<term><option>tpm2-measure-keyslot-nvpcr=</option></term>
|
<term><option>tpm2-measure-keyslot-nvpcr=</option></term>
|
||||||
|
|
||||||
<listitem><para>Controls whether to measure information about the used LUKS unlock keyslot to a TPM2
|
<listitem><para>Controls whether to measure information about the used LUKS unlock keyslot to a TPM2
|
||||||
non-volatile index (nvindex in PCR mode). If set to to an empty string (which is the default) no TPM2
|
non-volatile index (nvindex in PCR mode). Takes a boolean argument, or an NvPCR name. If set to false
|
||||||
nvindex extension is done, otherwise keyslot information is measured to an nvindex of the specified
|
or an empty string (which is the default) no TPM2 nvindex extension is done, otherwise keyslot
|
||||||
name, which is allocated if needed. It is recommended to set this to <literal>cryptsetup</literal> to
|
information is measured to an nvindex of the specified name, which is allocated if needed. If set to
|
||||||
enable this logic. The slot index and the used unlock mechanism (i.e. <literal>tpm2</literal>,
|
true the recommended default of <literal>cryptsetup</literal> is selected as NvPCR. The slot index
|
||||||
<literal>fido2</literal>, <literal>pkcs11</literal>) is measured along with the activated volume name
|
and the used unlock mechanism (i.e. <literal>tpm2</literal>, <literal>fido2</literal>,
|
||||||
and its UUID.</para>
|
<literal>pkcs11</literal>) are measured along with the activated volume name and its UUID.</para>
|
||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|||||||
@ -272,9 +272,13 @@
|
|||||||
<para>If the system was booted via
|
<para>If the system was booted via
|
||||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> and the
|
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> and the
|
||||||
stub reported to userspace that the kernel image was measured to a TPM2 PCR, then any discovered root and
|
stub reported to userspace that the kernel image was measured to a TPM2 PCR, then any discovered root and
|
||||||
<filename>/var/</filename> volume identifiers (and volume encryption key in case it is encrypted) will be
|
<filename>/var/</filename> volume identifiers (and volume encryption keys, in case they are encrypted)
|
||||||
automatically measured into PCR 15 on activation, via
|
will be automatically measured into PCR 15 on activation, via
|
||||||
<citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
|
<citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>. Moreover,
|
||||||
|
information about the LUKS key slot used to unlock the volume is measured into NvPCR
|
||||||
|
<literal>cryptsetup</literal>. Finally, if the root or <filename>/usr/</filename> partition is protected
|
||||||
|
via Verity its root hash and the serial/issuer of the key used for the provided root hash signature (if
|
||||||
|
any) are measured into the NvPCR <literal>verity</literal>.</para>
|
||||||
|
|
||||||
<para>Mount constraint metadata contained in the file systems is validated by pulling in
|
<para>Mount constraint metadata contained in the file systems is validated by pulling in
|
||||||
<citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>systemd-validatefs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||||
|
|||||||
@ -298,6 +298,18 @@ This is based on crypttab(5).
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>tpm2-measure-nvpcr=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Takes a boolean argument, or an NvPCR name as argument. May be used to enable
|
||||||
|
automatic measurement of the volume name and Verity root hash, as well as the serials and issuers of
|
||||||
|
the certificates used to generate the provided signatures (if any) will be measured. Passing false
|
||||||
|
disables the mechanism. Passing true enables measurement into the <literal>verity</literal> NvPCR. If
|
||||||
|
any other string is specified this selects the NvPCR to measure into by name.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<para>At early boot and when the system manager configuration is
|
<para>At early boot and when the system manager configuration is
|
||||||
|
|||||||
@ -6,7 +6,11 @@ KERNEL!="event*", GOTO="joystick_end"
|
|||||||
|
|
||||||
# joystick:<bustype>:v<vid>p<pid>:name:<name>:*
|
# joystick:<bustype>:v<vid>p<pid>:name:<name>:*
|
||||||
KERNELS=="input*", ENV{ID_BUS}!="", \
|
KERNELS=="input*", ENV{ID_BUS}!="", \
|
||||||
IMPORT{builtin}="hwdb 'joystick:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
|
IMPORT{builtin}="hwdb 'joystick:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'"
|
||||||
GOTO="joystick_end"
|
|
||||||
|
# Spread the hwdb override to ID_INTEGRATION, in the future we could remove the
|
||||||
|
# joystick hwdb entirely or retain it using the generic ID_INTEGRATION instead
|
||||||
|
# specific ID_INPUT_JOYSTICK_INTEGRATION.
|
||||||
|
ENV{ID_INPUT_JOYSTICK_INTEGRATION}!="", ENV{ID_INTEGRATION}="$env{ID_INPUT_JOYSTICK_INTEGRATION}"
|
||||||
|
|
||||||
LABEL="joystick_end"
|
LABEL="joystick_end"
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
# do not edit this file, it will be overwritten on update
|
# do not edit this file, it will be overwritten on update
|
||||||
|
|
||||||
ACTION=="remove", GOTO="touchpad_end"
|
ACTION=="remove", GOTO="touchpad_end"
|
||||||
ENV{ID_INPUT}=="", GOTO="touchpad_end"
|
|
||||||
ENV{ID_INPUT_TOUCHPAD}=="", GOTO="touchpad_end"
|
ENV{ID_INPUT_TOUCHPAD}=="", GOTO="touchpad_end"
|
||||||
KERNEL!="event*", GOTO="touchpad_end"
|
KERNEL!="event*", GOTO="touchpad_end"
|
||||||
|
|
||||||
# touchpad:<subsystem>:v<vid>p<pid>:name:<name>:*
|
# touchpad:<subsystem>:v<vid>p<pid>:name:<name>:*
|
||||||
KERNELS=="input*", ENV{ID_BUS}!="", \
|
KERNELS=="input*", ENV{ID_BUS}!="", \
|
||||||
IMPORT{builtin}="hwdb 'touchpad:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'", \
|
IMPORT{builtin}="hwdb 'touchpad:$env{ID_BUS}:v$attr{id/vendor}p$attr{id/product}:name:$attr{name}:'"
|
||||||
GOTO="touchpad_end"
|
|
||||||
|
# Spread the hwdb override to ID_INTEGRATION, in the future we could remove the
|
||||||
|
# touchpad hwdb entirely or retain it using the generic ID_INTEGRATION instead
|
||||||
|
# specific ID_INPUT_TOUCHPAD_INTEGRATION.
|
||||||
|
ENV{ID_INPUT_TOUCHPAD_INTEGRATION}!="", ENV{ID_INTEGRATION}="$env{ID_INPUT_TOUCHPAD_INTEGRATION}"
|
||||||
|
|
||||||
LABEL="touchpad_end"
|
LABEL="touchpad_end"
|
||||||
|
|||||||
@ -557,12 +557,14 @@ static int parse_one_option(const char *option) {
|
|||||||
|
|
||||||
} else if ((val = startswith(option, "tpm2-measure-keyslot-nvpcr="))) {
|
} else if ((val = startswith(option, "tpm2-measure-keyslot-nvpcr="))) {
|
||||||
|
|
||||||
if (isempty(val)) {
|
r = isempty(val) ? false : parse_boolean(val);
|
||||||
|
if (r == 0) {
|
||||||
arg_tpm2_measure_keyslot_nvpcr = mfree(arg_tpm2_measure_keyslot_nvpcr);
|
arg_tpm2_measure_keyslot_nvpcr = mfree(arg_tpm2_measure_keyslot_nvpcr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (r > 0)
|
||||||
if (!tpm2_nvpcr_name_is_valid(val)) {
|
val = "cryptsetup";
|
||||||
|
else if (!tpm2_nvpcr_name_is_valid(val)) {
|
||||||
log_warning("Invalid NvPCR name, ignoring: %s", option);
|
log_warning("Invalid NvPCR name, ignoring: %s", option);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1095,8 +1097,9 @@ static int measure_keyslot(
|
|||||||
const char *mechanism,
|
const char *mechanism,
|
||||||
int keyslot) {
|
int keyslot) {
|
||||||
|
|
||||||
|
#if HAVE_TPM2
|
||||||
int r;
|
int r;
|
||||||
|
#endif
|
||||||
assert(cd);
|
assert(cd);
|
||||||
assert(name);
|
assert(name);
|
||||||
|
|
||||||
@ -1105,6 +1108,7 @@ static int measure_keyslot(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if HAVE_TPM2
|
||||||
r = efi_measured_uki(LOG_WARNING);
|
r = efi_measured_uki(LOG_WARNING);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -1113,7 +1117,6 @@ static int measure_keyslot(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_TPM2
|
|
||||||
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
|
||||||
r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
|
r = tpm2_context_new_or_warn(arg_tpm2_device, &c);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
|||||||
@ -127,7 +127,7 @@ static int add_cryptsetup(
|
|||||||
* assignment, under the assumption that people who are fine to use sd-stub with its PCR
|
* assignment, under the assumption that people who are fine to use sd-stub with its PCR
|
||||||
* assignments are also OK with our PCR 15 use here. */
|
* assignments are also OK with our PCR 15 use here. */
|
||||||
if (r > 0)
|
if (r > 0)
|
||||||
if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes,tpm2-measure-keyslot-nvpcr=cryptsetup"))
|
if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes,tpm2-measure-keyslot-nvpcr=yes"))
|
||||||
return log_oom();
|
return log_oom();
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
log_debug("Will not measure volume key of volume '%s', not booted via systemd-stub with measurements enabled.", id);
|
log_debug("Will not measure volume key of volume '%s', not booted via systemd-stub with measurements enabled.", id);
|
||||||
@ -190,7 +190,8 @@ static int add_veritysetup(
|
|||||||
const char *id,
|
const char *id,
|
||||||
const char *data_what,
|
const char *data_what,
|
||||||
const char *hash_what,
|
const char *hash_what,
|
||||||
const char *mount_opts) {
|
const char *mount_opts,
|
||||||
|
MountPointFlags flags) {
|
||||||
|
|
||||||
#if HAVE_LIBCRYPTSETUP
|
#if HAVE_LIBCRYPTSETUP
|
||||||
int r;
|
int r;
|
||||||
@ -233,13 +234,26 @@ static int add_veritysetup(
|
|||||||
"After=%1$s %2$s\n",
|
"After=%1$s %2$s\n",
|
||||||
dd, dh);
|
dd, dh);
|
||||||
|
|
||||||
|
_cleanup_free_ char *options =
|
||||||
|
strdup("root-hash-signature=auto"); /* auto means: derive signature from udev property ID_DISSECT_PART_ROOTHASH_SIG */
|
||||||
|
if (!options)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (FLAGS_SET(flags, MOUNT_MEASURE)) {
|
||||||
|
r = efi_measured_uki(LOG_WARNING);
|
||||||
|
if (r > 0 && !strextend_with_separator(&options, ",", "tpm2-measure-nvpcr=yes"))
|
||||||
|
return log_oom();
|
||||||
|
if (r == 0)
|
||||||
|
log_debug("Will not measure root hash/signature of volume '%s', not booted via systemd-stub with measurements enabled.", id);
|
||||||
|
}
|
||||||
|
|
||||||
r = generator_write_veritysetup_service_section(
|
r = generator_write_veritysetup_service_section(
|
||||||
f,
|
f,
|
||||||
id,
|
id,
|
||||||
data_what,
|
data_what,
|
||||||
hash_what,
|
hash_what,
|
||||||
/* roothash= */ NULL, /* NULL means: derive root hash from udev property ID_DISSECT_PART_ROOTHASH */
|
/* roothash= */ NULL, /* NULL means: derive root hash from udev property ID_DISSECT_PART_ROOTHASH */
|
||||||
"root-hash-signature=auto"); /* auto means: derive signature from udev property ID_DISSECT_PART_ROOTHASH_SIG */
|
options);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -871,7 +885,8 @@ static int add_root_mount(void) {
|
|||||||
"root",
|
"root",
|
||||||
"/dev/disk/by-designator/root-verity-data",
|
"/dev/disk/by-designator/root-verity-data",
|
||||||
"/dev/disk/by-designator/root-verity",
|
"/dev/disk/by-designator/root-verity",
|
||||||
arg_root_options);
|
arg_root_options,
|
||||||
|
MOUNT_MEASURE);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@ -952,7 +967,8 @@ static int add_usr_mount(void) {
|
|||||||
"usr",
|
"usr",
|
||||||
"/dev/disk/by-designator/usr-verity-data",
|
"/dev/disk/by-designator/usr-verity-data",
|
||||||
"/dev/disk/by-designator/usr-verity",
|
"/dev/disk/by-designator/usr-verity",
|
||||||
arg_usr_options);
|
arg_usr_options,
|
||||||
|
MOUNT_MEASURE);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -369,6 +369,7 @@ typedef struct MethodExtendParameters {
|
|||||||
const char *nvpcr;
|
const char *nvpcr;
|
||||||
const char *text;
|
const char *text;
|
||||||
struct iovec data;
|
struct iovec data;
|
||||||
|
Tpm2UserspaceEventType event_type;
|
||||||
} MethodExtendParameters;
|
} MethodExtendParameters;
|
||||||
|
|
||||||
static void method_extend_parameters_done(MethodExtendParameters *p) {
|
static void method_extend_parameters_done(MethodExtendParameters *p) {
|
||||||
@ -377,6 +378,8 @@ static void method_extend_parameters_done(MethodExtendParameters *p) {
|
|||||||
iovec_done(&p->data);
|
iovec_done(&p->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_tpm2_userspace_event_type, Tpm2UserspaceEventType, tpm2_userspace_event_type_from_string);
|
||||||
|
|
||||||
static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
|
||||||
|
|
||||||
static const sd_json_dispatch_field dispatch_table[] = {
|
static const sd_json_dispatch_field dispatch_table[] = {
|
||||||
@ -384,10 +387,12 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va
|
|||||||
{ "nvpcr", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodExtendParameters, nvpcr), 0 },
|
{ "nvpcr", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodExtendParameters, nvpcr), 0 },
|
||||||
{ "text", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodExtendParameters, text), 0 },
|
{ "text", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, offsetof(MethodExtendParameters, text), 0 },
|
||||||
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodExtendParameters, data), 0 },
|
{ "data", SD_JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodExtendParameters, data), 0 },
|
||||||
|
{ "eventType", SD_JSON_VARIANT_STRING, json_dispatch_tpm2_userspace_event_type, offsetof(MethodExtendParameters, event_type), 0 },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
_cleanup_(method_extend_parameters_done) MethodExtendParameters p = {
|
_cleanup_(method_extend_parameters_done) MethodExtendParameters p = {
|
||||||
.pcr = UINT_MAX,
|
.pcr = UINT_MAX,
|
||||||
|
.event_type = _TPM2_USERSPACE_EVENT_TYPE_INVALID,
|
||||||
};
|
};
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -424,11 +429,11 @@ static int vl_method_extend(sd_varlink *link, sd_json_variant *parameters, sd_va
|
|||||||
return sd_varlink_error_invalid_parameter_name(link, "text");
|
return sd_varlink_error_invalid_parameter_name(link, "text");
|
||||||
|
|
||||||
if (p.nvpcr) {
|
if (p.nvpcr) {
|
||||||
r = extend_nvpcr_now(p.nvpcr, extend_iovec->iov_base, extend_iovec->iov_len, _TPM2_USERSPACE_EVENT_TYPE_INVALID);
|
r = extend_nvpcr_now(p.nvpcr, extend_iovec->iov_base, extend_iovec->iov_len, p.event_type);
|
||||||
if (r == -ENOENT)
|
if (r == -ENOENT)
|
||||||
return sd_varlink_error(link, "io.systemd.PCRExtend.NoSuchNvPCR", NULL);
|
return sd_varlink_error(link, "io.systemd.PCRExtend.NoSuchNvPCR", NULL);
|
||||||
} else
|
} else
|
||||||
r = extend_pcr_now(p.pcr, extend_iovec->iov_base, extend_iovec->iov_len, _TPM2_USERSPACE_EVENT_TYPE_INVALID);
|
r = extend_pcr_now(p.pcr, extend_iovec->iov_base, extend_iovec->iov_len, p.event_type);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -60,6 +60,7 @@
|
|||||||
#include "openssl-util.h"
|
#include "openssl-util.h"
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
|
#include "pcrextend-util.h"
|
||||||
#include "pidref.h"
|
#include "pidref.h"
|
||||||
#include "proc-cmdline.h"
|
#include "proc-cmdline.h"
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
@ -3219,13 +3220,13 @@ static int do_crypt_activate_verity(
|
|||||||
DissectImageFlags flags,
|
DissectImageFlags flags,
|
||||||
PartitionPolicyFlags policy_flags) {
|
PartitionPolicyFlags policy_flags) {
|
||||||
|
|
||||||
bool check_signature;
|
int r;
|
||||||
int r, k;
|
|
||||||
|
|
||||||
assert(cd);
|
assert(cd);
|
||||||
assert(name);
|
assert(name);
|
||||||
assert(verity);
|
assert(verity);
|
||||||
|
|
||||||
|
bool check_signature;
|
||||||
if (iovec_is_set(&verity->root_hash_sig) && FLAGS_SET(policy_flags, PARTITION_POLICY_SIGNED)) {
|
if (iovec_is_set(&verity->root_hash_sig) && FLAGS_SET(policy_flags, PARTITION_POLICY_SIGNED)) {
|
||||||
r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_SIGNATURE");
|
r = secure_getenv_bool("SYSTEMD_DISSECT_VERITY_SIGNATURE");
|
||||||
if (r < 0 && r != -ENXIO)
|
if (r < 0 && r != -ENXIO)
|
||||||
@ -3235,7 +3236,10 @@ static int do_crypt_activate_verity(
|
|||||||
} else
|
} else
|
||||||
check_signature = false;
|
check_signature = false;
|
||||||
|
|
||||||
|
bool measure_signature;
|
||||||
if (check_signature) {
|
if (check_signature) {
|
||||||
|
int k;
|
||||||
|
|
||||||
/* First, if we have support for signed keys in the kernel, then try that first. */
|
/* First, if we have support for signed keys in the kernel, then try that first. */
|
||||||
r = sym_crypt_activate_by_signed_key(
|
r = sym_crypt_activate_by_signed_key(
|
||||||
cd,
|
cd,
|
||||||
@ -3247,7 +3251,8 @@ static int do_crypt_activate_verity(
|
|||||||
CRYPT_ACTIVATE_READONLY);
|
CRYPT_ACTIVATE_READONLY);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
log_debug("Verity activation via kernel signature logic worked.");
|
log_debug("Verity activation via kernel signature logic worked.");
|
||||||
return 0;
|
measure_signature = true;
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug_errno(r, "Validation of dm-verity signature failed via the kernel, trying userspace validation instead: %m");
|
log_debug_errno(r, "Validation of dm-verity signature failed via the kernel, trying userspace validation instead: %m");
|
||||||
@ -3280,9 +3285,13 @@ static int do_crypt_activate_verity(
|
|||||||
|
|
||||||
/* Otherwise let's see what signature-less activation results in. */
|
/* Otherwise let's see what signature-less activation results in. */
|
||||||
|
|
||||||
|
measure_signature = true;
|
||||||
|
|
||||||
} else if (!FLAGS_SET(policy_flags, PARTITION_POLICY_VERITY))
|
} else if (!FLAGS_SET(policy_flags, PARTITION_POLICY_VERITY))
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL),
|
return log_debug_errno(SYNTHETIC_ERRNO(ERFKILL),
|
||||||
"No-signature activation of Verity volume not allowed by policy, refusing.");
|
"No-signature activation of Verity volume not allowed by policy, refusing.");
|
||||||
|
else
|
||||||
|
measure_signature = false;
|
||||||
|
|
||||||
r = sym_crypt_activate_by_volume_key(
|
r = sym_crypt_activate_by_volume_key(
|
||||||
cd,
|
cd,
|
||||||
@ -3294,6 +3303,12 @@ static int do_crypt_activate_verity(
|
|||||||
return log_debug_errno(r, "Activation of Verity via root hash failed: %m");
|
return log_debug_errno(r, "Activation of Verity via root hash failed: %m");
|
||||||
|
|
||||||
log_debug("Activation of Verity via root hash succeeded.");
|
log_debug("Activation of Verity via root hash succeeded.");
|
||||||
|
|
||||||
|
done:
|
||||||
|
(void) pcrextend_verity_now(
|
||||||
|
name,
|
||||||
|
&verity->root_hash,
|
||||||
|
measure_signature ? &verity->root_hash_sig : NULL);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -152,6 +152,7 @@ shared_sources = files(
|
|||||||
'pcre2-util.c',
|
'pcre2-util.c',
|
||||||
'pcrextend-util.c',
|
'pcrextend-util.c',
|
||||||
'pe-binary.c',
|
'pe-binary.c',
|
||||||
|
'pkcs7-util.c',
|
||||||
'pkcs11-util.c',
|
'pkcs11-util.c',
|
||||||
'plymouth-util.c',
|
'plymouth-util.c',
|
||||||
'polkit-agent.c',
|
'polkit-agent.c',
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "sd-device.h"
|
#include "sd-device.h"
|
||||||
#include "sd-id128.h"
|
#include "sd-id128.h"
|
||||||
|
#include "sd-varlink.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "blkid-util.h"
|
#include "blkid-util.h"
|
||||||
@ -10,10 +11,13 @@
|
|||||||
#include "errno-util.h"
|
#include "errno-util.h"
|
||||||
#include "escape.h"
|
#include "escape.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
|
#include "hexdecoct.h"
|
||||||
#include "id128-util.h"
|
#include "id128-util.h"
|
||||||
|
#include "iovec-util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mountpoint-util.h"
|
#include "mountpoint-util.h"
|
||||||
#include "pcrextend-util.h"
|
#include "pcrextend-util.h"
|
||||||
|
#include "pkcs7-util.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
|
||||||
@ -180,3 +184,110 @@ int pcrextend_product_id_word(char **ret) {
|
|||||||
*ret = TAKE_PTR(word);
|
*ret = TAKE_PTR(word);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pcrextend_verity_word(
|
||||||
|
const char *name,
|
||||||
|
const struct iovec *root_hash,
|
||||||
|
const struct iovec *root_hash_sig,
|
||||||
|
char **ret) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(name);
|
||||||
|
assert(iovec_is_set(root_hash));
|
||||||
|
|
||||||
|
_cleanup_free_ char *name_escaped = xescape(name, ":"); /* Avoid ambiguity around ":" */
|
||||||
|
if (!name_escaped)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_free_ char *h = hexmem(root_hash->iov_base, root_hash->iov_len);
|
||||||
|
if (!h)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_free_ char *sigs = NULL;
|
||||||
|
if (iovec_is_set(root_hash_sig)) {
|
||||||
|
size_t n_signers = 0;
|
||||||
|
Signer *signers = NULL;
|
||||||
|
|
||||||
|
/* Let's extract the X.509 issuer + serial number from the PKCS#7 signature and include that
|
||||||
|
* in the measurement record. This is useful since it allows us to have different signing
|
||||||
|
* keys for confext + sysext + other types of DDIs, and by means of this information we can
|
||||||
|
* discern which kind it was. Ideally, we'd measure the fingerprint of the X.509 certificate,
|
||||||
|
* but typically that's not available in a PKCS#7 signature. */
|
||||||
|
|
||||||
|
CLEANUP_ARRAY(signers, n_signers, signer_free_many);
|
||||||
|
|
||||||
|
r = pkcs7_extract_signers(root_hash_sig, &signers, &n_signers);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
FOREACH_ARRAY(i, signers, n_signers) {
|
||||||
|
_cleanup_free_ char *serial = hexmem(i->serial.iov_base, i->serial.iov_len);
|
||||||
|
if (!serial)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_free_ char *issuer = NULL;
|
||||||
|
if (base64mem(i->issuer.iov_base, i->issuer.iov_len, &issuer) < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
if (strextendf_with_separator(&sigs, ",", "%s/%s", serial, issuer) < 0)
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_free_ char *word = strjoin("verity:", name_escaped, ":", h, ":", strempty(sigs));
|
||||||
|
if (!word)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
*ret = TAKE_PTR(word);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pcrextend_verity_now(
|
||||||
|
const char *name,
|
||||||
|
const struct iovec *root_hash,
|
||||||
|
const struct iovec *root_hash_sig) {
|
||||||
|
|
||||||
|
#if HAVE_TPM2
|
||||||
|
int r;
|
||||||
|
|
||||||
|
_cleanup_free_ char *word = NULL;
|
||||||
|
r = pcrextend_verity_word(
|
||||||
|
name,
|
||||||
|
root_hash,
|
||||||
|
root_hash_sig,
|
||||||
|
&word);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
_cleanup_free_ sd_varlink *vl = NULL;
|
||||||
|
r = sd_varlink_connect_address(&vl, "/run/systemd/io.systemd.PCRExtend");
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
_cleanup_(sd_json_variant_unrefp) sd_json_variant *reply = NULL;
|
||||||
|
const char *error_id = NULL;
|
||||||
|
r = sd_varlink_callbo(
|
||||||
|
vl,
|
||||||
|
"io.systemd.PCRExtend.Extend",
|
||||||
|
/* ret_reply= */ NULL,
|
||||||
|
&error_id,
|
||||||
|
SD_JSON_BUILD_PAIR_STRING("nvpcr", "verity"),
|
||||||
|
SD_JSON_BUILD_PAIR_STRING("text", word),
|
||||||
|
SD_JSON_BUILD_PAIR_STRING("eventType", "dm_verity"));
|
||||||
|
if (r < 0)
|
||||||
|
return log_debug_errno(r, "Failed to issue io.systemd.PCRExtend.Extend() varlink call: %m");
|
||||||
|
if (error_id) {
|
||||||
|
r = sd_varlink_error_to_errno(error_id, reply);
|
||||||
|
if (r != -EBADR)
|
||||||
|
return log_debug_errno(r, "Failed to issue io.systemd.PCRExtend.Extend() varlink call: %m");
|
||||||
|
|
||||||
|
return log_debug_errno(r, "Failed to issue io.systemd.PCRExtend.Extend() varlink call: %s", error_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("Measurement of '%s' into 'images' NvPCR completed.", word);
|
||||||
|
return 1;
|
||||||
|
#else
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring Verity root hashes and signatures.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@ -4,3 +4,6 @@
|
|||||||
int pcrextend_file_system_word(const char *path, char **ret, char **ret_normalized_path);
|
int pcrextend_file_system_word(const char *path, char **ret, char **ret_normalized_path);
|
||||||
int pcrextend_machine_id_word(char **ret);
|
int pcrextend_machine_id_word(char **ret);
|
||||||
int pcrextend_product_id_word(char **ret);
|
int pcrextend_product_id_word(char **ret);
|
||||||
|
int pcrextend_verity_word(const char *name, const struct iovec *root_hash, const struct iovec *root_hash_sig, char **ret);
|
||||||
|
|
||||||
|
int pcrextend_verity_now(const char *name, const struct iovec *root_hash,const struct iovec *root_hash_sig);
|
||||||
|
|||||||
@ -243,7 +243,7 @@ int pe_read_section_data_by_name(
|
|||||||
|
|
||||||
assert(fd >= 0);
|
assert(fd >= 0);
|
||||||
assert(pe_header);
|
assert(pe_header);
|
||||||
assert(sections || pe_header->pe.NumberOfSections == 0);
|
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
|
||||||
assert(name);
|
assert(name);
|
||||||
|
|
||||||
section = pe_header_find_section(pe_header, sections, name);
|
section = pe_header_find_section(pe_header, sections, name);
|
||||||
@ -408,9 +408,9 @@ int pe_hash(int fd,
|
|||||||
return r;
|
return r;
|
||||||
|
|
||||||
/* Sort by location in file */
|
/* Sort by location in file */
|
||||||
typesafe_qsort(sections, pe_header->pe.NumberOfSections, section_offset_cmp);
|
typesafe_qsort(sections, le16toh(pe_header->pe.NumberOfSections), section_offset_cmp);
|
||||||
|
|
||||||
FOREACH_ARRAY(section, sections, pe_header->pe.NumberOfSections) {
|
FOREACH_ARRAY(section, sections, le16toh(pe_header->pe.NumberOfSections)) {
|
||||||
r = hash_file(fd, mdctx, section->PointerToRawData, section->SizeOfRawData);
|
r = hash_file(fd, mdctx, section->PointerToRawData, section->SizeOfRawData);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -537,7 +537,7 @@ int uki_hash(int fd,
|
|||||||
if (hsz < 0)
|
if (hsz < 0)
|
||||||
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
|
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get hash size.");
|
||||||
|
|
||||||
FOREACH_ARRAY(section, sections, pe_header->pe.NumberOfSections) {
|
FOREACH_ARRAY(section, sections, le16toh(pe_header->pe.NumberOfSections)) {
|
||||||
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mdctx = NULL;
|
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX *mdctx = NULL;
|
||||||
_cleanup_free_ char *n = NULL;
|
_cleanup_free_ char *n = NULL;
|
||||||
ssize_t i;
|
ssize_t i;
|
||||||
|
|||||||
84
src/shared/pkcs7-util.c
Normal file
84
src/shared/pkcs7-util.c
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "openssl-util.h"
|
||||||
|
#include "pkcs7-util.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#define SIGNERS_MAX 32
|
||||||
|
|
||||||
|
static void signer_done(Signer *signer) {
|
||||||
|
assert(signer);
|
||||||
|
|
||||||
|
iovec_done(&signer->issuer);
|
||||||
|
iovec_done(&signer->serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void signer_free_many(Signer *signers, size_t n) {
|
||||||
|
assert(signers || n == 0);
|
||||||
|
|
||||||
|
FOREACH_ARRAY(i, signers, n)
|
||||||
|
signer_done(i);
|
||||||
|
|
||||||
|
free(signers);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pkcs7_extract_signers(
|
||||||
|
const struct iovec *sig,
|
||||||
|
Signer **ret_signers,
|
||||||
|
size_t *ret_n_signers) {
|
||||||
|
|
||||||
|
assert(ret_signers);
|
||||||
|
assert(ret_n_signers);
|
||||||
|
|
||||||
|
if (!iovec_is_set(sig))
|
||||||
|
return -EBADMSG;
|
||||||
|
|
||||||
|
#if HAVE_OPENSSL
|
||||||
|
const unsigned char *d = sig->iov_base;
|
||||||
|
_cleanup_(PKCS7_freep) PKCS7 *p7 = NULL;
|
||||||
|
p7 = d2i_PKCS7(/* a= */ NULL, &d, (long) sig->iov_len);
|
||||||
|
if (!p7)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Failed to parse PKCS7 DER signature data.");
|
||||||
|
|
||||||
|
STACK_OF(PKCS7_SIGNER_INFO) *sinfos = PKCS7_get_signer_info(p7);
|
||||||
|
if (!sinfos)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "No signature information in PKCS7 signature?");
|
||||||
|
int n = sk_PKCS7_SIGNER_INFO_num(sinfos);
|
||||||
|
if (n == 0)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "No signatures in PKCS7 signature, refusing.");
|
||||||
|
if (n > SIGNERS_MAX) /* safety net, in case people send us weirdly complex signatures */
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Too many signatures, refusing.");
|
||||||
|
assert(n > 0);
|
||||||
|
|
||||||
|
size_t n_signers = 0;
|
||||||
|
Signer *signers = new(Signer, n);
|
||||||
|
if (!signers)
|
||||||
|
return log_oom_debug();
|
||||||
|
|
||||||
|
CLEANUP_ARRAY(signers, n_signers, signer_free_many);
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(PKCS7_get_signer_info(p7), i);
|
||||||
|
if (!si)
|
||||||
|
return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to get signer information.");
|
||||||
|
|
||||||
|
_cleanup_(signer_done) Signer signer = {};
|
||||||
|
|
||||||
|
_cleanup_free_ unsigned char *p = NULL;
|
||||||
|
int len = i2d_X509_NAME(si->issuer_and_serial->issuer, &p);
|
||||||
|
signer.issuer = IOVEC_MAKE(TAKE_PTR(p), len);
|
||||||
|
|
||||||
|
len = i2d_ASN1_INTEGER(si->issuer_and_serial->serial, &p);
|
||||||
|
signer.serial = IOVEC_MAKE(TAKE_PTR(p), len);
|
||||||
|
|
||||||
|
signers[n_signers++] = TAKE_STRUCT(signer);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_signers = TAKE_PTR(signers);
|
||||||
|
*ret_n_signers = n_signers;
|
||||||
|
return n;
|
||||||
|
#else
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
16
src/shared/pkcs7-util.h
Normal file
16
src/shared/pkcs7-util.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
typedef struct Signer {
|
||||||
|
struct iovec issuer;
|
||||||
|
struct iovec serial;
|
||||||
|
} Signer;
|
||||||
|
|
||||||
|
void signer_free_many(Signer *signers, size_t n);
|
||||||
|
|
||||||
|
int pkcs7_extract_signers(
|
||||||
|
const struct iovec *sig,
|
||||||
|
Signer **ret_signers,
|
||||||
|
size_t *ret_n_signers);
|
||||||
@ -6478,6 +6478,7 @@ static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MA
|
|||||||
[TPM2_EVENT_KEYSLOT] = "keyslot",
|
[TPM2_EVENT_KEYSLOT] = "keyslot",
|
||||||
[TPM2_EVENT_NVPCR_INIT] = "nvpcr-init",
|
[TPM2_EVENT_NVPCR_INIT] = "nvpcr-init",
|
||||||
[TPM2_EVENT_NVPCR_SEPARATOR] = "nvpcr-separator",
|
[TPM2_EVENT_NVPCR_SEPARATOR] = "nvpcr-separator",
|
||||||
|
[TPM2_EVENT_DM_VERITY] = "dm-verity",
|
||||||
};
|
};
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
|
DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
|
||||||
|
|||||||
@ -145,6 +145,7 @@ typedef enum Tpm2UserspaceEventType {
|
|||||||
TPM2_EVENT_KEYSLOT,
|
TPM2_EVENT_KEYSLOT,
|
||||||
TPM2_EVENT_NVPCR_INIT,
|
TPM2_EVENT_NVPCR_INIT,
|
||||||
TPM2_EVENT_NVPCR_SEPARATOR,
|
TPM2_EVENT_NVPCR_SEPARATOR,
|
||||||
|
TPM2_EVENT_DM_VERITY,
|
||||||
_TPM2_USERSPACE_EVENT_TYPE_MAX,
|
_TPM2_USERSPACE_EVENT_TYPE_MAX,
|
||||||
_TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL,
|
_TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL,
|
||||||
} Tpm2UserspaceEventType;
|
} Tpm2UserspaceEventType;
|
||||||
|
|||||||
@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
#include "varlink-io.systemd.PCRExtend.h"
|
#include "varlink-io.systemd.PCRExtend.h"
|
||||||
|
|
||||||
|
static SD_VARLINK_DEFINE_ENUM_TYPE(
|
||||||
|
EventType,
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(phase),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(filesystem),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(volume_key),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(machine_id),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(product_id),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(keyslot),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(nvpcr_init),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(nvpcr_separator),
|
||||||
|
SD_VARLINK_DEFINE_ENUM_VALUE(dm_verity));
|
||||||
|
|
||||||
static SD_VARLINK_DEFINE_METHOD(
|
static SD_VARLINK_DEFINE_METHOD(
|
||||||
Extend,
|
Extend,
|
||||||
SD_VARLINK_FIELD_COMMENT("PCR number to extend, in range of 0…23. Either this or 'nvpcr' must be specified, not both, not neither."),
|
SD_VARLINK_FIELD_COMMENT("PCR number to extend, in range of 0…23. Either this or 'nvpcr' must be specified, not both, not neither."),
|
||||||
@ -11,7 +23,9 @@ static SD_VARLINK_DEFINE_METHOD(
|
|||||||
SD_VARLINK_FIELD_COMMENT("Text string to measure. (Specify either this, or the 'data' field below, not both)"),
|
SD_VARLINK_FIELD_COMMENT("Text string to measure. (Specify either this, or the 'data' field below, not both)"),
|
||||||
SD_VARLINK_DEFINE_INPUT(text, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
SD_VARLINK_DEFINE_INPUT(text, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||||
SD_VARLINK_FIELD_COMMENT("Binary data to measure, encoded in Base64. (Specify either this, or the 'text' field above, not both)"),
|
SD_VARLINK_FIELD_COMMENT("Binary data to measure, encoded in Base64. (Specify either this, or the 'text' field above, not both)"),
|
||||||
SD_VARLINK_DEFINE_INPUT(data, SD_VARLINK_STRING, SD_VARLINK_NULLABLE));
|
SD_VARLINK_DEFINE_INPUT(data, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
|
||||||
|
SD_VARLINK_FIELD_COMMENT("Event type to include in the (userspace) event log). This is optional, and mostly for debugging."),
|
||||||
|
SD_VARLINK_DEFINE_INPUT_BY_TYPE(eventType, EventType, SD_VARLINK_NULLABLE));
|
||||||
|
|
||||||
static SD_VARLINK_DEFINE_ERROR(NoSuchNvPCR);
|
static SD_VARLINK_DEFINE_ERROR(NoSuchNvPCR);
|
||||||
|
|
||||||
@ -21,4 +35,6 @@ SD_VARLINK_DEFINE_INTERFACE(
|
|||||||
SD_VARLINK_INTERFACE_COMMENT("TPM PCR Extension APIs"),
|
SD_VARLINK_INTERFACE_COMMENT("TPM PCR Extension APIs"),
|
||||||
SD_VARLINK_SYMBOL_COMMENT("Measure some text or binary data into a PCR"),
|
SD_VARLINK_SYMBOL_COMMENT("Measure some text or binary data into a PCR"),
|
||||||
&vl_method_Extend,
|
&vl_method_Extend,
|
||||||
|
SD_VARLINK_SYMBOL_COMMENT("Event type to store in event log"),
|
||||||
|
&vl_type_EventType,
|
||||||
&vl_error_NoSuchNvPCR);
|
&vl_error_NoSuchNvPCR);
|
||||||
|
|||||||
@ -9,6 +9,20 @@
|
|||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "sysupdate-partition.h"
|
#include "sysupdate-partition.h"
|
||||||
|
|
||||||
|
/* App-specific IDs used as HMAC keys to derive "partial" and "pending" partition type UUIDs from the
|
||||||
|
* original partition type UUID. This way we can indicate sysupdate transfer state via a separate GPT
|
||||||
|
* partition type UUID instead of using a label prefix, saving precious label space. */
|
||||||
|
#define GPT_SYSUPDATE_PARTIAL_APP_ID SD_ID128_MAKE(ac,cf,a0,c2,da,24,46,0a,9f,c9,0b,b8,fc,78,52,19)
|
||||||
|
#define GPT_SYSUPDATE_PENDING_APP_ID SD_ID128_MAKE(80,f3,d6,1e,23,83,43,b9,81,f5,ce,37,93,f4,7d,4c)
|
||||||
|
|
||||||
|
int gpt_partition_type_uuid_for_sysupdate_partial(sd_id128_t type, sd_id128_t *ret) {
|
||||||
|
return sd_id128_get_app_specific(type, GPT_SYSUPDATE_PARTIAL_APP_ID, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int gpt_partition_type_uuid_for_sysupdate_pending(sd_id128_t type, sd_id128_t *ret) {
|
||||||
|
return sd_id128_get_app_specific(type, GPT_SYSUPDATE_PENDING_APP_ID, ret);
|
||||||
|
}
|
||||||
|
|
||||||
void partition_info_destroy(PartitionInfo *p) {
|
void partition_info_destroy(PartitionInfo *p) {
|
||||||
assert(p);
|
assert(p);
|
||||||
|
|
||||||
@ -242,6 +256,22 @@ int patch_partition(
|
|||||||
return log_error_errno(r, "Failed to update partition UUID: %m");
|
return log_error_errno(r, "Failed to update partition UUID: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (change & PARTITION_TYPE) {
|
||||||
|
_cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *pt = NULL;
|
||||||
|
|
||||||
|
pt = fdisk_new_parttype();
|
||||||
|
if (!pt)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = fdisk_parttype_set_typestr(pt, SD_ID128_TO_UUID_STRING(info->type));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to initialize partition type: %m");
|
||||||
|
|
||||||
|
r = fdisk_partition_set_type(pa, pt);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to update partition type: %m");
|
||||||
|
}
|
||||||
|
|
||||||
type = gpt_partition_type_from_uuid(info->type);
|
type = gpt_partition_type_from_uuid(info->type);
|
||||||
|
|
||||||
/* Tweak the read-only flag, but only if supported by the partition type */
|
/* Tweak the read-only flag, but only if supported by the partition type */
|
||||||
|
|||||||
@ -12,7 +12,8 @@ typedef enum PartitionChange {
|
|||||||
PARTITION_GROWFS = 1 << 3,
|
PARTITION_GROWFS = 1 << 3,
|
||||||
PARTITION_UUID = 1 << 4,
|
PARTITION_UUID = 1 << 4,
|
||||||
PARTITION_LABEL = 1 << 5,
|
PARTITION_LABEL = 1 << 5,
|
||||||
_PARTITION_CHANGE_MAX = (1 << 6) - 1, /* all of the above */
|
PARTITION_TYPE = 1 << 6,
|
||||||
|
_PARTITION_CHANGE_MAX = (1 << 7) - 1, /* all of the above */
|
||||||
_PARTITION_CHANGE_INVALID = -EINVAL,
|
_PARTITION_CHANGE_INVALID = -EINVAL,
|
||||||
} PartitionChange;
|
} PartitionChange;
|
||||||
|
|
||||||
@ -40,5 +41,8 @@ int partition_info_copy(PartitionInfo *dest, const PartitionInfo *src);
|
|||||||
|
|
||||||
int read_partition_info(struct fdisk_context *c, struct fdisk_table *t, size_t i, PartitionInfo *ret);
|
int read_partition_info(struct fdisk_context *c, struct fdisk_table *t, size_t i, PartitionInfo *ret);
|
||||||
|
|
||||||
|
int gpt_partition_type_uuid_for_sysupdate_partial(sd_id128_t type, sd_id128_t *ret);
|
||||||
|
int gpt_partition_type_uuid_for_sysupdate_pending(sd_id128_t type, sd_id128_t *ret);
|
||||||
|
|
||||||
int find_suitable_partition(const char *device, uint64_t space, sd_id128_t *partition_type, PartitionInfo *ret);
|
int find_suitable_partition(const char *device, uint64_t space, sd_id128_t *partition_type, PartitionInfo *ret);
|
||||||
int patch_partition(const char *device, const PartitionInfo *info, PartitionChange change);
|
int patch_partition(const char *device, const PartitionInfo *info, PartitionChange change);
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "sysupdate-cache.h"
|
#include "sysupdate-cache.h"
|
||||||
#include "sysupdate-instance.h"
|
#include "sysupdate-instance.h"
|
||||||
|
#include "sysupdate-partition.h"
|
||||||
#include "sysupdate-pattern.h"
|
#include "sysupdate-pattern.h"
|
||||||
#include "sysupdate-resource.h"
|
#include "sysupdate-resource.h"
|
||||||
#include "time-util.h"
|
#include "time-util.h"
|
||||||
@ -244,9 +245,7 @@ static int resource_load_from_blockdev(Resource *rr) {
|
|||||||
_cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
|
_cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL;
|
||||||
_cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL;
|
_cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL;
|
||||||
Instance *instance;
|
Instance *instance;
|
||||||
const char *pinfo_label_stripped;
|
|
||||||
bool is_partial = false, is_pending = false;
|
bool is_partial = false, is_pending = false;
|
||||||
const char *stripped;
|
|
||||||
|
|
||||||
r = read_partition_info(c, t, i, &pinfo);
|
r = read_partition_info(c, t, i, &pinfo);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -254,9 +253,28 @@ static int resource_load_from_blockdev(Resource *rr) {
|
|||||||
if (r == 0) /* not assigned */
|
if (r == 0) /* not assigned */
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Check if partition type matches */
|
/* Check if partition type matches, either directly or via derived partial/pending type
|
||||||
if (rr->partition_type_set && !sd_id128_equal(pinfo.type, rr->partition_type.uuid))
|
* UUIDs. The derived UUIDs are computed from the configured partition type by hashing it
|
||||||
|
* with a fixed app-specific ID, so we can detect the state without relying on label
|
||||||
|
* prefixes. */
|
||||||
|
if (rr->partition_type_set) {
|
||||||
|
sd_id128_t partial_type, pending_type;
|
||||||
|
|
||||||
|
r = gpt_partition_type_uuid_for_sysupdate_partial(rr->partition_type.uuid, &partial_type);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to derive partial partition type UUID: %m");
|
||||||
|
|
||||||
|
r = gpt_partition_type_uuid_for_sysupdate_pending(rr->partition_type.uuid, &pending_type);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to derive pending partition type UUID: %m");
|
||||||
|
|
||||||
|
if (sd_id128_equal(pinfo.type, partial_type))
|
||||||
|
is_partial = true;
|
||||||
|
else if (sd_id128_equal(pinfo.type, pending_type))
|
||||||
|
is_pending = true;
|
||||||
|
else if (!sd_id128_equal(pinfo.type, rr->partition_type.uuid))
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* A label of "_empty" means "not used so far" for us */
|
/* A label of "_empty" means "not used so far" for us */
|
||||||
if (streq_ptr(pinfo.label, "_empty")) {
|
if (streq_ptr(pinfo.label, "_empty")) {
|
||||||
@ -264,18 +282,7 @@ static int resource_load_from_blockdev(Resource *rr) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Match the label with any partial/pending prefix removed so the user’s existing patterns
|
r = pattern_match_many(rr->patterns, pinfo.label, &extracted_fields);
|
||||||
* match regardless of the instance’s state. */
|
|
||||||
if ((stripped = startswith(pinfo.label, "PRT#"))) {
|
|
||||||
pinfo_label_stripped = stripped;
|
|
||||||
is_partial = true;
|
|
||||||
} else if ((stripped = startswith(pinfo.label, "PND#"))) {
|
|
||||||
pinfo_label_stripped = stripped;
|
|
||||||
is_pending = true;
|
|
||||||
} else
|
|
||||||
pinfo_label_stripped = pinfo.label;
|
|
||||||
|
|
||||||
r = pattern_match_many(rr->patterns, pinfo_label_stripped, &extracted_fields);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to match pattern: %m");
|
return log_error_errno(r, "Failed to match pattern: %m");
|
||||||
if (IN_SET(r, PATTERN_MATCH_NO, PATTERN_MATCH_RETRY))
|
if (IN_SET(r, PATTERN_MATCH_NO, PATTERN_MATCH_RETRY))
|
||||||
|
|||||||
@ -67,8 +67,6 @@ Transfer* transfer_free(Transfer *t) {
|
|||||||
strv_free(t->appstream);
|
strv_free(t->appstream);
|
||||||
|
|
||||||
partition_info_destroy(&t->partition_info);
|
partition_info_destroy(&t->partition_info);
|
||||||
free(t->temporary_partial_partition_label);
|
|
||||||
free(t->temporary_pending_partition_label);
|
|
||||||
free(t->final_partition_label);
|
free(t->final_partition_label);
|
||||||
|
|
||||||
resource_destroy(&t->source);
|
resource_destroy(&t->source);
|
||||||
@ -766,12 +764,21 @@ static int transfer_instance_vacuum(
|
|||||||
|
|
||||||
case RESOURCE_PARTITION: {
|
case RESOURCE_PARTITION: {
|
||||||
PartitionInfo pinfo = instance->partition_info;
|
PartitionInfo pinfo = instance->partition_info;
|
||||||
|
PartitionChange change = PARTITION_LABEL;
|
||||||
|
|
||||||
/* label "_empty" means "no contents" for our purposes */
|
/* label "_empty" means "no contents" for our purposes */
|
||||||
pinfo.label = (char*) "_empty";
|
pinfo.label = (char*) "_empty";
|
||||||
|
|
||||||
log_debug("Relabelling partition '%s' to '%s'.", pinfo.device, pinfo.label);
|
/* If the partition had a derived partial/pending type UUID, restore the original
|
||||||
r = patch_partition(t->target.path, &pinfo, PARTITION_LABEL);
|
* partition type so that the slot is properly recognized as empty in subsequent
|
||||||
|
* scans. */
|
||||||
|
if ((instance->is_partial || instance->is_pending) && t->target.partition_type_set) {
|
||||||
|
pinfo.type = t->target.partition_type.uuid;
|
||||||
|
change |= PARTITION_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_debug("Resetting partition '%s' to empty.", pinfo.device);
|
||||||
|
r = patch_partition(t->target.path, &pinfo, change);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -1172,7 +1179,7 @@ static int run_callout(
|
|||||||
* and pending instances which are about to be installed (in which case, transfer_acquire_instance() is
|
* and pending instances which are about to be installed (in which case, transfer_acquire_instance() is
|
||||||
* skipped). */
|
* skipped). */
|
||||||
int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata *f) {
|
int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata *f) {
|
||||||
_cleanup_free_ char *formatted_pattern = NULL, *formatted_partial_pattern = NULL, *formatted_pending_pattern = NULL;
|
_cleanup_free_ char *formatted_pattern = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(t);
|
assert(t);
|
||||||
@ -1182,8 +1189,6 @@ int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata
|
|||||||
assert(!t->temporary_partial_path);
|
assert(!t->temporary_partial_path);
|
||||||
assert(!t->temporary_pending_path);
|
assert(!t->temporary_pending_path);
|
||||||
assert(!t->final_partition_label);
|
assert(!t->final_partition_label);
|
||||||
assert(!t->temporary_partial_partition_label);
|
|
||||||
assert(!t->temporary_pending_partition_label);
|
|
||||||
assert(!strv_isempty(t->target.patterns));
|
assert(!strv_isempty(t->target.patterns));
|
||||||
|
|
||||||
/* Format the target name using the first pattern specified */
|
/* Format the target name using the first pattern specified */
|
||||||
@ -1234,25 +1239,18 @@ int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata
|
|||||||
if (!r)
|
if (!r)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pattern);
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pattern);
|
||||||
|
|
||||||
if (!strprepend(&formatted_partial_pattern, "PRT#", formatted_pattern))
|
if (!t->target.partition_type_set)
|
||||||
return log_oom();
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Partition type must be set for partition targets.");
|
||||||
r = gpt_partition_label_valid(formatted_partial_pattern);
|
|
||||||
|
/* Derive temporary partition type UUIDs for partial/pending states from the configured
|
||||||
|
* partition type. This avoids the need for label prefixes. */
|
||||||
|
r = gpt_partition_type_uuid_for_sysupdate_partial(t->target.partition_type.uuid, &t->partition_type_partial);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to determine if formatted pattern is suitable as GPT partition label: %s", formatted_partial_pattern);
|
return log_error_errno(r, "Failed to derive partial partition type UUID: %m");
|
||||||
if (!r)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_partial_pattern);
|
|
||||||
|
|
||||||
free_and_replace(t->temporary_partial_partition_label, formatted_partial_pattern);
|
r = gpt_partition_type_uuid_for_sysupdate_pending(t->target.partition_type.uuid, &t->partition_type_pending);
|
||||||
|
|
||||||
if (!strprepend(&formatted_pending_pattern, "PND#", formatted_pattern))
|
|
||||||
return log_oom();
|
|
||||||
r = gpt_partition_label_valid(formatted_pending_pattern);
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to determine if formatted pattern is suitable as GPT partition label: %s", formatted_pending_pattern);
|
return log_error_errno(r, "Failed to derive pending partition type UUID: %m");
|
||||||
if (!r)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pending_pattern);
|
|
||||||
|
|
||||||
free_and_replace(t->temporary_pending_partition_label, formatted_pending_pattern);
|
|
||||||
|
|
||||||
t->final_partition_label = TAKE_PTR(formatted_pattern);
|
t->final_partition_label = TAKE_PTR(formatted_pattern);
|
||||||
}
|
}
|
||||||
@ -1312,13 +1310,18 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
|||||||
|
|
||||||
where = t->partition_info.device;
|
where = t->partition_info.device;
|
||||||
|
|
||||||
/* Rename the partition to `PRT#<VERSION>` to indicate that a transfer to it is in progress. */
|
/* Set the partition label and change the partition type to the derived "partial" type UUID
|
||||||
r = free_and_strdup_warn(&t->partition_info.label, t->temporary_partial_partition_label);
|
* to indicate that a transfer to it is in progress. */
|
||||||
|
r = free_and_strdup_warn(&t->partition_info.label, t->final_partition_label);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
t->partition_change = PARTITION_LABEL;
|
t->partition_info.type = t->partition_type_partial;
|
||||||
|
t->partition_change = PARTITION_LABEL | PARTITION_TYPE;
|
||||||
|
|
||||||
log_debug("Relabelling partition '%s' to '%s'.", t->partition_info.device, t->partition_info.label);
|
log_debug("Marking partition '%s' as partial (label='%s', type=%s).",
|
||||||
|
t->partition_info.device,
|
||||||
|
t->partition_info.label,
|
||||||
|
SD_ID128_TO_UUID_STRING(t->partition_info.type));
|
||||||
r = patch_partition(
|
r = patch_partition(
|
||||||
t->target.path,
|
t->target.path,
|
||||||
&t->partition_info,
|
&t->partition_info,
|
||||||
@ -1545,12 +1548,10 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (t->target.type == RESOURCE_PARTITION) {
|
if (t->target.type == RESOURCE_PARTITION) {
|
||||||
/* Now rename the partition again to `PND#<VERSION>` to indicate that the acquire is complete
|
/* Now change the partition type to the derived "pending" type UUID to indicate that the
|
||||||
* and the partition is ready for install. */
|
* acquire is complete and the partition is ready for install. */
|
||||||
r = free_and_strdup_warn(&t->partition_info.label, t->temporary_pending_partition_label);
|
t->partition_info.type = t->partition_type_pending;
|
||||||
if (r < 0)
|
t->partition_change = PARTITION_TYPE;
|
||||||
return r;
|
|
||||||
t->partition_change = PARTITION_LABEL;
|
|
||||||
|
|
||||||
if (f->partition_uuid_set) {
|
if (f->partition_uuid_set) {
|
||||||
t->partition_info.uuid = f->partition_uuid;
|
t->partition_info.uuid = f->partition_uuid;
|
||||||
@ -1577,7 +1578,9 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
|||||||
t->partition_change |= PARTITION_GROWFS;
|
t->partition_change |= PARTITION_GROWFS;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_debug("Relabelling partition '%s' to '%s'.", t->partition_info.device, t->partition_info.label);
|
log_debug("Marking partition '%s' as pending (type=%s).",
|
||||||
|
t->partition_info.device,
|
||||||
|
SD_ID128_TO_UUID_STRING(t->partition_info.type));
|
||||||
r = patch_partition(
|
r = patch_partition(
|
||||||
t->target.path,
|
t->target.path,
|
||||||
&t->partition_info,
|
&t->partition_info,
|
||||||
@ -1617,7 +1620,7 @@ int transfer_process_partial_and_pending_instance(Transfer *t, Instance *i) {
|
|||||||
|
|
||||||
/* This is the analogue of find_suitable_partition(), but since finding the suitable partition has
|
/* This is the analogue of find_suitable_partition(), but since finding the suitable partition has
|
||||||
* already happened in the acquire phase, the target should already have that information and it
|
* already happened in the acquire phase, the target should already have that information and it
|
||||||
* should already have been claimed as `PND#`. */
|
* should already have been claimed with the pending partition type UUID. */
|
||||||
if (t->target.type == RESOURCE_PARTITION) {
|
if (t->target.type == RESOURCE_PARTITION) {
|
||||||
assert(i->resource == &t->target);
|
assert(i->resource == &t->target);
|
||||||
assert(i->is_pending);
|
assert(i->is_pending);
|
||||||
@ -1665,14 +1668,17 @@ int transfer_install_instance(
|
|||||||
t->temporary_pending_path = mfree(t->temporary_pending_path);
|
t->temporary_pending_path = mfree(t->temporary_pending_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (t->temporary_pending_partition_label) {
|
if (t->final_partition_label) {
|
||||||
assert(t->target.type == RESOURCE_PARTITION);
|
assert(t->target.type == RESOURCE_PARTITION);
|
||||||
assert(t->final_partition_label);
|
assert(t->target.partition_type_set);
|
||||||
|
|
||||||
r = free_and_strdup_warn(&t->partition_info.label, t->final_partition_label);
|
r = free_and_strdup_warn(&t->partition_info.label, t->final_partition_label);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
t->partition_change = PARTITION_LABEL;
|
|
||||||
|
/* Restore the original partition type UUID now that the partition is fully installed. */
|
||||||
|
t->partition_info.type = t->target.partition_type.uuid;
|
||||||
|
t->partition_change = PARTITION_LABEL | PARTITION_TYPE;
|
||||||
|
|
||||||
r = patch_partition(
|
r = patch_partition(
|
||||||
t->target.path,
|
t->target.path,
|
||||||
|
|||||||
@ -48,8 +48,11 @@ typedef struct Transfer {
|
|||||||
PartitionInfo partition_info;
|
PartitionInfo partition_info;
|
||||||
PartitionChange partition_change;
|
PartitionChange partition_change;
|
||||||
char *final_partition_label;
|
char *final_partition_label;
|
||||||
char *temporary_partial_partition_label;
|
|
||||||
char *temporary_pending_partition_label;
|
/* Derived partition type UUIDs used to indicate partial/pending state on the partition type level,
|
||||||
|
* instead of polluting the partition label with prefixes */
|
||||||
|
sd_id128_t partition_type_partial;
|
||||||
|
sd_id128_t partition_type_pending;
|
||||||
|
|
||||||
Context *context;
|
Context *context;
|
||||||
} Transfer;
|
} Transfer;
|
||||||
|
|||||||
@ -405,6 +405,11 @@ executables += [
|
|||||||
'dependencies' : libopenssl,
|
'dependencies' : libopenssl,
|
||||||
'conditions' : ['HAVE_OPENSSL'],
|
'conditions' : ['HAVE_OPENSSL'],
|
||||||
},
|
},
|
||||||
|
test_template + {
|
||||||
|
'sources' : files('test-pkcs7-util.c'),
|
||||||
|
'dependencies' : libopenssl,
|
||||||
|
'conditions' : ['HAVE_OPENSSL'],
|
||||||
|
},
|
||||||
test_template + {
|
test_template + {
|
||||||
'sources' : files('test-parse-util.c'),
|
'sources' : files('test-parse-util.c'),
|
||||||
'dependencies' : libm,
|
'dependencies' : libm,
|
||||||
|
|||||||
55
src/test/test-pkcs7-util.c
Normal file
55
src/test/test-pkcs7-util.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "hexdecoct.h"
|
||||||
|
#include "iovec-util.h"
|
||||||
|
#include "pkcs7-util.h"
|
||||||
|
#include "tests.h"
|
||||||
|
|
||||||
|
TEST(pkcs7_extract_signers) {
|
||||||
|
|
||||||
|
const char tsig[] =
|
||||||
|
"MIIEgwYJKoZIhvcNAQcCoIIEdDCCBHACAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0BBwGgggLp"
|
||||||
|
"MIIC5TCCAc2gAwIBAgIURlvlj5ak0ZhvNS8hENNKwVv60x0wDQYJKoZIhvcNAQELBQAwGzEZMBcGA1UE"
|
||||||
|
"AwwQbWtvc2kgb2YgbGVubmFydDAeFw0yNTAyMDMxMTAwMjNaFw0yNzAyMDMxMTAwMjNaMBsxGTAXBgNV"
|
||||||
|
"BAMMEG1rb3NpIG9mIGxlbm5hcnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCldQdmHVgU"
|
||||||
|
"m4saaSqEpF1EVRf9pcIxpVShROBGAbxpxs4BQ7vV7zCg5bXwEVaqaENlIyzqPAj+YQifQS3Dj6LQfH3i"
|
||||||
|
"War+ciKAv4PYcESG+pcdBb2kJOS8cVD6abZlO9rInvdhXK6PhYF7VohMwMPj/yJYdO50skwA5OQsCHO6"
|
||||||
|
"amowh0tVzNzpbaJZg6wWIyTf3+ZzZdnOl5EvBCaUqeUhbaxRV9SrAw51rzAOnndUhW1we/vVKEGAPk3c"
|
||||||
|
"SCb0LLPBG20XX2C00UXgnCWBkU5rkq6BnWSuInhFKCa48avstIet1ZFBA5T5J83ncU9UOVFksEYBXFoc"
|
||||||
|
"lzcmcbx/2/oFAgMBAAGjITAfMB0GA1UdDgQWBBQewILFCixwq2rejOvJqmZSug2BBDANBgkqhkiG9w0B"
|
||||||
|
"AQsFAAOCAQEAIvaeNPaJUoIUN5lQC/kcCiKeys96WNRGL2wbTp5PqdnRw14sbWY5iC2z13ih3dmTI9NF"
|
||||||
|
"TBa7C/ji+5BaAfiJF17LOV00Y8eP5V94fHz4isb0sv5RzLsE4h8X7QFk4JBdV5GiCDzPXjxQAx9kM2so"
|
||||||
|
"9RGtL8EhHpNygYDgyZ18YeiwcUPkCXT+xG2rM6s/Xlsji0s/18ycI4G8AC8dj5HycyS9BiZHgKrkgqTb"
|
||||||
|
"VPo4zHYzhZdh0Qrd0J4YpoaotzQ35bkH9PtIkF6C7mE1Z7uMSGFkGQASgJ0BDTpM8QPAf2HIR2xxEtJR"
|
||||||
|
"ZXkwxxdC+W9AJAzqJldmCHYGSrSR54J0rDGCAV4wggFaAgEBMDMwGzEZMBcGA1UEAwwQbWtvc2kgb2Yg"
|
||||||
|
"bGVubmFydAIURlvlj5ak0ZhvNS8hENNKwVv60x0wDQYJYIZIAWUDBAIBBQAwDQYJKoZIhvcNAQEBBQAE"
|
||||||
|
"ggEAXccqvpiEWsz/xvuLhINVZKIOznVdqjkERbZSqCBK94BYESSd+cijaB4XbYaFUZ45Bb3uUDQ56Ojq"
|
||||||
|
"WoY1elEfqPyCb4vc887QoHmxI0BtdIaHhIDfCGBxhX8fwMknxqjgFa9YvONmDtv4QG4syTw+U3SEqBaa"
|
||||||
|
"Avftqaa4v4eLk4uZ0nMIgMkx4qOlaxknpP404/nyZPANkOIwDxviNtRBCN9zSiPSqo1zre1vqzaM57Ww"
|
||||||
|
"8zJASsPEzNR7OsPoLaIZv2OHXpowsRB78TuXGkQnm74T6xdG6DNs24jTYJuCPfGuYLHbrytdhXpFBS6m"
|
||||||
|
"Orz9715jK2NU5VvGhNVXX4chcw==";
|
||||||
|
|
||||||
|
_cleanup_free_ void *sig = NULL;
|
||||||
|
size_t siglen;
|
||||||
|
ASSERT_OK(unbase64mem(tsig, &sig, &siglen));
|
||||||
|
|
||||||
|
size_t n_signers = 0;
|
||||||
|
Signer *signers = NULL;
|
||||||
|
CLEANUP_ARRAY(signers, n_signers, signer_free_many);
|
||||||
|
|
||||||
|
ASSERT_OK_EQ(pkcs7_extract_signers(&IOVEC_MAKE(sig, siglen), &signers, &n_signers), 1);
|
||||||
|
ASSERT_EQ(n_signers, 1U);
|
||||||
|
ASSERT_EQ(signers[0].issuer.iov_len, 29U);
|
||||||
|
ASSERT_EQ(signers[0].serial.iov_len, 22U);
|
||||||
|
|
||||||
|
_cleanup_free_ char *issuer = NULL;
|
||||||
|
ASSERT_OK(base64mem(signers[0].issuer.iov_base, signers[0].issuer.iov_len, &issuer));
|
||||||
|
|
||||||
|
_cleanup_free_ char *serial = NULL;
|
||||||
|
ASSERT_OK(base64mem(signers[0].serial.iov_base, signers[0].serial.iov_len, &serial));
|
||||||
|
|
||||||
|
ASSERT_STREQ(issuer, "MBsxGTAXBgNVBAMMEG1rb3NpIG9mIGxlbm5hcnQ=");
|
||||||
|
ASSERT_STREQ(serial, "AhRGW+WPlqTRmG81LyEQ00rBW/rTHQ==");
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_TEST_MAIN(LOG_INFO);
|
||||||
@ -31,12 +31,12 @@ executables += [
|
|||||||
'HAVE_TPM2',
|
'HAVE_TPM2',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if conf.get('ENABLE_BOOTLOADER') == 1 and conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_TPM2') == 1
|
if conf.get('ENABLE_BOOTLOADER') == 1 and conf.get('HAVE_OPENSSL') == 1 and conf.get('HAVE_TPM2') == 1
|
||||||
nvpcrs = [ 'cryptsetup',
|
nvpcrs = [ 'cryptsetup',
|
||||||
'hardware' ]
|
'hardware',
|
||||||
|
'verity']
|
||||||
foreach n : nvpcrs
|
foreach n : nvpcrs
|
||||||
custom_target(
|
custom_target(
|
||||||
input : 'nvpcr/' + n + '.nvpcr.in',
|
input : 'nvpcr/' + n + '.nvpcr.in',
|
||||||
|
|||||||
5
src/tpm2-setup/nvpcr/verity.nvpcr.in
Normal file
5
src/tpm2-setup/nvpcr/verity.nvpcr.in
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name" : "verity",
|
||||||
|
"algorithm" : "sha256",
|
||||||
|
"nvIndex" : {{TPM2_NVPCR_BASE + 2}}
|
||||||
|
}
|
||||||
@ -16,9 +16,11 @@
|
|||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
|
#include "pcrextend-util.h"
|
||||||
#include "pretty-print.h"
|
#include "pretty-print.h"
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
#include "tpm2-util.h"
|
||||||
#include "verbs.h"
|
#include "verbs.h"
|
||||||
|
|
||||||
static char *arg_hash = NULL; /* the hash algorithm */
|
static char *arg_hash = NULL; /* the hash algorithm */
|
||||||
@ -38,12 +40,14 @@ static uint64_t arg_fec_roots = 2;
|
|||||||
static void *arg_root_hash_signature = NULL;
|
static void *arg_root_hash_signature = NULL;
|
||||||
static size_t arg_root_hash_signature_size = 0;
|
static size_t arg_root_hash_signature_size = 0;
|
||||||
static bool arg_root_hash_signature_auto = false;
|
static bool arg_root_hash_signature_auto = false;
|
||||||
|
static char *arg_tpm2_measure_nvpcr = NULL;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_salt, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_salt, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_uuid, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_uuid, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_fec_what, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_fec_what, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_root_hash_signature, freep);
|
||||||
|
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_nvpcr, freep);
|
||||||
|
|
||||||
static int help(void) {
|
static int help(void) {
|
||||||
_cleanup_free_ char *link = NULL;
|
_cleanup_free_ char *link = NULL;
|
||||||
@ -142,7 +146,7 @@ static int parse_options(const char *options) {
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
_cleanup_free_ char *word = NULL;
|
_cleanup_free_ char *word = NULL;
|
||||||
char *val;
|
const char *val;
|
||||||
|
|
||||||
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
|
r = extract_first_word(&options, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -290,6 +294,21 @@ static int parse_options(const char *options) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
} else if ((val = startswith(word, "tpm2-measure-nvpcr="))) {
|
||||||
|
r = isempty(val) ? 0 : parse_boolean(val);
|
||||||
|
if (r == 0) {
|
||||||
|
arg_tpm2_measure_nvpcr = mfree(arg_tpm2_measure_nvpcr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (r > 0)
|
||||||
|
val = "verity";
|
||||||
|
else if (!tpm2_nvpcr_name_is_valid(val)) {
|
||||||
|
log_warning("Invalid NvPCR name, ignoring: %s", word);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free_and_strdup(&arg_tpm2_measure_nvpcr, val) < 0)
|
||||||
|
return log_oom();
|
||||||
} else
|
} else
|
||||||
log_warning("Encountered unknown option '%s', ignoring.", word);
|
log_warning("Encountered unknown option '%s', ignoring.", word);
|
||||||
}
|
}
|
||||||
@ -423,6 +442,11 @@ static int verb_attach(int argc, char *argv[], void *userdata) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to set up verity device '%s': %m", volume);
|
return log_error_errno(r, "Failed to set up verity device '%s': %m", volume);
|
||||||
|
|
||||||
|
(void) pcrextend_verity_now(
|
||||||
|
volume,
|
||||||
|
&IOVEC_MAKE(rh, rh_size),
|
||||||
|
&IOVEC_MAKE(arg_root_hash_signature, arg_root_hash_signature_size));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,8 @@ at_exit() {
|
|||||||
jq --seq --slurp </run/log/systemd/tpm2-measure.log
|
jq --seq --slurp </run/log/systemd/tpm2-measure.log
|
||||||
fi
|
fi
|
||||||
|
|
||||||
rm -rf /run/nvpcr
|
rm -rf /run/nvpcr /tmp/nvpcr
|
||||||
|
rm -f /var/tmp/nvpcr.raw /run/verity.d/test-79-nvpcr.crt
|
||||||
}
|
}
|
||||||
|
|
||||||
trap at_exit EXIT
|
trap at_exit EXIT
|
||||||
@ -52,3 +53,46 @@ test "$DIGEST_ACTUAL2" != "$DIGEST_EXPECTED"
|
|||||||
DIGEST_MEASURED2="$(echo -n "schnurz" | openssl dgst -sha256 -hex -r | cut -d' ' -f1)"
|
DIGEST_MEASURED2="$(echo -n "schnurz" | openssl dgst -sha256 -hex -r | cut -d' ' -f1)"
|
||||||
DIGEST_EXPECTED2="$(echo "$DIGEST_EXPECTED$DIGEST_MEASURED2" | tr '[:lower:]' '[:upper:]' | basenc --base16 -d | openssl dgst -sha256 -hex -r | cut -d' ' -f1)"
|
DIGEST_EXPECTED2="$(echo "$DIGEST_EXPECTED$DIGEST_MEASURED2" | tr '[:lower:]' '[:upper:]' | basenc --base16 -d | openssl dgst -sha256 -hex -r | cut -d' ' -f1)"
|
||||||
test "$DIGEST_ACTUAL2" = "$DIGEST_EXPECTED2"
|
test "$DIGEST_ACTUAL2" = "$DIGEST_EXPECTED2"
|
||||||
|
|
||||||
|
mkdir /tmp/nvpcr
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
[ req_distinguished_name ]
|
||||||
|
C = DE
|
||||||
|
ST = Test State
|
||||||
|
L = Test Locality
|
||||||
|
O = Org Name
|
||||||
|
OU = Org Unit Name
|
||||||
|
CN = Common Name
|
||||||
|
emailAddress = test@email.com
|
||||||
|
EOF
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
mkdir /tmp/nvpcr/tree
|
||||||
|
touch /tmp/nvpcr/tree/file
|
||||||
|
|
||||||
|
SYSTEMD_REPART_OVERRIDE_FSTYPE=squashfs \
|
||||||
|
systemd-repart -P \
|
||||||
|
-s /tmp/nvpcr/tree \
|
||||||
|
--certificate=/tmp/nvpcr/test-70-nvpcr.crt \
|
||||||
|
--private-key=/tmp/nvpcr/test-70-nvpcr.key \
|
||||||
|
/var/tmp/nvpcr.raw
|
||||||
|
|
||||||
|
mkdir -p /run/verity.d
|
||||||
|
cp /tmp/nvpcr/test-70-nvpcr.crt /run/verity.d/
|
||||||
|
|
||||||
|
cp /run/log/systemd/tpm2-measure.log /tmp/nvpcr/log-before
|
||||||
|
|
||||||
|
systemd-dissect --image-policy='root=signed:=absent+unused' --mtree /var/tmp/nvpcr.raw
|
||||||
|
|
||||||
|
set +o pipefail
|
||||||
|
diff /tmp/nvpcr/log-before /run/log/systemd/tpm2-measure.log | grep -F '"content":{"nvIndexName":"verity","string":"verity:'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user