Merge pull request #25006 from poettering/pcr15

cryptsetup: measure LUKS volume keys to PCR 15
This commit is contained in:
Lennart Poettering
2023-01-17 11:04:03 +01:00
committed by GitHub
29 changed files with 1073 additions and 195 deletions
+2 -3
View File
@@ -451,9 +451,8 @@ Features:
and via the time window TPM logic invalidated if node doesn't keep itself
updated, or becomes corrupted in some way.
* Always measure the LUKS rootfs volume key into PCR 15, and derive the machine
ID from it securely. This would then allow us to bind secrets a specific
system securely.
* in the initrd, once the rootfs encryption key has been measured to PCR 15,
derive default machine ID to use from it, and pass it to host PID 1.
* tree-wide: convert as much as possible over to use sd_event_set_signal_exit(), instead
of manually hooking into SIGINT/SIGTERM
+6 -3
View File
@@ -506,7 +506,10 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
journal. Note that journal files in compact mode are limited to 4G to allow use of
32-bit offsets. Enabled by default.
`systemd-pcrphase`:
`systemd-pcrphase`, `systemd-cryptsetup`:
* `$SYSTEMD_PCRPHASE_STUB_VERIFY` – Takes a boolean. If false the requested
measurement is done even if no EFI stub usage was reported via EFI variables.
* `$SYSTEMD_FORCE_MEASURE=1` — If set, force measuring of resources (which are
marked for measurement) even if not booted on a kernel equipped with
systemd-stub. Normally, requested measurement of resources is conditionalized
on kernels that have booted with `systemd-stub`. With this environment
variable the test for that my be bypassed, for testing purposes.
+22
View File
@@ -700,6 +700,28 @@
order).</para></listitem>
</varlistentry>
<varlistentry>
<term><option>tpm2-measure-pcr=</option></term>
<listitem><para>Controls whether to measure the volume key of the encrypted volume to a TPM2 PCR. If
set to "no" (which is the default) no PCR extension is done. If set to "yes" the volume key is
measured into PCR 15. If set to a decimal integer in the range 0…23 the volume key is measured into
the specified PCR. The volume key is measured along with the activated volume name and its UUID. This
functionality is particularly useful for the encrypted volume backing the root file system, as it
then allows later TPM objects to be securely bound to the root file system and hence the specific
installation.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>tpm2-measure-bank=</option></term>
<listitem><para>Selects one or more TPM2 PCR banks to measure the volume key into, as configured with
<option>tpm2-measure-pcr=</option> above. Multiple banks may be specified, separated by a colon
character. If not specified automatically determines available and used banks. Expects a message
digest name (e.g. <literal>sha1</literal>, <literal>sha256</literal>, …) as argument, to identify the
bank.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>token-timeout=</option></term>
+4 -1
View File
@@ -966,7 +966,10 @@ manpages = [
['systemd-path', '1', [], ''],
['systemd-pcrphase.service',
'8',
['systemd-pcrphase',
['systemd-pcrfs-root.service',
'systemd-pcrfs@.service',
'systemd-pcrmachine.service',
'systemd-pcrphase',
'systemd-pcrphase-initrd.service',
'systemd-pcrphase-sysinit.service'],
'HAVE_GNU_EFI'],
+5
View File
@@ -324,6 +324,11 @@
<entry>14</entry>
<entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
</row>
<row>
<entry>15</entry>
<entry><citerefentry><refentrytitle>systemd-cryptsetup</refentrytitle><manvolnum>7</manvolnum></citerefentry> optionally measures the volume key of activated LUKS volumes into this PCR.</entry>
</row>
</tbody>
</tgroup>
</table>
+8
View File
@@ -221,6 +221,13 @@
systems, make sure to set the correct default subvolumes on them,
using <command>btrfs subvolume set-default</command>.</para>
<para>If the system was booted via
<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
<filename>/var/</filename> volume identifiers (and volume encryption key in case it is encrypted) will be
automatically measured into PCR 15 on activation, via
<citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para><filename>systemd-gpt-auto-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1>
@@ -278,6 +285,7 @@
<citerefentry><refentrytitle>systemd.swap</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry project='die-net'><refentrytitle>cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+46 -5
View File
@@ -20,15 +20,21 @@
<refname>systemd-pcrphase.service</refname>
<refname>systemd-pcrphase-sysinit.service</refname>
<refname>systemd-pcrphase-initrd.service</refname>
<refname>systemd-pcrmachine.service</refname>
<refname>systemd-pcrfs-root.service</refname>
<refname>systemd-pcrfs@.service</refname>
<refname>systemd-pcrphase</refname>
<refpurpose>Measure boot phase into TPM2 PCR 11</refpurpose>
<refpurpose>Measure boot phase into TPM2 PCR 11, machine ID and file system identity into PCR 15</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-pcrphase.service</filename></para>
<para><filename>systemd-pcrphase-sysinit.service</filename></para>
<para><filename>systemd-pcrphase-initrd.service</filename></para>
<para><filename>/usr/lib/systemd/system-pcrphase</filename> <replaceable>STRING</replaceable></para>
<para><filename>systemd-pcrmachine.service</filename></para>
<para><filename>systemd-pcrfs-root.service</filename></para>
<para><filename>systemd-pcrfs@.service</filename></para>
<para><filename>/usr/lib/systemd/system-pcrphase</filename> <optional><replaceable>STRING</replaceable></optional></para>
</refsynopsisdiv>
<refsect1>
@@ -39,13 +45,23 @@
<filename>systemd-pcrphase-initrd.service</filename> are system services that measure specific strings
into TPM2 PCR 11 during boot at various milestones of the boot process.</para>
<para><filename>systemd-pcrmachine.service</filename> is a system service that measures the machine ID
(see <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry>) into
PCR 15.</para>
<para><filename>systemd-pcrfs-root.service</filename> and <filename>systemd-pcrfs@.service</filename> are
services that measure file system identity information (i.e. mount point, file system type, label and
UUID, partition label and UUID) into PCR 15. <filename>systemd-pcrfs-root.service</filename> does so for
the root file system, <filename>systemd-pcrfs@.service</filename> is a template unit that measures the
file system indicated by its instance identifier instead.</para>
<para>These services require
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> to be
used in a unified kernel image (UKI). They execute no operation when the stub has not been used to invoke
the kernel. The stub will measure the invoked kernel and associated vendor resources into PCR 11 before
handing control to it; once userspace is invoked these services then will extend TPM2 PCR 11 with certain
literal strings indicating phases of the boot process. During a regular boot process the following
strings are used:</para>
literal strings indicating phases of the boot process. During a regular boot process PCR 11 is extended
with the following strings:</para>
<orderedlist>
<listitem><para><literal>enter-initrd</literal> — early when the initrd initializes, before activating
@@ -102,6 +118,14 @@
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry> to
pre-calculate expected PCR 11 values for specific boot phases (via the <option>--phase=</option> switch).
</para>
<para><filename>systemd-pcrfs-root.service</filename> and <filename>systemd-pcrfs@.service</filename> are
automatically pulled into the initial transaction by
<citerefentry><refentrytitle>systemd-gpt-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
for the root and <filename>/var/</filename> file
systems. <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
will do this for all mounts with the <option>x-systemd.pcrfs</option> mount option in
<filename>/etc/fstab</filename>.</para>
</refsect1>
<refsect1>
@@ -137,6 +161,21 @@
TPM2 device will cause the invocation to fail.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--machine-id</option></term>
<listitem><para>Instead of measuring a word specified on the command line into PCR 11, measure the
host's machine ID into PCR 15.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--file-system=</option></term>
<listitem><para>Instead of measuring a word specified on the command line into PCR 11, measure
identity information of the specified file system into PCR 15. The parameter must be the path to the
established mount point of the file system to measure.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
@@ -148,7 +187,9 @@
<para>
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-gpt-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
+14
View File
@@ -366,6 +366,20 @@
<varname>Options=</varname> setting in a unit file.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>x-systemd.pcrfs</option></term>
<listitem><para>Measures file system identity information (mount point, type, label, UUID, partition
label, partition UUID) into PCR 15 after the file system has been mounted. This ensures the
<citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
or <filename>systemd-pcrfs-root.service</filename> services are pulled in by the mount unit.</para>
<para>Note that this option can only be used in <filename>/etc/fstab</filename>, and will be ignored
when part of the <varname>Options=</varname> setting in a unit file. It is also implied for the root
and <filename>/usr/</filename> partitions dicovered by
<citerefentry><refentrytitle>systemd-gpt-auto-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>x-systemd.rw-only</option></term>
+3 -1
View File
@@ -2677,6 +2677,7 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
link_with : [libshared],
dependencies : [libopenssl,
tpm2,
libblkid,
versiondep],
install_rpath : rootpkglibdir,
install : true,
@@ -2920,7 +2921,8 @@ if conf.get('HAVE_LIBCRYPTSETUP') == 1
link_with : [libshared],
dependencies : [libcryptsetup,
libp11kit,
versiondep],
versiondep,
libopenssl],
install_rpath : rootpkglibdir,
install : true,
install_dir : rootlibexecdir)
+4
View File
@@ -87,6 +87,10 @@
#define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service"
#define SPECIAL_VOLATILE_ROOT_SERVICE "systemd-volatile-root.service"
#define SPECIAL_UDEVD_SERVICE "systemd-udevd.service"
#define SPECIAL_GROWFS_SERVICE "systemd-growfs@.service"
#define SPECIAL_GROWFS_ROOT_SERVICE "systemd-growfs-root.service"
#define SPECIAL_PCRFS_SERVICE "systemd-pcrfs@.service"
#define SPECIAL_PCRFS_ROOT_SERVICE "systemd-pcrfs-root.service"
/* Services systemd relies on */
#define SPECIAL_DBUS_SERVICE "dbus.service"
+189 -99
View File
@@ -2,14 +2,21 @@
#include <getopt.h>
#include <sd-device.h>
#include <sd-messages.h>
#include "blkid-util.h"
#include "blockdev-util.h"
#include "build.h"
#include "chase-symlinks.h"
#include "efi-loader.h"
#include "efivars.h"
#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "main-func.h"
#include "mountpoint-util.h"
#include "openssl-util.h"
#include "parse-util.h"
#include "parse-argument.h"
#include "pretty-print.h"
#include "tpm-pcr.h"
#include "tpm2-util.h"
@@ -17,9 +24,12 @@
static bool arg_graceful = false;
static char *arg_tpm2_device = NULL;
static char **arg_banks = NULL;
static char *arg_file_system = NULL;
static bool arg_machine_id = false;
STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_file_system, freep);
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
@@ -29,7 +39,9 @@ static int help(int argc, char *argv[], void *userdata) {
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] WORD ...\n"
printf("%1$s [OPTIONS...] WORD\n"
"%1$s [OPTIONS...] --file-system=PATH\n"
"%1$s [OPTIONS...] --machine-id\n"
"\n%5$sMeasure boot phase into TPM2 PCR 11.%6$s\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
@@ -37,6 +49,8 @@ static int help(int argc, char *argv[], void *userdata) {
" --bank=DIGEST Select TPM bank (SHA1, SHA256)\n"
" --tpm2-device=PATH Use specified TPM2 device\n"
" --graceful Exit gracefully if no TPM2 device is found\n"
" --file-system=PATH Measure UUID/labels of file system into PCR 15\n"
" --machine-id Measure machine ID into PCR 15\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -54,6 +68,8 @@ static int parse_argv(int argc, char *argv[]) {
ARG_BANK,
ARG_TPM2_DEVICE,
ARG_GRACEFUL,
ARG_FILE_SYSTEM,
ARG_MACHINE_ID,
};
static const struct option options[] = {
@@ -62,10 +78,12 @@ static int parse_argv(int argc, char *argv[]) {
{ "bank", required_argument, NULL, ARG_BANK },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
{ "file-system", required_argument, NULL, ARG_FILE_SYSTEM },
{ "machine-id", no_argument, NULL, ARG_MACHINE_ID },
{}
};
int c;
int c, r;
assert(argc >= 0);
assert(argv);
@@ -113,6 +131,17 @@ static int parse_argv(int argc, char *argv[]) {
arg_graceful = true;
break;
case ARG_FILE_SYSTEM:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_file_system);
if (r < 0)
return r;
break;
case ARG_MACHINE_ID:
arg_machine_id = true;
break;
case '?':
return -EINVAL;
@@ -120,49 +149,101 @@ static int parse_argv(int argc, char *argv[]) {
assert_not_reached();
}
if (arg_file_system && arg_machine_id)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--file-system= and --machine-id may not be combined.");
return 1;
}
static int determine_banks(struct tpm2_context *c) {
_cleanup_free_ TPMI_ALG_HASH *algs = NULL;
int n_algs, r;
static int determine_banks(struct tpm2_context *c, unsigned target_pcr_nr) {
_cleanup_strv_free_ char **l = NULL;
int r;
assert(c);
if (!strv_isempty(arg_banks)) /* Explicitly configured? Then use that */
return 0;
n_algs = tpm2_get_good_pcr_banks(c->esys_context, UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &algs);
if (n_algs <= 0)
return n_algs;
r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << target_pcr_nr, &l);
if (r < 0)
return r;
for (int i = 0; i < n_algs; i++) {
const EVP_MD *implementation;
const char *salg;
strv_free_and_replace(arg_banks, l);
return 0;
}
salg = tpm2_pcr_bank_to_string(algs[i]);
if (!salg)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unknown PCR algorithm, can't measure.");
static int get_file_system_word(
sd_device *d,
const char *prefix,
char **ret) {
implementation = EVP_get_digestbyname(salg);
if (!implementation)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unsupported PCR algorithm, can't measure.");
int r;
r = strv_extend(&arg_banks, EVP_MD_name(implementation));
assert(d);
assert(prefix);
assert(ret);
_cleanup_close_ int block_fd = sd_device_open(d, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
if (block_fd < 0)
return block_fd;
_cleanup_(blkid_free_probep) blkid_probe b = blkid_new_probe();
if (!b)
return -ENOMEM;
errno = 0;
r = blkid_probe_set_device(b, block_fd, 0, 0);
if (r != 0)
return errno_or_else(ENOMEM);
(void) blkid_probe_enable_superblocks(b, 1);
(void) blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE|BLKID_SUBLKS_UUID|BLKID_SUBLKS_LABEL);
(void) blkid_probe_enable_partitions(b, 1);
(void) blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
errno = 0;
r = blkid_do_safeprobe(b);
if (r == _BLKID_SAFEPROBE_ERROR)
return errno_or_else(EIO);
if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
return -ENOPKG;
assert(r == _BLKID_SAFEPROBE_FOUND);
_cleanup_strv_free_ char **l = strv_new(prefix);
if (!l)
return log_oom();
FOREACH_STRING(field, "TYPE", "UUID", "LABEL", "PART_ENTRY_UUID", "PART_ENTRY_TYPE", "PART_ENTRY_NAME") {
const char *v = NULL;
(void) blkid_probe_lookup_value(b, field, &v, NULL);
_cleanup_free_ char *escaped = xescape(strempty(v), ":"); /* Avoid ambiguity around ":" */
if (!escaped)
return log_oom();
r = strv_consume(&l, TAKE_PTR(escaped));
if (r < 0)
return log_oom();
}
assert(strv_length(l) == 7); /* We always want 7 components, to avoid ambiguous strings */
_cleanup_free_ char *word = strv_join(l, ":");
if (!word)
return log_oom();
*ret = TAKE_PTR(word);
return 0;
}
static int run(int argc, char *argv[]) {
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
_cleanup_free_ char *joined = NULL, *pcr_string = NULL;
const char *word;
unsigned pcr_nr;
_cleanup_free_ char *joined = NULL, *word = NULL;
unsigned target_pcr_nr;
size_t length;
TSS2_RC rc;
int r;
log_setup();
@@ -171,16 +252,79 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
return r;
if (optind+1 != argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected a single argument.");
if (arg_file_system) {
_cleanup_free_ char *normalized = NULL, *normalized_escaped = NULL;
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
_cleanup_close_ int dfd = -EBADF;
word = argv[optind];
if (optind != argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected no argument.");
/* Refuse to measure an empty word. We want to be able to write the series of measured words
* separated by colons, where multiple separating colons are collapsed. Thus it makes sense to
* disallow an empty word to avoid ambiguities. */
if (isempty(word))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "String to measure cannot be empty, refusing.");
dfd = chase_symlinks_and_open(arg_file_system, NULL, 0, O_DIRECTORY|O_CLOEXEC, &normalized);
if (dfd < 0)
return log_error_errno(dfd, "Failed to open path '%s': %m", arg_file_system);
r = fd_is_mount_point(dfd, NULL, 0);
if (r < 0)
return log_error_errno(r, "Failed to determine if path '%s' is mount point: %m", normalized);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "Specified path '%s' is not a mount point, refusing: %m", normalized);
normalized_escaped = xescape(normalized, ":"); /* Avoid ambiguity around ":" */
if (!normalized_escaped)
return log_oom();
_cleanup_free_ char* prefix = strjoin("file-system:", normalized_escaped);
if (!prefix)
return log_oom();
r = block_device_new_from_fd(dfd, BLOCK_DEVICE_LOOKUP_BACKING, &d);
if (r < 0) {
log_notice_errno(r, "Unable to determine backing block device of '%s', measuring generic fallback file system identity string: %m", arg_file_system);
word = strjoin(prefix, "::::::");
if (!word)
return log_oom();
} else {
r = get_file_system_word(d, prefix, &word);
if (r < 0)
return log_error_errno(r, "Failed to get file system identifier string for '%s': %m", arg_file_system);
}
target_pcr_nr = TPM_PCR_INDEX_VOLUME_KEY; /* → PCR 15 */
} else if (arg_machine_id) {
sd_id128_t mid;
if (optind != argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected no argument.");
r = sd_id128_get_machine(&mid);
if (r < 0)
return log_error_errno(r, "Failed to acquire machine ID: %m");
word = strjoin("machine-id:", SD_ID128_TO_STRING(mid));
if (!word)
return log_oom();
target_pcr_nr = TPM_PCR_INDEX_VOLUME_KEY; /* → PCR 15 */
} else {
if (optind+1 != argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected a single argument.");
word = strdup(argv[optind]);
if (!word)
return log_oom();
/* Refuse to measure an empty word. We want to be able to write the series of measured words
* separated by colons, where multiple separating colons are collapsed. Thus it makes sense to
* disallow an empty word to avoid ambiguities. */
if (isempty(word))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "String to measure cannot be empty, refusing.");
target_pcr_nr = TPM_PCR_INDEX_KERNEL_IMAGE; /* → PCR 11 */
}
if (arg_graceful && tpm2_support() != TPM2_SUPPORT_FULL) {
log_notice("No complete TPM2 support detected, exiting gracefully.");
@@ -189,32 +333,13 @@ static int run(int argc, char *argv[]) {
length = strlen(word);
int b = getenv_bool("SYSTEMD_PCRPHASE_STUB_VERIFY");
if (b < 0 && b != -ENXIO)
log_warning_errno(b, "Unable to parse $SYSTEMD_PCRPHASE_STUB_VERIFY value, ignoring.");
/* Skip logic if sd-stub is not used, after all PCR 11 might have a very different purpose then. */
r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
if (r == -ENOENT) {
if (b != 0) {
log_info("Kernel stub did not measure kernel image into PCR %u, skipping measurement.", TPM_PCR_INDEX_KERNEL_IMAGE);
return EXIT_SUCCESS;
} else
log_notice("Kernel stub did not measure kernel image into PCR %u, but told to measure anyway, hence proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
} else if (r < 0)
return log_error_errno(r, "Failed to read StubPcrKernelImage EFI variable: %m");
else {
/* Let's validate that the stub announced PCR 11 as we expected. */
r = safe_atou(pcr_string, &pcr_nr);
if (r < 0)
return log_error_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE) {
if (b != 0)
return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
else
log_notice("Kernel stub measured kernel image into PCR %u, which is different than expected %u, but told to measure anyway, hence proceeding.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
} else
log_debug("Kernel stub reported same PCR %u as we want to use, proceeding.", TPM_PCR_INDEX_KERNEL_IMAGE);
r = efi_stub_measured();
if (r < 0)
return log_error_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled: %m");
if (r == 0) {
log_info("Kernel stub did not measure kernel image into PCR %u, skipping userspace measurement, too.", TPM_PCR_INDEX_KERNEL_IMAGE);
return EXIT_SUCCESS;
}
r = dlopen_tpm2();
@@ -225,62 +350,27 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
r = determine_banks(&c);
r = determine_banks(&c, target_pcr_nr);
if (r < 0)
return r;
if (strv_isempty(arg_banks)) /* Still none? */
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Found a TPM2 without enabled PCR banks. Can't operate.");
TPML_DIGEST_VALUES values = {};
STRV_FOREACH(bank, arg_banks) {
const EVP_MD *implementation;
int id;
assert_se(implementation = EVP_get_digestbyname(*bank));
if (values.count >= ELEMENTSOF(values.digests))
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Too many banks selected.");
if ((size_t) EVP_MD_size(implementation) > sizeof(values.digests[values.count].digest))
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Hash result too large for TPM2.");
id = tpm2_pcr_bank_from_string(EVP_MD_name(implementation));
if (id < 0)
return log_error_errno(id, "Can't map hash name to TPM2.");
values.digests[values.count].hashAlg = id;
if (EVP_Digest(word, length, (unsigned char*) &values.digests[values.count].digest, NULL, implementation, NULL) != 1)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash word.");
values.count++;
}
joined = strv_join(arg_banks, ", ");
if (!joined)
return log_oom();
log_debug("Measuring '%s' into PCR index %u, banks %s.", word, TPM_PCR_INDEX_KERNEL_IMAGE, joined);
log_debug("Measuring '%s' into PCR index %u, banks %s.", word, target_pcr_nr, joined);
rc = sym_Esys_PCR_Extend(
c.esys_context,
ESYS_TR_PCR0 + TPM_PCR_INDEX_KERNEL_IMAGE, /* → PCR 11 */
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
&values);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to measure '%s': %s",
word,
sym_Tss2_RC_Decode(rc));
r = tpm2_extend_bytes(c.esys_context, arg_banks, target_pcr_nr, word, length, NULL, 0);
if (r < 0)
return r;
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
LOG_MESSAGE("Successfully extended PCR index %u with '%s' (banks %s).", TPM_PCR_INDEX_KERNEL_IMAGE, word, joined),
LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", target_pcr_nr, word, joined),
"MEASURING=%s", word,
"PCR=%u", TPM_PCR_INDEX_KERNEL_IMAGE,
"PCR=%u", target_pcr_nr,
"BANKS=%s", joined);
return EXIT_SUCCESS;
+221 -14
View File
@@ -8,6 +8,7 @@
#include <unistd.h>
#include "sd-device.h"
#include "sd-messages.h"
#include "alloc-util.h"
#include "ask-password-api.h"
@@ -18,6 +19,7 @@
#include "cryptsetup-util.h"
#include "device-util.h"
#include "efi-api.h"
#include "efi-loader.h"
#include "env-util.h"
#include "escape.h"
#include "fileio.h"
@@ -38,6 +40,7 @@
#include "random-util.h"
#include "string-table.h"
#include "strv.h"
#include "tpm-pcr.h"
#include "tpm2-util.h"
/* internal helper */
@@ -89,13 +92,15 @@ static bool arg_fido2_device_auto = false;
static void *arg_fido2_cid = NULL;
static size_t arg_fido2_cid_size = 0;
static char *arg_fido2_rp_id = NULL;
static char *arg_tpm2_device = NULL;
static char *arg_tpm2_device = NULL; /* These and the following fields are about locking an encypted volume to the local TPM */
static bool arg_tpm2_device_auto = false;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
static char *arg_tpm2_signature = NULL;
static bool arg_tpm2_pin = false;
static bool arg_headless = false;
static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
static unsigned arg_tpm2_measure_pcr = UINT_MAX; /* This and the following field is about measuring the unlocked volume key to the local TPM */
static char **arg_tpm2_measure_banks = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@@ -107,6 +112,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_fido2_cid, freep);
STATIC_DESTRUCTOR_REGISTER(arg_fido2_rp_id, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_measure_banks, strv_freep);
static const char* const passphrase_type_table[_PASSPHRASE_TYPE_MAX] = {
[PASSPHRASE_REGULAR] = "passphrase",
@@ -420,6 +426,48 @@ static int parse_one_option(const char *option) {
arg_tpm2_pin = r;
} else if ((val = startswith(option, "tpm2-measure-pcr="))) {
unsigned pcr;
r = safe_atou(val, &pcr);
if (r < 0) {
r = parse_boolean(val);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX;
} else if (pcr >= TPM2_PCRS_MAX) {
log_error("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
return 0;
}
arg_tpm2_measure_pcr = pcr;
} else if ((val = startswith(option, "tpm2-measure-bank="))) {
#if HAVE_OPENSSL
_cleanup_strv_free_ char **l = NULL;
l = strv_split(optarg, ":");
if (!l)
return log_oom();
STRV_FOREACH(i, l) {
const EVP_MD *implementation;
implementation = EVP_get_digestbyname(*i);
if (!implementation)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown bank '%s', refusing.", val);
if (strv_extend(&arg_tpm2_measure_banks, EVP_MD_name(implementation)) < 0)
return log_oom();
}
#else
log_error("Build lacks OpenSSL support, cannot measure to PCR banks, ignoring: %s", option);
#endif
} else if ((val = startswith(option, "try-empty-password="))) {
r = parse_boolean(val);
@@ -762,6 +810,157 @@ static int get_password(
return 0;
}
static int measure_volume_key(
struct crypt_device *cd,
const char *name,
const void *volume_key,
size_t volume_key_size) {
int r;
assert(cd);
assert(name);
assert(volume_key);
assert(volume_key_size > 0);
if (arg_tpm2_measure_pcr == UINT_MAX) {
log_debug("Not measuring volume key, deactivated.");
return 0;
}
r = efi_stub_measured();
if (r < 0)
return log_warning_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled: %m");
if (r == 0) {
log_debug("Kernel stub did not measure kernel image into the expected PCR, skipping userspace measurement, too.");
return 0;
}
#if HAVE_TPM2
r = dlopen_tpm2();
if (r < 0)
return log_error_errno(r, "Failed to load TPM2 libraries: %m");
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
r = tpm2_context_init(arg_tpm2_device, &c);
if (r < 0)
return r;
_cleanup_strv_free_ char **l = NULL;
if (strv_isempty(arg_tpm2_measure_banks)) {
r = tpm2_get_good_pcr_banks_strv(c.esys_context, UINT32_C(1) << arg_tpm2_measure_pcr, &l);
if (r < 0)
return r;
}
_cleanup_free_ char *joined = strv_join(l ?: arg_tpm2_measure_banks, ", ");
if (!joined)
return log_oom();
/* Note: we don't directly measure the volume key, it might be a security problem to send an
* unprotected direct hash of the secret volume key over the wire to the TPM. Hence let's instead
* send a HMAC signature instead. */
_cleanup_free_ char *escaped = NULL;
escaped = xescape(name, ":"); /* avoid ambiguity around ":" once we join things below */
if (!escaped)
return log_oom();
_cleanup_free_ char *s = NULL;
s = strjoin("cryptsetup:", escaped, ":", strempty(crypt_get_uuid(cd)));
if (!s)
return log_oom();
r = tpm2_extend_bytes(c.esys_context, l ?: arg_tpm2_measure_banks, arg_tpm2_measure_pcr, s, SIZE_MAX, volume_key, volume_key_size);
if (r < 0)
return r;
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
LOG_MESSAGE("Successfully extended PCR index %u with '%s' and volume key (banks %s).", arg_tpm2_measure_pcr, s, joined),
"MEASURING=%s", s,
"PCR=%u", arg_tpm2_measure_pcr,
"BANKS=%s", joined);
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support disabled, not measuring.");
#endif
}
static int measured_crypt_activate_by_volume_key(
struct crypt_device *cd,
const char *name,
const void *volume_key,
size_t volume_key_size,
uint32_t flags) {
int r;
assert(cd);
assert(name);
/* A wrapper around crypt_activate_by_volume_key() which also measures to a PCR if that's requested. */
r = crypt_activate_by_volume_key(cd, name, volume_key, volume_key_size, flags);
if (r < 0)
return r;
if (volume_key_size == 0) {
log_debug("Not measuring volume key, none specified.");
return r;
}
(void) measure_volume_key(cd, name, volume_key, volume_key_size); /* OK if fails */
return r;
}
static int measured_crypt_activate_by_passphrase(
struct crypt_device *cd,
const char *name,
int keyslot,
const char *passphrase,
size_t passphrase_size,
uint32_t flags) {
_cleanup_(erase_and_freep) void *vk = NULL;
size_t vks;
int r;
assert(cd);
/* A wrapper around crypt_activate_by_passphrase() which also measures to a PCR if that's
* requested. Note that we need the volume key for the measurement, and
* crypt_activate_by_passphrase() doesn't give us access to this. Hence, we operate indirectly, and
* retrieve the volume key first, and then activate through that. */
if (arg_tpm2_measure_pcr == UINT_MAX) {
log_debug("Not measuring volume key, deactivated.");
goto shortcut;
}
r = crypt_get_volume_key_size(cd);
if (r < 0)
return r;
if (r == 0) {
log_debug("Not measuring volume key, none defined.");
goto shortcut;
}
vk = malloc(vks = r);
if (!vk)
return -ENOMEM;
r = crypt_volume_key_get(cd, keyslot, vk, &vks, passphrase, passphrase_size);
if (r < 0)
return r;
return measured_crypt_activate_by_volume_key(cd, name, vk, vks, flags);
shortcut:
return crypt_activate_by_passphrase(cd, name, keyslot, passphrase, passphrase_size, flags);
}
static int attach_tcrypt(
struct crypt_device *cd,
const char *name,
@@ -830,7 +1029,7 @@ static int attach_tcrypt(
return log_error_errno(r, "Failed to load tcrypt superblock on device %s: %m", crypt_get_device_name(cd));
}
r = crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
r = measured_crypt_activate_by_volume_key(cd, name, NULL, 0, flags);
if (r < 0)
return log_error_errno(r, "Failed to activate tcrypt device %s: %m", crypt_get_device_name(cd));
@@ -928,6 +1127,14 @@ static int run_security_device_monitor(
}
static bool libcryptsetup_plugins_support(void) {
#if HAVE_TPM2
/* Currently, there's no way for us to query the volume key when plugins are used. Hence don't use
* plugins, if measurement has been requested. */
if (arg_tpm2_measure_pcr != UINT_MAX)
return false;
#endif
#if HAVE_LIBCRYPTSETUP_PLUGINS
int r;
@@ -1157,7 +1364,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
}
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
else {
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
ssize_t base64_encoded_size;
@@ -1168,7 +1375,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
if (base64_encoded_size < 0)
return log_oom();
r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
r = measured_crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
}
if (r == -EPERM) {
log_error_errno(r, "Failed to activate with FIDO2 decrypted key. (Key incorrect?)");
@@ -1305,7 +1512,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
assert(decrypted_key);
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
else {
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
ssize_t base64_encoded_size;
@@ -1322,7 +1529,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
if (base64_encoded_size < 0)
return log_oom();
r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
r = measured_crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
}
if (r == -EPERM) {
log_error_errno(r, "Failed to activate with PKCS#11 decrypted key. (Key incorrect?)");
@@ -1594,7 +1801,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
assert(decrypted_key);
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
r = measured_crypt_activate_by_volume_key(cd, name, decrypted_key, decrypted_key_size, flags);
else {
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
ssize_t base64_encoded_size;
@@ -1605,7 +1812,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
if (base64_encoded_size < 0)
return log_oom();
r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
r = measured_crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, base64_encoded_size, flags);
}
if (r == -EPERM) {
log_error_errno(r, "Failed to activate with TPM2 decrypted key. (Key incorrect?)");
@@ -1632,9 +1839,9 @@ static int attach_luks_or_plain_or_bitlk_by_key_data(
assert(key_data);
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags);
r = measured_crypt_activate_by_volume_key(cd, name, key_data, key_data_size, flags);
else
r = crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags);
r = measured_crypt_activate_by_passphrase(cd, name, arg_key_slot, key_data, key_data_size, flags);
if (r == -EPERM) {
log_error_errno(r, "Failed to activate. (Key incorrect?)");
return -EAGAIN; /* Log actual error, but return EAGAIN */
@@ -1685,9 +1892,9 @@ static int attach_luks_or_plain_or_bitlk_by_key_file(
return log_error_errno(r, "Failed to read key file '%s': %m", key_file);
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, kfdata, kfsize, flags);
r = measured_crypt_activate_by_volume_key(cd, name, kfdata, kfsize, flags);
else
r = crypt_activate_by_passphrase(cd, name, arg_key_slot, kfdata, kfsize, flags);
r = measured_crypt_activate_by_passphrase(cd, name, arg_key_slot, kfdata, kfsize, flags);
if (r == -EPERM) {
log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file);
return -EAGAIN; /* Log actual error, but return EAGAIN */
@@ -1713,9 +1920,9 @@ static int attach_luks_or_plain_or_bitlk_by_passphrase(
r = -EINVAL;
STRV_FOREACH(p, passwords) {
if (pass_volume_key)
r = crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
r = measured_crypt_activate_by_volume_key(cd, name, *p, arg_key_size, flags);
else
r = crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
r = measured_crypt_activate_by_passphrase(cd, name, arg_key_slot, *p, strlen(*p), flags);
if (r >= 0)
break;
}
+23 -4
View File
@@ -8,6 +8,7 @@
#include "bus-error.h"
#include "bus-locator.h"
#include "chase-symlinks.h"
#include "efi-loader.h"
#include "fd-util.h"
#include "fileio.h"
#include "fstab-util.h"
@@ -40,6 +41,7 @@ typedef enum MountPointFlags {
MOUNT_MAKEFS = 1 << 3,
MOUNT_GROWFS = 1 << 4,
MOUNT_RW_ONLY = 1 << 5,
MOUNT_PCRFS = 1 << 6,
} MountPointFlags;
static bool arg_sysroot_check = false;
@@ -176,6 +178,8 @@ static int add_swap(
if (flags & MOUNT_GROWFS)
/* TODO: swap devices must be wiped and recreated */
log_warning("%s: growing swap devices is currently unsupported.", what);
if (flags & MOUNT_PCRFS)
log_warning("%s: measuring swap devices is currently unsupported.", what);
if (!(flags & MOUNT_NOAUTO)) {
r = generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET,
@@ -525,6 +529,19 @@ static int add_mount(
return r;
}
if (flags & MOUNT_PCRFS) {
r = efi_stub_measured();
if (r < 0)
log_warning_errno(r, "Failed to detect if we are running on a kernel image with TPM measurement enabled, assuming not: %m");
else if (r == 0)
log_debug("Kernel stub did not measure kernel image into PCR, skipping userspace measurement, too.");
else {
r = generator_hook_up_pcrfs(dest, where, target_unit);
if (r < 0)
return r;
}
}
if (!FLAGS_SET(flags, MOUNT_AUTOMOUNT)) {
if (!FLAGS_SET(flags, MOUNT_NOAUTO) && strv_isempty(wanted_by) && strv_isempty(required_by)) {
r = generator_add_symlink(dest, target_unit,
@@ -658,7 +675,7 @@ static int parse_fstab(bool initrd) {
while ((me = getmntent(f))) {
_cleanup_free_ char *where = NULL, *what = NULL, *canonical_where = NULL;
bool makefs, growfs, noauto, nofail;
bool makefs, growfs, pcrfs, noauto, nofail;
MountPointFlags flags;
int k;
@@ -718,16 +735,18 @@ static int parse_fstab(bool initrd) {
makefs = fstab_test_option(me->mnt_opts, "x-systemd.makefs\0");
growfs = fstab_test_option(me->mnt_opts, "x-systemd.growfs\0");
pcrfs = fstab_test_option(me->mnt_opts, "x-systemd.pcrfs\0");
noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0");
nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0");
log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s noauto=%s nofail=%s",
log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s",
what, where, me->mnt_type,
yes_no(makefs), yes_no(growfs),
yes_no(makefs), yes_no(growfs), yes_no(pcrfs),
yes_no(noauto), yes_no(nofail));
flags = makefs * MOUNT_MAKEFS |
growfs * MOUNT_GROWFS |
pcrfs * MOUNT_PCRFS |
noauto * MOUNT_NOAUTO |
nofail * MOUNT_NOFAIL;
@@ -911,7 +930,7 @@ static int add_sysroot_mount(void) {
fstype,
opts,
is_device_path(what) ? 1 : 0, /* passno */
flags, /* makefs, growfs off, noauto off, nofail off, automount off */
flags, /* makefs off, pcrfs off, noauto off, nofail off, automount off */
SPECIAL_INITRD_ROOT_FS_TARGET);
}
+3
View File
@@ -17,6 +17,9 @@
/* This TPM PCR is where we extend the initrd sysext images into which we pass to the booted kernel */
#define TPM_PCR_INDEX_INITRD_SYSEXTS 13U
/* This TPM PCR is where we measure the root fs volume key (and maybe /var/'s) if it is split off */
#define TPM_PCR_INDEX_VOLUME_KEY 15U
/* List of PE sections that have special meaning for us in unified kernels. This is the canonical order in
* which we measure the sections into TPM PCR 11 (see above). PLEASE DO NOT REORDER! */
typedef enum UnifiedSection {
+37 -5
View File
@@ -52,10 +52,11 @@ static int add_cryptsetup(
const char *what,
bool rw,
bool require,
bool measure,
char **ret_device) {
#if HAVE_LIBCRYPTSETUP
_cleanup_free_ char *e = NULL, *n = NULL, *d = NULL;
_cleanup_free_ char *e = NULL, *n = NULL, *d = NULL, *options = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
@@ -89,7 +90,28 @@ static int add_cryptsetup(
"After=%s\n",
d, d);
r = generator_write_cryptsetup_service_section(f, id, what, NULL, rw ? NULL : "read-only");
if (!rw) {
options = strdup("read-only");
if (!options)
return log_oom();
}
if (measure) {
/* We only measure the root volume key into PCR 15 if we are booted with sd-stub (i.e. in a
* UKI), and sd-stub measured the UKI. We do this in order not to step into people's own 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. */
r = efi_stub_measured();
if (r < 0)
log_warning_errno(r, "Failed to determine whether booted via systemd-stub with measurements enabled, ignoring: %m");
else if (r == 0)
log_debug("Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id);
else if (!strextend_with_separator(&options, ",", "tpm2-measure-pcr=yes"))
return log_oom();
}
r = generator_write_cryptsetup_service_section(f, id, what, NULL, options);
if (r < 0)
return r;
@@ -144,6 +166,7 @@ static int add_mount(
const char *fstype,
bool rw,
bool growfs,
bool measure,
const char *options,
const char *description,
const char *post) {
@@ -164,7 +187,7 @@ static int add_mount(
log_debug("Adding %s: %s fstype=%s", where, what, fstype ?: "(any)");
if (streq_ptr(fstype, "crypto_LUKS")) {
r = add_cryptsetup(id, what, rw, true, &crypto_what);
r = add_cryptsetup(id, what, rw, /* require= */ true, measure, &crypto_what);
if (r < 0)
return r;
@@ -236,6 +259,12 @@ static int add_mount(
return r;
}
if (measure) {
r = generator_hook_up_pcrfs(arg_dest, where, post);
if (r < 0)
return r;
}
if (post) {
r = generator_add_symlink(arg_dest, post, "requires", unit);
if (r < 0)
@@ -291,6 +320,7 @@ static int add_partition_mount(
p->fstype,
p->rw,
p->growfs,
/* measure= */ STR_IN_SET(id, "root", "var"), /* by default measure rootfs and /var, since they contain the "identity" of the system */
NULL,
description,
SPECIAL_LOCAL_FS_TARGET);
@@ -315,7 +345,7 @@ static int add_partition_swap(DissectedPartition *p) {
}
if (streq_ptr(p->fstype, "crypto_LUKS")) {
r = add_cryptsetup("swap", p->node, true, true, &crypto_what);
r = add_cryptsetup("swap", p->node, /* rw= */ true, /* require= */ true, /* measure= */ false, &crypto_what);
if (r < 0)
return r;
what = crypto_what;
@@ -384,6 +414,7 @@ static int add_automount(
fstype,
rw,
growfs,
/* measure= */ false,
options,
description,
NULL);
@@ -592,7 +623,7 @@ static int add_root_cryptsetup(void) {
/* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
* sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
return add_cryptsetup("root", "/dev/gpt-auto-root-luks", true, false, NULL);
return add_cryptsetup("root", "/dev/gpt-auto-root-luks", /* rw= */ true, /* require= */ false, /* measure= */ true, NULL);
#else
return 0;
#endif
@@ -639,6 +670,7 @@ static int add_root_mount(void) {
arg_root_fstype,
/* rw= */ arg_root_rw > 0,
/* growfs= */ false,
/* measure= */ true,
arg_root_options,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);
+39
View File
@@ -2,10 +2,12 @@
#include "alloc-util.h"
#include "efi-loader.h"
#include "env-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "stat-util.h"
#include "strv.h"
#include "tpm-pcr.h"
#include "utf8.h"
#if ENABLE_EFI
@@ -236,6 +238,43 @@ int efi_stub_get_features(uint64_t *ret) {
return 0;
}
int efi_stub_measured(void) {
_cleanup_free_ char *pcr_string = NULL;
unsigned pcr_nr;
int r;
/* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11. Or in
* other words, if we are running on a TPM enabled UKI.
*
* Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
* being used, but it measured things into a different PCR than we are configured for in
* userspace. (i.e. we expect PCR 11 being used for this by both sd-stub and us) */
r = getenv_bool_secure("SYSTEMD_FORCE_MEASURE"); /* Give user a chance to override the variable test,
* for debugging purposes */
if (r >= 0)
return r;
if (r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
if (!is_efi_boot())
return 0;
r = efi_get_variable_string(EFI_LOADER_VARIABLE(StubPcrKernelImage), &pcr_string);
if (r == -ENOENT)
return 0;
if (r < 0)
return r;
r = safe_atou(pcr_string, &pcr_nr);
if (r < 0)
return log_debug_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
if (pcr_nr != TPM_PCR_INDEX_KERNEL_IMAGE)
return log_debug_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", pcr_nr, TPM_PCR_INDEX_KERNEL_IMAGE);
return 1;
}
int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
_cleanup_free_ char *v = NULL;
static struct stat cache_stat = {};
+2
View File
@@ -18,6 +18,8 @@ int efi_loader_get_entries(char ***ret);
int efi_loader_get_features(uint64_t *ret);
int efi_stub_get_features(uint64_t *ret);
int efi_stub_measured(void);
int efi_loader_get_config_timeout_one_shot(usec_t *ret);
int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat);
+152 -59
View File
@@ -59,25 +59,111 @@ int generator_open_unit_file(
return 0;
}
int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src) {
_cleanup_free_ char *bn = NULL;
const char *from, *to;
int generator_add_symlink_full(
const char *dir,
const char *dst,
const char *dep_type,
const char *src,
const char *instance) {
_cleanup_free_ char *dn = NULL, *fn = NULL, *instantiated = NULL, *to = NULL, *from = NULL;
int r;
/* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute)
* or ../<src> (otherwise). */
assert(dir);
assert(dst);
assert(dep_type);
assert(src);
r = path_extract_filename(src, &bn);
/* Adds a symlink from <dst>.<dep_type>/ to <src> (if src is absolute) or ../<src> (otherwise). If
* <instance> is specified, then <src> must be a template unit name, and we'll instantiate it. */
r = path_extract_directory(src, &dn);
if (r < 0 && r != -EDESTADDRREQ) /* EDESTADDRREQ → just a file name was passed */
return log_error_errno(r, "Failed to extract directory name from '%s': %m", src);
r = path_extract_filename(src, &fn);
if (r < 0)
return log_error_errno(r, "Failed to extract filename from '%s': %m", src);
return log_error_errno(r, "Failed to extract file name from '%s': %m", src);
if (r == O_DIRECTORY)
return log_error_errno(SYNTHETIC_ERRNO(EISDIR), "Expected path to regular file name, but got '%s', refusing.", src);
from = path_is_absolute(src) ? src : strjoina("../", src);
to = strjoina(dir, "/", dst, ".", dep_type, "/", bn);
if (instance) {
r = unit_name_replace_instance(fn, instance, &instantiated);
if (r < 0)
return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", fn, instance);
}
from = path_join(dn ?: "..", fn);
if (!from)
return log_oom();
to = strjoin(dir, "/", dst, ".", dep_type, "/", instantiated ?: fn);
if (!to)
return log_oom();
(void) mkdir_parents_label(to, 0755);
if (symlink(from, to) < 0)
if (errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
if (symlink(from, to) < 0 && errno != EEXIST)
return log_error_errno(errno, "Failed to create symlink \"%s\": %m", to);
return 0;
}
static int generator_add_ordering(
const char *dir,
const char *src,
const char *order,
const char *dst,
const char *instance) {
_cleanup_free_ char *instantiated = NULL, *p = NULL, *fn = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *to;
int r;
assert(dir);
assert(src);
assert(order);
assert(dst);
/* Adds in an explicit ordering dependency of type <order> from <src> to <dst>. If <instance> is
* specified, it is inserted into <dst>. */
if (instance) {
r = unit_name_replace_instance(dst, instance, &instantiated);
if (r < 0)
return log_error_errno(r, "Failed to instantiate '%s' for '%s': %m", dst, instance);
to = instantiated;
} else
to = dst;
fn = strjoin(src, ".d/50-order-", to, ".conf");
if (!fn)
return log_oom();
p = path_join(dir, fn);
if (!p)
return log_oom();
(void) mkdir_parents_label(p, 0755);
r = fopen_unlocked(p, "wxe", &f);
if (r < 0)
return log_error_errno(r, "Failed to create '%s': %m", p);
fprintf(f,
"# Automatically generated by %s\n\n"
"[Unit]\n"
"%s=%s\n",
program_invocation_short_name,
order,
to);
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to write drop-in '%s': %m", p);
return 0;
}
@@ -532,66 +618,73 @@ int generator_hook_up_growfs(
const char *where,
const char *target) {
_cleanup_free_ char *unit = NULL, *escaped = NULL, *where_unit = NULL, *unit_file = NULL;
_cleanup_fclose_ FILE *f = NULL;
const char *growfs_unit, *growfs_unit_path;
_cleanup_free_ char *where_unit = NULL, *instance = NULL;
int r;
assert(dir);
assert(where);
escaped = cescape(where);
if (!escaped)
return log_oom();
r = unit_name_from_path_instance("systemd-growfs", where, ".service", &unit);
r = unit_name_from_path(where, ".mount", &where_unit);
if (r < 0)
return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
where);
return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
if (empty_or_root(where)) {
growfs_unit = SPECIAL_GROWFS_ROOT_SERVICE;
growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_ROOT_SERVICE;
} else {
growfs_unit = SPECIAL_GROWFS_SERVICE;
growfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_GROWFS_SERVICE;
r = unit_name_path_escape(where, &instance);
if (r < 0)
return log_error_errno(r, "Failed to escape path '%s': %m", where);
}
if (target) {
r = generator_add_ordering(dir, target, "After", growfs_unit, instance);
if (r < 0)
return r;
}
return generator_add_symlink_full(dir, where_unit, "wants", growfs_unit_path, instance);
}
int generator_hook_up_pcrfs(
const char *dir,
const char *where,
const char *target) {
const char *pcrfs_unit, *pcrfs_unit_path;
_cleanup_free_ char *where_unit = NULL, *instance = NULL;
int r;
assert(dir);
assert(where);
r = unit_name_from_path(where, ".mount", &where_unit);
if (r < 0)
return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
where);
return log_error_errno(r, "Failed to make unit name from path '%s': %m", where);
unit_file = path_join(dir, unit);
if (!unit_file)
return log_oom();
if (empty_or_root(where)) {
pcrfs_unit = SPECIAL_PCRFS_ROOT_SERVICE;
pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_ROOT_SERVICE;
} else {
pcrfs_unit = SPECIAL_PCRFS_SERVICE;
pcrfs_unit_path = SYSTEM_DATA_UNIT_DIR "/" SPECIAL_PCRFS_SERVICE;
log_debug("Creating %s", unit_file);
r = unit_name_path_escape(where, &instance);
if (r < 0)
return log_error_errno(r, "Failed to escape path '%s': %m", where);
}
f = fopen(unit_file, "wxe");
if (!f)
return log_error_errno(errno, "Failed to create unit file %s: %m",
unit_file);
if (target) {
r = generator_add_ordering(dir, target, "After", pcrfs_unit, instance);
if (r < 0)
return r;
}
fprintf(f,
"# Automatically generated by %s\n\n"
"[Unit]\n"
"Description=Grow File System on %%f\n"
"Documentation=man:systemd-growfs@.service(8)\n"
"DefaultDependencies=no\n"
"BindsTo=%%i.mount\n"
"Conflicts=shutdown.target\n"
"After=systemd-repart.service %%i.mount\n"
"Before=shutdown.target%s%s\n",
program_invocation_short_name,
target ? " " : "",
strempty(target));
if (empty_or_root(where)) /* Make sure the root fs is actually writable before we resize it */
fprintf(f,
"After=systemd-remount-fs.service\n");
fprintf(f,
"\n"
"[Service]\n"
"Type=oneshot\n"
"RemainAfterExit=yes\n"
"ExecStart="SYSTEMD_GROWFS_PATH " %s\n"
"TimeoutSec=0\n",
escaped);
return generator_add_symlink(dir, where_unit, "wants", unit);
return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
}
int generator_enable_remount_fs_service(const char *dir) {
+9 -1
View File
@@ -12,7 +12,11 @@ int generator_open_unit_file(
const char *name,
FILE **file);
int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src);
int generator_add_symlink_full(const char *dir, const char *dst, const char *dep_type, const char *src, const char *instance);
static inline int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src) {
return generator_add_symlink_full(dir, dst, dep_type, src, NULL);
}
int generator_write_fsck_deps(
FILE *f,
@@ -77,6 +81,10 @@ int generator_hook_up_growfs(
const char *dir,
const char *where,
const char *target);
int generator_hook_up_pcrfs(
const char *dir,
const char *where,
const char *target);
int generator_enable_remount_fs_service(const char *dir);
+127
View File
@@ -730,6 +730,49 @@ int tpm2_get_good_pcr_banks(
return 0;
}
int tpm2_get_good_pcr_banks_strv(
ESYS_CONTEXT *c,
uint32_t pcr_mask,
char ***ret) {
_cleanup_free_ TPMI_ALG_HASH *algs = NULL;
_cleanup_strv_free_ char **l = NULL;
int n_algs;
assert(c);
assert(ret);
n_algs = tpm2_get_good_pcr_banks(c, pcr_mask, &algs);
if (n_algs < 0)
return n_algs;
for (int i = 0; i < n_algs; i++) {
_cleanup_free_ char *n = NULL;
const EVP_MD *implementation;
const char *salg;
salg = tpm2_pcr_bank_to_string(algs[i]);
if (!salg)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unknown PCR algorithm, can't measure.");
implementation = EVP_get_digestbyname(salg);
if (!implementation)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unsupported PCR algorithm, can't measure.");
n = strdup(ASSERT_PTR(EVP_MD_name(implementation)));
if (!n)
return log_oom();
ascii_strlower(n); /* OpenSSL uses uppercase digest names, we prefer them lower case. */
if (strv_consume(&l, TAKE_PTR(n)) < 0)
return log_oom();
}
*ret = TAKE_PTR(l);
return 0;
}
static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
struct sha256_ctx hash;
@@ -1876,6 +1919,90 @@ int tpm2_find_device_auto(
#endif
}
#if HAVE_TPM2
int tpm2_extend_bytes(
ESYS_CONTEXT *c,
char **banks,
unsigned pcr_index,
const void *data,
size_t data_size,
const void *secret,
size_t secret_size) {
#if HAVE_OPENSSL
TPML_DIGEST_VALUES values = {};
TSS2_RC rc;
assert(c);
assert(data || data_size == 0);
assert(secret || secret_size == 0);
if (data_size == SIZE_MAX)
data_size = strlen(data);
if (secret_size == SIZE_MAX)
secret_size = strlen(secret);
if (pcr_index >= TPM2_PCRS_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Can't measure into unsupported PCR %u, refusing.", pcr_index);
if (strv_isempty(banks))
return 0;
STRV_FOREACH(bank, banks) {
const EVP_MD *implementation;
int id;
assert_se(implementation = EVP_get_digestbyname(*bank));
if (values.count >= ELEMENTSOF(values.digests))
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Too many banks selected.");
if ((size_t) EVP_MD_size(implementation) > sizeof(values.digests[values.count].digest))
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Hash result too large for TPM2.");
id = tpm2_pcr_bank_from_string(EVP_MD_name(implementation));
if (id < 0)
return log_error_errno(id, "Can't map hash name to TPM2.");
values.digests[values.count].hashAlg = id;
/* So here's a twist: sometimes we want to measure secrets (e.g. root file system volume
* key), but we'd rather not leak a literal hash of the secret to the TPM (given that the
* wire is unprotected, and some other subsystem might use the simple, literal hash of the
* secret for other purposes, maybe because it needs a shorter secret derived from it for
* some unrelated purpose, who knows). Hence we instead measure an HMAC signature of a
* private non-secret string instead. */
if (secret_size > 0) {
if (!HMAC(implementation, secret, secret_size, data, data_size, (unsigned char*) &values.digests[values.count].digest, NULL))
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to calculate HMAC of data to measure.");
} else if (EVP_Digest(data, data_size, (unsigned char*) &values.digests[values.count].digest, NULL, implementation, NULL) != 1)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash data to measure.");
values.count++;
}
rc = sym_Esys_PCR_Extend(
c,
ESYS_TR_PCR0 + pcr_index,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
&values);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to measure into PCR %u: %s",
pcr_index,
sym_Tss2_RC_Decode(rc));
return 0;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"OpenSSL not supported on this build.");
#endif
}
#endif
int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
const char *p = ASSERT_PTR(s);
uint32_t mask = 0;
+3
View File
@@ -68,6 +68,9 @@ static inline void Esys_Freep(void *p) {
}
int tpm2_get_good_pcr_banks(ESYS_CONTEXT *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
int tpm2_get_good_pcr_banks_strv(ESYS_CONTEXT *c, uint32_t pcr_mask, char ***ret);
int tpm2_extend_bytes(ESYS_CONTEXT *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
#else
struct tpm2_context;
+1
View File
@@ -20,6 +20,7 @@ test_append_files() {
install_dmevent
generate_module_dependencies
inst_binary tpm2_pcrextend
inst_binary tpm2_pcrread
inst_binary openssl
}
+30
View File
@@ -172,6 +172,36 @@ else
echo "/usr/lib/systemd/systemd-measure or PCR sysfs files not found, skipping signed PCR policy test case"
fi
if [ -e /usr/lib/systemd/systemd-pcrphase ] && \
[ -f /sys/class/tpm/tpm0/pcr-sha256/11 ]; then
# Let's measure the machine ID
tpm2_pcrread sha256:15 -Q -o /tmp/oldpcr15
mv /etc/machine-id /etc/machine-id.save
echo 994013bf23864ee7992eab39a96dd3bb >/etc/machine-id
SYSTEMD_FORCE_MEASURE=1 /usr/lib/systemd/systemd-pcrphase --machine-id
mv /etc/machine-id.save /etc/machine-id
tpm2_pcrread sha256:15 -Q -o /tmp/newpcr15
# And check it matches expectations
( cat /tmp/oldpcr15 ;
echo -n "machine-id:994013bf23864ee7992eab39a96dd3bb" | openssl dgst -binary -sha256 ) | openssl dgst -binary -sha256 | cmp - /tmp/newpcr15
rm /tmp/oldpcr15 /tmp/newpcr15
# And similar for the boot phase measurement into PCR 11
tpm2_pcrread sha256:11 -Q -o /tmp/oldpcr11
SYSTEMD_FORCE_MEASURE=1 /usr/lib/systemd/systemd-pcrphase foobar
tpm2_pcrread sha256:11 -Q -o /tmp/newpcr11
( cat /tmp/oldpcr11 ;
echo -n "foobar" | openssl dgst -binary -sha256 ) | openssl dgst -binary -sha256 | cmp - /tmp/newpcr11
rm /tmp/oldpcr11 /tmp/newpcr11
else
echo "/usr/lib/systemd/systemd-pcrphase or PCR sysfs files not found, skipping PCR extension test case"
fi
echo OK >/testok
exit 0
+6
View File
@@ -265,6 +265,12 @@ in_units = [
'sysinit.target.wants/'],
['systemd-pcrphase.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',
'sysinit.target.wants/'],
['systemd-pcrmachine.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',
'sysinit.target.wants/'],
['systemd-pcrfs-root.service', ''],
['systemd-pcrfs@.service', ''],
['systemd-growfs-root.service', ''],
['systemd-growfs@.service', ''],
]
add_wants = []
+22
View File
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Grow Root File System
Documentation=man:systemd-growfs-root.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-repart.service systemd-remount-fs.service
Before=shutdown.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-growfs /
TimeoutSec=0
+23
View File
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Grow File System on %f
Documentation=man:systemd-growfs@.service(8)
DefaultDependencies=no
BindsTo=%i.mount
Conflicts=shutdown.target
After=systemd-repart.service %i.mount
Before=shutdown.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-growfs %f
TimeoutSec=0
+24
View File
@@ -0,0 +1,24 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=TPM2 PCR Root File System Measurement
Documentation=man:systemd-pcrfs-root.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-pcrmachine.service
Before=shutdown.target
AssertPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --file-system=/
+25
View File
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=TPM2 PCR File System Measurement of %f
Documentation=man:systemd-pcrfs@.service(8)
DefaultDependencies=no
BindsTo=%i.mount
Conflicts=shutdown.target
After=%i.mount systemd-pcrfs-root.service
Before=shutdown.target
AssertPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --file-system=%f
+23
View File
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=TPM2 PCR Machine ID Measurement
Documentation=man:systemd-pcrmachine.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
Before=sysinit.target shutdown.target
AssertPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --machine-id