mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-23 17:34:00 +03:00
Merge pull request #24771 from poettering/destroy-pcr11
extend boot phase information into PCR 11 during boot
This commit is contained in:
commit
c9d65b921b
36
TODO
36
TODO
@ -119,6 +119,12 @@ Deprecations and removals:
|
||||
|
||||
Features:
|
||||
|
||||
* systemd-measure: only require private key to be set when signing. iiuc we can
|
||||
generate the public key from it anyway.
|
||||
|
||||
* automatically propagate LUKS password credential into cryptsetup from host,
|
||||
so that one can unlock LUKS via VM hypervisor supplied password.
|
||||
|
||||
* add ability to path_is_valid() to classify paths that refer to a dir from
|
||||
those which may refer to anything, and use that in various places to filter
|
||||
early. i.e. stuff ending in "/", "/." and "/.." definitely refers to a
|
||||
@ -154,11 +160,6 @@ Features:
|
||||
* tmpfiles: currently if we fail to create an inode, we stat it first, and only
|
||||
then O_PATH open it. Reverse that.
|
||||
|
||||
* during the initrd → host transition measure a fixed value into TPM PCR 11
|
||||
(where we already measure the UKI into), so that unlock policies for disk
|
||||
enryption/credential encryption can be put together that only work in the
|
||||
initrd or only on the host (or both).
|
||||
|
||||
* Add support for extra verity configuration options to systemd-repart (FEC,
|
||||
hash type, etc)
|
||||
|
||||
@ -737,8 +738,16 @@ Features:
|
||||
one.
|
||||
|
||||
* we probably should extend the root verity hash of the root fs into some PCR
|
||||
on boot. (i.e. maybe add a crypttab option tpm2-measure=8 or so to measure it
|
||||
into PCR 8)
|
||||
on boot. (i.e. maybe add a veritytab option tpm2-measure=12 or so to measure
|
||||
it into PCR 12); Similar: we probably should extend the LUKS volume key of
|
||||
the root fs into some PCR on boot. (i.e. maybe add a crypttab option
|
||||
tpm2-measure=15 or so to measure it into PCR 15); once both are in place
|
||||
update gpt-auto-discovery to generate these by default for the partitions it
|
||||
discovers. Static vendor stuff should probably end up in PCR 12 (i.e. the
|
||||
verity hash), with local keys in PCR 15 (i.e. the encryption volume
|
||||
key). That way, we nicely distinguish resources supplied by the OS vendor
|
||||
(i.e. sysext, root verity) from those inherently local (i.e. encryption key),
|
||||
which is useful if they shall be signed separately.
|
||||
|
||||
* add a "policy" to the dissection logic. i.e. a bit mask what is OK to mount,
|
||||
what must be read-only, what requires encryption, and what requires
|
||||
@ -765,7 +774,6 @@ Features:
|
||||
* sysupdate:
|
||||
- add fuzzing to the pattern parser
|
||||
- support casync as download mechanism
|
||||
- direct TPM2 PCR change handling, possible renrolling LUKS2 media if needed.
|
||||
- "systemd-sysupdate update --all" support, that iterates through all components
|
||||
defined on the host, plus all images installed into /var/lib/machines/,
|
||||
/var/lib/portable/ and so on.
|
||||
@ -847,10 +855,6 @@ Features:
|
||||
* add tpm.target or so which is delayed until TPM2 device showed up in case
|
||||
firmware indicates there is one.
|
||||
|
||||
* Add concept for upgrading TPM2 enrollments, maybe a new switch
|
||||
--pcrs=4:<hash> or so, i.e. select a PCR to include in the hash, and then
|
||||
override its hash
|
||||
|
||||
* TPM2: auto-reenroll in cryptsetup, as fallback for hosed firmware upgrades
|
||||
and such
|
||||
|
||||
@ -1606,14 +1610,6 @@ Features:
|
||||
|
||||
* firstboot: make it useful to be run immediately after yum --installroot to set up a machine. (most specifically, make --copy-root-password work even if /etc/passwd already exists
|
||||
|
||||
* efi stub: optionally, load initrd from disk as a separate file, HMAC check it
|
||||
with key from TPM, bound to PCR, refusing if failing. This would then allow
|
||||
traditional distros that generate initrds locally to secure them with TPM:
|
||||
after generating the initrd, do the HMAC calculation, put result in initrd
|
||||
filename, done. This would then bind the validity of the initrd to the local
|
||||
host, and used kernel, and means people cannot change initrd or kernel
|
||||
without booting the kernel + initrd.
|
||||
|
||||
* EFI:
|
||||
- honor language efi variables for default language selection (if there are any?)
|
||||
- honor timezone efi variables for default timezone selection (if there are any?)
|
||||
|
@ -527,3 +527,15 @@ Support: %SUPPORT_URL%
|
||||
|
||||
For the first time during the current boot an NTP synchronization has been
|
||||
acquired and the local system clock adjustment has been initiated.
|
||||
|
||||
-- 3f7d5ef3e54f4302b4f0b143bb270cab
|
||||
Subject: TPM PCR Extended
|
||||
Defined-By: systemd
|
||||
Support: %SUPPORT_URL%
|
||||
|
||||
The string '@MEASURING@' has been extended into Trusted Platform Module's (TPM)
|
||||
Platform Configuration Register (PCR) @PCR@, on banks @BANKS@.
|
||||
|
||||
Whenever the system transitions to a new runtime phase, a different string is
|
||||
extended into the specified PCR, to ensure that security policies for TPM-bound
|
||||
secrets and other resources are limited to specific phases of the runtime.
|
||||
|
@ -966,6 +966,10 @@ manpages = [
|
||||
['systemd-nspawn', '1', [], ''],
|
||||
['systemd-oomd.service', '8', ['systemd-oomd'], 'ENABLE_OOMD'],
|
||||
['systemd-path', '1', [], ''],
|
||||
['systemd-pcrphase.service',
|
||||
'8',
|
||||
['systemd-pcrphase', 'systemd-pcrphase-initrd.service'],
|
||||
'HAVE_GNU_EFI'],
|
||||
['systemd-portabled.service', '8', ['systemd-portabled'], 'ENABLE_PORTABLED'],
|
||||
['systemd-pstore.service', '8', ['systemd-pstore'], 'ENABLE_PSTORE'],
|
||||
['systemd-quotacheck.service',
|
||||
|
@ -143,7 +143,10 @@
|
||||
unified kernel image, the latter picks the public key of the key pair used to sign the resulting PCR
|
||||
11 values. The former is the key that the booted system will likely use to lock disk and credential
|
||||
encryption to, the latter is the key used for unlocking such resources again. Hence, typically the
|
||||
same PEM key should be supplied in both cases.</para></listitem>
|
||||
same PEM key should be supplied in both cases.</para>
|
||||
|
||||
<para>If the <option>--public-key=</option> is not specified but <option>--private-key=</option> is
|
||||
specified the public key is automatically derived from the private key.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@ -156,6 +159,28 @@
|
||||
all suitable TPM2 devices currently discovered.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--phase=</option><replaceable>PHASE</replaceable></term>
|
||||
|
||||
<listitem><para>Controls which boot phase(s) to calculate expected PCR 11 values for. This takes a
|
||||
series of colon-separated strings that encode boot "paths" for entering a specific phase of the boot
|
||||
process. Each of the specified strings is measured by the
|
||||
<filename>systemd-pcrphase-initrd.service</filename> and
|
||||
<citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
into PCR 11 during different milestones of the boot process. This switch may be specified multiple
|
||||
times to calculate PCR values for multiple boot phases at once. If not used defaults to
|
||||
<literal>enter-initrd</literal>, <literal>enter-initrd:leave-initrd</literal>,
|
||||
<literal>enter-initrd:leave-initrd:ready</literal>, i.e. calculates expected PCR values for the boot
|
||||
phase in the initrd, during early boot, and during system runtime, but excluding the phases before
|
||||
the initrd or when shutting down. This setting is honoured both by <command>calculate</command> and
|
||||
<command>sign</command>. When used with the latter it's particularly useful for generating PCR
|
||||
signatures that can only be used for unlocking resources during specific parts of the boot
|
||||
process.</para>
|
||||
|
||||
<para>For further details about PCR boot phases, see
|
||||
<citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="json" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
@ -250,7 +275,8 @@
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
<citerefentry project='man-pages'><refentrytitle>objcopy</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
<citerefentry><refentrytitle>systemd-cryptsetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
|
||||
<citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>1</manvolnum></citerefentry>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
|
134
man/systemd-pcrphase.service.xml
Normal file
134
man/systemd-pcrphase.service.xml
Normal file
@ -0,0 +1,134 @@
|
||||
<?xml version='1.0'?> <!--*-nxml-*-->
|
||||
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
|
||||
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
|
||||
|
||||
<refentry id="systemd-pcrphase.service" conditional='HAVE_GNU_EFI'
|
||||
xmlns:xi="http://www.w3.org/2001/XInclude">
|
||||
|
||||
<refentryinfo>
|
||||
<title>systemd-pcrphase.service</title>
|
||||
<productname>systemd</productname>
|
||||
</refentryinfo>
|
||||
|
||||
<refmeta>
|
||||
<refentrytitle>systemd-pcrphase.service</refentrytitle>
|
||||
<manvolnum>8</manvolnum>
|
||||
</refmeta>
|
||||
|
||||
<refnamediv>
|
||||
<refname>systemd-pcrphase.service</refname>
|
||||
<refname>systemd-pcrphase-initrd.service</refname>
|
||||
<refname>systemd-pcrphase</refname>
|
||||
<refpurpose>Mark current boot process as successful</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<para><filename>systemd-pcrphase.service</filename></para>
|
||||
<para><filename>systemd-pcrphase-initrd.service</filename></para>
|
||||
<para><filename>/usr/lib/systemd/system-pcrphase</filename> <replaceable>STRING</replaceable></para>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
<title>Description</title>
|
||||
|
||||
<para><filename>systemd-pcrphase.service</filename> and
|
||||
<filename>systemd-pcrphase-initrd.service</filename> are system services that measure specific strings
|
||||
into TPM2 PCR 11 during boot.</para>
|
||||
|
||||
<para>These services require
|
||||
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> to be
|
||||
used in a unified kernel image (UKI) setup. They execute no operation when invoked 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
|
||||
certain literal strings indicating various phases of the boot process into TPM2 PCR 11. During a regular
|
||||
boot process the following strings are extended into PCR 11.</para>
|
||||
|
||||
<orderedlist>
|
||||
<listitem><para><literal>enter-initrd</literal> is extended into PCR 11 early when the initrd
|
||||
initializes, before activating system extension images for the initrd. It is supposed to act as barrier
|
||||
between the time where the kernel initializes, and where the initrd starts operating and enables
|
||||
system extension images, i.e. code shipped outside of the UKI. (This string is extended at start of
|
||||
<filename>systemd-pcrphase-initrd.service</filename>.)</para></listitem>
|
||||
|
||||
<listitem><para><literal>leave-initrd</literal> is extended into PCR 11 when the initrd is about to
|
||||
transition into the host file system, i.e. when it achieved its purpose. It is supposed to act as
|
||||
barrier between kernel/initrd code and host OS code. (This string is extended at stop of
|
||||
<filename>systemd-pcrphase-initrd.service</filename>.)</para></listitem>
|
||||
|
||||
<listitem><para><literal>ready</literal> is extended into PCR 11 during later boot-up, after remote
|
||||
file systems have been activated (i.e. after <filename>remote-fs.target</filename>), but before users
|
||||
are permitted to log in (i.e. before <filename>systemd-user-sessions.service</filename>). It is
|
||||
supposed to act as barrier between the time where unprivileged regular users are still prohibited to
|
||||
log in and where they are allowed to log in. (This string is extended at start of
|
||||
<filename>systemd-pcrphase.service</filename>.)</para></listitem>
|
||||
|
||||
<listitem><para><literal>shutdown</literal> is extended into PCR 11 during system shutdown. It is
|
||||
supposed to act as barrier between the time the system is fully up and running and where it is about to
|
||||
shut down. (This string is extended at stop of
|
||||
<filename>systemd-pcrphase.service</filename>.)</para></listitem>
|
||||
</orderedlist>
|
||||
|
||||
<para>During a regular system lifecycle, the strings <literal>enter-initrd</literal> →
|
||||
<literal>leave-initrd</literal> → <literal>ready</literal> → <literal>shutdown</literal> are extended into
|
||||
PCR 11, one after the other.</para>
|
||||
|
||||
<para>Specific phases of the boot process may be referenced via the series of strings measured, separated
|
||||
by colons (the "boot path"). For example, the boot path for the regular system runtime is
|
||||
<literal>enter-initrd:leave-initrd:ready</literal>, while the one for the initrd is just
|
||||
<literal>enter-initrd</literal>. The boot path for the the boot phase before the initrd, is an empty
|
||||
string; because that's hard to pass around a single colon (<literal>:</literal>) may be used
|
||||
instead. Note that the aforementioned four strings are just the default strings and individual systems
|
||||
might measure other strings at other times, and thus implement different and more fine-grained boot
|
||||
phases to bind policy to.</para>
|
||||
|
||||
<para>By binding policy of TPM2 objects to a specific boot path it is possible to restrict access to them
|
||||
to specific phases of the boot process, for example making it impossible to access the root file system's
|
||||
encryption key after the system transitioned from the initrd into the host root file system.</para>
|
||||
|
||||
<para>Use
|
||||
<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>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>Options</title>
|
||||
|
||||
<para>The <filename>/usr/lib/systemd/system-pcrphase</filename> executable may also be invoked from the
|
||||
command line, where it expects the word to extend into PCR 11, as well as the following switches:</para>
|
||||
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>--bank=</option></term>
|
||||
|
||||
<listitem><para>Takes the PCR banks to extend the specified word into. If not specified the tool
|
||||
automatically determines all enabled PCR banks and measures the word into all of
|
||||
them.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--tpm2-device=</option><replaceable>PATH</replaceable></term>
|
||||
|
||||
<listitem><para>Controls which TPM2 device to use. Expects a device node path referring to the TPM2
|
||||
chip (e.g. <filename>/dev/tpmrm0</filename>). Alternatively the special value <literal>auto</literal>
|
||||
may be specified, in order to automatically determine the device node of a suitable TPM2 device (of
|
||||
which there must be exactly one). The special value <literal>list</literal> may be used to enumerate
|
||||
all suitable TPM2 devices currently discovered.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
|
||||
</variablelist>
|
||||
</refsect1>
|
||||
|
||||
<refsect1>
|
||||
<title>See Also</title>
|
||||
<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>
|
||||
</para>
|
||||
</refsect1>
|
||||
|
||||
</refentry>
|
@ -2566,6 +2566,15 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
|
||||
install_rpath : rootpkglibdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
executable(
|
||||
'systemd-pcrphase',
|
||||
'src/boot/pcrphase.c',
|
||||
include_directories : includes,
|
||||
link_with : [libshared],
|
||||
dependencies : [libopenssl, tpm2],
|
||||
install_rpath : rootpkglibdir,
|
||||
install : true,
|
||||
install_dir : rootlibexecdir)
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -31,11 +31,13 @@ static char *arg_public_key = NULL;
|
||||
static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORMAT_COLOR_AUTO|JSON_FORMAT_OFF;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
static bool arg_current = false;
|
||||
static char **arg_phase = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_public_key, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_phase, strv_freep);
|
||||
|
||||
static inline void free_sections(char*(*sections)[_UNIFIED_SECTION_MAX]) {
|
||||
for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++)
|
||||
@ -63,7 +65,8 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
" --version Print version\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" -c --current Use current PCR values\n"
|
||||
" --bank=DIGEST Select TPM bank (SHA1, SHA256)\n"
|
||||
" --phase=PHASE Specify a boot phase to sign for\n"
|
||||
" --bank=DIGEST Select TPM bank (SHA1, SHA256, SHA384, SHA512)\n"
|
||||
" --tpm2-device=PATH Use specified TPM2 device\n"
|
||||
" --private-key=KEY Private key (PEM) to sign with\n"
|
||||
" --public-key=KEY Public key (PEM) to validate against\n"
|
||||
@ -89,6 +92,21 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *normalize_phase(const char *s) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
/* Let's normalize phase expressions. We split the series of colon-separated words up, then remove
|
||||
* all empty ones, and glue them back together again. In other words we remove duplicate ":", as well
|
||||
* as leading and trailing ones. */
|
||||
|
||||
l = strv_split(s, ":"); /* Split series of words */
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
/* Remove all empty words and glue things back together */
|
||||
return strv_join(strv_remove(l, ""), ":");
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
@ -108,6 +126,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_PUBLIC_KEY,
|
||||
ARG_TPM2_DEVICE,
|
||||
ARG_JSON,
|
||||
ARG_PHASE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@ -127,6 +146,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "private-key", required_argument, NULL, ARG_PRIVATE_KEY },
|
||||
{ "public-key", required_argument, NULL, ARG_PUBLIC_KEY },
|
||||
{ "json", required_argument, NULL, ARG_JSON },
|
||||
{ "phase", required_argument, NULL, ARG_PHASE },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -219,6 +239,20 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
|
||||
break;
|
||||
|
||||
case ARG_PHASE: {
|
||||
char *n;
|
||||
|
||||
n = normalize_phase(optarg);
|
||||
if (!n)
|
||||
return log_oom();
|
||||
|
||||
r = strv_consume(&arg_phase, TAKE_PTR(n));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -241,14 +275,36 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
if (arg_sections[us])
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --current switch cannot be used in combination with --linux= and related switches.");
|
||||
|
||||
if (strv_isempty(arg_phase)) {
|
||||
/* If no phases are specifically selected, pick everything from the beginning of the initrd
|
||||
* to the beginning of shutdown. */
|
||||
if (strv_extend_strv(&arg_phase,
|
||||
STRV_MAKE("enter-initrd",
|
||||
"enter-initrd:leave-initrd",
|
||||
"enter-initrd:leave-initrd:ready"),
|
||||
/* filter_duplicates= */ false) < 0)
|
||||
return log_oom();
|
||||
} else {
|
||||
strv_sort(arg_phase);
|
||||
strv_uniq(arg_phase);
|
||||
}
|
||||
|
||||
_cleanup_free_ char *j = NULL;
|
||||
j = strv_join(arg_phase, ", ");
|
||||
if (!j)
|
||||
return log_oom();
|
||||
|
||||
log_debug("Measuring boot phases: %s", j);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* The PCR 11 state for one specific bank */
|
||||
typedef struct PcrState {
|
||||
char *bank;
|
||||
const EVP_MD *md;
|
||||
void *value;
|
||||
size_t value_size;
|
||||
void *saved_value; /* A copy of the original value we calculated, used by pcr_states_save()/pcr_states_restore() to come later back to */
|
||||
} PcrState;
|
||||
|
||||
static void pcr_state_free_all(PcrState **pcr_state) {
|
||||
@ -260,6 +316,7 @@ static void pcr_state_free_all(PcrState **pcr_state) {
|
||||
for (size_t i = 0; (*pcr_state)[i].value; i++) {
|
||||
free((*pcr_state)[i].bank);
|
||||
free((*pcr_state)[i].value);
|
||||
free((*pcr_state)[i].saved_value);
|
||||
}
|
||||
|
||||
*pcr_state = mfree(*pcr_state);
|
||||
@ -320,6 +377,8 @@ static int measure_kernel(PcrState *pcr_states, size_t n) {
|
||||
assert(n > 0);
|
||||
assert(pcr_states);
|
||||
|
||||
/* Virtually measures the components of a unified kernel image into PCR 11 */
|
||||
|
||||
if (arg_current) {
|
||||
/* Shortcut things, if we should just use the current PCR value */
|
||||
|
||||
@ -432,6 +491,54 @@ static int measure_kernel(PcrState *pcr_states, size_t n) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int measure_phase(PcrState *pcr_states, size_t n, const char *phase) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert(pcr_states);
|
||||
assert(n > 0);
|
||||
|
||||
/* Measure a phase string into PCR 11. This splits up the "phase" expression at colons, and then
|
||||
* virtually extends each specified word into PCR 11, to model how during boot we measure a series of
|
||||
* words into PCR 11, one for each phase. */
|
||||
|
||||
l = strv_split(phase, ":");
|
||||
if (!l)
|
||||
return log_oom();
|
||||
|
||||
STRV_FOREACH(word, l) {
|
||||
size_t wl;
|
||||
|
||||
if (isempty(*word))
|
||||
continue;
|
||||
|
||||
wl = strlen(*word);
|
||||
|
||||
for (size_t i = 0; i < n; i++) { /* For each bank */
|
||||
_cleanup_free_ void *b = NULL;
|
||||
int bsz;
|
||||
|
||||
bsz = EVP_MD_size(pcr_states[i].md);
|
||||
assert(bsz > 0);
|
||||
|
||||
b = malloc(bsz);
|
||||
if (!b)
|
||||
return log_oom();
|
||||
|
||||
/* First hash the word itself */
|
||||
if (EVP_Digest(*word, wl, b, NULL, pcr_states[i].md, NULL) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash word '%s'.", *word);
|
||||
|
||||
/* And then extend the PCR with the resulting hash */
|
||||
r = pcr_state_extend(pcr_states + i, b, bsz);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcr_states_allocate(PcrState **ret) {
|
||||
_cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
|
||||
size_t n = 0;
|
||||
@ -473,6 +580,39 @@ static int pcr_states_allocate(PcrState **ret) {
|
||||
return (int) n;
|
||||
}
|
||||
|
||||
static int pcr_states_save(PcrState *pcr_states, size_t n) {
|
||||
assert(pcr_states);
|
||||
assert(n > 0);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
_cleanup_free_ void *saved = NULL;
|
||||
|
||||
if (!pcr_states[i].value)
|
||||
continue;
|
||||
|
||||
saved = memdup(pcr_states[i].value, pcr_states[i].value_size);
|
||||
if (!saved)
|
||||
return log_oom();
|
||||
|
||||
free_and_replace(pcr_states[i].saved_value, saved);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcr_states_restore(PcrState *pcr_states, size_t n) {
|
||||
assert(pcr_states);
|
||||
assert(n > 0);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
|
||||
assert(pcr_states[i].value);
|
||||
assert(pcr_states[i].saved_value);
|
||||
|
||||
memcpy(pcr_states[i].value, pcr_states[i].saved_value, pcr_states[i].value_size);
|
||||
}
|
||||
}
|
||||
|
||||
static int verb_calculate(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
|
||||
_cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
|
||||
@ -492,34 +632,64 @@ static int verb_calculate(int argc, char *argv[], void *userdata) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (arg_json_format_flags & JSON_FORMAT_OFF) {
|
||||
_cleanup_free_ char *hd = NULL;
|
||||
/* Save the current state, so that we later can restore to it. This way we can measure the PCR values
|
||||
* for multiple different boot phases without heaving to start from zero each time */
|
||||
r = pcr_states_save(pcr_states, n);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
|
||||
if (!hd)
|
||||
return log_oom();
|
||||
STRV_FOREACH(phase, arg_phase) {
|
||||
|
||||
printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, pcr_states[i].bank, hd);
|
||||
} else {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *bv = NULL;
|
||||
r = measure_phase(pcr_states, n, *phase);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = json_build(&bv,
|
||||
JSON_BUILD_ARRAY(
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
|
||||
JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))
|
||||
)
|
||||
)
|
||||
);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build JSON object: %m");
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
if (arg_json_format_flags & JSON_FORMAT_OFF) {
|
||||
_cleanup_free_ char *hd = NULL;
|
||||
|
||||
r = json_variant_set_field(&w, pcr_states[i].bank, bv);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add bank info to object: %m");
|
||||
if (i == 0) {
|
||||
fflush(stdout);
|
||||
fprintf(stderr, "%s# PCR[%" PRIu32 "] Phase <%s>%s\n",
|
||||
ansi_grey(),
|
||||
TPM_PCR_INDEX_KERNEL_IMAGE,
|
||||
isempty(*phase) ? ":" : *phase,
|
||||
ansi_normal());
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
hd = hexmem(pcr_states[i].value, pcr_states[i].value_size);
|
||||
if (!hd)
|
||||
return log_oom();
|
||||
|
||||
printf("%" PRIu32 ":%s=%s\n", TPM_PCR_INDEX_KERNEL_IMAGE, pcr_states[i].bank, hd);
|
||||
} else {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *bv = NULL, *array = NULL;
|
||||
|
||||
array = json_variant_ref(json_variant_by_key(w, pcr_states[i].bank));
|
||||
|
||||
r = json_build(&bv,
|
||||
JSON_BUILD_OBJECT(
|
||||
JSON_BUILD_PAIR_CONDITION(!isempty(*phase), "phase", JSON_BUILD_STRING(*phase)),
|
||||
JSON_BUILD_PAIR("pcr", JSON_BUILD_INTEGER(TPM_PCR_INDEX_KERNEL_IMAGE)),
|
||||
JSON_BUILD_PAIR("hash", JSON_BUILD_HEX(pcr_states[i].value, pcr_states[i].value_size))
|
||||
)
|
||||
);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build JSON object: %m");
|
||||
|
||||
r = json_variant_append_array(&array, bv);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to append JSON object to array: %m");
|
||||
|
||||
r = json_variant_set_field(&w, pcr_states[i].bank, array);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add bank info to object: %m");
|
||||
}
|
||||
}
|
||||
|
||||
/* Return to the original kernel measurement for the next phase calculation */
|
||||
pcr_states_restore(pcr_states, n);
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) {
|
||||
@ -538,7 +708,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
|
||||
_cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL;
|
||||
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
|
||||
_cleanup_fclose_ FILE *privkeyf = NULL , *pubkeyf = NULL;
|
||||
_cleanup_fclose_ FILE *privkeyf = NULL;
|
||||
ESYS_TR session_handle = ESYS_TR_NONE;
|
||||
TSS2_RC rc;
|
||||
size_t n;
|
||||
@ -549,8 +719,6 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
|
||||
if (!arg_private_key)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No private key specified, use --private-key=.");
|
||||
if (!arg_public_key)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No public key specified, use --public-key=.");
|
||||
|
||||
/* When signing we only support JSON output */
|
||||
arg_json_format_flags &= ~JSON_FORMAT_OFF;
|
||||
@ -559,17 +727,40 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
|
||||
if (!privkeyf)
|
||||
return log_error_errno(errno, "Failed to open private key file '%s': %m", arg_private_key);
|
||||
|
||||
pubkeyf = fopen(arg_public_key, "re");
|
||||
if (!pubkeyf)
|
||||
return log_error_errno(errno, "Failed to open public key file '%s': %m", arg_public_key);
|
||||
|
||||
privkey = PEM_read_PrivateKey(privkeyf, NULL, NULL, NULL);
|
||||
if (!privkey)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse private key '%s'.", arg_private_key);
|
||||
|
||||
pubkey = PEM_read_PUBKEY(pubkeyf, NULL, NULL, NULL);
|
||||
if (!pubkey)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key);
|
||||
if (arg_public_key) {
|
||||
_cleanup_fclose_ FILE *pubkeyf = NULL;
|
||||
|
||||
pubkeyf = fopen(arg_public_key, "re");
|
||||
if (!pubkeyf)
|
||||
return log_error_errno(errno, "Failed to open public key file '%s': %m", arg_public_key);
|
||||
|
||||
pubkey = PEM_read_PUBKEY(pubkeyf, NULL, NULL, NULL);
|
||||
if (!pubkey)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse public key '%s'.", arg_public_key);
|
||||
} else {
|
||||
_cleanup_free_ char *data = NULL;
|
||||
_cleanup_fclose_ FILE *tf = NULL;
|
||||
size_t sz;
|
||||
|
||||
/* No public key was specified, let's derive it automatically, if we can */
|
||||
|
||||
tf = open_memstream_unlocked(&data, &sz);
|
||||
if (!tf)
|
||||
return log_oom();
|
||||
|
||||
if (i2d_PUBKEY_fp(tf, privkey) != 1)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from private key file '%s'.", arg_private_key);
|
||||
|
||||
fflush(tf);
|
||||
rewind(tf);
|
||||
|
||||
if (!d2i_PUBKEY_fp(tf, &pubkey))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to parse extracted public key of private key file '%s'.", arg_private_key);
|
||||
}
|
||||
|
||||
r = pcr_states_allocate(&pcr_states);
|
||||
if (r < 0)
|
||||
|
262
src/boot/pcrphase.c
Normal file
262
src/boot/pcrphase.c
Normal file
@ -0,0 +1,262 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <getopt.h>
|
||||
|
||||
#include <sd-messages.h>
|
||||
|
||||
#include "efivars.h"
|
||||
#include "main-func.h"
|
||||
#include "openssl-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "pretty-print.h"
|
||||
#include "tpm-pcr.h"
|
||||
#include "tpm2-util.h"
|
||||
|
||||
static char *arg_tpm2_device = NULL;
|
||||
static char **arg_banks = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
|
||||
|
||||
static int help(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
int r;
|
||||
|
||||
r = terminal_urlify_man("systemd-pcrphase", "1", &link);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
printf("%1$s [OPTIONS...] COMMAND ...\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"
|
||||
" --version Print version\n"
|
||||
" --bank=DIGEST Select TPM bank (SHA1, SHA256)\n"
|
||||
" --tpm2-device=PATH Use specified TPM2 device\n"
|
||||
"\nSee the %2$s for details.\n",
|
||||
program_invocation_short_name,
|
||||
link,
|
||||
ansi_underline(),
|
||||
ansi_normal(),
|
||||
ansi_highlight(),
|
||||
ansi_normal());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_argv(int argc, char *argv[]) {
|
||||
enum {
|
||||
ARG_VERSION = 0x100,
|
||||
ARG_BANK,
|
||||
ARG_TPM2_DEVICE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "bank", required_argument, NULL, ARG_BANK },
|
||||
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
|
||||
{}
|
||||
};
|
||||
|
||||
int c;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
|
||||
case 'h':
|
||||
help(0, NULL, NULL);
|
||||
return 0;
|
||||
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case ARG_BANK: {
|
||||
const EVP_MD *implementation;
|
||||
|
||||
implementation = EVP_get_digestbyname(optarg);
|
||||
if (!implementation)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown bank '%s', refusing.", optarg);
|
||||
|
||||
if (strv_extend(&arg_banks, EVP_MD_name(implementation)) < 0)
|
||||
return log_oom();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_TPM2_DEVICE: {
|
||||
_cleanup_free_ char *device = NULL;
|
||||
|
||||
if (streq(optarg, "list"))
|
||||
return tpm2_list_devices();
|
||||
|
||||
if (!streq(optarg, "auto")) {
|
||||
device = strdup(optarg);
|
||||
if (!device)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
free_and_replace(arg_tpm2_device, device);
|
||||
break;
|
||||
}
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int determine_banks(struct tpm2_context *c) {
|
||||
_cleanup_free_ TPMI_ALG_HASH *algs = NULL;
|
||||
int n_algs, 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;
|
||||
|
||||
for (int i = 0; i < n_algs; i++) {
|
||||
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.");
|
||||
|
||||
r = strv_extend(&arg_banks, EVP_MD_name(implementation));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
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;
|
||||
size_t length;
|
||||
TSS2_RC rc;
|
||||
int r;
|
||||
|
||||
log_setup();
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
if (optind+1 != argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected a single argument.");
|
||||
|
||||
word = argv[optind];
|
||||
|
||||
/* 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.");
|
||||
|
||||
length = strlen(word);
|
||||
|
||||
/* 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) {
|
||||
log_info("Kernel stub did not measure kernel image into PCR %u, skipping measurement.", TPM_PCR_INDEX_KERNEL_IMAGE);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to read StubPcrKernelImage EFI variable: %m");
|
||||
|
||||
/* 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)
|
||||
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);
|
||||
|
||||
r = dlopen_tpm2();
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to load TPM2 libraries: %m");
|
||||
|
||||
r = tpm2_context_init(arg_tpm2_device, &c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = determine_banks(&c);
|
||||
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);
|
||||
|
||||
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));
|
||||
|
||||
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),
|
||||
"MEASURING=%s", word,
|
||||
"PCR=%u", TPM_PCR_INDEX_KERNEL_IMAGE,
|
||||
"BANKS=%s", joined);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
DEFINE_MAIN_FUNCTION(run);
|
@ -37,6 +37,7 @@ TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_
|
||||
TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
|
||||
TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL;
|
||||
TSS2_RC (*sym_Esys_LoadExternal)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR hierarchy, ESYS_TR *objectHandle);
|
||||
TSS2_RC (*sym_Esys_PCR_Extend)(ESYS_CONTEXT *esysContext, ESYS_TR pcrHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPML_DIGEST_VALUES *digests);
|
||||
TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
|
||||
TSS2_RC (*sym_Esys_PolicyAuthorize)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *approvedPolicy, const TPM2B_NONCE *policyRef, const TPM2B_NAME *keySign, const TPMT_TK_VERIFIED *checkTicket);
|
||||
TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3) = NULL;
|
||||
@ -72,6 +73,7 @@ int dlopen_tpm2(void) {
|
||||
DLSYM_ARG(Esys_Initialize),
|
||||
DLSYM_ARG(Esys_Load),
|
||||
DLSYM_ARG(Esys_LoadExternal),
|
||||
DLSYM_ARG(Esys_PCR_Extend),
|
||||
DLSYM_ARG(Esys_PCR_Read),
|
||||
DLSYM_ARG(Esys_PolicyAuthorize),
|
||||
DLSYM_ARG(Esys_PolicyAuthValue),
|
||||
@ -499,6 +501,36 @@ static int tpm2_pcr_mask_good(
|
||||
return good;
|
||||
}
|
||||
|
||||
static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) {
|
||||
|
||||
assert(selection);
|
||||
|
||||
/* As per https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
|
||||
* TPM2 on a Client PC must have at least 24 PCRs. If this TPM has less, just skip over it. */
|
||||
if (selection->sizeofSelect < TPM2_PCRS_MAX/8) {
|
||||
log_debug("Skipping TPM2 PCR bank %s with fewer than 24 PCRs.",
|
||||
strna(tpm2_pcr_bank_to_string(selection->hash)));
|
||||
return false;
|
||||
}
|
||||
|
||||
assert_cc(TPM2_PCRS_MAX % 8 == 0);
|
||||
|
||||
/* It's not enough to check how many PCRs there are, we also need to check that the 24 are
|
||||
* enabled for this bank. Otherwise this TPM doesn't qualify. */
|
||||
bool valid = true;
|
||||
for (size_t j = 0; j < TPM2_PCRS_MAX/8; j++)
|
||||
if (selection->pcrSelect[j] != 0xFF) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
log_debug("TPM2 PCR bank %s has fewer than 24 PCR bits enabled, ignoring.",
|
||||
strna(tpm2_pcr_bank_to_string(selection->hash)));
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
static int tpm2_get_best_pcr_bank(
|
||||
ESYS_CONTEXT *c,
|
||||
uint32_t pcr_mask,
|
||||
@ -508,6 +540,7 @@ static int tpm2_get_best_pcr_bank(
|
||||
TPMI_ALG_HASH supported_hash = 0, hash_with_valid_pcr = 0;
|
||||
TPMI_YES_NO more;
|
||||
TSS2_RC rc;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
|
||||
@ -528,38 +561,17 @@ static int tpm2_get_best_pcr_bank(
|
||||
assert(pcap->capability == TPM2_CAP_PCRS);
|
||||
|
||||
for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
|
||||
bool valid = true;
|
||||
int good;
|
||||
|
||||
/* For now we are only interested in the SHA1 and SHA256 banks */
|
||||
if (!IN_SET(pcap->data.assignedPCR.pcrSelections[i].hash, TPM2_ALG_SHA256, TPM2_ALG_SHA1))
|
||||
continue;
|
||||
|
||||
/* As per
|
||||
* https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClient_PFP_r1p05_v23_pub.pdf a
|
||||
* TPM2 on a Client PC must have at least 24 PCRs. If this TPM has less, just skip over
|
||||
* it. */
|
||||
if (pcap->data.assignedPCR.pcrSelections[i].sizeofSelect < TPM2_PCRS_MAX/8) {
|
||||
log_debug("Skipping TPM2 PCR bank %s with fewer than 24 PCRs.",
|
||||
strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
|
||||
r = tpm2_bank_has24(pcap->data.assignedPCR.pcrSelections + i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
continue;
|
||||
}
|
||||
|
||||
assert_cc(TPM2_PCRS_MAX % 8 == 0);
|
||||
|
||||
/* It's not enough to check how many PCRs there are, we also need to check that the 24 are
|
||||
* enabled for this bank. Otherwise this TPM doesn't qualify. */
|
||||
for (size_t j = 0; j < TPM2_PCRS_MAX/8; j++)
|
||||
if (pcap->data.assignedPCR.pcrSelections[i].pcrSelect[j] != 0xFF) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
log_debug("TPM2 PCR bank %s has fewer than 24 PCR bits enabled, ignoring.",
|
||||
strna(tpm2_pcr_bank_to_string(pcap->data.assignedPCR.pcrSelections[i].hash)));
|
||||
continue;
|
||||
}
|
||||
|
||||
good = tpm2_pcr_mask_good(c, pcap->data.assignedPCR.pcrSelections[i].hash, pcr_mask);
|
||||
if (good < 0)
|
||||
@ -616,6 +628,85 @@ static int tpm2_get_best_pcr_bank(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tpm2_get_good_pcr_banks(
|
||||
ESYS_CONTEXT *c,
|
||||
uint32_t pcr_mask,
|
||||
TPMI_ALG_HASH **ret) {
|
||||
|
||||
_cleanup_free_ TPMI_ALG_HASH *good_banks = NULL, *fallback_banks = NULL;
|
||||
_cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
|
||||
size_t n_good_banks = 0, n_fallback_banks = 0;
|
||||
TPMI_YES_NO more;
|
||||
TSS2_RC rc;
|
||||
int r;
|
||||
|
||||
assert(c);
|
||||
assert(ret);
|
||||
|
||||
rc = sym_Esys_GetCapability(
|
||||
c,
|
||||
ESYS_TR_NONE,
|
||||
ESYS_TR_NONE,
|
||||
ESYS_TR_NONE,
|
||||
TPM2_CAP_PCRS,
|
||||
0,
|
||||
1,
|
||||
&more,
|
||||
&pcap);
|
||||
if (rc != TSS2_RC_SUCCESS)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
|
||||
"Failed to determine TPM2 PCR bank capabilities: %s", sym_Tss2_RC_Decode(rc));
|
||||
|
||||
assert(pcap->capability == TPM2_CAP_PCRS);
|
||||
|
||||
for (size_t i = 0; i < pcap->data.assignedPCR.count; i++) {
|
||||
|
||||
/* Let's see if this bank is superficially OK, i.e. has at least 24 enabled registers */
|
||||
r = tpm2_bank_has24(pcap->data.assignedPCR.pcrSelections + i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!r)
|
||||
continue;
|
||||
|
||||
/* Let's now see if this bank has any of the selected PCRs actually initialized */
|
||||
r = tpm2_pcr_mask_good(c, pcap->data.assignedPCR.pcrSelections[i].hash, pcr_mask);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (n_good_banks + n_fallback_banks >= INT_MAX)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(E2BIG), "Too many good TPM2 banks?");
|
||||
|
||||
if (r) {
|
||||
if (!GREEDY_REALLOC(good_banks, n_good_banks+1))
|
||||
return log_oom();
|
||||
|
||||
good_banks[n_good_banks++] = pcap->data.assignedPCR.pcrSelections[i].hash;
|
||||
} else {
|
||||
if (!GREEDY_REALLOC(fallback_banks, n_fallback_banks+1))
|
||||
return log_oom();
|
||||
|
||||
fallback_banks[n_fallback_banks++] = pcap->data.assignedPCR.pcrSelections[i].hash;
|
||||
}
|
||||
}
|
||||
|
||||
/* Preferably, use the good banks (i.e. the ones the PCR values are actually initialized so
|
||||
* far). Otherwise use the fallback banks (i.e. which exist and are enabled, but so far not used. */
|
||||
if (n_good_banks > 0) {
|
||||
log_debug("Found %zu fully initialized TPM2 banks.", n_good_banks);
|
||||
*ret = TAKE_PTR(good_banks);
|
||||
return (int) n_good_banks;
|
||||
}
|
||||
if (n_fallback_banks > 0) {
|
||||
log_debug("Found %zu enabled but un-initialized TPM2 banks.", n_fallback_banks);
|
||||
*ret = TAKE_PTR(fallback_banks);
|
||||
return (int) n_fallback_banks;
|
||||
}
|
||||
|
||||
/* No suitable banks found. */
|
||||
*ret = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
|
||||
struct sha256_ctx hash;
|
||||
|
||||
|
@ -26,6 +26,7 @@ extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1
|
||||
extern TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion);
|
||||
extern TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle);
|
||||
extern TSS2_RC (*sym_Esys_LoadExternal)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR hierarchy, ESYS_TR *objectHandle);
|
||||
extern TSS2_RC (*sym_Esys_PCR_Extend)(ESYS_CONTEXT *esysContext, ESYS_TR pcrHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPML_DIGEST_VALUES *digests);
|
||||
extern TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues);
|
||||
extern TSS2_RC (*sym_Esys_PolicyAuthorize)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *approvedPolicy, const TPM2B_NONCE *policyRef, const TPM2B_NAME *keySign, const TPMT_TK_VERIFIED *checkTicket);
|
||||
extern TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3);
|
||||
@ -66,6 +67,8 @@ static inline void Esys_Freep(void *p) {
|
||||
sym_Esys_Free(*(void**) p);
|
||||
}
|
||||
|
||||
int tpm2_get_good_pcr_banks(ESYS_CONTEXT *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
|
||||
|
||||
#else
|
||||
struct tpm2_context;
|
||||
#endif
|
||||
|
@ -189,6 +189,9 @@ _SD_BEGIN_DECLARATIONS;
|
||||
#define SD_MESSAGE_SHUTDOWN_CANCELED SD_ID128_MAKE(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
|
||||
#define SD_MESSAGE_SHUTDOWN_CANCELED_STR SD_ID128_MAKE_STR(24,9f,6f,b9,e6,e2,42,8c,96,f3,f0,87,56,81,ff,a3)
|
||||
|
||||
#define SD_MESSAGE_TPM_PCR_EXTEND SD_ID128_MAKE(3f,7d,5e,f3,e5,4f,43,02,b4,f0,b1,43,bb,27,0c,ab)
|
||||
#define SD_MESSAGE_TPM_PCR_EXTEND_STR SD_ID128_MAKE_STR(3f,7d,5e,f3,e5,4f,43,02,b4,f0,b1,43,bb,27,0c,ab)
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
#endif
|
||||
|
@ -69,14 +69,27 @@ if [[ -e /usr/lib/systemd/systemd-measure ]]; then
|
||||
11:sha384=5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087
|
||||
11:sha512=961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b
|
||||
EOF
|
||||
|
||||
/usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 | cmp - /tmp/result
|
||||
/usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: | cmp - /tmp/result
|
||||
|
||||
cat >/tmp/result.json <<EOF
|
||||
{"sha1":[{"pcr":11,"hash":"5177e4ad69db92192c10e5f80402bf81bfec8a81"}],"sha256":[{"pcr":11,"hash":"37b48bd0b222394dbe3cceff2fca4660c4b0a90ae9369ec90b42f14489989c13"}],"sha384":[{"pcr":11,"hash":"5573f9b2caf55b1d0a6a701f890662d682af961899f0419cf1e2d5ea4a6a68c1f25bd4f5b8a0865eeee82af90f5cb087"}],"sha512":[{"pcr":11,"hash":"961305d7e9981d6606d1ce97b3a9a1f92610cac033e9c39064895f0e306abc1680463d55767bd98e751eae115bdef3675a9ee1d29ed37da7885b1db45bb2555b"}]}
|
||||
EOF
|
||||
/usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=: -j | diff -u - /tmp/result.json
|
||||
|
||||
/usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 -j | diff -u - /tmp/result.json
|
||||
cat >/tmp/result <<EOF
|
||||
11:sha1=6765ee305db063040c454d32697d922b3d4f232b
|
||||
11:sha256=21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699
|
||||
11:sha384=08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2
|
||||
11:sha512=65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0
|
||||
EOF
|
||||
/usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo | cmp - /tmp/result
|
||||
|
||||
cat >/tmp/result.json <<EOF
|
||||
{"sha1":[{"phase":"foo","pcr":11,"hash":"6765ee305db063040c454d32697d922b3d4f232b"}],"sha256":[{"phase":"foo","pcr":11,"hash":"21c49c1242042649e09c156546fd7d425ccc3c67359f840507b30be4e0f6f699"}],"sha384":[{"phase":"foo","pcr":11,"hash":"08d0b003a134878eee552070d51d58abe942f457ca85704131dd36f73728e7327ca837594bc9d5ac7de818d02a3d5dd2"}],"sha512":[{"phase":"foo","pcr":11,"hash":"65120f6ebc04b156421c6f3d543b2fad545363d9ca61c514205459e9c0e0b22e09c23605eae5853e38458ef3ca54e087168af8d8a882a98d220d9391e48be6d0"}]}
|
||||
EOF
|
||||
/usr/lib/systemd/systemd-measure calculate --linux=/tmp/tpmdata1 --initrd=/tmp/tpmdata2 --bank=sha1 --bank=sha256 --bank=sha384 --bank=sha512 --phase=foo -j | diff -u - /tmp/result.json
|
||||
|
||||
rm /tmp/result /tmp/result.json
|
||||
else
|
||||
echo "/usr/lib/systemd/systemd-measure not found, skipping PCR policy test case"
|
||||
fi
|
||||
@ -89,7 +102,7 @@ if [ -e /usr/lib/systemd/systemd-measure ] && \
|
||||
openssl rsa -pubout -in "/tmp/pcrsign-private.pem" -out "/tmp/pcrsign-public.pem"
|
||||
|
||||
# Sign current PCR state with it
|
||||
/usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" | tee "/tmp/pcrsign.sig"
|
||||
/usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: | tee "/tmp/pcrsign.sig"
|
||||
dd if=/dev/urandom of=/tmp/pcrtestdata bs=1024 count=64
|
||||
systemd-creds encrypt /tmp/pcrtestdata /tmp/pcrtestdata.encrypted --with-key=host+tpm2-with-public-key --tpm2-public-key="/tmp/pcrsign-public.pem"
|
||||
systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" | cmp - /tmp/pcrtestdata
|
||||
@ -99,7 +112,7 @@ if [ -e /usr/lib/systemd/systemd-measure ] && \
|
||||
systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig" > /dev/null && { echo 'unexpected success'; exit 1; }
|
||||
|
||||
# Sign new PCR state, decrypting should work now.
|
||||
/usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" > "/tmp/pcrsign.sig2"
|
||||
/usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig2"
|
||||
systemd-creds decrypt /tmp/pcrtestdata.encrypted - --tpm2-signature="/tmp/pcrsign.sig2" | cmp - /tmp/pcrtestdata
|
||||
|
||||
# Now, do the same, but with a cryptsetup binding
|
||||
@ -121,7 +134,7 @@ if [ -e /usr/lib/systemd/systemd-measure ] && \
|
||||
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig2",headless=1 && { echo 'unexpected success'; exit 1; }
|
||||
|
||||
# But once we sign the current PCRs, we should be able to unlock again
|
||||
/usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" > "/tmp/pcrsign.sig3"
|
||||
/usr/lib/systemd/systemd-measure sign --current --bank=sha1 --bank=sha256 --private-key="/tmp/pcrsign-private.pem" --public-key="/tmp/pcrsign-public.pem" --phase=: > "/tmp/pcrsign.sig3"
|
||||
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=0 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
|
||||
/usr/lib/systemd/systemd-cryptsetup detach test-volume2
|
||||
SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE=1 /usr/lib/systemd/systemd-cryptsetup attach test-volume2 $img - tpm2-device=auto,tpm2-signature="/tmp/pcrsign.sig3",headless=1
|
||||
|
@ -260,6 +260,10 @@ in_units = [
|
||||
'sysinit.target.wants/ initrd-root-fs.target.wants/'],
|
||||
['user-runtime-dir@.service', ''],
|
||||
['user@.service', ''],
|
||||
['systemd-pcrphase-initrd.service', 'HAVE_GNU_EFI ENABLE_INITRD',
|
||||
'initrd.target.wants/'],
|
||||
['systemd-pcrphase.service', 'HAVE_GNU_EFI',
|
||||
'sysinit.target.wants/'],
|
||||
]
|
||||
|
||||
add_wants = []
|
||||
|
24
units/systemd-pcrphase-initrd.service.in
Normal file
24
units/systemd-pcrphase-initrd.service.in
Normal 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 Barrier (initrd)
|
||||
Documentation=man:systemd-pcrphase-initrd.service(8)
|
||||
DefaultDependencies=no
|
||||
Conflicts=shutdown.target initrd-switch-root.target
|
||||
Before=sysinit.target cryptsetup-pre.target cryptsetup.target shutdown.target initrd-switch-root.target systemd-sysext.service
|
||||
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 enter-initrd
|
||||
ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase leave-initrd
|
23
units/systemd-pcrphase.service.in
Normal file
23
units/systemd-pcrphase.service.in
Normal 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 Barrier (Host)
|
||||
Documentation=man:systemd-pcrphase.service(8)
|
||||
After=remote-fs.target remote-cryptsetup.target
|
||||
Before=systemd-user-sessions.service
|
||||
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 ready
|
||||
ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase shutdown
|
Loading…
Reference in New Issue
Block a user