Merge pull request #25006 from poettering/pcr15
cryptsetup: measure LUKS volume keys to PCR 15
This commit is contained in:
@@ -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
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>,
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -20,6 +20,7 @@ test_append_files() {
|
||||
install_dmevent
|
||||
generate_module_dependencies
|
||||
inst_binary tpm2_pcrextend
|
||||
inst_binary tpm2_pcrread
|
||||
inst_binary openssl
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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=/
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user