systemd-252-27

Resolves: RHEL-1086,RHEL-11591,RHEL-16182,RHEL-19483,RHEL-7026
This commit is contained in:
Jan Macku 2024-02-15 09:36:23 +01:00
parent 3e543ba0c0
commit e5f65c3fc6
186 changed files with 34526 additions and 1 deletions

View File

@ -0,0 +1,28 @@
From 02db660b590df6e281468ff078904a83c6d52f8c Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <fsumsal@redhat.com>
Date: Thu, 25 Jan 2024 12:33:29 +0100
Subject: [PATCH] test: merge TEST-20-MAINPIDGAMES into TEST-07-PID1 (fixup)
Forgotten snippet from the original commit, complements commit
77827462f17ba6de2c56c7e242d1468f9c112cb3.
(cherry picked from commit 3a4b86264eef6bd51e880386388e8b3f95cbaa33)
Related: RHEL-1086
---
test/units/testsuite-07.main-PID-change.sh | 2 ++
1 file changed, 2 insertions(+)
diff --git a/test/units/testsuite-07.main-PID-change.sh b/test/units/testsuite-07.main-PID-change.sh
index be4631f10d..bd5a63a272 100755
--- a/test/units/testsuite-07.main-PID-change.sh
+++ b/test/units/testsuite-07.main-PID-change.sh
@@ -151,6 +151,8 @@ systemd-run --unit=test-mainpidsh3.service \
-p RuntimeDirectory=mainpidsh3 \
-p PIDFile=/run/mainpidsh3/pid \
-p DynamicUser=1 \
+ `# Make sanitizers happy when DynamicUser=1 pulls in instrumented systemd NSS modules` \
+ -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
-p TimeoutStartSec=2s \
/dev/shm/test-mainpid3.sh \
&& { echo 'unexpected success'; exit 1; }

View File

@ -0,0 +1,28 @@
From dc476081db6f76458b9fe78a1ba70505c8fa1e4f Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Wed, 24 Jan 2024 19:19:29 +0100
Subject: [PATCH] test: use the default nsec3-iterations value
In Knot 3.2 the nsec3-iterations default was changed to 0 and Knot now
issues a warning if the value is > 0. Let's just use the default value,
since it's not something that's important for our tests.
(cherry picked from commit 0652cf8e7b08c97a52a0995eb8f0dc6bb20a4de0)
Related: RHEL-1086
---
test/knot-data/knot.conf | 1 -
1 file changed, 1 deletion(-)
diff --git a/test/knot-data/knot.conf b/test/knot-data/knot.conf
index 6ea0cca3db..cfe478fe1c 100644
--- a/test/knot-data/knot.conf
+++ b/test/knot-data/knot.conf
@@ -52,7 +52,6 @@ policy:
ksk-lifetime: 365d
ksk-submission: parent_zone_sbm
nsec3: on
- nsec3-iterations: 10
propagation-delay: 1s
signing-threads: 4
zone-max-ttl: 1s

View File

@ -0,0 +1,40 @@
From 101069af4b1ccee4f8c9723edef17e6213926fec Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Tue, 30 Jan 2024 16:27:58 +0100
Subject: [PATCH] test: explicitly set nsec3-iterations to 0
knot v3.2 and later does this by default. knot v3.1 still has the default set to
10, but it also introduced a warning that the default will be changed to 0 in
later versions, so it effectively complains about its own default, which then
fails the config check. Let's just set the value explicitly to zero to avoid
that.
~# knotc --version
knotc (Knot DNS), version 3.1.6
~# grep nsec3-iterations test/knot-data/knot.conf || echo nope
nope
~# knotc -c /build/test/knot-data/knot.conf conf-check
warning: config, policy[auto_rollover_nsec3].nsec3-iterations defaults to 10, since version 3.2 the default becomes 0
Configuration is valid
Follow-up to 0652cf8e7b.
(cherry picked from commit cb3244c0dcea80ad35e5bcaf7a07bd449ac65325)
Related: RHEL-1086
---
test/knot-data/knot.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/test/knot-data/knot.conf b/test/knot-data/knot.conf
index cfe478fe1c..b925812312 100644
--- a/test/knot-data/knot.conf
+++ b/test/knot-data/knot.conf
@@ -51,6 +51,7 @@ policy:
ds-push: parent_zone_server
ksk-lifetime: 365d
ksk-submission: parent_zone_sbm
+ nsec3-iterations: 0
nsec3: on
propagation-delay: 1s
signing-threads: 4

View File

@ -0,0 +1,75 @@
From d83713ea522032bb135bf7f442779a431840a6db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20Koutn=C3=BD?= <mkoutny@suse.com>
Date: Wed, 18 Jan 2023 23:20:31 +0100
Subject: [PATCH] core: mount namespaces: Remove auxiliary bind mounts
directory after unit termination
Unit that requires its own mount namespace creates a temporary directory
to implement dynamic bind mounts (org.freedesktop.systemd1.Manager.BindMountUnit).
However, this directory is never removed and they will accumulate for
each unique unit (e.g. templated units of systemd-coredump@).
Attach the auxiliary runtime directory existence to lifetime of other
"runtime" only per-unit directories.
(cherry picked from commit b9f976fb45635e09cd709dbedd0afb03d4b73c05)
Resolves: RHEL-19483
---
src/core/execute.c | 17 +++++++++++++++++
src/core/execute.h | 1 +
src/core/unit.c | 1 +
3 files changed, 19 insertions(+)
diff --git a/src/core/execute.c b/src/core/execute.c
index b5b7de6d2a..ea36254241 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -5478,6 +5478,23 @@ int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_p
return 0;
}
+int exec_context_destroy_mount_ns_dir(Unit *u) {
+ _cleanup_free_ char *p = NULL;
+
+ if (!u || !MANAGER_IS_SYSTEM(u->manager))
+ return 0;
+
+ p = path_join("/run/systemd/propagate/", u->id);
+ if (!p)
+ return -ENOMEM;
+
+ /* This is only filled transiently (see mount_in_namespace()), should be empty or even non-existent*/
+ if (rmdir(p) < 0 && errno != ENOENT)
+ log_unit_debug_errno(u, errno, "Unable to remove propagation dir '%s', ignoring: %m", p);
+
+ return 0;
+}
+
static void exec_command_done(ExecCommand *c) {
assert(c);
diff --git a/src/core/execute.h b/src/core/execute.h
index a2cf22806b..4c54422073 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -453,6 +453,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix);
int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_root);
int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_root, const char *unit);
+int exec_context_destroy_mount_ns_dir(Unit *u);
const char* exec_context_fdname(const ExecContext *c, int fd_index);
diff --git a/src/core/unit.c b/src/core/unit.c
index 438213a47a..c9a42ee3d7 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -5778,6 +5778,7 @@ void unit_destroy_runtime_data(Unit *u, const ExecContext *context) {
exec_context_destroy_runtime_directory(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME]);
exec_context_destroy_credentials(context, u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id);
+ exec_context_destroy_mount_ns_dir(u);
}
int unit_clean(Unit *u, ExecCleanMask mask) {

View File

@ -0,0 +1,126 @@
From d13e882c482ecda4c726d3f48577deb01fbfc605 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Fri, 9 Feb 2024 13:17:35 +0100
Subject: [PATCH] ci: deploy systemd man to GitHub Pages
rhel-only
Related: RHEL-1086
---
.github/workflows/deploy-man-pages.yml | 107 +++++++++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 .github/workflows/deploy-man-pages.yml
diff --git a/.github/workflows/deploy-man-pages.yml b/.github/workflows/deploy-man-pages.yml
new file mode 100644
index 0000000000..08c3d6e322
--- /dev/null
+++ b/.github/workflows/deploy-man-pages.yml
@@ -0,0 +1,107 @@
+name: Deploy systemd man to Pages
+
+on:
+ push:
+ branches: [ main ]
+ paths:
+ - man/*
+ - .github/workflows/deploy-man-pages.yml
+ schedule:
+ # Run every Monday at 4:00 AM UTC
+ - cron: 0 4 * * 1
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+ group: pages
+ cancel-in-progress: false
+
+jobs:
+ # Single deploy job since we're just deploying
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+
+ permissions:
+ pages: write
+ id-token: write
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install dependencies
+ run: |
+ RELEASE="$(lsb_release -cs)"
+ sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list"
+ sudo add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci
+ sudo apt-get -y update
+ sudo apt-get -y build-dep systemd
+ sudo apt-get install -y \
+ cryptsetup-bin \
+ expect \
+ fdisk \
+ gettext \
+ iputils-ping \
+ isc-dhcp-client \
+ itstool \
+ kbd \
+ libblkid-dev \
+ libbpf-dev \
+ libc6-dev-i386 \
+ libcap-dev \
+ libcurl4-gnutls-dev \
+ libfdisk-dev \
+ libfido2-dev \
+ libgpg-error-dev \
+ liblz4-dev \
+ liblzma-dev \
+ libmicrohttpd-dev \
+ libmount-dev \
+ libp11-kit-dev \
+ libpwquality-dev \
+ libqrencode-dev \
+ libssl-dev \
+ libtss2-dev \
+ libxkbcommon-dev \
+ libxtables-dev \
+ libzstd-dev \
+ meson \
+ mold \
+ mount \
+ net-tools \
+ ninja-build \
+ perl \
+ python3-evdev \
+ python3-jinja2 \
+ python3-lxml \
+ python3-pip \
+ python3-pyparsing \
+ python3-setuptools \
+ quota \
+ strace \
+ unifont \
+ util-linux \
+ zstd \
+
+ - name: Build HTML man pages
+ run: |
+ meson build
+ ninja -C build man/html
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v4
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./build/man
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4

View File

@ -0,0 +1,81 @@
From ce51ce80e2f0c42600d9a157db121d9aa63459e5 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Fri, 9 Feb 2024 14:04:02 +0100
Subject: [PATCH] doc: add missing `<listitem>` to
`systemd.net-naming-scheme.xml`
follow-up to: dcc59dffa5116bf96618065cd60742cb660224b8
rhel-only
Related: RHEL-7026
---
man/systemd.net-naming-scheme.xml | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml
index 3bab402e98..532d6ddb15 100644
--- a/man/systemd.net-naming-scheme.xml
+++ b/man/systemd.net-naming-scheme.xml
@@ -483,19 +483,19 @@
<varlistentry>
<term><constant>rhel-8.1</constant></term>
- <para>Same as naming scheme <constant>rhel-8.0</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.0</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>rhel-8.2</constant></term>
- <para>Same as naming scheme <constant>rhel-8.0</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.0</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>rhel-8.3</constant></term>
- <para>Same as naming scheme <constant>rhel-8.0</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.0</constant>.</para></listitem>
</varlistentry>
<varlistentry>
@@ -511,13 +511,13 @@
<varlistentry>
<term><constant>rhel-8.5</constant></term>
- <para>Same as naming scheme <constant>rhel-8.4</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.4</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>rhel-8.6</constant></term>
- <para>Same as naming scheme <constant>rhel-8.4</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.4</constant>.</para></listitem>
</varlistentry>
<varlistentry>
@@ -538,19 +538,19 @@
<varlistentry>
<term><constant>rhel-8.8</constant></term>
- <para>Same as naming scheme <constant>rhel-8.7</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.7</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>rhel-8.9</constant></term>
- <para>Same as naming scheme <constant>rhel-8.7</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.7</constant>.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>rhel-8.10</constant></term>
- <para>Same as naming scheme <constant>rhel-8.7</constant>.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-8.7</constant>.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -0,0 +1,145 @@
From 9fc363f03fb69db8bf25a6c854489d58eb11617e Mon Sep 17 00:00:00 2001
From: Lukas Nykryn <lnykryn@redhat.com>
Date: Fri, 9 Feb 2024 15:51:36 +0100
Subject: [PATCH] man: reorder the list of supported naming schemes
Let's put the upstream version first, followed by the RHEL9 backports
and move RHEL8 ones to separate section.
rhel-only
Related: RHEL-7026
---
man/systemd.net-naming-scheme.xml | 104 +++++++++++++-----------------
1 file changed, 46 insertions(+), 58 deletions(-)
diff --git a/man/systemd.net-naming-scheme.xml b/man/systemd.net-naming-scheme.xml
index 532d6ddb15..a5903c6d04 100644
--- a/man/systemd.net-naming-scheme.xml
+++ b/man/systemd.net-naming-scheme.xml
@@ -460,26 +460,58 @@
</varlistentry>
<varlistentry>
- <term><constant>rhel-8.0</constant></term>
+ <term><constant>rhel-9.0</constant></term>
- <listitem><para>Naming was changed for virtual network interfaces created with SR-IOV and NPAR and
- for devices where the PCI network controller device does not have a slot number associated.</para>
+ <listitem><para>Since version <constant>v247</constant> we no longer set
+ <varname>ID_NET_NAME_SLOT</varname> if we detect that a PCI device associated with a slot is a PCI
+ bridge as that would create naming conflict when there are more child devices on that bridge. Now,
+ this is relaxed and we will use slot information to generate the name based on it but only if
+ the PCI device has multiple functions. This is safe because distinct function number is a part of
+ the device name for multifunction devices.</para>
+ </listitem>
+ </varlistentry>
- <para>SR-IOV virtual devices are named based on the name of the parent interface, with a suffix of
- <literal>v<replaceable>port</replaceable></literal>, where <replaceable>port</replaceable> is the
- virtual device number. Previously those virtual devices were named as if completely independent.
- </para>
+ <varlistentry>
+ <term><constant>rhel-9.1</constant></term>
- <para>The ninth and later NPAR virtual devices are named following the scheme used for the first
- eight NPAR partitions. Previously those devices were not renamed and the kernel default
- ("eth<replaceable>N</replaceable>") was used.</para>
+ <listitem><para>Same as naming scheme <constant>rhel-9.0</constant>.</para></listitem>
+ </varlistentry>
- <para>Names are also generated for PCI devices where the PCI network controller device does not
- have an associated slot number itself, but one of its parents does. Previously those devices were
- not renamed and the kernel default was used.</para>
+ <varlistentry>
+ <term><constant>rhel-9.2</constant></term>
+
+ <listitem><para>Same as naming scheme <constant>rhel-9.0</constant>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>rhel-9.3</constant></term>
+
+ <listitem><para>Naming was changed for SR-IOV virtual device representors.</para>
+
+ <para>The <literal>r<replaceable>slot</replaceable></literal> suffix was added to differentiate SR-IOV
+ virtual device representors attached to a single physical device interface.
+ </para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><constant>rhel-9.4</constant></term>
+
+ <listitem><para>Same as naming scheme <constant>rhel-9.3</constant>.</para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>By default <constant>rhel-9.0</constant> is used.</para>
+ <refsect2>
+ <title>RHEL-8 schemes</title>
+ <para>It is also possible to pick a scheme from RHEL-8</para>
+ <variablelist>
+ <varlistentry>
+ <term><constant>rhel-8.0</constant></term>
+
+ <listitem><para>Same as naming scheme <constant>v239</constant>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><constant>rhel-8.1</constant></term>
@@ -552,52 +584,8 @@
<listitem><para>Same as naming scheme <constant>rhel-8.7</constant>.</para></listitem>
</varlistentry>
-
- <varlistentry>
- <term><constant>rhel-9.0</constant></term>
-
- <listitem><para>Since version <constant>v247</constant> we no longer set
- <varname>ID_NET_NAME_SLOT</varname> if we detect that a PCI device associated with a slot is a PCI
- bridge as that would create naming conflict when there are more child devices on that bridge. Now,
- this is relaxed and we will use slot information to generate the name based on it but only if
- the PCI device has multiple functions. This is safe because distinct function number is a part of
- the device name for multifunction devices.</para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>rhel-9.1</constant></term>
-
- <listitem><para>Same as naming scheme <constant>rhel-9.0</constant>.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>rhel-9.2</constant></term>
-
- <listitem><para>Same as naming scheme <constant>rhel-9.0</constant>.</para></listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>rhel-9.3</constant></term>
-
- <listitem><para>Naming was changed for SR-IOV virtual device representors.</para>
-
- <para>The <literal>r<replaceable>slot</replaceable></literal> suffix was added to differentiate SR-IOV
- virtual device representors attached to a single physical device interface.
- </para>
- </listitem>
- </varlistentry>
-
- <varlistentry>
- <term><constant>rhel-9.4</constant></term>
-
- <listitem><para>Same as naming scheme <constant>rhel-9.3</constant>.</para></listitem>
- </varlistentry>
-
</variablelist>
-
- <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
- particular version of systemd).</para>
+ </refsect2>
</refsect1>
<refsect1>

View File

@ -0,0 +1,440 @@
From 841222c6f5b94ee4dfa53ce257cf021dc1ffeac3 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 11 Jan 2023 10:42:05 +0100
Subject: [PATCH] tree-wide: fix return value handling of base64mem()
This returns an ssize_t, not an int. On populare archs that's the
difference between 64bit and 32bit. hence, let's be more careful here,
and not silently drop half the bits on the ground by assigning the
return value to "int".
As noticed by @malikabhi05:
https://github.com/systemd/systemd/pull/24754#discussion_r1062903159
(cherry picked from commit 5e476b851251dd5addd39f06ebdf05bb3efb0be7)
Related: RHEL-16182
---
src/cryptenroll/cryptenroll-fido2.c | 9 ++++----
src/cryptenroll/cryptenroll-pkcs11.c | 9 ++++----
src/cryptenroll/cryptenroll-tpm2.c | 9 ++++----
.../cryptsetup-token-systemd-tpm2.c | 9 ++++----
.../cryptsetup-tokens/luks2-fido2.c | 9 ++++----
.../cryptsetup-tokens/luks2-pkcs11.c | 9 ++++----
src/cryptsetup/cryptsetup.c | 21 +++++++++++--------
src/home/homectl-fido2.c | 14 +++++++------
src/home/homectl-pkcs11.c | 7 ++++---
src/home/homework-fido2.c | 7 ++++---
src/home/homework-fscrypt.c | 9 ++++----
src/partition/repart.c | 9 ++++----
12 files changed, 68 insertions(+), 53 deletions(-)
diff --git a/src/cryptenroll/cryptenroll-fido2.c b/src/cryptenroll/cryptenroll-fido2.c
index 80adaefa17..87432e0d5e 100644
--- a/src/cryptenroll/cryptenroll-fido2.c
+++ b/src/cryptenroll/cryptenroll-fido2.c
@@ -21,6 +21,7 @@ int enroll_fido2(
_cleanup_free_ char *keyslot_as_string = NULL;
size_t cid_size, salt_size, secret_size;
_cleanup_free_ void *cid = NULL;
+ ssize_t base64_encoded_size;
const char *node, *un;
int r, keyslot;
@@ -53,9 +54,9 @@ int enroll_fido2(
return r;
/* Before we use the secret, we base64 encode it, for compat with homed, and to make it easier to type in manually */
- r = base64mem(secret, secret_size, &base64_encoded);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode secret key: %m");
+ base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
r = cryptsetup_set_minimal_pbkdf(cd);
if (r < 0)
@@ -67,7 +68,7 @@ int enroll_fido2(
volume_key,
volume_key_size,
base64_encoded,
- strlen(base64_encoded));
+ base64_encoded_size);
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new FIDO2 key to %s: %m", node);
diff --git a/src/cryptenroll/cryptenroll-pkcs11.c b/src/cryptenroll/cryptenroll-pkcs11.c
index 9f07a2e01d..54b6b86242 100644
--- a/src/cryptenroll/cryptenroll-pkcs11.c
+++ b/src/cryptenroll/cryptenroll-pkcs11.c
@@ -21,6 +21,7 @@ int enroll_pkcs11(
size_t decrypted_key_size, encrypted_key_size;
_cleanup_free_ void *encrypted_key = NULL;
_cleanup_(X509_freep) X509 *cert = NULL;
+ ssize_t base64_encoded_size;
const char *node;
EVP_PKEY *pkey;
int keyslot, r;
@@ -60,9 +61,9 @@ int enroll_pkcs11(
/* Let's base64 encode the key to use, for compat with homed (and it's easier to type it in by
* keyboard, if that might ever end up being necessary.) */
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode secret key: %m");
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
r = cryptsetup_set_minimal_pbkdf(cd);
if (r < 0)
@@ -74,7 +75,7 @@ int enroll_pkcs11(
volume_key,
volume_key_size,
base64_encoded,
- strlen(base64_encoded));
+ base64_encoded_size);
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new PKCS#11 key to %s: %m", node);
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index 5c902908c4..96d5fc0695 100644
--- a/src/cryptenroll/cryptenroll-tpm2.c
+++ b/src/cryptenroll/cryptenroll-tpm2.c
@@ -145,6 +145,7 @@ int enroll_tpm2(struct crypt_device *cd,
uint16_t pcr_bank, primary_alg;
const char *node;
_cleanup_(erase_and_freep) char *pin_str = NULL;
+ ssize_t base64_encoded_size;
int r, keyslot;
TPM2Flags flags = 0;
@@ -230,9 +231,9 @@ int enroll_tpm2(struct crypt_device *cd,
}
/* let's base64 encode the key to use, for compat with homed (and it's easier to every type it in by keyboard, if that might end up being necessary. */
- r = base64mem(secret, secret_size, &base64_encoded);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode secret key: %m");
+ base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
r = cryptsetup_set_minimal_pbkdf(cd);
if (r < 0)
@@ -244,7 +245,7 @@ int enroll_tpm2(struct crypt_device *cd,
volume_key,
volume_key_size,
base64_encoded,
- strlen(base64_encoded));
+ base64_encoded_size);
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index abe80720af..1eb924529c 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -51,6 +51,7 @@ _public_ int cryptsetup_token_open_pin(
.search_pcr_mask = UINT32_MAX
};
uint16_t pcr_bank, primary_alg;
+ ssize_t base64_encoded_size;
TPM2Flags flags = 0;
const char *json;
int r;
@@ -116,13 +117,13 @@ _public_ int cryptsetup_token_open_pin(
return log_debug_open_error(cd, r);
/* Before using this key as passphrase we base64 encode it, for compat with homed */
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
- return log_debug_open_error(cd, r);
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return log_debug_open_error(cd, base64_encoded_size);
/* free'd automatically by libcryptsetup */
- *ret_password_len = strlen(base64_encoded);
*ret_password = TAKE_PTR(base64_encoded);
+ *ret_password_len = base64_encoded_size;
return 0;
}
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
index a0e1ccbeeb..a1c85e600c 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
@@ -25,6 +25,7 @@ int acquire_luks2_key(
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
_cleanup_strv_free_erase_ char **pins = NULL;
+ ssize_t base64_encoded_size;
assert(ret_keyslot_passphrase);
assert(ret_keyslot_passphrase_size);
@@ -58,12 +59,12 @@ int acquire_luks2_key(
return r;
/* Before using this key as passphrase we base64 encode it, for compat with homed */
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
- return crypt_log_error_errno(cd, r, "Failed to base64 encode key: %m");
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return crypt_log_error_errno(cd, (int) base64_encoded_size, "Failed to base64 encode key: %m");
*ret_keyslot_passphrase = TAKE_PTR(base64_encoded);
- *ret_keyslot_passphrase_size = strlen(*ret_keyslot_passphrase);
+ *ret_keyslot_passphrase_size = base64_encoded_size;
return 0;
}
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
index 2e0450aa5b..1ed9e2bb86 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
@@ -187,6 +187,7 @@ int acquire_luks2_key(
_cleanup_free_ char *pkcs11_uri = NULL;
_cleanup_free_ void *encrypted_key = NULL;
systemd_pkcs11_plugin_params *pkcs11_params = userdata;
+ ssize_t base64_encoded_size;
assert(json);
assert(ret_password);
@@ -213,12 +214,12 @@ int acquire_luks2_key(
if (r < 0)
return r;
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
- return crypt_log_error_errno(cd, r, "Can not base64 encode key: %m");
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return crypt_log_error_errno(cd, (int) base64_encoded_size, "Can not base64 encode key: %m");
*ret_password = TAKE_PTR(base64_encoded);
- *ret_password_size = strlen(*ret_password);
+ *ret_password_size = base64_encoded_size;
return 0;
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 7aa36b4b03..a25fb28948 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -1176,14 +1176,15 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
r = 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;
/* Before using this key as passphrase we base64 encode it, for compat with homed */
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
return log_oom();
- r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, strlen(base64_encoded), flags);
+ r = 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?)");
@@ -1323,6 +1324,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
r = 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;
/* Before using this key as passphrase we base64 encode it. Why? For compatibility
* with homed's PKCS#11 hookup: there we want to use the key we acquired through
@@ -1332,11 +1334,11 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
* without embedded NUL here too, and that's easiest to generate from a binary blob
* via base64 encoding. */
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
return log_oom();
- r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, strlen(base64_encoded), flags);
+ r = 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?)");
@@ -1611,14 +1613,15 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
r = 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;
/* Before using this key as passphrase we base64 encode it, for compat with homed */
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
return log_oom();
- r = crypt_activate_by_passphrase(cd, name, keyslot, base64_encoded, strlen(base64_encoded), flags);
+ r = 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?)");
diff --git a/src/home/homectl-fido2.c b/src/home/homectl-fido2.c
index 61f0d081a3..3cbdf912aa 100644
--- a/src/home/homectl-fido2.c
+++ b/src/home/homectl-fido2.c
@@ -26,14 +26,15 @@ static int add_fido2_credential_id(
_cleanup_(json_variant_unrefp) JsonVariant *w = NULL;
_cleanup_strv_free_ char **l = NULL;
_cleanup_free_ char *escaped = NULL;
+ ssize_t escaped_size;
int r;
assert(v);
assert(cid);
- r = base64mem(cid, cid_size, &escaped);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode FIDO2 credential ID: %m");
+ escaped_size = base64mem(cid, cid_size, &escaped);
+ if (escaped_size < 0)
+ return log_error_errno(escaped_size, "Failed to base64 encode FIDO2 credential ID: %m");
w = json_variant_ref(json_variant_by_key(*v, "fido2HmacCredential"));
if (w) {
@@ -73,13 +74,14 @@ static int add_fido2_salt(
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL, *hashed = NULL;
+ ssize_t base64_encoded_size;
int r;
/* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
* expect a NUL terminated string, and we use a binary key */
- r = base64mem(secret, secret_size, &base64_encoded);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode secret key: %m");
+ base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
r = hash_password(base64_encoded, &hashed);
if (r < 0)
diff --git a/src/home/homectl-pkcs11.c b/src/home/homectl-pkcs11.c
index 69c9d97aca..dc6ecf1665 100644
--- a/src/home/homectl-pkcs11.c
+++ b/src/home/homectl-pkcs11.c
@@ -19,6 +19,7 @@ static int add_pkcs11_encrypted_key(
_cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL, *hashed = NULL;
+ ssize_t base64_encoded_size;
int r;
assert(v);
@@ -30,9 +31,9 @@ static int add_pkcs11_encrypted_key(
/* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends
* expect a NUL terminated string, and we use a binary key */
- r = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode secret key: %m");
+ base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
r = hash_password(base64_encoded, &hashed);
if (r < 0)
diff --git a/src/home/homework-fido2.c b/src/home/homework-fido2.c
index 23fda4a355..5c7cd52e1b 100644
--- a/src/home/homework-fido2.c
+++ b/src/home/homework-fido2.c
@@ -17,6 +17,7 @@ int fido2_use_token(
_cleanup_(erase_and_freep) void *hmac = NULL;
size_t hmac_size;
Fido2EnrollFlags flags = 0;
+ ssize_t ss;
int r;
assert(h);
@@ -65,9 +66,9 @@ int fido2_use_token(
if (r < 0)
return r;
- r = base64mem(hmac, hmac_size, ret);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode HMAC secret: %m");
+ ss = base64mem(hmac, hmac_size, ret);
+ if (ss < 0)
+ return log_error_errno(ss, "Failed to base64 encode HMAC secret: %m");
return 0;
}
diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c
index 5106961f38..bd32393d93 100644
--- a/src/home/homework-fscrypt.c
+++ b/src/home/homework-fscrypt.c
@@ -408,6 +408,7 @@ static int fscrypt_slot_set(
_cleanup_free_ void *encrypted = NULL;
const EVP_CIPHER *cc;
size_t encrypted_size;
+ ssize_t ss;
r = crypto_random_bytes(salt, sizeof(salt));
if (r < 0)
@@ -458,12 +459,12 @@ static int fscrypt_slot_set(
assert((size_t) encrypted_size_out1 + (size_t) encrypted_size_out2 < encrypted_size);
encrypted_size = (size_t) encrypted_size_out1 + (size_t) encrypted_size_out2;
- r = base64mem(salt, sizeof(salt), &salt_base64);
- if (r < 0)
+ ss = base64mem(salt, sizeof(salt), &salt_base64);
+ if (ss < 0)
return log_oom();
- r = base64mem(encrypted, encrypted_size, &encrypted_base64);
- if (r < 0)
+ ss = base64mem(encrypted, encrypted_size, &encrypted_base64);
+ if (ss < 0)
return log_oom();
joined = strjoin(salt_base64, ":", encrypted_base64);
diff --git a/src/partition/repart.c b/src/partition/repart.c
index c4ca9840c8..8875e09389 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -3022,6 +3022,7 @@ static int partition_encrypt(
_cleanup_free_ void *pubkey = NULL;
_cleanup_free_ void *blob = NULL, *hash = NULL;
size_t secret_size, blob_size, hash_size, pubkey_size = 0;
+ ssize_t base64_encoded_size;
uint16_t pcr_bank, primary_alg;
int keyslot;
@@ -3049,9 +3050,9 @@ static int partition_encrypt(
if (r < 0)
return log_error_errno(r, "Failed to seal to TPM2: %m");
- r = base64mem(secret, secret_size, &base64_encoded);
- if (r < 0)
- return log_error_errno(r, "Failed to base64 encode secret key: %m");
+ base64_encoded_size = base64mem(secret, secret_size, &base64_encoded);
+ if (base64_encoded_size < 0)
+ return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m");
r = cryptsetup_set_minimal_pbkdf(cd);
if (r < 0)
@@ -3063,7 +3064,7 @@ static int partition_encrypt(
volume_key,
volume_key_size,
base64_encoded,
- strlen(base64_encoded));
+ base64_encoded_size);
if (keyslot < 0)
return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node);

View File

@ -0,0 +1,105 @@
From faa2d81c6c5733d2eaa28d142f88917ed9c8300b Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 6 Dec 2022 13:07:34 -0500
Subject: [PATCH] Consolidate various TAKE_* into TAKE_GENERIC(), add
TAKE_STRUCT()
(cherry picked from commit 40c5cc2b214fd47ebfe85786a2a220bd3e9f275a)
Related: RHEL-16182
---
src/basic/fd-util.h | 8 +-------
src/basic/process-util.h | 10 ++--------
src/fundamental/macro-fundamental.h | 17 +++++++++++------
src/shared/keyring-util.h | 10 ++--------
4 files changed, 16 insertions(+), 29 deletions(-)
diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h
index 29c7d86f27..1fdb8c8fcb 100644
--- a/src/basic/fd-util.h
+++ b/src/basic/fd-util.h
@@ -90,13 +90,7 @@ static inline int make_null_stdio(void) {
}
/* Like TAKE_PTR() but for file descriptors, resetting them to -1 */
-#define TAKE_FD(fd) \
- ({ \
- int *_fd_ = &(fd); \
- int _ret_ = *_fd_; \
- *_fd_ = -1; \
- _ret_; \
- })
+#define TAKE_FD(fd) TAKE_GENERIC(fd, int, -1)
/* Like free_and_replace(), but for file descriptors */
#define close_and_replace(a, b) \
diff --git a/src/basic/process-util.h b/src/basic/process-util.h
index ed2f73673e..e49bc47b2c 100644
--- a/src/basic/process-util.h
+++ b/src/basic/process-util.h
@@ -176,14 +176,8 @@ int get_oom_score_adjust(int *ret);
assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX);
-/* Like TAKE_PTR() but for child PIDs, resetting them to 0 */
-#define TAKE_PID(pid) \
- ({ \
- pid_t *_ppid_ = &(pid); \
- pid_t _pid_ = *_ppid_; \
- *_ppid_ = 0; \
- _pid_; \
- })
+/* Like TAKE_PTR() but for pid_t, resetting them to 0 */
+#define TAKE_PID(pid) TAKE_GENERIC(pid, pid_t, 0)
int pidfd_get_pid(int fd, pid_t *ret);
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
index faab16ab31..e0665d9dcb 100644
--- a/src/fundamental/macro-fundamental.h
+++ b/src/fundamental/macro-fundamental.h
@@ -299,13 +299,18 @@
/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time
* resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */
-#define TAKE_PTR(ptr) \
- ({ \
- typeof(ptr) *_pptr_ = &(ptr); \
- typeof(ptr) _ptr_ = *_pptr_; \
- *_pptr_ = NULL; \
- _ptr_; \
+#define TAKE_GENERIC(var, type, nullvalue) \
+ ({ \
+ type *_pvar_ = &(var); \
+ type _var_ = *_pvar_; \
+ type _nullvalue_ = nullvalue; \
+ *_pvar_ = _nullvalue_; \
+ _var_; \
})
+#define TAKE_PTR_TYPE(ptr, type) TAKE_GENERIC(ptr, type, NULL)
+#define TAKE_PTR(ptr) TAKE_PTR_TYPE(ptr, typeof(ptr))
+#define TAKE_STRUCT_TYPE(s, type) TAKE_GENERIC(s, type, {})
+#define TAKE_STRUCT(s) TAKE_STRUCT_TYPE(s, typeof(s))
/*
* STRLEN - return the length of a string literal, minus the trailing NUL byte.
diff --git a/src/shared/keyring-util.h b/src/shared/keyring-util.h
index 838e990b80..c8c53f1be1 100644
--- a/src/shared/keyring-util.h
+++ b/src/shared/keyring-util.h
@@ -5,13 +5,7 @@
#include "missing_keyctl.h"
-/* TAKE_FD but for key_serial_t instead of fds */
-#define TAKE_KEY_SERIAL(key_serial) \
- ({ \
- key_serial_t *_key_serialp_ = &(key_serial); \
- key_serial_t _key_serial_ = *_key_serialp_; \
- *_key_serialp_ = -1; \
- _key_serial_; \
- })
+/* Like TAKE_PTR() but for key_serial_t, resetting them to -1 */
+#define TAKE_KEY_SERIAL(key_serial) TAKE_GENERIC(key_serial, key_serial_t, -1)
int keyring_read(key_serial_t serial, void **ret, size_t *ret_size);

View File

@ -0,0 +1,93 @@
From 49586386d23c0aef7e40ab6922a484b2ed64edc6 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Mon, 14 Nov 2022 17:26:45 +0100
Subject: [PATCH] pcrphase: add $SYSTEMD_PCRPHASE_STUB_VERIFY env var for
overriding stub check
(cherry picked from commit 6337be0a4ec2d3cf3268b51aa705ee58cfb2b394)
Related: RHEL-16182
---
docs/ENVIRONMENT.md | 7 ++++++-
src/boot/pcrphase.c | 35 ++++++++++++++++++++++++-----------
2 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md
index f1a4692b59..7b2dd13673 100644
--- a/docs/ENVIRONMENT.md
+++ b/docs/ENVIRONMENT.md
@@ -471,7 +471,7 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
`systemd-journald`:
-* `$SYSTEMD_JOURNAL_COMPACT` - Takes a boolean. If enabled, journal files are written
+* `$SYSTEMD_JOURNAL_COMPACT` Takes a boolean. If enabled, journal files are written
in a more compact format that reduces the amount of disk space required by the
journal. Note that journal files in compact mode are limited to 4G to allow use of
32-bit offsets. Enabled by default.
@@ -483,3 +483,8 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
compression mode of existing journal files are not changed. To make the
specified algorithm takes an effect immediately, you need to explicitly run
`journalctl --rotate`.
+
+`systemd-pcrphase`:
+
+* `$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.
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 267f66767c..f57d628e84 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -5,6 +5,7 @@
#include <sd-messages.h>
#include "efivars.h"
+#include "env-util.h"
#include "main-func.h"
#include "openssl-util.h"
#include "parse-util.h"
@@ -174,21 +175,33 @@ 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) {
- 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)
+ 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");
-
- /* 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);
+ 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 = dlopen_tpm2();
if (r < 0)

View File

@ -0,0 +1,135 @@
From 6b14fa7bcf40ba6dd289fbf7cda835d37d243dc5 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 15 Dec 2022 18:07:20 +0100
Subject: [PATCH] pcrphase: gracefully exit if TPM2 support is incomplete
If everything points to the fact that TPM2 should work, but then the
driver fails to initialize we should handle this gracefully and not
cause failing services all over the place.
Fixes: #25700
(cherry picked from commit 0318d54539fe168822447889ac0e858a10c55f74)
Related: RHEL-16182
---
man/systemd-pcrphase.service.xml | 8 ++++++++
src/boot/pcrphase.c | 13 +++++++++++++
units/systemd-pcrphase-initrd.service.in | 4 ++--
units/systemd-pcrphase-sysinit.service.in | 4 ++--
units/systemd-pcrphase.service.in | 4 ++--
5 files changed, 27 insertions(+), 6 deletions(-)
diff --git a/man/systemd-pcrphase.service.xml b/man/systemd-pcrphase.service.xml
index 9eda503e4c..9b7cc80b3a 100644
--- a/man/systemd-pcrphase.service.xml
+++ b/man/systemd-pcrphase.service.xml
@@ -131,6 +131,14 @@
all suitable TPM2 devices currently discovered.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--graceful</option></term>
+
+ <listitem><para>If no TPM2 firmware, kernel subsystem, kernel driver or device support is found, exit
+ with exit status 0 (i.e. indicate success). If this is not specified any attempt to measure without a
+ TPM2 device will cause the invocation to fail.</para></listitem>
+ </varlistentry>
+
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index f57d628e84..8e91e80e22 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -13,6 +13,7 @@
#include "tpm-pcr.h"
#include "tpm2-util.h"
+static bool arg_graceful = false;
static char *arg_tpm2_device = NULL;
static char **arg_banks = NULL;
@@ -34,6 +35,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --version Print version\n"
" --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"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@@ -50,6 +52,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_VERSION = 0x100,
ARG_BANK,
ARG_TPM2_DEVICE,
+ ARG_GRACEFUL,
};
static const struct option options[] = {
@@ -57,6 +60,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "version", no_argument, NULL, ARG_VERSION },
{ "bank", required_argument, NULL, ARG_BANK },
{ "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE },
+ { "graceful", no_argument, NULL, ARG_GRACEFUL },
{}
};
@@ -104,6 +108,10 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
+ case ARG_GRACEFUL:
+ arg_graceful = true;
+ break;
+
case '?':
return -EINVAL;
@@ -173,6 +181,11 @@ static int run(int argc, char *argv[]) {
if (isempty(word))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "String to measure cannot be empty, refusing.");
+ if (arg_graceful && tpm2_support() != TPM2_SUPPORT_FULL) {
+ log_notice("No complete TPM2 support detected, exiting gracefully.");
+ return EXIT_SUCCESS;
+ }
+
length = strlen(word);
int b = getenv_bool("SYSTEMD_PCRPHASE_STUB_VERIFY");
diff --git a/units/systemd-pcrphase-initrd.service.in b/units/systemd-pcrphase-initrd.service.in
index c1ad5ef844..e437c7e1ce 100644
--- a/units/systemd-pcrphase-initrd.service.in
+++ b/units/systemd-pcrphase-initrd.service.in
@@ -20,5 +20,5 @@ ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-4
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase enter-initrd
-ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase leave-initrd
+ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --graceful enter-initrd
+ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase --graceful leave-initrd
diff --git a/units/systemd-pcrphase-sysinit.service.in b/units/systemd-pcrphase-sysinit.service.in
index 6b5ba7d878..a22fbbe935 100644
--- a/units/systemd-pcrphase-sysinit.service.in
+++ b/units/systemd-pcrphase-sysinit.service.in
@@ -21,5 +21,5 @@ ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-4
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase sysinit
-ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase final
+ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --graceful sysinit
+ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase --graceful final
diff --git a/units/systemd-pcrphase.service.in b/units/systemd-pcrphase.service.in
index ce469befa8..5ba437e5b1 100644
--- a/units/systemd-pcrphase.service.in
+++ b/units/systemd-pcrphase.service.in
@@ -19,5 +19,5 @@ ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-4
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase ready
-ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase shutdown
+ExecStart={{ROOTLIBEXECDIR}}/systemd-pcrphase --graceful ready
+ExecStop={{ROOTLIBEXECDIR}}/systemd-pcrphase --graceful shutdown

View File

@ -0,0 +1,129 @@
From 62b9997afb850843f8fa52c66c3320f0f969d400 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 11 Oct 2022 18:07:46 +0200
Subject: [PATCH] tpm2-util: split out code that derives "good" TPM2 banks into
an strv from pcrphase and generalize it in tpm2-util.c
That way we can reuse it later from different places.
(cherry picked from commit e4481cc512f48d423115b10d4ae1c8e1381ff84b)
Related: RHEL-16182
---
src/boot/pcrphase.c | 28 ++++++---------------------
src/shared/tpm2-util.c | 43 ++++++++++++++++++++++++++++++++++++++++++
src/shared/tpm2-util.h | 1 +
3 files changed, 50 insertions(+), 22 deletions(-)
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 8e91e80e22..6e3a564f35 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -123,35 +123,19 @@ static int parse_argv(int argc, char *argv[]) {
}
static int determine_banks(struct tpm2_context *c) {
- _cleanup_free_ TPMI_ALG_HASH *algs = NULL;
- int n_algs, r;
+ _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;
-
- 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();
- }
+ r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &l);
+ if (r < 0)
+ return r;
+ strv_free_and_replace(arg_banks, l);
return 0;
}
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 8171b3e9e9..45ece9d1a6 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -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;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index c240335ae6..6d83281be0 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -68,6 +68,7 @@ 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);
#else
struct tpm2_context;

View File

@ -0,0 +1,173 @@
From bbb9be16572c4fcc31387c3d3bfd628644db4723 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 11 Oct 2022 18:20:14 +0200
Subject: [PATCH] tpm2-util: split out code that extends a PCR from pcrphase
This way we can reuse it later outside of pcrphase
(cherry picked from commit 15c591d1e2b555070f540cafb1b3d1e564e3410a)
Related: RHEL-16182
---
src/boot/pcrphase.c | 42 ++------------------------
src/shared/tpm2-util.c | 67 ++++++++++++++++++++++++++++++++++++++++++
src/shared/tpm2-util.h | 2 ++
3 files changed, 72 insertions(+), 39 deletions(-)
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 6e3a564f35..62bdf0ad29 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -145,7 +145,6 @@ static int run(int argc, char *argv[]) {
const char *word;
unsigned pcr_nr;
size_t length;
- TSS2_RC rc;
int r;
log_setup();
@@ -214,50 +213,15 @@ static int run(int argc, char *argv[]) {
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));
+ r = tpm2_extend_bytes(c.esys_context, arg_banks, TPM_PCR_INDEX_KERNEL_IMAGE, word, length); /* → PCR 11 */
+ if (r < 0)
+ return r;
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 45ece9d1a6..336c681c71 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1907,6 +1907,73 @@ 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 sz) {
+
+#if HAVE_OPENSSL
+ TPML_DIGEST_VALUES values = {};
+ TSS2_RC rc;
+
+ assert(c);
+ assert(data || sz == 0);
+
+ 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;
+
+ if (EVP_Digest(data, sz, (unsigned char*) &values.digests[values.count].digest, NULL, implementation, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash word.");
+
+ 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;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 6d83281be0..4cab52a949 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -70,6 +70,8 @@ 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 sz);
+
#else
struct tpm2_context;
#endif

View File

@ -0,0 +1,100 @@
From 8bc4975bcffdefd46b1fd95ccf4edf7287d2c3d3 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 14 Oct 2022 14:38:35 +0200
Subject: [PATCH] tpm2-util: optionally do HMAC in tpm2_extend_bytes() in case
we process sensitive data
When measuring data into a PCR we are supposed to hash the data on the
CPU and then pass the hash value over the wire to the TPM2. That's all
good as long as the data we intend to measure is not sensitive.
Let's be extra careful though if we want to measure sensitive data, for
example the root file system volume key. Instead of just hashing that
and passing it over the wire to the TPM2, let's do a HMAC signature
instead. It's also a hash operation, but should protect our secret
reasonably well and not leak direct information about it to wiretappers.
(cherry picked from commit 9885c8745d313588350325e8e2110887bf78c442)
Related: RHEL-16182
---
src/boot/pcrphase.c | 2 +-
src/shared/tpm2-util.c | 25 +++++++++++++++++++++----
src/shared/tpm2-util.h | 2 +-
3 files changed, 23 insertions(+), 6 deletions(-)
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 62bdf0ad29..1f3dc4ab3a 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -219,7 +219,7 @@ static int run(int argc, char *argv[]) {
log_debug("Measuring '%s' into PCR index %u, banks %s.", word, TPM_PCR_INDEX_KERNEL_IMAGE, joined);
- r = tpm2_extend_bytes(c.esys_context, arg_banks, TPM_PCR_INDEX_KERNEL_IMAGE, word, length); /* → PCR 11 */
+ r = tpm2_extend_bytes(c.esys_context, arg_banks, TPM_PCR_INDEX_KERNEL_IMAGE, word, length, NULL, 0); /* → PCR 11 */
if (r < 0)
return r;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 336c681c71..aca7f22e54 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1913,14 +1913,22 @@ int tpm2_extend_bytes(
char **banks,
unsigned pcr_index,
const void *data,
- size_t sz) {
+ 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 || sz == 0);
+ 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);
@@ -1946,8 +1954,17 @@ int tpm2_extend_bytes(
values.digests[values.count].hashAlg = id;
- if (EVP_Digest(data, sz, (unsigned char*) &values.digests[values.count].digest, NULL, implementation, NULL) != 1)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to hash word.");
+ /* 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++;
}
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 4cab52a949..96e6c31b0a 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -70,7 +70,7 @@ 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 sz);
+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;

View File

@ -0,0 +1,407 @@
From 46659ff4eac28b7a87658668894058bd63c28e81 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 12 Oct 2022 09:56:32 +0200
Subject: [PATCH] cryptsetup: add tpm2-measure-pcr= and tpm2-measure-bank=
crypttab options
These options allow measuring the volume key used for unlocking the
volume to a TPM2 PCR. This is ideally used for the volume key of the
root file system and can then be used to bind other resources to the
root file system volume in a secure way.
See: #24503
(cherry picked from commit 94c0c85e302d00923dc5bbf9d1b937875f1d0c66)
Related: RHEL-16182
---
meson.build | 3 +-
src/cryptsetup/cryptsetup.c | 226 +++++++++++++++++++++++++++++++++---
src/fundamental/tpm-pcr.h | 3 +
3 files changed, 217 insertions(+), 15 deletions(-)
diff --git a/meson.build b/meson.build
index e09c426a72..fe7b47eef5 100644
--- a/meson.build
+++ b/meson.build
@@ -2837,7 +2837,8 @@ if conf.get('HAVE_LIBCRYPTSETUP') == 1
include_directories : includes,
link_with : [libshared],
dependencies : [libcryptsetup,
- libp11kit],
+ libp11kit,
+ libopenssl],
install_rpath : rootpkglibdir,
install : true,
install_dir : rootlibexecdir)
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index a25fb28948..20862e926d 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -8,6 +8,7 @@
#include <unistd.h>
#include "sd-device.h"
+#include "sd-messages.h"
#include "alloc-util.h"
#include "ask-password-api.h"
@@ -38,6 +39,7 @@
#include "random-util.h"
#include "string-table.h"
#include "strv.h"
+#include "tpm-pcr.h"
#include "tpm2-util.h"
/* internal helper */
@@ -89,13 +91,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 +111,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 +425,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 +809,149 @@ 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;
+ }
+
+#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 +1020,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 +1118,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;
@@ -1173,7 +1371,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;
@@ -1184,7 +1382,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?)");
@@ -1321,7 +1519,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;
@@ -1338,7 +1536,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?)");
@@ -1610,7 +1808,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;
@@ -1621,7 +1819,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?)");
@@ -1648,9 +1846,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 */
@@ -1701,9 +1899,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 */
@@ -1729,9 +1927,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;
}
diff --git a/src/fundamental/tpm-pcr.h b/src/fundamental/tpm-pcr.h
index 794d593825..ec4c3a2b85 100644
--- a/src/fundamental/tpm-pcr.h
+++ b/src/fundamental/tpm-pcr.h
@@ -25,6 +25,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 {

View File

@ -0,0 +1,62 @@
From 4b28fbe37b02e0df2bd746303108a5e3ed089209 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 14 Oct 2022 15:27:34 +0200
Subject: [PATCH] man: document the new crypttab measurement options
(cherry picked from commit 572f78767f9958559aa4a3060fc5c9a006766240)
Related: RHEL-16182
---
man/crypttab.xml | 22 ++++++++++++++++++++++
man/systemd-cryptenroll.xml | 5 +++++
2 files changed, 27 insertions(+)
diff --git a/man/crypttab.xml b/man/crypttab.xml
index cbbb8ab2a9..1dd9bb1bb6 100644
--- a/man/crypttab.xml
+++ b/man/crypttab.xml
@@ -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>
diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml
index ad338cdcc5..f08d95c6fb 100644
--- a/man/systemd-cryptenroll.xml
+++ b/man/systemd-cryptenroll.xml
@@ -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>

View File

@ -0,0 +1,149 @@
From 0e471978c582a614467d20d041f65c935c407abf Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 14 Oct 2022 15:54:09 +0200
Subject: [PATCH] gpt-auto-generator: automatically measure root/var volume
keys into PCR 15
let's enable PCR 15 measurements automatically if gpt-auto discovery is
used and systemd-stub is also used.
(cherry picked from commit ff386f985bb51a48a11f74f6370dedf1bbfb4658)
Related: RHEL-16182
---
man/systemd-gpt-auto-generator.xml | 8 +++++
src/gpt-auto-generator/gpt-auto-generator.c | 36 ++++++++++++++++++---
2 files changed, 39 insertions(+), 5 deletions(-)
diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml
index 8ad249ec5d..f26bda511c 100644
--- a/man/systemd-gpt-auto-generator.xml
+++ b/man/systemd-gpt-auto-generator.xml
@@ -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>
@@ -272,6 +279,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>,
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 0bab43e69a..2134185f04 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -47,10 +47,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;
@@ -84,7 +85,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_get_variable(EFI_LOADER_VARIABLE(StubPcrKernelImage), NULL, NULL, NULL); /* we don't actually care which PCR the UKI used for itself */
+ if (r == -ENOENT)
+ log_debug_errno(r, "Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id);
+ else if (r < 0)
+ log_debug_errno(r, "Failed to determine whether booted via systemd-stub with measurements enabled, ignoring: %m");
+ 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;
@@ -139,6 +161,7 @@ static int add_mount(
const char *fstype,
bool rw,
bool growfs,
+ bool measure,
const char *options,
const char *description,
const char *post) {
@@ -159,7 +182,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;
@@ -277,6 +300,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);
@@ -301,7 +325,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;
@@ -374,6 +398,7 @@ static int add_automount(
fstype,
rw,
growfs,
+ /* measure= */ false,
opt,
description,
NULL);
@@ -582,7 +607,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
@@ -629,6 +654,7 @@ static int add_root_mount(void) {
NULL,
/* rw= */ arg_root_rw > 0,
/* growfs= */ false,
+ /* measure= */ true,
NULL,
"Root Partition",
in_initrd() ? SPECIAL_INITRD_ROOT_FS_TARGET : SPECIAL_LOCAL_FS_TARGET);

View File

@ -0,0 +1,169 @@
From 4c9eb27048c07b0cf93377344ddfe2f8980a88e4 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Thu, 1 Dec 2022 15:37:59 +0100
Subject: [PATCH] blkid-util: define enum for blkid_do_safeprobe() return
values
libblkid really should define an enum for this on its own, but it
currently doesn't and returns literal numeric values. Lets make this
more readable by adding our own symbolic names via an enum.
(cherry picked from commit 2e3944b872cf57dbccdda14ec66772e8fdd2273b)
Related: RHEL-16182
---
src/home/homework-luks.c | 16 ++++++++++------
src/partition/repart.c | 9 +++++----
src/shared/blkid-util.h | 10 ++++++++++
src/shared/dissect-image.c | 16 ++++++++++------
src/shared/find-esp.c | 10 ++++++----
5 files changed, 41 insertions(+), 20 deletions(-)
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index 53fa61b103..8795707f6e 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -148,10 +148,12 @@ static int probe_file_system_by_fd(
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return errno_or_else(EIO);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
return -ENOPKG;
- if (r != 0)
- return errno > 0 ? -errno : -EIO;
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (!fstype)
@@ -665,10 +667,12 @@ static int luks_validate(
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) /* nothing found or ambiguous result */
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return errno_or_else(EIO);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
return -ENOPKG;
- if (r != 0)
- return errno > 0 ? -errno : -EIO;
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (streq_ptr(fstype, "crypto_LUKS")) {
diff --git a/src/partition/repart.c b/src/partition/repart.c
index 8875e09389..cbd900969e 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -4604,12 +4604,14 @@ static int resolve_copy_blocks_auto_candidate(
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1)) { /* nothing found or ambiguous result */
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND)) {
log_debug("Didn't find partition table on block device '%s'.", p);
return false;
}
- if (r != 0)
- return log_error_errno(errno_or_else(EIO), "Unable to probe for partition table of '%s': %m", p);
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
(void) blkid_probe_lookup_value(b, "PTTYPE", &pttype, NULL);
if (!streq_ptr(pttype, "gpt")) {
@@ -4621,7 +4623,6 @@ static int resolve_copy_blocks_auto_candidate(
pl = blkid_probe_get_partitions(b);
if (!pl)
return log_error_errno(errno_or_else(EIO), "Unable read partition table of '%s': %m", p);
- errno = 0;
pp = blkid_partlist_devno_to_partition(pl, partition_devno);
if (!pp) {
diff --git a/src/shared/blkid-util.h b/src/shared/blkid-util.h
index aa444990fd..5df39eccfc 100644
--- a/src/shared/blkid-util.h
+++ b/src/shared/blkid-util.h
@@ -7,4 +7,14 @@
# include "macro.h"
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(blkid_probe, blkid_free_probe, NULL);
+
+/* Define symbolic names for blkid_do_safeprobe() return values, since blkid only uses literal numbers. We
+ * prefix these symbolic definitions with underscores, to not invade libblkid's namespace needlessly. */
+enum {
+ _BLKID_SAFEPROBE_FOUND = 0,
+ _BLKID_SAFEPROBE_NOT_FOUND = 1,
+ _BLKID_SAFEPROBE_AMBIGUOUS = -2,
+ _BLKID_SAFEPROBE_ERROR = -1,
+};
+
#endif
diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c
index d2abd5a087..7721769061 100644
--- a/src/shared/dissect-image.c
+++ b/src/shared/dissect-image.c
@@ -201,14 +201,16 @@ int probe_filesystem_full(int fd, const char *path, char **ret_fstype) {
errno = 0;
r = blkid_do_safeprobe(b);
- if (r == 1)
+ if (r == _BLKID_SAFEPROBE_NOT_FOUND)
goto not_found;
- if (r == -2)
+ if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN),
"Results ambiguous for partition %s", path);
- if (r != 0)
+ if (r == _BLKID_SAFEPROBE_ERROR)
return log_debug_errno(errno_or_else(EIO), "Failed to probe partition %s: %m", path);
+ assert(r == _BLKID_SAFEPROBE_FOUND);
+
(void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);
if (fstype) {
@@ -506,10 +508,12 @@ static int dissect_image(
errno = 0;
r = blkid_do_safeprobe(b);
- if (IN_SET(r, -2, 1))
- return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
- if (r != 0)
+ if (r == _BLKID_SAFEPROBE_ERROR)
return errno_or_else(EIO);
+ if (IN_SET(r, _BLKID_SAFEPROBE_AMBIGUOUS, _BLKID_SAFEPROBE_NOT_FOUND))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table.");
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
if ((!(flags & DISSECT_IMAGE_GPT_ONLY) &&
(flags & DISSECT_IMAGE_GENERIC_ROOT)) ||
diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c
index fa234c8b5f..f005432887 100644
--- a/src/shared/find-esp.c
+++ b/src/shared/find-esp.c
@@ -571,12 +571,14 @@ static int verify_xbootldr_blkid(
errno = 0;
r = blkid_do_safeprobe(b);
- if (r == -2)
+ if (r == _BLKID_SAFEPROBE_AMBIGUOUS)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system is ambiguous.", node);
- else if (r == 1)
+ if (r == _BLKID_SAFEPROBE_NOT_FOUND)
return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "%s: File system does not contain a label.", node);
- else if (r != 0)
- return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "%s: Failed to probe file system: %m", node);
+ if (r == _BLKID_SAFEPROBE_ERROR)
+ return log_error_errno(errno_or_else(EIO), "%s: Failed to probe file system: %m", node);
+
+ assert(r == _BLKID_SAFEPROBE_FOUND);
r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &type, NULL);
if (r != 0)

View File

@ -0,0 +1,372 @@
From d2b974f55d80f09d544a3af6a2ef987df4284260 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 14 Oct 2022 23:29:48 +0200
Subject: [PATCH] pcrphase: make tool more generic, reuse for measuring machine
id/fs uuids
See: #24503
(cherry picked from commit 17984c55513fc18f9bd4878c37fa87d278ab1e1d)
Related: RHEL-16182
---
meson.build | 4 +-
src/boot/pcrphase.c | 210 +++++++++++++++++++++++++++++++++++++++-----
2 files changed, 189 insertions(+), 25 deletions(-)
diff --git a/meson.build b/meson.build
index fe7b47eef5..54155eee1f 100644
--- a/meson.build
+++ b/meson.build
@@ -2610,7 +2610,9 @@ if conf.get('HAVE_BLKID') == 1 and conf.get('HAVE_GNU_EFI') == 1
'src/boot/pcrphase.c',
include_directories : includes,
link_with : [libshared],
- dependencies : [libopenssl, tpm2],
+ dependencies : [libopenssl,
+ tpm2,
+ libblkid],
install_rpath : rootpkglibdir,
install : true,
install_dir : rootlibexecdir)
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 1f3dc4ab3a..12629b2be3 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -2,12 +2,20 @@
#include <getopt.h>
+#include <sd-device.h>
#include <sd-messages.h>
+#include "blkid-util.h"
+#include "blockdev-util.h"
+#include "chase-symlinks.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-argument.h"
#include "parse-util.h"
#include "pretty-print.h"
#include "tpm-pcr.h"
@@ -16,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;
@@ -28,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"
@@ -36,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,
@@ -53,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[] = {
@@ -61,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);
@@ -112,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;
@@ -119,10 +149,13 @@ 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) {
+static int determine_banks(struct tpm2_context *c, unsigned target_pcr_nr) {
_cleanup_strv_free_ char **l = NULL;
int r;
@@ -131,7 +164,7 @@ static int determine_banks(struct tpm2_context *c) {
if (!strv_isempty(arg_banks)) /* Explicitly configured? Then use that */
return 0;
- r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << TPM_PCR_INDEX_KERNEL_IMAGE, &l);
+ r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << target_pcr_nr, &l);
if (r < 0)
return r;
@@ -139,11 +172,77 @@ static int determine_banks(struct tpm2_context *c) {
return 0;
}
+static int get_file_system_word(
+ sd_device *d,
+ const char *prefix,
+ char **ret) {
+
+ int r;
+
+ 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_free_ char *joined = NULL, *pcr_string = NULL, *word = NULL;
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
- _cleanup_free_ char *joined = NULL, *pcr_string = NULL;
- const char *word;
- unsigned pcr_nr;
+ unsigned target_pcr_nr, efi_pcr_nr;
size_t length;
int r;
@@ -153,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;
+
+ if (optind != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected no argument.");
- word = argv[optind];
+ 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);
- /* 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.");
+ 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.");
@@ -187,14 +349,14 @@ static int run(int argc, char *argv[]) {
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);
+ r = safe_atou(pcr_string, &efi_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 (efi_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);
+ return log_error_errno(SYNTHETIC_ERRNO(EREMOTE), "Kernel stub measured kernel image into PCR %u, which is different than expected %u.", efi_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);
+ log_notice("Kernel stub measured kernel image into PCR %u, which is different than expected %u, but told to measure anyway, hence proceeding.", efi_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);
}
@@ -207,7 +369,7 @@ 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? */
@@ -217,17 +379,17 @@ static int run(int argc, char *argv[]) {
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);
- r = tpm2_extend_bytes(c.esys_context, arg_banks, TPM_PCR_INDEX_KERNEL_IMAGE, word, length, NULL, 0); /* → PCR 11 */
+ 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;

View File

@ -0,0 +1,61 @@
From 46f2825866379e5019516269c9de88b8e2ba7c78 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Sun, 16 Oct 2022 18:21:12 +0200
Subject: [PATCH] units: measure /etc/machine-id into PCR 15 during early boot
We want PCR 15 to be useful for binding per-system policy to. Let's
measure the machine ID into it, to ensure that every OS we can
distinguish will get a different PCR (even if the root disk encryption
key is already measured into it).
(cherry picked from commit 072c8f650519f47a575b1e39509599ace21e2c8f)
Related: RHEL-16182
---
units/meson.build | 2 ++
units/systemd-pcrmachine.service.in | 23 +++++++++++++++++++++++
2 files changed, 25 insertions(+)
create mode 100644 units/systemd-pcrmachine.service.in
diff --git a/units/meson.build b/units/meson.build
index a99f27adc5..9046e5d066 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -266,6 +266,8 @@ in_units = [
'sysinit.target.wants/'],
['systemd-growfs-root.service', ''],
['systemd-growfs@.service', ''],
+ ['systemd-pcrmachine.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',
+ 'sysinit.target.wants/'],
]
add_wants = []
diff --git a/units/systemd-pcrmachine.service.in b/units/systemd-pcrmachine.service.in
new file mode 100644
index 0000000000..e154a7eec1
--- /dev/null
+++ b/units/systemd-pcrmachine.service.in
@@ -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

View File

@ -0,0 +1,268 @@
From 1d797fa7c074a9b6aa770466a3718a41d17d4aaf Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Sun, 16 Oct 2022 23:25:04 +0200
Subject: [PATCH] generators: optionally, measure file systems at boot
If we use gpt-auto-generator, automatically measure root fs and /var.
Otherwise, add x-systemd.measure option to request this.
(cherry picked from commit 04959faa632272a8fc9cdac3121b2e4af721c1b6)
Related: RHEL-16182
---
src/basic/special.h | 2 ++
src/fstab-generator/fstab-generator.c | 21 +++++++++---
src/gpt-auto-generator/gpt-auto-generator.c | 6 ++++
src/shared/generator.c | 37 +++++++++++++++++++++
src/shared/generator.h | 4 +++
units/meson.build | 2 ++
units/systemd-pcrfs-root.service.in | 24 +++++++++++++
units/systemd-pcrfs@.service.in | 25 ++++++++++++++
8 files changed, 116 insertions(+), 5 deletions(-)
create mode 100644 units/systemd-pcrfs-root.service.in
create mode 100644 units/systemd-pcrfs@.service.in
diff --git a/src/basic/special.h b/src/basic/special.h
index 9bb36c5732..0e4342eb40 100644
--- a/src/basic/special.h
+++ b/src/basic/special.h
@@ -89,6 +89,8 @@
#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"
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index c3fe285344..c4915a37d3 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -42,6 +42,7 @@ typedef enum MountPointFlags {
MOUNT_MAKEFS = 1 << 3,
MOUNT_GROWFS = 1 << 4,
MOUNT_RW_ONLY = 1 << 5,
+ MOUNT_PCRFS = 1 << 6,
} MountPointFlags;
typedef struct Mount {
@@ -238,9 +239,9 @@ static int add_swap(
return true;
}
- log_debug("Found swap entry what=%s makefs=%s growfs=%s noauto=%s nofail=%s",
+ log_debug("Found swap entry what=%s makefs=%s growfs=%s pcrfs=%s noauto=%s nofail=%s",
what,
- yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS),
+ yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS),
yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL));
r = unit_name_from_path(what, ".swap", &name);
@@ -291,6 +292,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,
@@ -642,6 +645,12 @@ static int add_mount(
return r;
}
+ if (flags & MOUNT_PCRFS) {
+ 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,
@@ -784,6 +793,8 @@ static MountPointFlags fstab_options_to_flags(const char *options, bool is_swap)
flags |= MOUNT_MAKEFS;
if (fstab_test_option(options, "x-systemd.growfs\0"))
flags |= MOUNT_GROWFS;
+ if (fstab_test_option(options, "x-systemd.pcrfs\0"))
+ flags |= MOUNT_PCRFS;
if (fstab_test_yes_no_option(options, "noauto\0" "auto\0"))
flags |= MOUNT_NOAUTO;
if (fstab_test_yes_no_option(options, "nofail\0" "fail\0"))
@@ -912,9 +923,9 @@ static int parse_fstab_one(
}
- 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, strna(fstype),
- yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS),
+ yes_no(flags & MOUNT_MAKEFS), yes_no(flags & MOUNT_GROWFS), yes_no(flags & MOUNT_PCRFS),
yes_no(flags & MOUNT_NOAUTO), yes_no(flags & MOUNT_NOFAIL));
bool is_sysroot = in_initrd() && path_equal(where, "/sysroot");
@@ -1127,7 +1138,7 @@ static int add_sysroot_mount(void) {
fstype,
opts,
is_device_path(what) ? 1 : 0, /* passno */
- 0, /* makefs off, growfs off, noauto off, nofail off, automount off */
+ 0, /* makefs off, pcrfs off, noauto off, nofail off, automount off */
SPECIAL_INITRD_ROOT_FS_TARGET);
}
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 2134185f04..2620a12f03 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -245,6 +245,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)
diff --git a/src/shared/generator.c b/src/shared/generator.c
index f1c5e506ab..284e5fc580 100644
--- a/src/shared/generator.c
+++ b/src/shared/generator.c
@@ -645,6 +645,43 @@ int generator_hook_up_growfs(
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);
+
+ 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;
+
+ 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", pcrfs_unit, instance);
+ if (r < 0)
+ return r;
+ }
+
+ return generator_add_symlink_full(dir, where_unit, "wants", pcrfs_unit_path, instance);
+}
+
int generator_enable_remount_fs_service(const char *dir) {
/* Pull in systemd-remount-fs.service */
return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants",
diff --git a/src/shared/generator.h b/src/shared/generator.h
index a4049dbd8f..111900fd45 100644
--- a/src/shared/generator.h
+++ b/src/shared/generator.h
@@ -81,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);
diff --git a/units/meson.build b/units/meson.build
index 9046e5d066..3a1f5229a0 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -264,6 +264,8 @@ in_units = [
'sysinit.target.wants/'],
['systemd-pcrphase.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', ''],
['systemd-pcrmachine.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',
diff --git a/units/systemd-pcrfs-root.service.in b/units/systemd-pcrfs-root.service.in
new file mode 100644
index 0000000000..b0da413bb4
--- /dev/null
+++ b/units/systemd-pcrfs-root.service.in
@@ -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=/
diff --git a/units/systemd-pcrfs@.service.in b/units/systemd-pcrfs@.service.in
new file mode 100644
index 0000000000..ec1ff118c3
--- /dev/null
+++ b/units/systemd-pcrfs@.service.in
@@ -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

View File

@ -0,0 +1,282 @@
From f4a9a464838c75f76731c5e6800a35cc4ec62cad Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Mon, 17 Oct 2022 14:50:56 +0200
Subject: [PATCH] tpm2: add common helper for checking if we are running on UKI
with TPM measurements
Let's introduce a common implementation of a function that checks
whether we are booted on a kernel with systemd-stub that has TPM PCR
measurements enabled. Do our own userspace measurements only if we
detect that.
PCRs are scarce and most likely there are projects which already make
use of them in other ways. Hence, instead of blindly stepping into their
territory let's conditionalize things so that people have to explicitly
buy into our PCR assignments before we start measuring things into them.
Specifically bind everything to an UKI that reported measurements.
This was previously already implemented in systemd-pcrphase, but with
this change we expand this to all tools that process PCR measurement
settings.
The env var to override the check is renamed to SYSTEMD_FORCE_MEASURE,
to make it more generic (since we'll use it at multiple places now).
This is not a compat break, since the original env var for that was not
included in any stable release yet.
(cherry picked from commit 6c51b49ce0892ff923233a6031add4877100f5b0)
Related: RHEL-16182
---
docs/ENVIRONMENT.md | 9 +++--
src/boot/pcrphase.c | 38 +++++---------------
src/cryptsetup/cryptsetup.c | 9 +++++
src/fstab-generator/fstab-generator.c | 12 +++++--
src/gpt-auto-generator/gpt-auto-generator.c | 10 +++---
src/shared/efi-loader.c | 39 +++++++++++++++++++++
src/shared/efi-loader.h | 2 ++
7 files changed, 80 insertions(+), 39 deletions(-)
diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md
index 7b2dd13673..51b1e851ff 100644
--- a/docs/ENVIRONMENT.md
+++ b/docs/ENVIRONMENT.md
@@ -484,7 +484,10 @@ SYSTEMD_HOME_DEBUG_SUFFIX=foo \
specified algorithm takes an effect immediately, you need to explicitly run
`journalctl --rotate`.
-`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.
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 12629b2be3..fda9a8420d 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -8,15 +8,14 @@
#include "blkid-util.h"
#include "blockdev-util.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-argument.h"
-#include "parse-util.h"
#include "pretty-print.h"
#include "tpm-pcr.h"
#include "tpm2-util.h"
@@ -240,9 +239,9 @@ static int get_file_system_word(
}
static int run(int argc, char *argv[]) {
- _cleanup_free_ char *joined = NULL, *pcr_string = NULL, *word = NULL;
_cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
- unsigned target_pcr_nr, efi_pcr_nr;
+ _cleanup_free_ char *joined = NULL, *word = NULL;
+ unsigned target_pcr_nr;
size_t length;
int r;
@@ -333,32 +332,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, &efi_pcr_nr);
- if (r < 0)
- return log_error_errno(r, "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
- if (efi_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.", efi_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.", efi_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();
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 20862e926d..4d587fed1e 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -19,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"
@@ -827,6 +828,14 @@ static int measure_volume_key(
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)
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index c4915a37d3..b9606a5341 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -8,6 +8,7 @@
#include "bus-error.h"
#include "bus-locator.h"
#include "chase-symlinks.h"
+#include "efi-loader.h"
#include "env-util.h"
#include "fd-util.h"
#include "fileio.h"
@@ -646,9 +647,16 @@ static int add_mount(
}
if (flags & MOUNT_PCRFS) {
- r = generator_hook_up_pcrfs(dest, where, target_unit);
+ r = efi_stub_measured();
if (r < 0)
- return r;
+ 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)) {
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 2620a12f03..27139a624e 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -97,11 +97,11 @@ static int add_cryptsetup(
* 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_get_variable(EFI_LOADER_VARIABLE(StubPcrKernelImage), NULL, NULL, NULL); /* we don't actually care which PCR the UKI used for itself */
- if (r == -ENOENT)
- log_debug_errno(r, "Will not measure volume key of volume '%s', because not booted via systemd-stub with measurements enabled.", id);
- else if (r < 0)
- log_debug_errno(r, "Failed to determine whether booted via systemd-stub with measurements enabled, ignoring: %m");
+ 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();
}
diff --git a/src/shared/efi-loader.c b/src/shared/efi-loader.c
index 1340412cda..621fa082ba 100644
--- a/src/shared/efi-loader.c
+++ b/src/shared/efi-loader.c
@@ -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 = {};
diff --git a/src/shared/efi-loader.h b/src/shared/efi-loader.h
index 84968869ab..56ccdee9c1 100644
--- a/src/shared/efi-loader.h
+++ b/src/shared/efi-loader.h
@@ -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);

View File

@ -0,0 +1,160 @@
From b7c36073f9a645967feba035e21468976b567adb Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Mon, 17 Oct 2022 15:20:53 +0200
Subject: [PATCH] man: document new machine-id/fs measurement options
(cherry picked from commit 2bd33c909c0cf02a2a794ac83d66e8b32879c25d)
Related: RHEL-16182
---
man/rules/meson.build | 5 ++-
man/systemd-pcrphase.service.xml | 57 +++++++++++++++++++++++++++-----
man/systemd.mount.xml | 14 ++++++++
3 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/man/rules/meson.build b/man/rules/meson.build
index c7045840f2..65a16b1e2a 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -971,7 +971,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'],
diff --git a/man/systemd-pcrphase.service.xml b/man/systemd-pcrphase.service.xml
index 9b7cc80b3a..95b0e05269 100644
--- a/man/systemd-pcrphase.service.xml
+++ b/man/systemd-pcrphase.service.xml
@@ -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) 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>
+ 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 PCR 11 is extended
+ with the following strings:</para>
<orderedlist>
<listitem><para><literal>enter-initrd</literal> is extended into PCR 11 early when the initrd
@@ -104,6 +120,14 @@
<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>
+
+ <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>
@@ -139,6 +163,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" />
@@ -150,7 +189,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>
diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml
index 773ca04cd6..3dbc623f44 100644
--- a/man/systemd.mount.xml
+++ b/man/systemd.mount.xml
@@ -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>

View File

@ -0,0 +1,67 @@
From 994451b5e3010e2b12c01522bed9cc246304696e Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 16 Dec 2022 16:25:34 +0100
Subject: [PATCH] test: add simple integration test for checking PCR extension
works as it should
(cherry picked from commit f44ed151c6c203f01a9fe8623b282ecd4ef2e0a9)
Related: RHEL-16182
---
test/TEST-70-TPM2/test.sh | 1 +
test/units/testsuite-70.sh | 30 ++++++++++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/test/TEST-70-TPM2/test.sh b/test/TEST-70-TPM2/test.sh
index 7c19821ad2..f448a4a5f1 100755
--- a/test/TEST-70-TPM2/test.sh
+++ b/test/TEST-70-TPM2/test.sh
@@ -20,6 +20,7 @@ test_append_files() {
install_dmevent
generate_module_dependencies
inst_binary tpm2_pcrextend
+ inst_binary tpm2_pcrread
inst_binary openssl
}
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
index 89cd2a3f82..3b4d66b686 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/testsuite-70.sh
@@ -155,6 +155,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

28
0492-update-TODO.patch Normal file
View File

@ -0,0 +1,28 @@
From 48d3b9b2ee68a41e41ccb493e24c0283d752e4f8 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 14 Oct 2022 21:21:46 +0200
Subject: [PATCH] update TODO
(cherry picked from commit a67a50e8f4a3d19713fe9b84653616fcba5ae14c)
Related: RHEL-16182
---
TODO | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/TODO b/TODO
index 8c67f93f35..aa3f1c596c 100644
--- a/TODO
+++ b/TODO
@@ -354,9 +354,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.
* nspawn: maybe allow TPM passthrough, backed by swtpm, and measure --image=
hash into its PCR 11, so that nspawn instances can be TPM enabled, and

View File

@ -0,0 +1,139 @@
From 7f8a43eff0d800f21e9f873010637d08da13da67 Mon Sep 17 00:00:00 2001
From: Antonio Alvarez Feijoo <antonio.feijoo@suse.com>
Date: Wed, 7 Dec 2022 16:52:27 +0100
Subject: [PATCH] cryptsetup: retry TPM2 unseal operation if it fails with
TPM2_RC_PCR_CHANGED
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Quoting "Trusted Platform Module Library - Part 3: Commands (Rev. 01.59)":
"pcrUpdateCounter this parameter is updated by TPM2_PolicyPCR(). This value
may only be set once during a policy. Each time TPM2_PolicyPCR() executes, it
checks to see if policySession->pcrUpdateCounter has its default state,
indicating that this is the first TPM2_PolicyPCR(). If it has its default value,
then policySession->pcrUpdateCounter is set to the current value of
pcrUpdateCounter. If policySession->pcrUpdateCounter does not have its default
value and its value is not the same as pcrUpdateCounter, the TPM shall return
TPM_RC_PCR_CHANGED.
If this parameter and pcrUpdateCounter are not the same, it indicates that PCR
have changed since checked by the previous TPM2_PolicyPCR(). Since they have
changed, the previous PCR validation is no longer valid."
The TPM will return TPM_RC_PCR_CHANGED if any PCR value changes (no matter
which) between validating the PCRs binded to the enrollment and unsealing the
HMAC key, so this patch adds a retry mechanism in this case.
Fixes #24906
(cherry picked from commit 0254e4d66af7aa893b31b2326335ded5dde48b51)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 81 ++++++++++++++++++++++++------------------
1 file changed, 46 insertions(+), 35 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index aca7f22e54..d1a4e9cd11 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1608,6 +1608,8 @@ finish:
return r;
}
+#define RETRY_UNSEAL_MAX 30u
+
int tpm2_unseal(const char *device,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
@@ -1719,44 +1721,53 @@ int tpm2_unseal(const char *device,
if (r < 0)
goto finish;
- r = tpm2_make_policy_session(
- c.esys_context,
- primary,
- hmac_session,
- TPM2_SE_POLICY,
- hash_pcr_mask,
- pcr_bank,
- pubkey, pubkey_size,
- pubkey_pcr_mask,
- signature,
- !!pin,
- &session,
- &policy_digest,
- /* ret_pcr_bank= */ NULL);
- if (r < 0)
- goto finish;
+ for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
+ r = tpm2_make_policy_session(
+ c.esys_context,
+ primary,
+ hmac_session,
+ TPM2_SE_POLICY,
+ hash_pcr_mask,
+ pcr_bank,
+ pubkey, pubkey_size,
+ pubkey_pcr_mask,
+ signature,
+ !!pin,
+ &session,
+ &policy_digest,
+ /* ret_pcr_bank= */ NULL);
+ if (r < 0)
+ goto finish;
- /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
- * wait until the TPM2 tells us to go away. */
- if (known_policy_hash_size > 0 &&
- memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
- return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Current policy digest does not match stored policy digest, cancelling "
- "TPM2 authentication attempt.");
+ /* If we know the policy hash to expect, and it doesn't match, we can shortcut things here, and not
+ * wait until the TPM2 tells us to go away. */
+ if (known_policy_hash_size > 0 &&
+ memcmp_nn(policy_digest->buffer, policy_digest->size, known_policy_hash, known_policy_hash_size) != 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Current policy digest does not match stored policy digest, cancelling "
+ "TPM2 authentication attempt.");
- log_debug("Unsealing HMAC key.");
+ log_debug("Unsealing HMAC key.");
- rc = sym_Esys_Unseal(
- c.esys_context,
- hmac_key,
- session,
- hmac_session, /* use HMAC session to enable parameter encryption */
- ESYS_TR_NONE,
- &unsealed);
- if (rc != TSS2_RC_SUCCESS) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
- goto finish;
+ rc = sym_Esys_Unseal(
+ c.esys_context,
+ hmac_key,
+ session,
+ hmac_session, /* use HMAC session to enable parameter encryption */
+ ESYS_TR_NONE,
+ &unsealed);
+ if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
+ log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
+ session = tpm2_flush_context_verbose(c.esys_context, session);
+ continue;
+ }
+ if (rc != TSS2_RC_SUCCESS) {
+ r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to unseal HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+ goto finish;
+ }
+
+ break;
}
secret = memdup(unsealed->buffer, unsealed->size);

View File

@ -0,0 +1,151 @@
From ffff09335760efc655faf093b6fbb364cfae4ad7 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Sat, 7 Jan 2023 22:16:52 +0100
Subject: [PATCH] boot: Simplify object erasure
This erase_obj() machinery looks like voodoo and creates an awful lot of
noise as soon as we get back to building with -O0. We can do this in a
more simple way by introducing a struct that holds the information we
need on cleanup. When building with optimization enabled, all this gets
inlined and the eraser vanishes.
(cherry picked from commit 3f92dc2fd4070b213e6bc85263a9bef06ec9a486)
Related: RHEL-16182
---
src/basic/memory-util.c | 18 ----------
src/basic/memory-util.h | 12 +------
src/boot/efi/random-seed.c | 1 +
src/fundamental/memory-util-fundamental.h | 42 +++++++++++++++++++++++
src/fundamental/meson.build | 1 +
5 files changed, 45 insertions(+), 29 deletions(-)
create mode 100644 src/fundamental/memory-util-fundamental.h
diff --git a/src/basic/memory-util.c b/src/basic/memory-util.c
index 2983762117..c4f54c7b4e 100644
--- a/src/basic/memory-util.c
+++ b/src/basic/memory-util.c
@@ -38,21 +38,3 @@ bool memeqbyte(uint8_t byte, const void *data, size_t length) {
/* Now we know first 16 bytes match, memcmp() with self. */
return memcmp(data, p + 16, length) == 0;
}
-
-#if !HAVE_EXPLICIT_BZERO
-/*
- * The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that
- * it points to any function in particular (such as memset(), which it then might further "optimize"). This
- * approach is inspired by openssl's crypto/mem_clr.c.
- */
-typedef void *(*memset_t)(void *,int,size_t);
-
-static volatile memset_t memset_func = memset;
-
-void* explicit_bzero_safe(void *p, size_t l) {
- if (l > 0)
- memset_func(p, '\0', l);
-
- return p;
-}
-#endif
diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h
index eea9c0e92f..d26a0918e1 100644
--- a/src/basic/memory-util.h
+++ b/src/basic/memory-util.h
@@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "macro.h"
+#include "memory-util-fundamental.h"
size_t page_size(void) _pure_;
#define PAGE_ALIGN(l) ALIGN_TO((l), page_size())
@@ -91,17 +92,6 @@ static inline void *mempmem_safe(const void *haystack, size_t haystacklen, const
return (uint8_t*) p + needlelen;
}
-#if HAVE_EXPLICIT_BZERO
-static inline void* explicit_bzero_safe(void *p, size_t l) {
- if (l > 0)
- explicit_bzero(p, l);
-
- return p;
-}
-#else
-void *explicit_bzero_safe(void *p, size_t l);
-#endif
-
static inline void* erase_and_free(void *p) {
size_t l;
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index aea4f7e532..3c9df5bb54 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -3,6 +3,7 @@
#include <efi.h>
#include <efilib.h>
+#include "memory-util-fundamental.h"
#include "missing_efi.h"
#include "random-seed.h"
#include "secure-boot.h"
diff --git a/src/fundamental/memory-util-fundamental.h b/src/fundamental/memory-util-fundamental.h
new file mode 100644
index 0000000000..9015300ae8
--- /dev/null
+++ b/src/fundamental/memory-util-fundamental.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <stddef.h>
+
+#ifdef SD_BOOT
+# include "efi-string.h"
+#else
+# include <string.h>
+#endif
+
+#include "macro-fundamental.h"
+
+#if defined(HAVE_EXPLICIT_BZERO)
+static inline void *explicit_bzero_safe(void *p, size_t l) {
+ if (p && l > 0)
+ explicit_bzero(p, l);
+
+ return p;
+}
+#else
+static inline void *explicit_bzero_safe(void *p, size_t l) {
+ if (p && l > 0) {
+ memset(p, 0, l);
+ __asm__ __volatile__("" : : "r"(p) : "memory");
+ }
+ return p;
+}
+#endif
+
+struct VarEraser {
+ void *p;
+ size_t size;
+};
+
+static inline void erase_var(struct VarEraser *e) {
+ explicit_bzero_safe(e->p, e->size);
+}
+
+/* Mark var to be erased when leaving scope. */
+#define CLEANUP_ERASE(var) \
+ _cleanup_(erase_var) _unused_ struct VarEraser CONCATENATE(_eraser_, UNIQ) = { .p = &var, .size = sizeof(var) }
diff --git a/src/fundamental/meson.build b/src/fundamental/meson.build
index 3810d6b456..4b8e32337d 100644
--- a/src/fundamental/meson.build
+++ b/src/fundamental/meson.build
@@ -6,6 +6,7 @@ fundamental_headers = files(
'bootspec-fundamental.h',
'efivars-fundamental.h',
'macro-fundamental.h',
+ 'memory-util-fundamental.h',
'sha256.h',
'string-util-fundamental.h',
'tpm-pcr.h',

View File

@ -0,0 +1,494 @@
From a86b58ed1419b9af8a486dcaf95608eb07241965 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 10 Jan 2023 12:39:58 +0100
Subject: [PATCH] tree-wide: use CLEANUP_ERASE() at various places
Let's use this new macro wherever it makes sense, as it allows us to
shorten or clean-up paths, and makes it less likely to miss a return
path.
(cherry picked from commit 692597c84395ad2b3f8e221bb1eca55a9dfc544f)
Related: RHEL-16182
---
src/home/homework-fscrypt.c | 58 ++++++++++--------------------
src/shared/ask-password-api.c | 66 +++++++++++++----------------------
src/shared/creds-util.c | 24 ++++++-------
src/shared/ethtool-util.c | 11 +++---
src/shared/tpm2-util.c | 15 ++++----
5 files changed, 67 insertions(+), 107 deletions(-)
diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c
index bd32393d93..455a4c88fc 100644
--- a/src/home/homework-fscrypt.c
+++ b/src/home/homework-fscrypt.c
@@ -58,10 +58,10 @@ static int fscrypt_upload_volume_key(
};
memcpy(key.raw, volume_key, volume_key_size);
+ CLEANUP_ERASE(key);
+
/* Upload to the kernel */
serial = add_key("logon", description, &key, sizeof(key), where);
- explicit_bzero_safe(&key, sizeof(key));
-
if (serial < 0)
return log_error_errno(errno, "Failed to install master key in keyring: %m");
@@ -124,20 +124,18 @@ static int fscrypt_slot_try_one(
* resulting hash.
*/
+ CLEANUP_ERASE(derived);
+
if (PKCS5_PBKDF2_HMAC(
password, strlen(password),
salt, salt_size,
0xFFFF, EVP_sha512(),
- sizeof(derived), derived) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
- goto finish;
- }
+ sizeof(derived), derived) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
context = EVP_CIPHER_CTX_new();
- if (!context) {
- r = log_oom();
- goto finish;
- }
+ if (!context)
+ return log_oom();
/* We use AES256 in counter mode */
assert_se(cc = EVP_aes_256_ctr());
@@ -145,13 +143,8 @@ static int fscrypt_slot_try_one(
/* We only use the first half of the derived key */
assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
- if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
- goto finish;
- }
-
- /* Flush out the derived key now, we don't need it anymore */
- explicit_bzero_safe(derived, sizeof(derived));
+ if (EVP_DecryptInit_ex(context, cc, NULL, derived, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize decryption context.");
decrypted_size = encrypted_size + EVP_CIPHER_key_length(cc) * 2;
decrypted = malloc(decrypted_size);
@@ -184,10 +177,6 @@ static int fscrypt_slot_try_one(
*ret_decrypted_size = decrypted_size;
return 0;
-
-finish:
- explicit_bzero_safe(derived, sizeof(derived));
- return r;
}
static int fscrypt_slot_try_many(
@@ -414,20 +403,18 @@ static int fscrypt_slot_set(
if (r < 0)
return log_error_errno(r, "Failed to generate salt: %m");
+ CLEANUP_ERASE(derived);
+
if (PKCS5_PBKDF2_HMAC(
password, strlen(password),
salt, sizeof(salt),
0xFFFF, EVP_sha512(),
- sizeof(derived), derived) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
- goto finish;
- }
+ sizeof(derived), derived) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PBKDF2 failed");
context = EVP_CIPHER_CTX_new();
- if (!context) {
- r = log_oom();
- goto finish;
- }
+ if (!context)
+ return log_oom();
/* We use AES256 in counter mode */
cc = EVP_aes_256_ctr();
@@ -435,13 +422,8 @@ static int fscrypt_slot_set(
/* We only use the first half of the derived key */
assert(sizeof(derived) >= (size_t) EVP_CIPHER_key_length(cc));
- if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1) {
- r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
- goto finish;
- }
-
- /* Flush out the derived key now, we don't need it anymore */
- explicit_bzero_safe(derived, sizeof(derived));
+ if (EVP_EncryptInit_ex(context, cc, NULL, derived, NULL) != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to initialize encryption context.");
encrypted_size = volume_key_size + EVP_CIPHER_key_length(cc) * 2;
encrypted = malloc(encrypted_size);
@@ -478,10 +460,6 @@ static int fscrypt_slot_set(
log_info("Written key slot %s.", label);
return 0;
-
-finish:
- explicit_bzero_safe(derived, sizeof(derived));
- return r;
}
int home_create_fscrypt(
diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c
index 871af2ec99..5f05271caa 100644
--- a/src/shared/ask-password-api.c
+++ b/src/shared/ask-password-api.c
@@ -253,6 +253,8 @@ int ask_password_plymouth(
if (r < 0)
return r;
+ CLEANUP_ERASE(buffer);
+
pollfd[POLL_SOCKET].fd = fd;
pollfd[POLL_SOCKET].events = POLLIN;
pollfd[POLL_INOTIFY].fd = notify;
@@ -266,20 +268,16 @@ int ask_password_plymouth(
else
timeout = USEC_INFINITY;
- if (flag_file && access(flag_file, F_OK) < 0) {
- r = -errno;
- goto finish;
- }
+ if (flag_file && access(flag_file, F_OK) < 0)
+ return -errno;
r = ppoll_usec(pollfd, notify >= 0 ? 2 : 1, timeout);
if (r == -EINTR)
continue;
if (r < 0)
- goto finish;
- if (r == 0) {
- r = -ETIME;
- goto finish;
- }
+ return r;
+ if (r == 0)
+ return -ETIME;
if (notify >= 0 && pollfd[POLL_INOTIFY].revents != 0)
(void) flush_fd(notify);
@@ -292,13 +290,10 @@ int ask_password_plymouth(
if (ERRNO_IS_TRANSIENT(errno))
continue;
- r = -errno;
- goto finish;
- }
- if (k == 0) {
- r = -EIO;
- goto finish;
+ return -errno;
}
+ if (k == 0)
+ return -EIO;
p += k;
@@ -310,14 +305,12 @@ int ask_password_plymouth(
* with a normal password request */
packet = mfree(packet);
- if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
- r = -ENOMEM;
- goto finish;
- }
+ if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0)
+ return -ENOMEM;
r = loop_write(fd, packet, n+1, true);
if (r < 0)
- goto finish;
+ return r;
flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
p = 0;
@@ -325,8 +318,7 @@ int ask_password_plymouth(
}
/* No password, because UI not shown */
- r = -ENOENT;
- goto finish;
+ return -ENOENT;
} else if (IN_SET(buffer[0], 2, 9)) {
uint32_t size;
@@ -338,35 +330,25 @@ int ask_password_plymouth(
memcpy(&size, buffer+1, sizeof(size));
size = le32toh(size);
- if (size + 5 > sizeof(buffer)) {
- r = -EIO;
- goto finish;
- }
+ if (size + 5 > sizeof(buffer))
+ return -EIO;
if (p-5 < size)
continue;
l = strv_parse_nulstr(buffer + 5, size);
- if (!l) {
- r = -ENOMEM;
- goto finish;
- }
+ if (!l)
+ return -ENOMEM;
*ret = l;
break;
- } else {
+ } else
/* Unknown packet */
- r = -EIO;
- goto finish;
- }
+ return -EIO;
}
- r = 0;
-
-finish:
- explicit_bzero_safe(buffer, sizeof(buffer));
- return r;
+ return 0;
}
#define NO_ECHO "(no echo) "
@@ -428,6 +410,8 @@ int ask_password_tty(
return -errno;
}
+ CLEANUP_ERASE(passphrase);
+
/* If the caller didn't specify a TTY, then use the controlling tty, if we can. */
if (ttyfd < 0)
ttyfd = cttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC);
@@ -631,7 +615,6 @@ int ask_password_tty(
}
x = strndup(passphrase, p);
- explicit_bzero_safe(passphrase, sizeof(passphrase));
if (!x) {
r = -ENOMEM;
goto finish;
@@ -891,6 +874,8 @@ int ask_password_agent(
goto finish;
}
+ CLEANUP_ERASE(passphrase);
+
cmsg_close_all(&msghdr);
if (n == 0) {
@@ -915,7 +900,6 @@ int ask_password_agent(
l = strv_new("");
else
l = strv_parse_nulstr(passphrase+1, n-1);
- explicit_bzero_safe(passphrase, n);
if (!l) {
r = -ENOMEM;
goto finish;
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
index ecf90e2084..9ac0320c58 100644
--- a/src/shared/creds-util.c
+++ b/src/shared/creds-util.c
@@ -165,7 +165,6 @@ static int make_credential_host_secret(
void **ret_data,
size_t *ret_size) {
- struct credential_host_secret_format buf;
_cleanup_free_ char *t = NULL;
_cleanup_close_ int fd = -1;
int r;
@@ -189,21 +188,23 @@ static int make_credential_host_secret(
if (r < 0)
log_debug_errno(r, "Failed to set file attributes for secrets file, ignoring: %m");
- buf = (struct credential_host_secret_format) {
+ struct credential_host_secret_format buf = {
.machine_id = machine_id,
};
+ CLEANUP_ERASE(buf);
+
r = crypto_random_bytes(buf.data, sizeof(buf.data));
if (r < 0)
- goto finish;
+ goto fail;
r = loop_write(fd, &buf, sizeof(buf), false);
if (r < 0)
- goto finish;
+ goto fail;
if (fsync(fd) < 0) {
r = -errno;
- goto finish;
+ goto fail;
}
warn_not_encrypted(fd, flags, dirname, fn);
@@ -211,17 +212,17 @@ static int make_credential_host_secret(
if (t) {
r = rename_noreplace(dfd, t, dfd, fn);
if (r < 0)
- goto finish;
+ goto fail;
t = mfree(t);
} else if (linkat(fd, "", dfd, fn, AT_EMPTY_PATH) < 0) {
r = -errno;
- goto finish;
+ goto fail;
}
if (fsync(dfd) < 0) {
r = -errno;
- goto finish;
+ goto fail;
}
if (ret_data) {
@@ -230,7 +231,7 @@ static int make_credential_host_secret(
copy = memdup(buf.data, sizeof(buf.data));
if (!copy) {
r = -ENOMEM;
- goto finish;
+ goto fail;
}
*ret_data = copy;
@@ -239,13 +240,12 @@ static int make_credential_host_secret(
if (ret_size)
*ret_size = sizeof(buf.data);
- r = 0;
+ return 0;
-finish:
+fail:
if (t && unlinkat(dfd, t, 0) < 0)
log_debug_errno(errno, "Failed to remove temporary credential key: %m");
- explicit_bzero_safe(&buf, sizeof(buf));
return r;
}
diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c
index e39b2f754b..1900537917 100644
--- a/src/shared/ethtool-util.c
+++ b/src/shared/ethtool-util.c
@@ -434,6 +434,8 @@ int ethtool_set_wol(
strscpy(ifr.ifr_name, sizeof(ifr.ifr_name), ifname);
+ CLEANUP_ERASE(ecmd);
+
if (ioctl(*ethtool_fd, SIOCETHTOOL, &ifr) < 0)
return -errno;
@@ -466,16 +468,11 @@ int ethtool_set_wol(
need_update = true;
}
- if (!need_update) {
- explicit_bzero_safe(&ecmd, sizeof(ecmd));
+ if (!need_update)
return 0;
- }
ecmd.cmd = ETHTOOL_SWOL;
- r = RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
-
- explicit_bzero_safe(&ecmd, sizeof(ecmd));
- return r;
+ return RET_NERRNO(ioctl(*ethtool_fd, SIOCETHTOOL, &ifr));
}
int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring) {
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index d1a4e9cd11..7e98ec851b 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -778,13 +778,14 @@ static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
assert(auth);
assert(pin);
+
auth->size = SHA256_DIGEST_SIZE;
+ CLEANUP_ERASE(hash);
+
sha256_init_ctx(&hash);
sha256_process_bytes(pin, len, &hash);
sha256_finish_ctx(&hash, auth->buffer);
-
- explicit_bzero_safe(&hash, sizeof(hash));
}
static int tpm2_make_encryption_session(
@@ -816,11 +817,11 @@ static int tpm2_make_encryption_session(
if (pin) {
TPM2B_AUTH auth = {};
+ CLEANUP_ERASE(auth);
+
hash_pin(pin, strlen(pin), &auth);
rc = sym_Esys_TR_SetAuth(c, bind_key, &auth);
- /* ESAPI knows about it, so clear it from our memory */
- explicit_bzero_safe(&auth, sizeof(auth));
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
@@ -1412,8 +1413,8 @@ int tpm2_seal(const char *device,
static const TPML_PCR_SELECTION creation_pcr = {};
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *blob = NULL, *hash = NULL;
- TPM2B_SENSITIVE_CREATE hmac_sensitive;
ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE;
+ TPM2B_SENSITIVE_CREATE hmac_sensitive;
TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template;
TPMI_ALG_HASH pcr_bank;
@@ -1453,6 +1454,8 @@ int tpm2_seal(const char *device,
start = now(CLOCK_MONOTONIC);
+ CLEANUP_ERASE(hmac_sensitive);
+
r = tpm2_context_init(device, &c);
if (r < 0)
return r;
@@ -1541,7 +1544,6 @@ int tpm2_seal(const char *device,
}
secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
- explicit_bzero_safe(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
if (!secret) {
r = log_oom();
goto finish;
@@ -1602,7 +1604,6 @@ int tpm2_seal(const char *device,
r = 0;
finish:
- explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive));
primary = tpm2_flush_context_verbose(c.esys_context, primary);
session = tpm2_flush_context_verbose(c.esys_context, session);
return r;

View File

@ -0,0 +1,49 @@
From fa2bce7c0447bd836fc8c2020ac714e4a47b9900 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 25 Jan 2023 11:54:44 +0100
Subject: [PATCH] dlfcn: add new safe_dclose() helper
Let's allow destructing loaded module handles in our usual way that is
fine with NULL handles, and also returns the NULL handle again.
(cherry picked from commit f2592ef0e113aef0e8e7141cab2b17521760b064)
Related: RHEL-16182
---
src/shared/dlfcn-util.h | 8 ++++++++
src/shared/tpm2-util.c | 6 +-----
2 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/shared/dlfcn-util.h b/src/shared/dlfcn-util.h
index d786d035d7..7bd5ff4595 100644
--- a/src/shared/dlfcn-util.h
+++ b/src/shared/dlfcn-util.h
@@ -20,3 +20,11 @@ int dlopen_many_sym_or_warn_sentinel(void **dlp, const char *filename, int log_l
* "foobar" is loaded into a variable "sym_foobar". */
#define DLSYM_ARG(arg) \
&sym_##arg, STRINGIFY(arg)
+
+static inline void *safe_dlclose(void *p) {
+ if (!p)
+ return NULL;
+
+ assert_se(dlclose(p) == 0);
+ return NULL;
+}
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 7e98ec851b..278cdf3692 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -110,11 +110,7 @@ void tpm2_context_destroy(struct tpm2_context *c) {
sym_Esys_Finalize(&c->esys_context);
c->tcti_context = mfree(c->tcti_context);
-
- if (c->tcti_dl) {
- dlclose(c->tcti_dl);
- c->tcti_dl = NULL;
- }
+ c->tcti_dl = safe_dlclose(c->tcti_dl);
}
static inline void Esys_Finalize_wrapper(ESYS_CONTEXT **c) {

View File

@ -0,0 +1,237 @@
From 73fd8e125a5c22cd341c056b774ab6cb2122b951 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 9 Dec 2022 17:20:24 -0500
Subject: [PATCH] tpm2: rename tpm2 alg id<->string functions
The 'pcr_bank' functions operate on hash algs, and are not specific to the PCR
banks, while the 'primary_alg' functions operate on asymmetric algs, and are
not specific to primary keys.
(cherry picked from commit 7bfe0a48d9df6e9488aaec2eeb5bfec051681e40)
Related: RHEL-16182
---
src/boot/measure.c | 2 +-
.../cryptsetup-token-systemd-tpm2.c | 8 ++--
src/shared/creds-util.c | 4 +-
src/shared/tpm2-util.c | 44 +++++++++----------
src/shared/tpm2-util.h | 8 ++--
5 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/src/boot/measure.c b/src/boot/measure.c
index 84a7c357a4..8af3a337d6 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -837,7 +837,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
assert(sizeof(intermediate_digest.buffer) >= SHA256_DIGEST_SIZE);
sha256_direct(p->value, p->value_size, intermediate_digest.buffer);
- int tpmalg = tpm2_pcr_bank_from_string(EVP_MD_name(p->md));
+ int tpmalg = tpm2_hash_alg_from_string(EVP_MD_name(p->md));
if (tpmalg < 0) {
log_error_errno(tpmalg, "Unsupported PCR bank");
goto finish;
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index 1eb924529c..98bcaac4d8 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -220,10 +220,10 @@ _public_ void cryptsetup_token_dump(
return (void) crypt_log_debug_errno(cd, r, "Can not dump " TOKEN_NAME " content: %m");
crypt_log(cd, "\ttpm2-hash-pcrs: %s\n", strna(hash_pcrs_str));
- crypt_log(cd, "\ttpm2-pcr-bank: %s\n", strna(tpm2_pcr_bank_to_string(pcr_bank)));
+ crypt_log(cd, "\ttpm2-pcr-bank: %s\n", strna(tpm2_hash_alg_to_string(pcr_bank)));
crypt_log(cd, "\ttpm2-pubkey:" CRYPT_DUMP_LINE_SEP "%s\n", pubkey_str);
crypt_log(cd, "\ttpm2-pubkey-pcrs: %s\n", strna(pubkey_pcrs_str));
- crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_primary_alg_to_string(primary_alg)));
+ crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_asym_alg_to_string(primary_alg)));
crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
@@ -281,7 +281,7 @@ _public_ int cryptsetup_token_validate(
return 1;
}
- if (tpm2_pcr_bank_from_string(json_variant_string(w)) < 0) {
+ if (tpm2_hash_alg_from_string(json_variant_string(w)) < 0) {
crypt_log_debug(cd, "TPM2 PCR bank invalid or not supported: %s.", json_variant_string(w));
return 1;
}
@@ -298,7 +298,7 @@ _public_ int cryptsetup_token_validate(
return 1;
}
- if (tpm2_primary_alg_from_string(json_variant_string(w)) < 0) {
+ if (tpm2_asym_alg_from_string(json_variant_string(w)) < 0) {
crypt_log_debug(cd, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w));
return 1;
}
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
index 9ac0320c58..e9cafb8097 100644
--- a/src/shared/creds-util.c
+++ b/src/shared/creds-util.c
@@ -944,9 +944,9 @@ int decrypt_credential_and_warn(
if (!TPM2_PCR_MASK_VALID(t->pcr_mask))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR mask out of range.");
- if (!tpm2_pcr_bank_to_string(le16toh(t->pcr_bank)))
+ if (!tpm2_hash_alg_to_string(le16toh(t->pcr_bank)))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 PCR bank invalid or not supported");
- if (!tpm2_primary_alg_to_string(le16toh(t->primary_alg)))
+ if (!tpm2_asym_alg_to_string(le16toh(t->primary_alg)))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "TPM2 primary key algorithm invalid or not supported.");
if (le32toh(t->blob_size) > CREDENTIAL_FIELD_SIZE_MAX)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unexpected TPM2 blob size.");
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 278cdf3692..fe4d63b775 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -528,7 +528,7 @@ static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) {
* 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)));
+ strna(tpm2_hash_alg_to_string(selection->hash)));
return false;
}
@@ -545,7 +545,7 @@ static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) {
if (!valid)
log_debug("TPM2 PCR bank %s has fewer than 24 PCR bits enabled, ignoring.",
- strna(tpm2_pcr_bank_to_string(selection->hash)));
+ strna(tpm2_hash_alg_to_string(selection->hash)));
return valid;
}
@@ -747,7 +747,7 @@ int tpm2_get_good_pcr_banks_strv(
const EVP_MD *implementation;
const char *salg;
- salg = tpm2_pcr_bank_to_string(algs[i]);
+ salg = tpm2_hash_alg_to_string(algs[i]);
if (!salg)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM2 operates with unknown PCR algorithm, can't measure.");
@@ -971,7 +971,7 @@ static int find_signature(
if (!json_variant_is_object(v))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Signature is not a JSON object.");
- k = tpm2_pcr_bank_to_string(pcr_bank);
+ k = tpm2_hash_alg_to_string(pcr_bank);
if (!k)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Don't know PCR bank %" PRIu16, pcr_bank);
@@ -1956,7 +1956,7 @@ int tpm2_extend_bytes(
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));
+ id = tpm2_hash_alg_from_string(EVP_MD_name(implementation));
if (id < 0)
return log_error_errno(id, "Can't map hash name to TPM2.");
@@ -2140,8 +2140,8 @@ int tpm2_make_luks2_json(
JSON_BUILD_PAIR("keyslots", JSON_BUILD_ARRAY(JSON_BUILD_STRING(keyslot_as_string))),
JSON_BUILD_PAIR("tpm2-blob", JSON_BUILD_BASE64(blob, blob_size)),
JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(hmj)),
- JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))),
- JSON_BUILD_PAIR_CONDITION(!!tpm2_primary_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_primary_alg_to_string(primary_alg))),
+ JSON_BUILD_PAIR_CONDITION(!!tpm2_hash_alg_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_hash_alg_to_string(pcr_bank))),
+ JSON_BUILD_PAIR_CONDITION(!!tpm2_asym_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_asym_alg_to_string(primary_alg))),
JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)),
JSON_BUILD_PAIR("tpm2-pin", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PIN)),
JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey_pcrs", JSON_BUILD_VARIANT(pkmj)),
@@ -2209,7 +2209,7 @@ int tpm2_parse_luks2_json(
if (!json_variant_is_string(w))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 PCR bank is not a string.");
- r = tpm2_pcr_bank_from_string(json_variant_string(w));
+ r = tpm2_hash_alg_from_string(json_variant_string(w));
if (r < 0)
return log_debug_errno(r, "TPM2 PCR bank invalid or not supported: %s", json_variant_string(w));
@@ -2225,9 +2225,9 @@ int tpm2_parse_luks2_json(
if (!json_variant_is_string(w))
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 primary key algorithm is not a string.");
- r = tpm2_primary_alg_from_string(json_variant_string(w));
+ r = tpm2_asym_alg_from_string(json_variant_string(w));
if (r < 0)
- return log_debug_errno(r, "TPM2 primary key algorithm invalid or not supported: %s", json_variant_string(w));
+ return log_debug_errno(r, "TPM2 asymmetric algorithm invalid or not supported: %s", json_variant_string(w));
primary_alg = r;
}
@@ -2299,31 +2299,31 @@ int tpm2_parse_luks2_json(
return 0;
}
-const char *tpm2_pcr_bank_to_string(uint16_t bank) {
- if (bank == TPM2_ALG_SHA1)
+const char *tpm2_hash_alg_to_string(uint16_t alg) {
+ if (alg == TPM2_ALG_SHA1)
return "sha1";
- if (bank == TPM2_ALG_SHA256)
+ if (alg == TPM2_ALG_SHA256)
return "sha256";
- if (bank == TPM2_ALG_SHA384)
+ if (alg == TPM2_ALG_SHA384)
return "sha384";
- if (bank == TPM2_ALG_SHA512)
+ if (alg == TPM2_ALG_SHA512)
return "sha512";
return NULL;
}
-int tpm2_pcr_bank_from_string(const char *bank) {
- if (strcaseeq_ptr(bank, "sha1"))
+int tpm2_hash_alg_from_string(const char *alg) {
+ if (strcaseeq_ptr(alg, "sha1"))
return TPM2_ALG_SHA1;
- if (strcaseeq_ptr(bank, "sha256"))
+ if (strcaseeq_ptr(alg, "sha256"))
return TPM2_ALG_SHA256;
- if (strcaseeq_ptr(bank, "sha384"))
+ if (strcaseeq_ptr(alg, "sha384"))
return TPM2_ALG_SHA384;
- if (strcaseeq_ptr(bank, "sha512"))
+ if (strcaseeq_ptr(alg, "sha512"))
return TPM2_ALG_SHA512;
return -EINVAL;
}
-const char *tpm2_primary_alg_to_string(uint16_t alg) {
+const char *tpm2_asym_alg_to_string(uint16_t alg) {
if (alg == TPM2_ALG_ECC)
return "ecc";
if (alg == TPM2_ALG_RSA)
@@ -2331,7 +2331,7 @@ const char *tpm2_primary_alg_to_string(uint16_t alg) {
return NULL;
}
-int tpm2_primary_alg_from_string(const char *alg) {
+int tpm2_asym_alg_from_string(const char *alg) {
if (strcaseeq_ptr(alg, "ecc"))
return TPM2_ALG_ECC;
if (strcaseeq_ptr(alg, "rsa"))
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 96e6c31b0a..9e302021ab 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -125,11 +125,11 @@ static inline bool TPM2_PCR_MASK_VALID(uint64_t pcr_mask) {
#define TPM2_ALG_RSA 0x1
#endif
-const char *tpm2_pcr_bank_to_string(uint16_t bank);
-int tpm2_pcr_bank_from_string(const char *bank);
+const char *tpm2_hash_alg_to_string(uint16_t alg);
+int tpm2_hash_alg_from_string(const char *alg);
-const char *tpm2_primary_alg_to_string(uint16_t alg);
-int tpm2_primary_alg_from_string(const char *alg);
+const char *tpm2_asym_alg_to_string(uint16_t alg);
+int tpm2_asym_alg_from_string(const char *alg);
typedef struct {
uint32_t search_pcr_mask;

View File

@ -0,0 +1,151 @@
From 55edf6d2080573d3395aec6f9f99e62cf8bd8d01 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 23 Jan 2023 19:52:56 -0500
Subject: [PATCH] tpm2: rename struct tpm2_context to Tpm2Context
This aligns with systemd coding guidelines for struct naming
(cherry picked from commit bd860983a6f884e37e88915f545d5520f92890ec)
Related: RHEL-16182
---
src/boot/measure.c | 2 +-
src/boot/pcrphase.c | 4 ++--
src/cryptsetup/cryptsetup.c | 2 +-
src/shared/tpm2-util.c | 10 +++++-----
src/shared/tpm2-util.h | 14 +++++++-------
5 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/src/boot/measure.c b/src/boot/measure.c
index 8af3a337d6..d71a7a1d13 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -717,7 +717,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_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_(tpm2_context_destroy) Tpm2Context c = {};
_cleanup_fclose_ FILE *privkeyf = NULL;
ESYS_TR session_handle = ESYS_TR_NONE;
TSS2_RC rc;
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index fda9a8420d..694e131ac1 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -154,7 +154,7 @@ static int parse_argv(int argc, char *argv[]) {
return 1;
}
-static int determine_banks(struct tpm2_context *c, unsigned target_pcr_nr) {
+static int determine_banks(Tpm2Context *c, unsigned target_pcr_nr) {
_cleanup_strv_free_ char **l = NULL;
int r;
@@ -239,7 +239,7 @@ static int get_file_system_word(
}
static int run(int argc, char *argv[]) {
- _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
+ _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
_cleanup_free_ char *joined = NULL, *word = NULL;
unsigned target_pcr_nr;
size_t length;
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 4d587fed1e..712d208741 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -841,7 +841,7 @@ static int measure_volume_key(
if (r < 0)
return log_error_errno(r, "Failed to load TPM2 libraries: %m");
- _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
+ _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
r = tpm2_context_init(arg_tpm2_device, &c);
if (r < 0)
return r;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index fe4d63b775..5c4d5476a3 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -103,7 +103,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal));
}
-void tpm2_context_destroy(struct tpm2_context *c) {
+void tpm2_context_destroy(Tpm2Context *c) {
assert(c);
if (c->esys_context)
@@ -137,7 +137,7 @@ ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle) {
return ESYS_TR_NONE;
}
-int tpm2_context_init(const char *device, struct tpm2_context *ret) {
+int tpm2_context_init(const char *device, Tpm2Context *ret) {
_cleanup_(Esys_Finalize_wrapper) ESYS_CONTEXT *c = NULL;
_cleanup_free_ TSS2_TCTI_CONTEXT *tcti = NULL;
_cleanup_(dlclosep) void *dl = NULL;
@@ -237,7 +237,7 @@ int tpm2_context_init(const char *device, struct tpm2_context *ret) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to start up TPM: %s", sym_Tss2_RC_Decode(rc));
- *ret = (struct tpm2_context) {
+ *ret = (Tpm2Context) {
.esys_context = TAKE_PTR(c),
.tcti_context = TAKE_PTR(tcti),
.tcti_dl = TAKE_PTR(dl),
@@ -1402,7 +1402,7 @@ int tpm2_seal(const char *device,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg) {
- _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
+ _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
_cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
_cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
@@ -1623,7 +1623,7 @@ int tpm2_unseal(const char *device,
void **ret_secret,
size_t *ret_secret_size) {
- _cleanup_(tpm2_context_destroy) struct tpm2_context c = {};
+ _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_session = ESYS_TR_NONE,
hmac_key = ESYS_TR_NONE;
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 9e302021ab..bc960c6f50 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -52,11 +52,11 @@ int dlopen_tpm2(void);
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg);
int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, void **ret_secret, size_t *ret_secret_size);
-struct tpm2_context {
+typedef struct {
void *tcti_dl;
TSS2_TCTI_CONTEXT *tcti_context;
ESYS_CONTEXT *esys_context;
-};
+} Tpm2Context;
ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle);
@@ -72,12 +72,12 @@ 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;
-#endif
+#else /* HAVE_TPM2 */
+typedef struct {} Tpm2Context;
+#endif /* HAVE_TPM2 */
-int tpm2_context_init(const char *device, struct tpm2_context *ret);
-void tpm2_context_destroy(struct tpm2_context *c);
+int tpm2_context_init(const char *device, Tpm2Context *ret);
+void tpm2_context_destroy(Tpm2Context *c);
int tpm2_list_devices(void);
int tpm2_find_device_auto(int log_level, char **ret);

View File

@ -0,0 +1,454 @@
From 32a83032a4a5b239b72bde647128a004521db799 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 23 Jan 2023 19:52:56 -0500
Subject: [PATCH] tpm2: use ref counter for Tpm2Context
This will be used by Tpm2Handle instances, which is added in later patches.
The refcounting allows the context to be retained until all Tpm2Handles have
been cleaned up, and the initial ref is released, before cleaning the context.
(cherry picked from commit 68d084cee56e2686fb840106de20e267482183be)
Related: RHEL-16182
---
src/boot/measure.c | 14 +++---
src/boot/pcrphase.c | 8 ++--
src/cryptsetup/cryptsetup.c | 8 ++--
src/shared/tpm2-util.c | 91 ++++++++++++++++++-------------------
src/shared/tpm2-util.h | 11 +++--
5 files changed, 68 insertions(+), 64 deletions(-)
diff --git a/src/boot/measure.c b/src/boot/measure.c
index d71a7a1d13..701d5471a1 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -717,7 +717,6 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
_cleanup_(pcr_state_free_all) PcrState *pcr_states = NULL;
_cleanup_(EVP_PKEY_freep) EVP_PKEY *privkey = NULL, *pubkey = NULL;
- _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
_cleanup_fclose_ FILE *privkeyf = NULL;
ESYS_TR session_handle = ESYS_TR_NONE;
TSS2_RC rc;
@@ -793,7 +792,8 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = tpm2_context_init(arg_tpm2_device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
@@ -812,7 +812,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
PcrState *p = pcr_states + i;
rc = sym_Esys_StartAuthSession(
- c.esys_context,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -847,7 +847,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection);
rc = sym_Esys_PolicyPCR(
- c.esys_context,
+ c->esys_context,
session_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -862,7 +862,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
_cleanup_(Esys_Freep) TPM2B_DIGEST *pcr_policy_digest = NULL;
rc = sym_Esys_PolicyGetDigest(
- c.esys_context,
+ c->esys_context,
session_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -874,7 +874,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
goto finish;
}
- session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
+ session_handle = tpm2_flush_context_verbose(c->esys_context, session_handle);
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL;
mdctx = EVP_MD_CTX_new();
@@ -965,7 +965,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
r = 0;
finish:
- session_handle = tpm2_flush_context_verbose(c.esys_context, session_handle);
+ session_handle = tpm2_flush_context_verbose(c->esys_context, session_handle);
return r;
}
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 694e131ac1..bbe58fa209 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -239,7 +239,6 @@ static int get_file_system_word(
}
static int run(int argc, char *argv[]) {
- _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
_cleanup_free_ char *joined = NULL, *word = NULL;
unsigned target_pcr_nr;
size_t length;
@@ -345,11 +344,12 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to load TPM2 libraries: %m");
- r = tpm2_context_init(arg_tpm2_device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
- r = determine_banks(&c, target_pcr_nr);
+ r = determine_banks(c, target_pcr_nr);
if (r < 0)
return r;
if (strv_isempty(arg_banks)) /* Still none? */
@@ -361,7 +361,7 @@ static int run(int argc, char *argv[]) {
log_debug("Measuring '%s' into PCR index %u, banks %s.", word, target_pcr_nr, joined);
- r = tpm2_extend_bytes(c.esys_context, arg_banks, target_pcr_nr, word, length, NULL, 0);
+ r = tpm2_extend_bytes(c->esys_context, arg_banks, target_pcr_nr, word, length, NULL, 0);
if (r < 0)
return r;
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 712d208741..08744bda0c 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -841,14 +841,14 @@ static int measure_volume_key(
if (r < 0)
return log_error_errno(r, "Failed to load TPM2 libraries: %m");
- _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
- r = tpm2_context_init(arg_tpm2_device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(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);
+ r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << arg_tpm2_measure_pcr, &l);
if (r < 0)
return r;
}
@@ -871,7 +871,7 @@ static int measure_volume_key(
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);
+ 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;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 5c4d5476a3..51bb1c082d 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -103,23 +103,21 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal));
}
-void tpm2_context_destroy(Tpm2Context *c) {
- assert(c);
+static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
+ if (!c)
+ return NULL;
if (c->esys_context)
sym_Esys_Finalize(&c->esys_context);
c->tcti_context = mfree(c->tcti_context);
c->tcti_dl = safe_dlclose(c->tcti_dl);
-}
-static inline void Esys_Finalize_wrapper(ESYS_CONTEXT **c) {
- /* A wrapper around Esys_Finalize() for use with _cleanup_(). Only reasons we need this wrapper is
- * because the function itself warn logs if we'd pass a pointer to NULL, and we don't want that. */
- if (*c)
- sym_Esys_Finalize(c);
+ return mfree(c);
}
+DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
+
ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle) {
TSS2_RC rc;
@@ -137,13 +135,19 @@ ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle) {
return ESYS_TR_NONE;
}
-int tpm2_context_init(const char *device, Tpm2Context *ret) {
- _cleanup_(Esys_Finalize_wrapper) ESYS_CONTEXT *c = NULL;
- _cleanup_free_ TSS2_TCTI_CONTEXT *tcti = NULL;
- _cleanup_(dlclosep) void *dl = NULL;
+int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
+ _cleanup_tpm2_context_ Tpm2Context *context = NULL;
TSS2_RC rc;
int r;
+ assert(ret_context);
+
+ context = new0(Tpm2Context, 1);
+ if (!context)
+ return log_oom();
+
+ context->n_ref = 1;
+
r = dlopen_tpm2();
if (r < 0)
return log_error_errno(r, "TPM2 support not installed: %m");
@@ -191,11 +195,11 @@ int tpm2_context_init(const char *device, Tpm2Context *ret) {
if (!filename_is_valid(fn))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "TPM2 driver name '%s' not valid, refusing.", driver);
- dl = dlopen(fn, RTLD_NOW);
- if (!dl)
+ context->tcti_dl = dlopen(fn, RTLD_NOW);
+ if (!context->tcti_dl)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to load %s: %s", fn, dlerror());
- func = dlsym(dl, TSS2_TCTI_INFO_SYMBOL);
+ func = dlsym(context->tcti_dl, TSS2_TCTI_INFO_SYMBOL);
if (!func)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to find TCTI info symbol " TSS2_TCTI_INFO_SYMBOL ": %s",
@@ -205,7 +209,6 @@ int tpm2_context_init(const char *device, Tpm2Context *ret) {
if (!info)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Unable to get TCTI info data.");
-
log_debug("Loaded TCTI module '%s' (%s) [Version %" PRIu32 "]", info->name, info->description, info->version);
rc = info->init(NULL, &sz, NULL);
@@ -213,22 +216,22 @@ int tpm2_context_init(const char *device, Tpm2Context *ret) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
- tcti = malloc0(sz);
- if (!tcti)
+ context->tcti_context = malloc0(sz);
+ if (!context->tcti_context)
return log_oom();
- rc = info->init(tcti, &sz, param);
+ rc = info->init(context->tcti_context, &sz, param);
if (rc != TPM2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TCTI context: %s", sym_Tss2_RC_Decode(rc));
}
- rc = sym_Esys_Initialize(&c, tcti, NULL);
+ rc = sym_Esys_Initialize(&context->esys_context, context->tcti_context, NULL);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize TPM context: %s", sym_Tss2_RC_Decode(rc));
- rc = sym_Esys_Startup(c, TPM2_SU_CLEAR);
+ rc = sym_Esys_Startup(context->esys_context, TPM2_SU_CLEAR);
if (rc == TPM2_RC_INITIALIZE)
log_debug("TPM already started up.");
else if (rc == TSS2_RC_SUCCESS)
@@ -237,11 +240,7 @@ int tpm2_context_init(const char *device, Tpm2Context *ret) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to start up TPM: %s", sym_Tss2_RC_Decode(rc));
- *ret = (Tpm2Context) {
- .esys_context = TAKE_PTR(c),
- .tcti_context = TAKE_PTR(tcti),
- .tcti_dl = TAKE_PTR(dl),
- };
+ *ret_context = TAKE_PTR(context);
return 0;
}
@@ -1402,7 +1401,6 @@ int tpm2_seal(const char *device,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg) {
- _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
_cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
_cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
@@ -1452,21 +1450,22 @@ int tpm2_seal(const char *device,
CLEANUP_ERASE(hmac_sensitive);
- r = tpm2_context_init(device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(device, &c);
if (r < 0)
return r;
- r = tpm2_make_primary(c.esys_context, &primary, 0, &primary_alg);
+ r = tpm2_make_primary(c->esys_context, &primary, 0, &primary_alg);
if (r < 0)
return r;
/* we cannot use the bind key before its created */
- r = tpm2_make_encryption_session(c.esys_context, primary, ESYS_TR_NONE, NULL, &session);
+ r = tpm2_make_encryption_session(c->esys_context, primary, ESYS_TR_NONE, NULL, &session);
if (r < 0)
goto finish;
r = tpm2_make_policy_session(
- c.esys_context,
+ c->esys_context,
primary,
session,
TPM2_SE_TRIAL,
@@ -1506,7 +1505,7 @@ int tpm2_seal(const char *device,
assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
- (void) tpm2_credit_random(c.esys_context);
+ (void) tpm2_credit_random(c->esys_context);
log_debug("Generating secret key data.");
@@ -1519,7 +1518,7 @@ int tpm2_seal(const char *device,
log_debug("Creating HMAC key.");
rc = sym_Esys_Create(
- c.esys_context,
+ c->esys_context,
primary,
session, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
@@ -1600,8 +1599,8 @@ int tpm2_seal(const char *device,
r = 0;
finish:
- primary = tpm2_flush_context_verbose(c.esys_context, primary);
- session = tpm2_flush_context_verbose(c.esys_context, session);
+ primary = tpm2_flush_context_verbose(c->esys_context, primary);
+ session = tpm2_flush_context_verbose(c->esys_context, session);
return r;
}
@@ -1623,7 +1622,6 @@ int tpm2_unseal(const char *device,
void **ret_secret,
size_t *ret_secret_size) {
- _cleanup_(tpm2_context_destroy) Tpm2Context c = {};
ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_session = ESYS_TR_NONE,
hmac_key = ESYS_TR_NONE;
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
@@ -1674,11 +1672,12 @@ int tpm2_unseal(const char *device,
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to unmarshal public key: %s", sym_Tss2_RC_Decode(rc));
- r = tpm2_context_init(device, &c);
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(device, &c);
if (r < 0)
return r;
- r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL);
+ r = tpm2_make_primary(c->esys_context, &primary, primary_alg, NULL);
if (r < 0)
return r;
@@ -1691,7 +1690,7 @@ int tpm2_unseal(const char *device,
* primary key is not verified and they could attack there as well.
*/
rc = sym_Esys_Load(
- c.esys_context,
+ c->esys_context,
primary,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
@@ -1714,13 +1713,13 @@ int tpm2_unseal(const char *device,
goto finish;
}
- r = tpm2_make_encryption_session(c.esys_context, primary, hmac_key, pin, &hmac_session);
+ r = tpm2_make_encryption_session(c->esys_context, primary, hmac_key, pin, &hmac_session);
if (r < 0)
goto finish;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
r = tpm2_make_policy_session(
- c.esys_context,
+ c->esys_context,
primary,
hmac_session,
TPM2_SE_POLICY,
@@ -1747,7 +1746,7 @@ int tpm2_unseal(const char *device,
log_debug("Unsealing HMAC key.");
rc = sym_Esys_Unseal(
- c.esys_context,
+ c->esys_context,
hmac_key,
session,
hmac_session, /* use HMAC session to enable parameter encryption */
@@ -1755,7 +1754,7 @@ int tpm2_unseal(const char *device,
&unsealed);
if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
- session = tpm2_flush_context_verbose(c.esys_context, session);
+ session = tpm2_flush_context_verbose(c->esys_context, session);
continue;
}
if (rc != TSS2_RC_SUCCESS) {
@@ -1783,9 +1782,9 @@ int tpm2_unseal(const char *device,
r = 0;
finish:
- primary = tpm2_flush_context_verbose(c.esys_context, primary);
- session = tpm2_flush_context_verbose(c.esys_context, session);
- hmac_key = tpm2_flush_context_verbose(c.esys_context, hmac_key);
+ primary = tpm2_flush_context_verbose(c->esys_context, primary);
+ session = tpm2_flush_context_verbose(c->esys_context, session);
+ hmac_key = tpm2_flush_context_verbose(c->esys_context, hmac_key);
return r;
}
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index bc960c6f50..65c875899e 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -53,11 +53,19 @@ int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, si
int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, void **ret_secret, size_t *ret_secret_size);
typedef struct {
+ unsigned n_ref;
+
void *tcti_dl;
TSS2_TCTI_CONTEXT *tcti_context;
ESYS_CONTEXT *esys_context;
} Tpm2Context;
+int tpm2_context_new(const char *device, Tpm2Context **ret_context);
+Tpm2Context *tpm2_context_ref(Tpm2Context *context);
+Tpm2Context *tpm2_context_unref(Tpm2Context *context);
+DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
+#define _cleanup_tpm2_context_ _cleanup_(tpm2_context_unrefp)
+
ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle);
void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret);
@@ -76,9 +84,6 @@ int tpm2_extend_bytes(ESYS_CONTEXT *c, char **banks, unsigned pcr_index, const v
typedef struct {} Tpm2Context;
#endif /* HAVE_TPM2 */
-int tpm2_context_init(const char *device, Tpm2Context *ret);
-void tpm2_context_destroy(Tpm2Context *c);
-
int tpm2_list_devices(void);
int tpm2_find_device_auto(int log_level, char **ret);

View File

@ -0,0 +1,482 @@
From 2932f601fd1957a1778f58301e8c529ed308fa42 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 6 Dec 2022 13:07:34 -0500
Subject: [PATCH] tpm2: use Tpm2Context* instead of ESYS_CONTEXT*
This is needed for later patches that use Tpm2Handle, which requires access
to the Tpm2Context.
(cherry picked from commit 23e9ccc24ae0e7d3f3f609c69dbde171f1c55302)
Related: RHEL-16182
---
src/boot/measure.c | 4 +-
src/boot/pcrphase.c | 4 +-
src/cryptsetup/cryptsetup.c | 4 +-
src/shared/tpm2-util.c | 90 ++++++++++++++++++-------------------
src/shared/tpm2-util.h | 8 ++--
5 files changed, 55 insertions(+), 55 deletions(-)
diff --git a/src/boot/measure.c b/src/boot/measure.c
index 701d5471a1..1bb35e5f76 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -874,7 +874,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
goto finish;
}
- session_handle = tpm2_flush_context_verbose(c->esys_context, session_handle);
+ session_handle = tpm2_flush_context_verbose(c, session_handle);
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL;
mdctx = EVP_MD_CTX_new();
@@ -965,7 +965,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
r = 0;
finish:
- session_handle = tpm2_flush_context_verbose(c->esys_context, session_handle);
+ session_handle = tpm2_flush_context_verbose(c, session_handle);
return r;
}
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index bbe58fa209..6c37d34fd6 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -163,7 +163,7 @@ static int determine_banks(Tpm2Context *c, unsigned target_pcr_nr) {
if (!strv_isempty(arg_banks)) /* Explicitly configured? Then use that */
return 0;
- r = tpm2_get_good_pcr_banks_strv(c->esys_context, UINT32_C(1) << target_pcr_nr, &l);
+ r = tpm2_get_good_pcr_banks_strv(c, UINT32_C(1) << target_pcr_nr, &l);
if (r < 0)
return r;
@@ -361,7 +361,7 @@ static int run(int argc, char *argv[]) {
log_debug("Measuring '%s' into PCR index %u, banks %s.", word, target_pcr_nr, joined);
- r = tpm2_extend_bytes(c->esys_context, arg_banks, target_pcr_nr, word, length, NULL, 0);
+ r = tpm2_extend_bytes(c, arg_banks, target_pcr_nr, word, length, NULL, 0);
if (r < 0)
return r;
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 08744bda0c..a78272bc11 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -848,7 +848,7 @@ static int measure_volume_key(
_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);
+ r = tpm2_get_good_pcr_banks_strv(c, UINT32_C(1) << arg_tpm2_measure_pcr, &l);
if (r < 0)
return r;
}
@@ -871,7 +871,7 @@ static int measure_volume_key(
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);
+ r = tpm2_extend_bytes(c, l ?: arg_tpm2_measure_banks, arg_tpm2_measure_pcr, s, SIZE_MAX, volume_key, volume_key_size);
if (r < 0)
return r;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 51bb1c082d..2111d0c638 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -118,13 +118,13 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
-ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle) {
+ESYS_TR tpm2_flush_context_verbose(Tpm2Context *c, ESYS_TR handle) {
TSS2_RC rc;
- if (!c || handle == ESYS_TR_NONE)
+ if (!c || !c->esys_context || handle == ESYS_TR_NONE)
return ESYS_TR_NONE;
- rc = sym_Esys_FlushContext(c, handle);
+ rc = sym_Esys_FlushContext(c->esys_context, handle);
if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
* in error paths, where we cannot do anything about failures anymore. And
* when it is called in successful codepaths by this time we already did
@@ -247,7 +247,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
#define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
-static int tpm2_credit_random(ESYS_CONTEXT *c) {
+static int tpm2_credit_random(Tpm2Context *c) {
size_t rps, done = 0;
TSS2_RC rc;
usec_t t;
@@ -274,7 +274,7 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
_cleanup_(Esys_Freep) TPM2B_DIGEST *buffer = NULL;
rc = sym_Esys_GetRandom(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -306,7 +306,7 @@ static int tpm2_credit_random(ESYS_CONTEXT *c) {
}
static int tpm2_make_primary(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
ESYS_TR *ret_primary,
TPMI_ALG_PUBLIC alg,
TPMI_ALG_PUBLIC *ret_alg) {
@@ -363,7 +363,7 @@ static int tpm2_make_primary(
if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
rc = sym_Esys_CreatePrimary(
- c,
+ c->esys_context,
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
@@ -392,7 +392,7 @@ static int tpm2_make_primary(
if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
rc = sym_Esys_CreatePrimary(
- c,
+ c->esys_context,
ESYS_TR_RH_OWNER,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
@@ -468,7 +468,7 @@ static unsigned find_nth_bit(uint32_t mask, unsigned n) {
}
static int tpm2_pcr_mask_good(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
TPMI_ALG_HASH bank,
uint32_t mask) {
@@ -486,7 +486,7 @@ static int tpm2_pcr_mask_good(
tpm2_pcr_mask_to_selection(mask, bank, &selection);
rc = sym_Esys_PCR_Read(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -550,7 +550,7 @@ static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) {
}
static int tpm2_get_best_pcr_bank(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
uint32_t pcr_mask,
TPMI_ALG_HASH *ret) {
@@ -563,7 +563,7 @@ static int tpm2_get_best_pcr_bank(
assert(c);
rc = sym_Esys_GetCapability(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -647,7 +647,7 @@ static int tpm2_get_best_pcr_bank(
}
int tpm2_get_good_pcr_banks(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
uint32_t pcr_mask,
TPMI_ALG_HASH **ret) {
@@ -662,7 +662,7 @@ int tpm2_get_good_pcr_banks(
assert(ret);
rc = sym_Esys_GetCapability(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -726,7 +726,7 @@ int tpm2_get_good_pcr_banks(
}
int tpm2_get_good_pcr_banks_strv(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
uint32_t pcr_mask,
char ***ret) {
@@ -784,7 +784,7 @@ static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
}
static int tpm2_make_encryption_session(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
ESYS_TR primary,
ESYS_TR bind_key,
const char *pin,
@@ -816,7 +816,7 @@ static int tpm2_make_encryption_session(
hash_pin(pin, strlen(pin), &auth);
- rc = sym_Esys_TR_SetAuth(c, bind_key, &auth);
+ rc = sym_Esys_TR_SetAuth(c->esys_context, bind_key, &auth);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
@@ -830,7 +830,7 @@ static int tpm2_make_encryption_session(
* means that the random salt will be encrypted with the well-known key. That way, only the TPM can
* recover the salt, which is then used for key derivation. */
rc = sym_Esys_StartAuthSession(
- c,
+ c->esys_context,
primary,
bind_key,
ESYS_TR_NONE,
@@ -848,7 +848,7 @@ static int tpm2_make_encryption_session(
/* Enable parameter encryption/decryption with AES in CFB mode. Together with HMAC digests (which are
* always used for sessions), this provides confidentiality, integrity and replay protection for
* operations that use this session. */
- rc = sym_Esys_TRSess_SetAttributes(c, session, sessionAttributes, 0xff);
+ rc = sym_Esys_TRSess_SetAttributes(c->esys_context, session, sessionAttributes, 0xff);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(
SYNTHETIC_ERRNO(ENOTRECOVERABLE),
@@ -1047,7 +1047,7 @@ static int find_signature(
#endif
static int tpm2_make_policy_session(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
ESYS_TR primary,
ESYS_TR parent_session,
TPM2_SE session_type,
@@ -1124,7 +1124,7 @@ static int tpm2_make_policy_session(
#endif
rc = sym_Esys_StartAuthSession(
- c,
+ c->esys_context,
primary,
ESYS_TR_NONE,
parent_session,
@@ -1150,7 +1150,7 @@ static int tpm2_make_policy_session(
goto finish;
rc = sym_Esys_LoadExternal(
- c,
+ c->esys_context,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1173,7 +1173,7 @@ static int tpm2_make_policy_session(
/* Acquire the "name" of what we just loaded */
_cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
rc = sym_Esys_TR_GetName(
- c,
+ c->esys_context,
pubkey_handle,
&pubkey_name);
if (rc != TSS2_RC_SUCCESS) {
@@ -1186,7 +1186,7 @@ static int tpm2_make_policy_session(
TPML_PCR_SELECTION pcr_selection;
tpm2_pcr_mask_to_selection(pubkey_pcr_mask, pcr_bank, &pcr_selection);
rc = sym_Esys_PolicyPCR(
- c,
+ c->esys_context,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1202,7 +1202,7 @@ static int tpm2_make_policy_session(
/* Get the policy hash of the PCR policy */
_cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
rc = sym_Esys_PolicyGetDigest(
- c,
+ c->esys_context,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1255,7 +1255,7 @@ static int tpm2_make_policy_session(
memcpy(policy_signature.signature.rsassa.sig.buffer, signature_raw, signature_size);
rc = sym_Esys_VerifySignature(
- c,
+ c->esys_context,
pubkey_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1281,7 +1281,7 @@ static int tpm2_make_policy_session(
}
rc = sym_Esys_PolicyAuthorize(
- c,
+ c->esys_context,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1306,7 +1306,7 @@ static int tpm2_make_policy_session(
TPML_PCR_SELECTION pcr_selection;
tpm2_pcr_mask_to_selection(hash_pcr_mask, pcr_bank, &pcr_selection);
rc = sym_Esys_PolicyPCR(
- c,
+ c->esys_context,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1324,7 +1324,7 @@ static int tpm2_make_policy_session(
log_debug("Configuring PIN policy.");
rc = sym_Esys_PolicyAuthValue(
- c,
+ c->esys_context,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1341,7 +1341,7 @@ static int tpm2_make_policy_session(
log_debug("Acquiring policy digest.");
rc = sym_Esys_PolicyGetDigest(
- c,
+ c->esys_context,
session,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1455,17 +1455,17 @@ int tpm2_seal(const char *device,
if (r < 0)
return r;
- r = tpm2_make_primary(c->esys_context, &primary, 0, &primary_alg);
+ r = tpm2_make_primary(c, &primary, 0, &primary_alg);
if (r < 0)
return r;
/* we cannot use the bind key before its created */
- r = tpm2_make_encryption_session(c->esys_context, primary, ESYS_TR_NONE, NULL, &session);
+ r = tpm2_make_encryption_session(c, primary, ESYS_TR_NONE, NULL, &session);
if (r < 0)
goto finish;
r = tpm2_make_policy_session(
- c->esys_context,
+ c,
primary,
session,
TPM2_SE_TRIAL,
@@ -1505,7 +1505,7 @@ int tpm2_seal(const char *device,
assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
- (void) tpm2_credit_random(c->esys_context);
+ (void) tpm2_credit_random(c);
log_debug("Generating secret key data.");
@@ -1599,8 +1599,8 @@ int tpm2_seal(const char *device,
r = 0;
finish:
- primary = tpm2_flush_context_verbose(c->esys_context, primary);
- session = tpm2_flush_context_verbose(c->esys_context, session);
+ primary = tpm2_flush_context_verbose(c, primary);
+ session = tpm2_flush_context_verbose(c, session);
return r;
}
@@ -1677,7 +1677,7 @@ int tpm2_unseal(const char *device,
if (r < 0)
return r;
- r = tpm2_make_primary(c->esys_context, &primary, primary_alg, NULL);
+ r = tpm2_make_primary(c, &primary, primary_alg, NULL);
if (r < 0)
return r;
@@ -1713,13 +1713,13 @@ int tpm2_unseal(const char *device,
goto finish;
}
- r = tpm2_make_encryption_session(c->esys_context, primary, hmac_key, pin, &hmac_session);
+ r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &hmac_session);
if (r < 0)
goto finish;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
r = tpm2_make_policy_session(
- c->esys_context,
+ c,
primary,
hmac_session,
TPM2_SE_POLICY,
@@ -1754,7 +1754,7 @@ int tpm2_unseal(const char *device,
&unsealed);
if (rc == TPM2_RC_PCR_CHANGED && i > 0) {
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
- session = tpm2_flush_context_verbose(c->esys_context, session);
+ session = tpm2_flush_context_verbose(c, session);
continue;
}
if (rc != TSS2_RC_SUCCESS) {
@@ -1782,9 +1782,9 @@ int tpm2_unseal(const char *device,
r = 0;
finish:
- primary = tpm2_flush_context_verbose(c->esys_context, primary);
- session = tpm2_flush_context_verbose(c->esys_context, session);
- hmac_key = tpm2_flush_context_verbose(c->esys_context, hmac_key);
+ primary = tpm2_flush_context_verbose(c, primary);
+ session = tpm2_flush_context_verbose(c, session);
+ hmac_key = tpm2_flush_context_verbose(c, hmac_key);
return r;
}
@@ -1916,7 +1916,7 @@ int tpm2_find_device_auto(
#if HAVE_TPM2
int tpm2_extend_bytes(
- ESYS_CONTEXT *c,
+ Tpm2Context *c,
char **banks,
unsigned pcr_index,
const void *data,
@@ -1977,7 +1977,7 @@ int tpm2_extend_bytes(
}
rc = sym_Esys_PCR_Extend(
- c,
+ c->esys_context,
ESYS_TR_PCR0 + pcr_index,
ESYS_TR_PASSWORD,
ESYS_TR_NONE,
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 65c875899e..0266f8128f 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -66,7 +66,7 @@ Tpm2Context *tpm2_context_unref(Tpm2Context *context);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
#define _cleanup_tpm2_context_ _cleanup_(tpm2_context_unrefp)
-ESYS_TR tpm2_flush_context_verbose(ESYS_CONTEXT *c, ESYS_TR handle);
+ESYS_TR tpm2_flush_context_verbose(Tpm2Context *c, ESYS_TR handle);
void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret);
@@ -75,10 +75,10 @@ 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);
-int tpm2_get_good_pcr_banks_strv(ESYS_CONTEXT *c, uint32_t pcr_mask, char ***ret);
+int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
+int tpm2_get_good_pcr_banks_strv(Tpm2Context *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);
+int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
#else /* HAVE_TPM2 */
typedef struct {} Tpm2Context;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
From 039b8bc3887c7b052aa24453ac1212a6072459d2 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 19 Dec 2022 09:58:05 -0500
Subject: [PATCH] tpm2: simplify tpm2_seal() blob creation
TPM2 marshalling will never increase the total size, only possibly decrease.
There is no need for checking for insufficient size if the buffer size
is set to the sizeof both objects to be marshalled.
(cherry picked from commit e8858f1104d87179abd8d9c413292e42f1eaf7c0)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 40 ++++++++++++++--------------------------
1 file changed, 14 insertions(+), 26 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index a01c6537b5..6620f365d9 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1418,12 +1418,11 @@ int tpm2_seal(const char *device,
_cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
static const TPML_PCR_SELECTION creation_pcr = {};
_cleanup_(erase_and_freep) void *secret = NULL;
- _cleanup_free_ void *blob = NULL, *hash = NULL;
+ _cleanup_free_ void *hash = NULL;
TPM2B_SENSITIVE_CREATE hmac_sensitive;
TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template;
TPMI_ALG_HASH pcr_bank;
- size_t k, blob_size;
usec_t start;
TSS2_RC rc;
int r;
@@ -1553,33 +1552,22 @@ int tpm2_seal(const char *device,
log_debug("Marshalling private and public part of HMAC key.");
- k = ALIGN8(sizeof(*private)) + ALIGN8(sizeof(*public)); /* Some roughly sensible start value */
- for (;;) {
- _cleanup_free_ void *buf = NULL;
- size_t offset = 0;
-
- buf = malloc(k);
- if (!buf)
- return log_oom();
+ _cleanup_free_ void *blob = NULL;
+ size_t max_size = sizeof(*private) + sizeof(*public), blob_size = 0;
- rc = sym_Tss2_MU_TPM2B_PRIVATE_Marshal(private, buf, k, &offset);
- if (rc == TSS2_RC_SUCCESS) {
- rc = sym_Tss2_MU_TPM2B_PUBLIC_Marshal(public, buf, k, &offset);
- if (rc == TSS2_RC_SUCCESS) {
- blob = TAKE_PTR(buf);
- blob_size = offset;
- break;
- }
- }
- if (rc != TSS2_MU_RC_INSUFFICIENT_BUFFER)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to marshal private/public key: %s", sym_Tss2_RC_Decode(rc));
+ blob = malloc0(max_size);
+ if (!blob)
+ return log_oom();
- if (k > SIZE_MAX / 2)
- return log_oom();
+ rc = sym_Tss2_MU_TPM2B_PRIVATE_Marshal(private, blob, max_size, &blob_size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal private key: %s", sym_Tss2_RC_Decode(rc));
- k *= 2;
- }
+ rc = sym_Tss2_MU_TPM2B_PUBLIC_Marshal(public, blob, max_size, &blob_size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc));
hash = memdup(policy_digest->buffer, policy_digest->size);
if (!hash)

View File

@ -0,0 +1,620 @@
From ec44604785b325481cb7d25d29dc991d0e4109e2 Mon Sep 17 00:00:00 2001
From: William Roberts <william.c.roberts@intel.com>
Date: Wed, 18 Jan 2023 08:45:53 -0600
Subject: [PATCH] tpm2: add salt to pin
Add a salt to the pin and store it in the TPM2 LUKS header for future
this. This adds entropy to user supplied pins and helps brute forcing
the passphrase on the key residing in the TPM or brute forcing bind key
encrypted sessions with low entropy passphrases.
Signed-off-by: malikabhi05 <abhishek.malik@intel.com>
Signed-off-by: William Roberts <william.c.roberts@intel.com>
(cherry picked from commit aae6eb96117acd54ce5ac572aac6a11b34c4ad99)
Related: RHEL-16182
---
src/cryptenroll/cryptenroll-tpm2.c | 28 +++++++
.../cryptsetup-token-systemd-tpm2.c | 15 +++-
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c | 21 +++++
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h | 2 +
src/cryptsetup/cryptsetup-tpm2.c | 31 ++++++-
src/cryptsetup/cryptsetup-tpm2.h | 8 ++
src/cryptsetup/cryptsetup.c | 7 +-
src/partition/repart.c | 1 +
src/shared/tpm2-util.c | 80 ++++++++++++++++++-
src/shared/tpm2-util.h | 11 ++-
src/test/test-tpm2.c | 41 ++++++++++
11 files changed, 230 insertions(+), 15 deletions(-)
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index 96d5fc0695..3098b2e7ac 100644
--- a/src/cryptenroll/cryptenroll-tpm2.c
+++ b/src/cryptenroll/cryptenroll-tpm2.c
@@ -8,6 +8,8 @@
#include "hexdecoct.h"
#include "json.h"
#include "memory-util.h"
+#include "random-util.h"
+#include "sha256.h"
#include "tpm2-util.h"
static int search_policy_hash(
@@ -148,6 +150,14 @@ int enroll_tpm2(struct crypt_device *cd,
ssize_t base64_encoded_size;
int r, keyslot;
TPM2Flags flags = 0;
+ uint8_t binary_salt[SHA256_DIGEST_SIZE] = {};
+ /*
+ * erase the salt, we'd rather attempt to not have this in a coredump
+ * as an attacker would have all the parameters but pin used to create
+ * the session key. This problem goes away when we move to a trusted
+ * primary key, aka the SRK.
+ */
+ CLEANUP_ERASE(binary_salt);
assert(cd);
assert(volume_key);
@@ -161,6 +171,22 @@ int enroll_tpm2(struct crypt_device *cd,
r = get_pin(&pin_str, &flags);
if (r < 0)
return r;
+
+ r = crypto_random_bytes(binary_salt, sizeof(binary_salt));
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire random salt: %m");
+
+ uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
+ CLEANUP_ERASE(salted_pin);
+ r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), binary_salt, sizeof(binary_salt), salted_pin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to perform PBKDF2: %m");
+
+ pin_str = erase_and_free(pin_str);
+ /* re-stringify pin_str */
+ base64_encoded_size = base64mem(salted_pin, sizeof(salted_pin), &pin_str);
+ if (base64_encoded_size < 0)
+ return log_error_errno(base64_encoded_size, "Failed to base64 encode salted pin: %m");
}
r = tpm2_load_pcr_public_key(pubkey_path, &pubkey, &pubkey_size);
@@ -258,6 +284,8 @@ int enroll_tpm2(struct crypt_device *cd,
primary_alg,
blob, blob_size,
hash, hash_size,
+ use_pin ? binary_salt : NULL,
+ use_pin ? sizeof(binary_salt) : 0,
flags,
&v);
if (r < 0)
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index 98bcaac4d8..319b0ca64d 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -42,8 +42,8 @@ _public_ int cryptsetup_token_open_pin(
void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) {
_cleanup_(erase_and_freep) char *base64_encoded = NULL, *pin_string = NULL;
- _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL;
- size_t blob_size, policy_hash_size, decrypted_key_size, pubkey_size;
+ _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL;
+ size_t blob_size, policy_hash_size, decrypted_key_size, pubkey_size, salt_size = 0;
_cleanup_(erase_and_freep) void *decrypted_key = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
@@ -90,6 +90,8 @@ _public_ int cryptsetup_token_open_pin(
&blob_size,
&policy_hash,
&policy_hash_size,
+ &salt,
+ &salt_size,
&flags);
if (r < 0)
return log_debug_open_error(cd, r);
@@ -110,6 +112,8 @@ _public_ int cryptsetup_token_open_pin(
blob_size,
policy_hash,
policy_hash_size,
+ salt,
+ salt_size,
flags,
&decrypted_key,
&decrypted_key_size);
@@ -168,9 +172,9 @@ _public_ void cryptsetup_token_dump(
const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) {
_cleanup_free_ char *hash_pcrs_str = NULL, *pubkey_pcrs_str = NULL, *blob_str = NULL, *policy_hash_str = NULL, *pubkey_str = NULL;
- _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL;
+ _cleanup_free_ void *blob = NULL, *pubkey = NULL, *policy_hash = NULL, *salt = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- size_t blob_size, policy_hash_size, pubkey_size;
+ size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
uint16_t pcr_bank, primary_alg;
TPM2Flags flags = 0;
@@ -195,6 +199,8 @@ _public_ void cryptsetup_token_dump(
&blob_size,
&policy_hash,
&policy_hash_size,
+ &salt,
+ &salt_size,
&flags);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
@@ -227,6 +233,7 @@ _public_ void cryptsetup_token_dump(
crypt_log(cd, "\ttpm2-blob: %s\n", blob_str);
crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str);
crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN));
+ crypt_log(cd, "\ttpm2-salt: %s\n", true_false(salt));
}
/*
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
index be496d4949..80a2c0d316 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
@@ -9,6 +9,7 @@
#include "luks2-tpm2.h"
#include "parse-util.h"
#include "random-util.h"
+#include "sha256.h"
#include "strv.h"
#include "tpm2-util.h"
@@ -26,12 +27,15 @@ int acquire_luks2_key(
size_t key_data_size,
const void *policy_hash,
size_t policy_hash_size,
+ const void *salt,
+ size_t salt_size,
TPM2Flags flags,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size) {
_cleanup_(json_variant_unrefp) JsonVariant *signature_json = NULL;
_cleanup_free_ char *auto_device = NULL;
+ _cleanup_(erase_and_freep) char *b64_salted_pin = NULL;
int r;
assert(ret_decrypted_key);
@@ -50,6 +54,23 @@ int acquire_luks2_key(
if ((flags & TPM2_FLAGS_USE_PIN) && !pin)
return -ENOANO;
+ /* If we're using a PIN, and the luks header has a salt, it better have a pin too */
+ if ((flags & TPM2_FLAGS_USE_PIN) && salt && !pin)
+ return -ENOANO;
+
+ if (pin) {
+ uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
+ CLEANUP_ERASE(salted_pin);
+ r = tpm2_util_pbkdf2_hmac_sha256(pin, strlen(pin), salt, salt_size, salted_pin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to perform PBKDF2: %m");
+
+ r = base64mem(salted_pin, sizeof(salted_pin), &b64_salted_pin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to base64 encode salted pin: %m");
+ pin = b64_salted_pin;
+ }
+
if (pubkey_pcr_mask != 0) {
r = tpm2_load_pcr_signature(signature_path, &signature_json);
if (r < 0)
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
index f3625124e5..36d514caa0 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h
@@ -20,6 +20,8 @@ int acquire_luks2_key(
size_t key_data_size,
const void *policy_hash,
size_t policy_hash_size,
+ const void *salt,
+ size_t salt_size,
TPM2Flags flags,
void **ret_decrypted_key,
size_t *ret_decrypted_key_size);
diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c
index 838c02bfc9..2a8a38c593 100644
--- a/src/cryptsetup/cryptsetup-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tpm2.c
@@ -9,6 +9,7 @@
#include "json.h"
#include "parse-util.h"
#include "random-util.h"
+#include "sha256.h"
#include "tpm2-util.h"
static int get_pin(usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_pin_str) {
@@ -69,6 +70,8 @@ int acquire_tpm2_key(
size_t key_data_size,
const void *policy_hash,
size_t policy_hash_size,
+ const void *salt,
+ size_t salt_size,
TPM2Flags flags,
usec_t until,
bool headless,
@@ -140,7 +143,7 @@ int acquire_tpm2_key(
ret_decrypted_key_size);
for (int i = 5;; i--) {
- _cleanup_(erase_and_freep) char *pin_str = NULL;
+ _cleanup_(erase_and_freep) char *pin_str = NULL, *b64_salted_pin = NULL;
if (i <= 0)
return -EACCES;
@@ -149,13 +152,28 @@ int acquire_tpm2_key(
if (r < 0)
return r;
+ if (salt) {
+ uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
+ CLEANUP_ERASE(salted_pin);
+
+ r = tpm2_util_pbkdf2_hmac_sha256(pin_str, strlen(pin_str), salt, salt_size, salted_pin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to perform PBKDF2: %m");
+
+ r = base64mem(salted_pin, sizeof(salted_pin), &b64_salted_pin);
+ if (r < 0)
+ return log_error_errno(r, "Failed to base64 encode salted pin: %m");
+ } else
+ /* no salting needed, backwards compat with non-salted pins */
+ b64_salted_pin = TAKE_PTR(pin_str);
+
r = tpm2_unseal(device,
hash_pcr_mask,
pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
signature_json,
- pin_str,
+ b64_salted_pin,
primary_alg,
blob,
blob_size,
@@ -188,6 +206,8 @@ int find_tpm2_auto_data(
size_t *ret_blob_size,
void **ret_policy_hash,
size_t *ret_policy_hash_size,
+ void **ret_salt,
+ size_t *ret_salt_size,
TPM2Flags *ret_flags,
int *ret_keyslot,
int *ret_token) {
@@ -197,9 +217,9 @@ int find_tpm2_auto_data(
assert(cd);
for (token = start_token; token < sym_crypt_token_max(CRYPT_LUKS2); token++) {
- _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL;
+ _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
- size_t blob_size, policy_hash_size, pubkey_size;
+ size_t blob_size, policy_hash_size, pubkey_size, salt_size = 0;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
uint16_t pcr_bank, primary_alg;
TPM2Flags flags;
@@ -221,6 +241,7 @@ int find_tpm2_auto_data(
&primary_alg,
&blob, &blob_size,
&policy_hash, &policy_hash_size,
+ &salt, &salt_size,
&flags);
if (r == -EUCLEAN) /* Gracefully handle issues in JSON fields not owned by us */
continue;
@@ -243,6 +264,8 @@ int find_tpm2_auto_data(
*ret_blob_size = blob_size;
*ret_policy_hash = TAKE_PTR(policy_hash);
*ret_policy_hash_size = policy_hash_size;
+ *ret_salt = TAKE_PTR(salt);
+ *ret_salt_size = salt_size;
*ret_keyslot = keyslot;
*ret_token = token;
*ret_flags = flags;
diff --git a/src/cryptsetup/cryptsetup-tpm2.h b/src/cryptsetup/cryptsetup-tpm2.h
index a34eb8443d..f6549b7d1d 100644
--- a/src/cryptsetup/cryptsetup-tpm2.h
+++ b/src/cryptsetup/cryptsetup-tpm2.h
@@ -28,6 +28,8 @@ int acquire_tpm2_key(
size_t key_data_size,
const void *policy_hash,
size_t policy_hash_size,
+ const void *salt,
+ size_t salt_size,
TPM2Flags flags,
usec_t until,
bool headless,
@@ -49,6 +51,8 @@ int find_tpm2_auto_data(
size_t *ret_blob_size,
void **ret_policy_hash,
size_t *ret_policy_hash_size,
+ void **ret_salt,
+ size_t *ret_salt_size,
TPM2Flags *ret_flags,
int *ret_keyslot,
int *ret_token);
@@ -72,6 +76,8 @@ static inline int acquire_tpm2_key(
size_t key_data_size,
const void *policy_hash,
size_t policy_hash_size,
+ const void *salt,
+ size_t salt_size,
TPM2Flags flags,
usec_t until,
bool headless,
@@ -97,6 +103,8 @@ static inline int find_tpm2_auto_data(
size_t *ret_blob_size,
void **ret_policy_hash,
size_t *ret_policy_hash_size,
+ void **ret_salt,
+ size_t *ret_salt_size,
TPM2Flags *ret_flags,
int *ret_keyslot,
int *ret_token) {
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index a78272bc11..d5ce252e57 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -1674,6 +1674,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
key_file, arg_keyfile_size, arg_keyfile_offset,
key_data, key_data_size,
/* policy_hash= */ NULL, /* policy_hash_size= */ 0, /* we don't know the policy hash */
+ /* salt= */ NULL, /* salt_size= */ 0,
arg_tpm2_pin ? TPM2_FLAGS_USE_PIN : 0,
until,
arg_headless,
@@ -1719,8 +1720,8 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
* works. */
for (;;) {
- _cleanup_free_ void *pubkey = NULL;
- size_t pubkey_size = 0;
+ _cleanup_free_ void *pubkey = NULL, *salt = NULL;
+ size_t pubkey_size = 0, salt_size = 0;
uint32_t hash_pcr_mask, pubkey_pcr_mask;
uint16_t pcr_bank, primary_alg;
TPM2Flags tpm2_flags;
@@ -1736,6 +1737,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
&primary_alg,
&blob, &blob_size,
&policy_hash, &policy_hash_size,
+ &salt, &salt_size,
&tpm2_flags,
&keyslot,
&token);
@@ -1765,6 +1767,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
/* key_file= */ NULL, /* key_file_size= */ 0, /* key_file_offset= */ 0, /* no key file */
blob, blob_size,
policy_hash, policy_hash_size,
+ salt, salt_size,
tpm2_flags,
until,
arg_headless,
diff --git a/src/partition/repart.c b/src/partition/repart.c
index cbd900969e..0075932c09 100644
--- a/src/partition/repart.c
+++ b/src/partition/repart.c
@@ -3077,6 +3077,7 @@ static int partition_encrypt(
primary_alg,
blob, blob_size,
hash, hash_size,
+ NULL, 0, /* no salt because tpm2_seal has no pin */
0,
&v);
if (r < 0)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 6620f365d9..c22a200a5c 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -12,6 +12,7 @@
#include "format-table.h"
#include "fs-util.h"
#include "hexdecoct.h"
+#include "hmac.h"
#include "memory-util.h"
#include "openssl-util.h"
#include "parse-util.h"
@@ -2080,6 +2081,8 @@ int tpm2_make_luks2_json(
size_t blob_size,
const void *policy_hash,
size_t policy_hash_size,
+ const void *salt,
+ size_t salt_size,
TPM2Flags flags,
JsonVariant **ret) {
@@ -2119,7 +2122,8 @@ int tpm2_make_luks2_json(
JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)),
JSON_BUILD_PAIR("tpm2-pin", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PIN)),
JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey_pcrs", JSON_BUILD_VARIANT(pkmj)),
- JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey", JSON_BUILD_BASE64(pubkey, pubkey_size))));
+ JSON_BUILD_PAIR_CONDITION(pubkey_pcr_mask != 0, "tpm2_pubkey", JSON_BUILD_BASE64(pubkey, pubkey_size)),
+ JSON_BUILD_PAIR_CONDITION(salt, "tpm2_salt", JSON_BUILD_BASE64(salt, salt_size))));
if (r < 0)
return r;
@@ -2142,10 +2146,12 @@ int tpm2_parse_luks2_json(
size_t *ret_blob_size,
void **ret_policy_hash,
size_t *ret_policy_hash_size,
+ void **ret_salt,
+ size_t *ret_salt_size,
TPM2Flags *ret_flags) {
- _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL;
- size_t blob_size = 0, policy_hash_size = 0, pubkey_size = 0;
+ _cleanup_free_ void *blob = NULL, *policy_hash = NULL, *pubkey = NULL, *salt = NULL;
+ size_t blob_size = 0, policy_hash_size = 0, pubkey_size = 0, salt_size = 0;
uint32_t hash_pcr_mask = 0, pubkey_pcr_mask = 0;
uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */
uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */
@@ -2230,6 +2236,13 @@ int tpm2_parse_luks2_json(
SET_FLAG(flags, TPM2_FLAGS_USE_PIN, json_variant_boolean(w));
}
+ w = json_variant_by_key(v, "tpm2_salt");
+ if (w) {
+ r = json_variant_unbase64(w, &salt, &salt_size);
+ if (r < 0)
+ return log_debug_errno(r, "Invalid base64 data in 'tpm2_salt' field.");
+ }
+
w = json_variant_by_key(v, "tpm2_pubkey_pcrs");
if (w) {
r = tpm2_parse_pcr_json_array(w, &pubkey_pcr_mask);
@@ -2267,6 +2280,10 @@ int tpm2_parse_luks2_json(
*ret_policy_hash = TAKE_PTR(policy_hash);
if (ret_policy_hash_size)
*ret_policy_hash_size = policy_hash_size;
+ if (ret_salt)
+ *ret_salt = TAKE_PTR(salt);
+ if (ret_salt_size)
+ *ret_salt_size = salt_size;
if (ret_flags)
*ret_flags = flags;
@@ -2431,3 +2448,60 @@ int pcr_mask_to_string(uint32_t mask, char **ret) {
*ret = TAKE_PTR(buf);
return 0;
}
+
+#define PBKDF2_HMAC_SHA256_ITERATIONS 10000
+
+/*
+ * Implements PBKDF2 HMAC SHA256 for a derived keylen of 32
+ * bytes and for PBKDF2_HMAC_SHA256_ITERATIONS count.
+ * I found the wikipedia entry relevant and it contains links to
+ * relevant RFCs:
+ * - https://en.wikipedia.org/wiki/PBKDF2
+ * - https://www.rfc-editor.org/rfc/rfc2898#section-5.2
+ */
+int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
+ size_t passlen,
+ const void *salt,
+ size_t saltlen,
+ uint8_t ret_key[static SHA256_DIGEST_SIZE]) {
+
+ uint8_t _cleanup_(erase_and_freep) *buffer = NULL;
+ uint8_t u[SHA256_DIGEST_SIZE];
+
+ /* To keep this simple, since derived KeyLen (dkLen in docs)
+ * Is the same as the hash output, we don't need multiple
+ * blocks. Part of the algorithm is to add the block count
+ * in, but this can be hardcoded to 1.
+ */
+ static const uint8_t block_cnt[] = { 0, 0, 0, 1 };
+
+ assert (saltlen > 0);
+ assert (saltlen <= (SIZE_MAX - sizeof(block_cnt)));
+ assert (passlen > 0);
+
+ /*
+ * Build a buffer of salt + block_cnt and hmac_sha256 it we
+ * do this as we don't have a context builder for HMAC_SHA256.
+ */
+ buffer = malloc(saltlen + sizeof(block_cnt));
+ if (!buffer)
+ return -ENOMEM;
+
+ memcpy(buffer, salt, saltlen);
+ memcpy(&buffer[saltlen], block_cnt, sizeof(block_cnt));
+
+ hmac_sha256(pass, passlen, buffer, saltlen + sizeof(block_cnt), u);
+
+ /* dk needs to be an unmodified u as u gets modified in the loop */
+ memcpy(ret_key, u, SHA256_DIGEST_SIZE);
+ uint8_t *dk = ret_key;
+
+ for (size_t i = 1; i < PBKDF2_HMAC_SHA256_ITERATIONS; i++) {
+ hmac_sha256(pass, passlen, u, sizeof(u), u);
+
+ for (size_t j=0; j < sizeof(u); j++)
+ dk[j] ^= u[j];
+ }
+
+ return 0;
+}
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 9819a33569..d26a945a90 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -5,6 +5,7 @@
#include "json.h"
#include "macro.h"
+#include "sha256.h"
typedef enum TPM2Flags {
TPM2_FLAGS_USE_PIN = 1 << 0,
@@ -104,8 +105,8 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret);
int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret);
int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret);
-int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, TPM2Flags flags, JsonVariant **ret);
-int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, TPM2Flags *ret_flags);
+int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *salt, size_t salt_size, TPM2Flags flags, JsonVariant **ret);
+int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, void **ret_salt, size_t *ret_salt_size, TPM2Flags *ret_flags);
#define TPM2_PCRS_MAX 24U
@@ -173,3 +174,9 @@ int tpm2_load_pcr_signature(const char *path, JsonVariant **ret);
int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pubkey_size);
int pcr_mask_to_string(uint32_t mask, char **ret);
+
+int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
+ size_t passlen,
+ const void *salt,
+ size_t saltlen,
+ uint8_t res[static SHA256_DIGEST_SIZE]);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index c5f3d41da9..04e08490b3 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -28,4 +28,45 @@ TEST(tpm2_parse_pcrs) {
test_tpm2_parse_pcrs_one("foo", 0, -EINVAL);
}
+TEST(tpm2_util_pbkdf2_hmac_sha256) {
+
+ /*
+ * The test vectors from RFC 6070 [1] are for dkLen of 20 as it's SHA1
+ * other RFCs I bumped into had various differing dkLen and iter counts,
+ * so this was generated using Python's hmacmodule.
+ *
+ * 1. https://www.rfc-editor.org/rfc/rfc6070.html#page-2
+ */
+ static const struct {
+ const uint8_t pass[256];
+ size_t passlen;
+ const uint8_t salt[256];
+ size_t saltlen;
+ uint8_t expected[SHA256_DIGEST_SIZE];
+ } test_vectors[] = {
+ { .pass={'f', 'o', 'o', 'p', 'a', 's', 's'}, .passlen=7, .salt={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'}, .saltlen=16, .expected={0xCB, 0xEA, 0x27, 0x23, 0x9A, 0x65, 0x99, 0xF6, 0x8C, 0x26, 0x54, 0x80, 0x5C, 0x63, 0x61, 0xD2, 0x91, 0x0A, 0x60, 0x3F, 0xC2, 0xF5, 0xF0, 0xAB, 0x55, 0x8B, 0x46, 0x07, 0x60, 0x93, 0xAB, 0xCB} },
+ { .pass={'f', 'o', 'o', 'p', 'a', 's', 's'}, .passlen=7, .salt={0x00, 'h', 'f', 's', 'd', 'j', 'h', 'f', 'd', 'j', 'h', 'j', 'd', 'f', 's'}, .saltlen=15, .expected={0x2B, 0xDF, 0x52, 0x29, 0x48, 0x3F, 0x98, 0x25, 0x01, 0x19, 0xB4, 0x42, 0xBC, 0xA7, 0x38, 0x5D, 0xCD, 0x08, 0xBD, 0xDC, 0x33, 0xBF, 0x32, 0x5E, 0x31, 0x87, 0x54, 0xFF, 0x2C, 0x23, 0x68, 0xFF} },
+ { .pass={'f', 'o', 'o', 'p', 'a', 's', 's'}, .passlen=7, .salt={'m', 'y', 's', 'a', 0x00, 'l', 't'}, .saltlen=7, .expected={0x7C, 0x24, 0xB4, 0x4D, 0x30, 0x11, 0x53, 0x24, 0x87, 0x56, 0x24, 0x10, 0xBA, 0x9F, 0xF2, 0x4E, 0xBB, 0xF5, 0x03, 0x56, 0x2B, 0xB1, 0xA1, 0x92, 0x8B, 0x5F, 0x32, 0x02, 0x23, 0x1F, 0x79, 0xE6} },
+ { .pass={'p', 'a', 's', 's', 'w', 'i', 't', 'h', 'n', 'u', 'l', 'l', 0x00, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}, .passlen=21, .salt={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'}, .saltlen=16, .expected={0xE9, 0x53, 0xB7, 0x1D, 0xAB, 0xD1, 0xC1, 0xF3, 0xC4, 0x7F, 0x18, 0x96, 0xDD, 0xD7, 0x6B, 0xC6, 0x6A, 0xBD, 0xFB, 0x12, 0x7C, 0xF8, 0x68, 0xDC, 0x6E, 0xEF, 0x29, 0xCC, 0x1B, 0x30, 0x5B, 0x74} },
+ { .pass={'p', 'a', 's', 's', 'w', 'i', 't', 'h', 'n', 'u', 'l', 'l', 0x00, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}, .passlen=21, .salt={0x00, 'h', 'f', 's', 'd', 'j', 'h', 'f', 'd', 'j', 'h', 'j', 'd', 'f', 's'}, .saltlen=15, .expected={0x51, 0xA3, 0x82, 0xA5, 0x2F, 0x48, 0x84, 0xB3, 0x02, 0x0D, 0xC2, 0x42, 0x9A, 0x8F, 0x86, 0xCC, 0x66, 0xFD, 0x65, 0x87, 0x89, 0x07, 0x2B, 0x07, 0x82, 0x42, 0xD6, 0x6D, 0x43, 0xB8, 0xFD, 0xCF} },
+ { .pass={'p', 'a', 's', 's', 'w', 'i', 't', 'h', 'n', 'u', 'l', 'l', 0x00, 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'}, .passlen=21, .salt={'m', 'y', 's', 'a', 0x00, 'l', 't'}, .saltlen=7, .expected={0xEC, 0xFB, 0x5D, 0x5F, 0xF6, 0xA6, 0xE0, 0x79, 0x50, 0x64, 0x36, 0x64, 0xA3, 0x9A, 0x5C, 0xF3, 0x7A, 0x87, 0x0B, 0x64, 0x51, 0x59, 0x75, 0x64, 0x8B, 0x78, 0x2B, 0x62, 0x8F, 0x68, 0xD9, 0xCC} },
+ { .pass={0x00, 'p', 'a', 's', 's'}, .passlen=5, .salt={'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'}, .saltlen=16, .expected={0x8A, 0x9A, 0x47, 0x9A, 0x91, 0x22, 0x2F, 0x56, 0x29, 0x4F, 0x26, 0x00, 0xE7, 0xB3, 0xEB, 0x63, 0x6D, 0x51, 0xF2, 0x60, 0x17, 0x08, 0x20, 0x70, 0x82, 0x8F, 0xA3, 0xD7, 0xBE, 0x2B, 0xD5, 0x5D} },
+ { .pass={0x00, 'p', 'a', 's', 's'}, .passlen=5, .salt={0x00, 'h', 'f', 's', 'd', 'j', 'h', 'f', 'd', 'j', 'h', 'j', 'd', 'f', 's'}, .saltlen=15, .expected={0x72, 0x3A, 0xF5, 0xF7, 0xCD, 0x6C, 0x12, 0xDD, 0x53, 0x28, 0x46, 0x0C, 0x19, 0x0E, 0xF2, 0x91, 0xDE, 0xEA, 0xF9, 0x6F, 0x74, 0x32, 0x34, 0x3F, 0x84, 0xED, 0x8D, 0x2A, 0xDE, 0xC9, 0xC6, 0x34} },
+ { .pass={0x00, 'p', 'a', 's', 's'}, .passlen=5, .salt={'m', 'y', 's', 'a', 0x00, 'l', 't'}, .saltlen=7, .expected={0xE3, 0x07, 0x12, 0xBE, 0xEE, 0xF5, 0x5D, 0x18, 0x72, 0xF4, 0xCF, 0xF1, 0x20, 0x6B, 0xD6, 0x66, 0xCD, 0x7C, 0xE7, 0x4F, 0xC2, 0x16, 0x70, 0x5B, 0x9B, 0x2F, 0x7D, 0xE2, 0x3B, 0x42, 0x3A, 0x1B} },
+ };
+
+ uint8_t res[SHA256_DIGEST_SIZE];
+ for(size_t i = 0; i < sizeof(test_vectors)/sizeof(test_vectors[0]); i++) {
+
+ int rc = tpm2_util_pbkdf2_hmac_sha256(
+ test_vectors[i].pass,
+ test_vectors[i].passlen,
+ test_vectors[i].salt,
+ test_vectors[i].saltlen,
+ res);
+ assert_se(rc == 0);
+ assert_se(memcmp(test_vectors[i].expected, res, SHA256_DIGEST_SIZE) == 0);
+ }
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -0,0 +1,30 @@
From 01f312e212e8ad095800856a7247a0a2d85cc99d Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 2 Feb 2023 15:58:10 -0500
Subject: [PATCH] basic/macro: add macro to iterate variadic args
(cherry picked from commit e179f2d89c9f0c951636d74de00136b4075cd1ac)
Related: RHEL-16182
---
src/basic/macro.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 72a2c7267e..9c36683ef9 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -458,4 +458,13 @@ assert_cc(sizeof(dummy_t) == 0);
_q && _q > (base) ? &_q[-1] : NULL; \
})
+/* Iterate through each variadic arg. All must be the same type as 'entry' or must be implicitly
+ * convertable. The iteration variable 'entry' must already be defined. */
+#define VA_ARGS_FOREACH(entry, ...) \
+ _VA_ARGS_FOREACH(entry, UNIQ_T(_entries_, UNIQ), UNIQ_T(_current_, UNIQ), ##__VA_ARGS__)
+#define _VA_ARGS_FOREACH(entry, _entries_, _current_, ...) \
+ for (typeof(entry) _entries_[] = { __VA_ARGS__ }, *_current_ = _entries_; \
+ ((long)(_current_ - _entries_) < (long)ELEMENTSOF(_entries_)) && ({ entry = *_current_; true; }); \
+ _current_++)
+
#include "log.h"

View File

@ -0,0 +1,207 @@
From 354d5e213410a9ec5627bae8243f69fc1bba7b3c Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Sun, 26 Feb 2023 08:02:16 -0500
Subject: [PATCH] test/test-macro: add tests for FOREACH_VA_ARGS()
(cherry picked from commit 326ef267004bf8362cf0b0066af8ce56e0e2941e)
Related: RHEL-16182
---
src/test/test-macro.c | 184 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 184 insertions(+)
diff --git a/src/test/test-macro.c b/src/test/test-macro.c
index 6a5f4bbeb7..bb79ea0dbe 100644
--- a/src/test/test-macro.c
+++ b/src/test/test-macro.c
@@ -290,6 +290,190 @@ TEST(foreach_pointer) {
assert_se(k == 11);
}
+TEST(foreach_va_args) {
+ size_t i;
+
+ i = 0;
+ uint8_t u8, u8_1 = 1, u8_2 = 2, u8_3 = 3;
+ VA_ARGS_FOREACH(u8, u8_2, 8, 0xff, u8_1, u8_3, 0, 1) {
+ switch(i++) {
+ case 0: assert_se(u8 == u8_2); break;
+ case 1: assert_se(u8 == 8); break;
+ case 2: assert_se(u8 == 0xff); break;
+ case 3: assert_se(u8 == u8_1); break;
+ case 4: assert_se(u8 == u8_3); break;
+ case 5: assert_se(u8 == 0); break;
+ case 6: assert_se(u8 == 1); break;
+ default: assert_se(false);
+ }
+ }
+ assert_se(i == 7);
+ i = 0;
+ VA_ARGS_FOREACH(u8, 0) {
+ assert_se(u8 == 0);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ i = 0;
+ VA_ARGS_FOREACH(u8, 0xff) {
+ assert_se(u8 == 0xff);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ VA_ARGS_FOREACH(u8)
+ assert_se(false);
+
+ i = 0;
+ uint32_t u32, u32_1 = 0xffff0000, u32_2 = 10, u32_3 = 0xffff;
+ VA_ARGS_FOREACH(u32, 1, 100, u32_2, 1000, u32_3, u32_1, 1, 0) {
+ switch(i++) {
+ case 0: assert_se(u32 == 1); break;
+ case 1: assert_se(u32 == 100); break;
+ case 2: assert_se(u32 == u32_2); break;
+ case 3: assert_se(u32 == 1000); break;
+ case 4: assert_se(u32 == u32_3); break;
+ case 5: assert_se(u32 == u32_1); break;
+ case 6: assert_se(u32 == 1); break;
+ case 7: assert_se(u32 == 0); break;
+ default: assert_se(false);
+ }
+ }
+ assert_se(i == 8);
+ i = 0;
+ VA_ARGS_FOREACH(u32, 0) {
+ assert_se(u32 == 0);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ i = 0;
+ VA_ARGS_FOREACH(u32, 1000) {
+ assert_se(u32 == 1000);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ VA_ARGS_FOREACH(u32)
+ assert_se(false);
+
+ i = 0;
+ uint64_t u64, u64_1 = 0xffffffffffffffff, u64_2 = 50, u64_3 = 0xffff;
+ VA_ARGS_FOREACH(u64, 44, 0, u64_3, 100, u64_2, u64_1, 50000) {
+ switch(i++) {
+ case 0: assert_se(u64 == 44); break;
+ case 1: assert_se(u64 == 0); break;
+ case 2: assert_se(u64 == u64_3); break;
+ case 3: assert_se(u64 == 100); break;
+ case 4: assert_se(u64 == u64_2); break;
+ case 5: assert_se(u64 == u64_1); break;
+ case 6: assert_se(u64 == 50000); break;
+ default: assert_se(false);
+ }
+ }
+ assert_se(i == 7);
+ i = 0;
+ VA_ARGS_FOREACH(u64, 0) {
+ assert_se(u64 == 0);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ i = 0;
+ VA_ARGS_FOREACH(u64, 0xff00ff00000000) {
+ assert_se(u64 == 0xff00ff00000000);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ VA_ARGS_FOREACH(u64)
+ assert_se(false);
+
+ struct test {
+ int a;
+ char b;
+ };
+
+ i = 0;
+ struct test s,
+ s_1 = { .a = 0, .b = 'c', },
+ s_2 = { .a = 100000, .b = 'z', },
+ s_3 = { .a = 0xff, .b = 'q', },
+ s_4 = { .a = 1, .b = 'x', };
+ VA_ARGS_FOREACH(s, s_1, (struct test){ .a = 10, .b = 'd', }, s_2, (struct test){}, s_3, s_4) {
+ switch(i++) {
+ case 0: assert_se(s.a == 0 ); assert_se(s.b == 'c'); break;
+ case 1: assert_se(s.a == 10 ); assert_se(s.b == 'd'); break;
+ case 2: assert_se(s.a == 100000); assert_se(s.b == 'z'); break;
+ case 3: assert_se(s.a == 0 ); assert_se(s.b == 0 ); break;
+ case 4: assert_se(s.a == 0xff ); assert_se(s.b == 'q'); break;
+ case 5: assert_se(s.a == 1 ); assert_se(s.b == 'x'); break;
+ default: assert_se(false);
+ }
+ }
+ assert_se(i == 6);
+ i = 0;
+ VA_ARGS_FOREACH(s, (struct test){ .a = 1, .b = 'A', }) {
+ assert_se(s.a == 1);
+ assert_se(s.b == 'A');
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ VA_ARGS_FOREACH(s)
+ assert_se(false);
+
+ i = 0;
+ struct test *p, *p_1 = &s_1, *p_2 = &s_2, *p_3 = &s_3, *p_4 = &s_4;
+ VA_ARGS_FOREACH(p, p_1, NULL, p_2, p_3, NULL, p_4, NULL) {
+ switch(i++) {
+ case 0: assert_se(p == p_1); break;
+ case 1: assert_se(p == NULL); break;
+ case 2: assert_se(p == p_2); break;
+ case 3: assert_se(p == p_3); break;
+ case 4: assert_se(p == NULL); break;
+ case 5: assert_se(p == p_4); break;
+ case 6: assert_se(p == NULL); break;
+ default: assert_se(false);
+ }
+ }
+ assert_se(i == 7);
+ i = 0;
+ VA_ARGS_FOREACH(p, p_3) {
+ assert_se(p == p_3);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ VA_ARGS_FOREACH(p)
+ assert_se(false);
+
+ i = 0;
+ void *v, *v_1 = p_1, *v_2 = p_2, *v_3 = p_3;
+ uint32_t *u32p = &u32;
+ VA_ARGS_FOREACH(v, v_1, NULL, u32p, v_3, p_2, p_4, v_2, NULL) {
+ switch(i++) {
+ case 0: assert_se(v == v_1); break;
+ case 1: assert_se(v == NULL); break;
+ case 2: assert_se(v == u32p); break;
+ case 3: assert_se(v == v_3); break;
+ case 4: assert_se(v == p_2); break;
+ case 5: assert_se(v == p_4); break;
+ case 6: assert_se(v == v_2); break;
+ case 7: assert_se(v == NULL); break;
+ default: assert_se(false);
+ }
+ }
+ assert_se(i == 8);
+ i = 0;
+ VA_ARGS_FOREACH(v, NULL) {
+ assert_se(v == NULL);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ i = 0;
+ VA_ARGS_FOREACH(v, v_1) {
+ assert_se(v == v_1);
+ assert_se(i++ == 0);
+ }
+ assert_se(i == 1);
+ VA_ARGS_FOREACH(v)
+ assert_se(false);
+}
+
TEST(align_to) {
assert_se(ALIGN_TO(0, 1) == 0);
assert_se(ALIGN_TO(1, 1) == 1);

View File

@ -0,0 +1,98 @@
From 55a262020c0a61b639c17a1be4f7b5b6fd3f5087 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 2 Feb 2023 15:58:10 -0500
Subject: [PATCH] basic/bitfield: add bitfield operations
Add macros to manage bits in a bitfield (e.g. uint32_t, uint64_t, etc),
such as setting, clearing, checking bits, and iterating all set bits.
These are similiar to the bitmap operations, but operate on basic types
instead of requiring a Bitmap object.
(cherry picked from commit 33d9beed07b3dab05d07d57e8af13bb19b9e3095)
Related: RHEL-16182
---
src/basic/bitfield.h | 73 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 src/basic/bitfield.h
diff --git a/src/basic/bitfield.h b/src/basic/bitfield.h
new file mode 100644
index 0000000000..25bc0ebda7
--- /dev/null
+++ b/src/basic/bitfield.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "macro.h"
+
+/* Bit index (0-based) to mask of specified type. Assertion failure if index is out of range. */
+#define _INDEX_TO_MASK(type, i, uniq) \
+ ({ \
+ int UNIQ_T(_i, uniq) = (i); \
+ assert(UNIQ_T(_i, uniq) < (int)sizeof(type) * 8); \
+ ((type)1) << UNIQ_T(_i, uniq); \
+ })
+#define INDEX_TO_MASK(type, i) \
+ ({ \
+ assert_cc(sizeof(type) <= sizeof(unsigned long long)); \
+ assert_cc(__builtin_choose_expr(__builtin_constant_p(i), i, 0) < (int)(sizeof(type) * 8)); \
+ __builtin_choose_expr(__builtin_constant_p(i), \
+ ((type)1) << (i), \
+ _INDEX_TO_MASK(type, i, UNIQ)); \
+ })
+
+/* Builds a mask of specified type with multiple bits set. Note the result will not be constant, even if all
+ * indexes are constant. */
+#define INDEXES_TO_MASK(type, ...) \
+ UNIQ_INDEXES_TO_MASK(type, UNIQ, ##__VA_ARGS__)
+#define UNIQ_INDEXES_TO_MASK(type, uniq, ...) \
+ ({ \
+ typeof(type) UNIQ_T(_mask, uniq) = (type)0; \
+ int UNIQ_T(_i, uniq); \
+ VA_ARGS_FOREACH(UNIQ_T(_i, uniq), ##__VA_ARGS__) \
+ UNIQ_T(_mask, uniq) |= INDEX_TO_MASK(type, UNIQ_T(_i, uniq)); \
+ UNIQ_T(_mask, uniq); \
+ })
+
+/* Same as the FLAG macros, but accept a 0-based bit index instead of a mask. Results in assertion failure if
+ * index is out of range for the type. */
+#define SET_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), true)
+#define CLEAR_BIT(bits, i) SET_FLAG(bits, INDEX_TO_MASK(typeof(bits), i), false)
+#define BIT_SET(bits, i) FLAGS_SET(bits, INDEX_TO_MASK(typeof(bits), i))
+
+/* As above, but accepts multiple indexes. Note the result will not be constant, even if all indexes are
+ * constant. */
+#define SET_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), true)
+#define CLEAR_BITS(bits, ...) SET_FLAG(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__), false)
+#define BITS_SET(bits, ...) FLAGS_SET(bits, INDEXES_TO_MASK(typeof(bits), ##__VA_ARGS__))
+
+/* Iterate through each set bit. Index is 0-based and type int. */
+#define BIT_FOREACH(index, bits) _BIT_FOREACH(index, bits, UNIQ)
+#define _BIT_FOREACH(index, bits, uniq) \
+ for (int UNIQ_T(_last, uniq) = -1, index; \
+ (index = BIT_NEXT_SET(bits, UNIQ_T(_last, uniq))) >= 0; \
+ UNIQ_T(_last, uniq) = index)
+
+/* Find the next set bit after 0-based index 'prev'. Result is 0-based index of next set bit, or -1 if no
+ * more bits are set. */
+#define BIT_FIRST_SET(bits) BIT_NEXT_SET(bits, -1)
+#define BIT_NEXT_SET(bits, prev) \
+ UNIQ_BIT_NEXT_SET(bits, prev, UNIQ)
+#define UNIQ_BIT_NEXT_SET(bits, prev, uniq) \
+ ({ \
+ typeof(bits) UNIQ_T(_bits, uniq) = (bits); \
+ int UNIQ_T(_prev, uniq) = (prev); \
+ int UNIQ_T(_next, uniq); \
+ _BIT_NEXT_SET(UNIQ_T(_bits, uniq), \
+ UNIQ_T(_prev, uniq), \
+ UNIQ_T(_next, uniq)); \
+ })
+#define _BIT_NEXT_SET(bits, prev, next) \
+ ((int)(prev + 1) == (int)sizeof(bits) * 8 \
+ ? -1 /* Prev index was msb. */ \
+ : ((next = __builtin_ffsll(((unsigned long long)(bits)) >> (prev + 1))) == 0 \
+ ? -1 /* No more bits set. */ \
+ : prev + next))

View File

@ -0,0 +1,260 @@
From 8d96572fadee034a80ff4ffe5b901cb55c22edaf Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 2 Feb 2023 16:00:11 -0500
Subject: [PATCH] test/test-bitfield: add tests for bitfield macros
(cherry picked from commit 5e31ddd11e28beb0348dda2afd2e9082599bdf46)
Related: RHEL-16182
---
src/test/meson.build | 2 +
src/test/test-bitfield.c | 227 +++++++++++++++++++++++++++++++++++++++
2 files changed, 229 insertions(+)
create mode 100644 src/test/test-bitfield.c
diff --git a/src/test/meson.build b/src/test/meson.build
index 536ab08652..976794b22b 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -683,6 +683,8 @@ tests += [
[files('test-hmac.c')],
[files('test-sha256.c')],
+
+ [files('test-bitfield.c')],
]
############################################################
diff --git a/src/test/test-bitfield.c b/src/test/test-bitfield.c
new file mode 100644
index 0000000000..74ebd5cc48
--- /dev/null
+++ b/src/test/test-bitfield.c
@@ -0,0 +1,227 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <stddef.h>
+
+#include "bitfield.h"
+#include "log.h"
+#include "tests.h"
+
+#define TEST_BITS(bits, v, ...) \
+ ({ \
+ assert_se((!!BITS_SET(bits, ##__VA_ARGS__)) == v); \
+ assert_se((!!BITS_SET(~(bits), ##__VA_ARGS__)) == !v); \
+ })
+#define TEST_BIT(bits, v, i) \
+ ({ \
+ assert_se((!!BIT_SET(bits, i)) == v); \
+ assert_se((!!BIT_SET(~(bits), i)) == !v); \
+ TEST_BITS(bits, v, i); \
+ })
+
+#define TEST_BIT_SET(bits, i) TEST_BIT(bits, 1, i)
+#define TEST_BIT_CLEAR(bits, i) TEST_BIT(bits, 0, i)
+
+#define TEST_BITS_SET(bits, ...) TEST_BITS(bits, 1, ##__VA_ARGS__)
+#define TEST_BITS_CLEAR(bits, ...) TEST_BITS(bits, 0, ##__VA_ARGS__)
+
+TEST(bits) {
+ int count;
+
+ /* Test uint8_t */
+ TEST_BIT_SET(0x81, 0);
+ TEST_BIT_SET(0x81, 7);
+ TEST_BITS_SET(0x81, 0, 7);
+ TEST_BIT_CLEAR(0x81, 4);
+ TEST_BIT_CLEAR(0x81, 6);
+ TEST_BITS_CLEAR(0x81, 1, 2, 3, 4, 5, 6);
+ uint8_t expected8 = 0;
+ BIT_FOREACH(i, 0x81)
+ expected8 |= UINT8_C(1) << i;
+ assert_se(expected8 == 0x81);
+ uint8_t u8 = 0x91;
+ TEST_BIT_SET(u8, 4);
+ TEST_BITS_SET(u8, 0, 4, 7);
+ TEST_BIT_CLEAR(u8, 2);
+ TEST_BITS_CLEAR(u8, 1, 2, 3, 5, 6);
+ SET_BIT(u8, 1);
+ TEST_BITS_SET(u8, 0, 1, 4, 7);
+ TEST_BITS_CLEAR(u8, 2, 3, 5, 6);
+ SET_BITS(u8, 3, 5);
+ TEST_BITS_SET(u8, 0, 1, 3, 4, 5, 7);
+ TEST_BITS_CLEAR(u8, 2, 6);
+ CLEAR_BIT(u8, 4);
+ TEST_BITS_SET(u8, 0, 1, 3, 5, 7);
+ TEST_BITS_CLEAR(u8, 2, 4, 6);
+ CLEAR_BITS(u8, 1);
+ CLEAR_BITS(u8, 0, 7);
+ TEST_BITS_SET(u8, 3, 5);
+ TEST_BITS_CLEAR(u8, 0, 1, 2, 4, 6, 7);
+ expected8 = 0;
+ BIT_FOREACH(i, u8)
+ expected8 |= UINT8_C(1) << i;
+ assert_se(expected8 == u8);
+ u8 = 0;
+ TEST_BITS_CLEAR(u8, 0, 1, 2, 3, 4, 5, 6, 7);
+ BIT_FOREACH(i, u8)
+ assert_se(0);
+ u8 = ~u8;
+ TEST_BITS_SET(u8, 0, 1, 2, 3, 4, 5, 6, 7);
+ count = 0;
+ BIT_FOREACH(i, u8)
+ count++;
+ assert_se(count == 8);
+ uint8_t _u8 = u8;
+ SET_BITS(u8);
+ assert_se(_u8 == u8);
+ CLEAR_BITS(u8);
+ assert_se(_u8 == u8);
+
+ /* Test uint16_t */
+ TEST_BIT_SET(0x1f81, 10);
+ TEST_BITS_SET(0x1f81, 0, 7, 8, 9, 10, 11, 12);
+ TEST_BIT_CLEAR(0x1f81, 13);
+ TEST_BITS_CLEAR(0x1f81, 1, 2, 3, 4, 5, 6, 13, 14, 15);
+ uint16_t expected16 = 0;
+ BIT_FOREACH(i, 0x1f81)
+ expected16 |= UINT16_C(1) << i;
+ assert_se(expected16 == 0x1f81);
+ uint16_t u16 = 0xf060;
+ TEST_BIT_SET(u16, 12);
+ TEST_BITS_SET(u16, 5, 6, 12, 13, 14, 15);
+ TEST_BIT_CLEAR(u16, 9);
+ TEST_BITS_CLEAR(u16, 0, 1, 2, 3, 4, 7, 8, 9, 10, 11);
+ SET_BITS(u16, 1, 8);
+ TEST_BITS_SET(u16, 1, 5, 6, 8, 12, 13, 14, 15);
+ TEST_BITS_CLEAR(u16, 0, 2, 3, 4, 7, 9, 10, 11);
+ CLEAR_BITS(u16, 13, 14);
+ TEST_BITS_SET(u16, 1, 5, 6, 8, 12, 15);
+ TEST_BITS_CLEAR(u16, 0, 2, 3, 4, 7, 9, 10, 11, 13, 14);
+ expected16 = 0;
+ BIT_FOREACH(i, u16)
+ expected16 |= UINT16_C(1) << i;
+ assert_se(expected16 == u16);
+ u16 = 0;
+ TEST_BITS_CLEAR(u16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+ BIT_FOREACH(i, u16)
+ assert_se(0);
+ u16 = ~u16;
+ TEST_BITS_SET(u16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
+ count = 0;
+ BIT_FOREACH(i, u16)
+ count++;
+ assert_se(count == 16);
+ uint16_t _u16 = u16;
+ SET_BITS(u16);
+ assert_se(_u16 == u16);
+ CLEAR_BITS(u16);
+ assert_se(_u16 == u16);
+
+ /* Test uint32_t */
+ TEST_BIT_SET(0x80224f10, 11);
+ TEST_BITS_SET(0x80224f10, 4, 8, 9, 10, 11, 14, 17, 21, 31);
+ TEST_BIT_CLEAR(0x80224f10, 28);
+ TEST_BITS_CLEAR(0x80224f10, 0, 1, 2, 3, 5, 6, 7, 12, 13, 15, 16, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30);
+ uint32_t expected32 = 0;
+ BIT_FOREACH(i, 0x80224f10)
+ expected32 |= UINT32_C(1) << i;
+ assert_se(expected32 == 0x80224f10);
+ uint32_t u32 = 0x605e0388;
+ TEST_BIT_SET(u32, 3);
+ TEST_BIT_SET(u32, 30);
+ TEST_BITS_SET(u32, 3, 7, 8, 9, 17, 18, 19, 20, 22, 29, 30);
+ TEST_BIT_CLEAR(u32, 0);
+ TEST_BIT_CLEAR(u32, 31);
+ TEST_BITS_CLEAR(u32, 0, 1, 2, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 21, 23, 24, 25, 26, 27, 28, 31);
+ SET_BITS(u32, 1, 25, 26);
+ TEST_BITS_SET(u32, 1, 3, 7, 8, 9, 17, 18, 19, 20, 22, 25, 26, 29, 30);
+ TEST_BITS_CLEAR(u32, 0, 2, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 21, 23, 24, 27, 28, 31);
+ CLEAR_BITS(u32, 29, 17, 1);
+ TEST_BITS_SET(u32, 3, 7, 8, 9, 18, 19, 20, 22, 25, 26, 30);
+ TEST_BITS_CLEAR(u32, 0, 1, 2, 4, 5, 6, 10, 11, 12, 13, 14, 15, 16, 17, 21, 23, 24, 27, 28, 29, 31);
+ expected32 = 0;
+ BIT_FOREACH(i, u32)
+ expected32 |= UINT32_C(1) << i;
+ assert_se(expected32 == u32);
+ u32 = 0;
+ TEST_BITS_CLEAR(u32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31);
+ BIT_FOREACH(i, u32)
+ assert_se(0);
+ u32 = ~u32;
+ TEST_BITS_SET(u32, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31);
+ count = 0;
+ BIT_FOREACH(i, u32)
+ count++;
+ assert_se(count == 32);
+ uint32_t _u32 = u32;
+ SET_BITS(u32);
+ assert_se(_u32 == u32);
+ CLEAR_BITS(u32);
+ assert_se(_u32 == u32);
+
+ /* Test uint64_t */
+ TEST_BIT_SET(0x18ba1400f4857460, 60);
+ TEST_BITS_SET(0x18ba1400f4857460, 5, 6, 10, 12, 13, 14, 16, 18, 23, 26, 28, 29, 30, 31, 42, 44, 49, 51, 52, 53, 55, 59, 60);
+ TEST_BIT_CLEAR(UINT64_C(0x18ba1400f4857460), 0);
+ TEST_BIT_CLEAR(UINT64_C(0x18ba1400f4857460), 63);
+ TEST_BITS_CLEAR(UINT64_C(0x18ba1400f4857460), 0, 1, 2, 3, 4, 7, 8, 9, 11, 15, 17, 19, 20, 21, 22, 24, 25, 27, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 43, 45, 46, 47, 48, 50, 54, 56, 57, 58, 61, 62, 63);
+ uint64_t expected64 = 0;
+ BIT_FOREACH(i, 0x18ba1400f4857460)
+ expected64 |= UINT64_C(1) << i;
+ assert_se(expected64 == 0x18ba1400f4857460);
+ uint64_t u64 = 0xa90e2d8507a65739;
+ TEST_BIT_SET(u64, 0);
+ TEST_BIT_SET(u64, 63);
+ TEST_BITS_SET(u64, 0, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 63);
+ TEST_BIT_CLEAR(u64, 1);
+ TEST_BITS_CLEAR(u64, 1, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 62);
+ SET_BIT(u64, 1);
+ TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 63);
+ TEST_BITS_CLEAR(u64, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 62);
+ CLEAR_BIT(u64, 63);
+ TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61);
+ TEST_BITS_CLEAR(u64, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 62, 63);
+ SET_BIT(u64, 62);
+ TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 8, 9, 10, 12, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62);
+ TEST_BITS_CLEAR(u64, 2, 6, 7, 11, 13, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 38, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 63);
+ SET_BITS(u64, 63, 62, 7, 13, 38, 40);
+ TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 17, 18, 21, 23, 24, 25, 26, 32, 34, 38, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62, 63);
+ TEST_BITS_CLEAR(u64, 2, 6, 11, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 33, 35, 36, 37, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60);
+ CLEAR_BIT(u64, 32);
+ TEST_BITS_SET(u64, 0, 1, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 17, 18, 21, 23, 24, 25, 26, 34, 38, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62, 63);
+ TEST_BITS_CLEAR(u64, 2, 6, 11, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60);
+ CLEAR_BITS(u64, 0, 2, 11, 63, 32, 58);
+ TEST_BITS_SET(u64, 1, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14, 17, 18, 21, 23, 24, 25, 26, 34, 38, 39, 40, 42, 43, 45, 49, 50, 51, 56, 59, 61, 62);
+ TEST_BITS_CLEAR(u64, 0, 2, 6, 11, 15, 16, 19, 20, 22, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 41, 44, 46, 47, 48, 52, 53, 54, 55, 57, 58, 60, 63);
+ expected64 = 0;
+ BIT_FOREACH(i, u64)
+ expected64 |= UINT64_C(1) << i;
+ assert_se(expected64 == u64);
+ u64 = 0;
+ TEST_BITS_CLEAR(u64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63);
+ BIT_FOREACH(i, u64)
+ assert_se(0);
+ u64 = ~u64;
+ TEST_BITS_SET(u64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63);
+ count = 0;
+ BIT_FOREACH(i, u64)
+ count++;
+ assert_se(count == 64);
+ uint64_t _u64 = u64;
+ SET_BITS(u64);
+ assert_se(_u64 == u64);
+ CLEAR_BITS(u64);
+ assert_se(_u64 == u64);
+
+ /* Verify these use cases are constant-folded. */
+ assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint8_t, 1)));
+ assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint16_t, 1)));
+ assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint32_t, 1)));
+ assert_cc(__builtin_constant_p(INDEX_TO_MASK(uint64_t, 1)));
+
+ assert_cc(__builtin_constant_p(BIT_SET((uint8_t)2, 1)));
+ assert_cc(__builtin_constant_p(BIT_SET((uint16_t)2, 1)));
+ assert_cc(__builtin_constant_p(BIT_SET((uint32_t)2, 1)));
+ assert_cc(__builtin_constant_p(BIT_SET((uint64_t)2, 1)));
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -0,0 +1,182 @@
From ba5a271dbb71edc1985b7d9f837d7904ad2a4529 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 16 Dec 2022 16:33:08 -0500
Subject: [PATCH] tpm2: add tpm2_get_policy_digest()
(cherry picked from commit 23b972d571650014ab5f22610da80a62f53f2245)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 99 ++++++++++++++++++++++++------------------
1 file changed, 57 insertions(+), 42 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index c22a200a5c..b5eabb8159 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -476,6 +476,54 @@ void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION
};
}
+static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
+ if (!DEBUG_LOGGING || !buffer || size == 0)
+ return;
+
+ _cleanup_free_ char *h = hexmem(buffer, size);
+ log_debug("%s: %s", msg ?: "Buffer", strna(h));
+}
+
+static void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) {
+ if (digest)
+ tpm2_log_debug_buffer(digest->buffer, digest->size, msg ?: "Digest");
+}
+
+static int tpm2_get_policy_digest(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+ TSS2_RC rc;
+
+ if (!DEBUG_LOGGING && !ret_policy_digest)
+ return 0;
+
+ assert(c);
+ assert(session);
+
+ log_debug("Acquiring policy digest.");
+
+ _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
+ rc = sym_Esys_PolicyGetDigest(
+ c->esys_context,
+ session->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ &policy_digest);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ tpm2_log_debug_digest(policy_digest, "Session policy digest");
+
+ if (ret_policy_digest)
+ *ret_policy_digest = TAKE_PTR(policy_digest);
+
+ return 0;
+}
+
static unsigned find_nth_bit(uint32_t mask, unsigned n) {
uint32_t bit = 1;
@@ -1100,7 +1148,6 @@ static int tpm2_make_policy_session(
.keyBits.aes = 128,
.mode.aes = TPM2_ALG_CFB,
};
- _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
TSS2_RC rc;
int r;
@@ -1237,16 +1284,9 @@ static int tpm2_make_policy_session(
/* Get the policy hash of the PCR policy */
_cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
- rc = sym_Esys_PolicyGetDigest(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &approved_policy);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_get_policy_digest(c, session, &approved_policy);
+ if (r < 0)
+ return r;
/* When we are unlocking and have a signature, let's pass it to the TPM */
_cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL;
@@ -1361,38 +1401,13 @@ static int tpm2_make_policy_session(
sym_Tss2_RC_Decode(rc));
}
- if (DEBUG_LOGGING || ret_policy_digest) {
- log_debug("Acquiring policy digest.");
-
- rc = sym_Esys_PolicyGetDigest(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &policy_digest);
-
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
-
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *h = NULL;
-
- h = hexmem(policy_digest->buffer, policy_digest->size);
- if (!h)
- return log_oom();
-
- log_debug("Session policy digest: %s", h);
- }
- }
+ r = tpm2_get_policy_digest(c, session, ret_policy_digest);
+ if (r < 0)
+ return r;
if (ret_session)
*ret_session = TAKE_PTR(session);
- if (ret_policy_digest)
- *ret_policy_digest = TAKE_PTR(policy_digest);
-
if (ret_pcr_bank)
*ret_pcr_bank = pcr_bank;
@@ -1414,7 +1429,6 @@ int tpm2_seal(const char *device,
uint16_t *ret_pcr_bank,
uint16_t *ret_primary_alg) {
- _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
_cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
_cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
static const TPML_PCR_SELECTION creation_pcr = {};
@@ -1423,7 +1437,6 @@ int tpm2_seal(const char *device,
TPM2B_SENSITIVE_CREATE hmac_sensitive;
TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template;
- TPMI_ALG_HASH pcr_bank;
usec_t start;
TSS2_RC rc;
int r;
@@ -1477,6 +1490,8 @@ int tpm2_seal(const char *device,
if (r < 0)
return r;
+ _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
+ TPMI_ALG_HASH pcr_bank;
r = tpm2_make_policy_session(
c,
primary,
@@ -1608,7 +1623,6 @@ int tpm2_unseal(const char *device,
size_t *ret_secret_size) {
_cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
- _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
_cleanup_(erase_and_freep) char *secret = NULL;
TPM2B_PRIVATE private = {};
TPM2B_PUBLIC public = {};
@@ -1708,6 +1722,7 @@ int tpm2_unseal(const char *device,
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
_cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
+ _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
c,
primary,

View File

@ -0,0 +1,90 @@
From 926160f00356c562eea2d87bb88c1cd3febe0655 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 27 Feb 2023 06:44:13 -0500
Subject: [PATCH] tpm2: add TPM2_PCR_VALID()
(cherry picked from commit aa07a4fa353d758562c4bec8c7d3b1d44b55e573)
Related: RHEL-16182
---
.../cryptsetup-token-systemd-tpm2.c | 2 +-
src/cryptsetup/cryptsetup.c | 2 +-
src/shared/tpm2-util.c | 2 +-
src/shared/tpm2-util.h | 18 ++++++++++++------
4 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index 319b0ca64d..e8bc091191 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -271,7 +271,7 @@ _public_ int cryptsetup_token_validate(
}
u = json_variant_unsigned(e);
- if (u >= TPM2_PCRS_MAX) {
+ if (!TPM2_PCR_VALID(u)) {
crypt_log_debug(cd, "TPM2 PCR number out of range.");
return 1;
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index d5ce252e57..d46a88c9fb 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -438,7 +438,7 @@ static int parse_one_option(const char *option) {
}
pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX;
- } else if (pcr >= TPM2_PCRS_MAX) {
+ } else if (!TPM2_PCR_VALID(pcr)) {
log_error("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
return 0;
}
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index b5eabb8159..0cbb32f819 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -462,7 +462,7 @@ void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION
assert(ret);
/* We only do 24bit here, as that's what PC TPMs are supposed to support */
- assert(mask <= 0xFFFFFFU);
+ assert(TPM2_PCR_MASK_VALID(mask));
*ret = (TPML_PCR_SELECTION) {
.count = 1,
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index d26a945a90..07a8a89800 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -11,6 +11,18 @@ typedef enum TPM2Flags {
TPM2_FLAGS_USE_PIN = 1 << 0,
} TPM2Flags;
+
+/* 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. This hardcodes our expectation of 24. */
+#define TPM2_PCRS_MAX 24U
+#define TPM2_PCRS_MASK ((UINT32_C(1) << TPM2_PCRS_MAX) - 1)
+static inline bool TPM2_PCR_VALID(unsigned pcr) {
+ return pcr < TPM2_PCRS_MAX;
+}
+static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
+ return pcr_mask <= TPM2_PCRS_MASK;
+}
+
#if HAVE_TPM2
#include <tss2/tss2_esys.h>
@@ -108,12 +120,6 @@ int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret);
int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *salt, size_t salt_size, TPM2Flags flags, JsonVariant **ret);
int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, void **ret_pubkey, size_t *ret_pubkey_size, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, void **ret_blob, size_t *ret_blob_size, void **ret_policy_hash, size_t *ret_policy_hash_size, void **ret_salt, size_t *ret_salt_size, TPM2Flags *ret_flags);
-#define TPM2_PCRS_MAX 24U
-
-static inline bool TPM2_PCR_MASK_VALID(uint64_t pcr_mask) {
- return pcr_mask < (UINT64_C(1) << TPM2_PCRS_MAX); /* Support 24 PCR banks */
-}
-
/* Default to PCR 7 only */
#define TPM2_PCR_MASK_DEFAULT (UINT32_C(1) << 7)

View File

@ -0,0 +1,629 @@
From 90d9f2996c10a2be090fd89be7409c81eabceb4c Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 6 Feb 2023 11:31:59 -0500
Subject: [PATCH] tpm2: add/rename functions to manage pcr selections
This renames some functions to match other to/from_string() naming,
and allows better management of TPML_PCR_SELECTION and TPMS_PCR_SELECTION
structs.
(cherry picked from commit c69bd0abdbd06ee89068227c67890358f5764c3d)
Related: RHEL-16182
---
src/boot/measure.c | 4 +-
.../cryptsetup-token-systemd-tpm2.c | 12 +-
src/shared/tpm2-util.c | 350 +++++++++++++++---
src/shared/tpm2-util.h | 31 +-
src/test/test-tpm2.c | 32 +-
5 files changed, 358 insertions(+), 71 deletions(-)
diff --git a/src/boot/measure.c b/src/boot/measure.c
index 65a48a01cd..86edf77c52 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -844,7 +844,9 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
return log_error_errno(tpmalg, "Unsupported PCR bank");
TPML_PCR_SELECTION pcr_selection;
- tpm2_pcr_mask_to_selection(1 << TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, &pcr_selection);
+ tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE,
+ tpmalg,
+ &pcr_selection);
rc = sym_Esys_PolicyPCR(
c->esys_context,
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index e8bc091191..b5d66e389d 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -205,13 +205,13 @@ _public_ void cryptsetup_token_dump(
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON fields: %m");
- r = pcr_mask_to_string(hash_pcr_mask, &hash_pcrs_str);
- if (r < 0)
- return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m");
+ hash_pcrs_str = tpm2_pcr_mask_to_string(hash_pcr_mask);
+ if (!hash_pcrs_str)
+ return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
- r = pcr_mask_to_string(pubkey_pcr_mask, &pubkey_pcrs_str);
- if (r < 0)
- return (void) crypt_log_debug_errno(cd, r, "Cannot format PCR hash mask: %m");
+ pubkey_pcrs_str = tpm2_pcr_mask_to_string(pubkey_pcr_mask);
+ if (!pubkey_pcrs_str)
+ return (void) crypt_log_debug_errno(cd, ENOMEM, "Cannot format PCR hash mask: %m");
r = crypt_dump_buffer_to_hex_string(blob, blob_size, &blob_str);
if (r < 0)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 0cbb32f819..cf62524e34 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -458,24 +458,292 @@ static int tpm2_make_primary(
return 0;
}
-void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret) {
+/* Utility functions for TPMS_PCR_SELECTION. */
+
+/* Convert a TPMS_PCR_SELECTION object to a mask. */
+void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret) {
+ assert(s);
+ assert(s->sizeofSelect <= sizeof(s->pcrSelect));
assert(ret);
- /* We only do 24bit here, as that's what PC TPMs are supposed to support */
- assert(TPM2_PCR_MASK_VALID(mask));
+ uint32_t mask = 0;
+ for (unsigned i = 0; i < s->sizeofSelect; i++)
+ SET_FLAG(mask, (uint32_t)s->pcrSelect[i] << (i * 8), true);
+ *ret = mask;
+}
- *ret = (TPML_PCR_SELECTION) {
- .count = 1,
- .pcrSelections[0] = {
- .hash = bank,
- .sizeofSelect = 3,
- .pcrSelect[0] = mask & 0xFF,
- .pcrSelect[1] = (mask >> 8) & 0xFF,
- .pcrSelect[2] = (mask >> 16) & 0xFF,
+/* Convert a mask and hash alg to a TPMS_PCR_SELECTION object. */
+void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TPMS_PCR_SELECTION *ret) {
+ assert(ret);
+
+ /* This is currently hardcoded at 24 PCRs, above. */
+ if (!TPM2_PCR_MASK_VALID(mask))
+ log_warning("PCR mask selections (%x) out of range, ignoring.",
+ mask & ~((uint32_t)TPM2_PCRS_MASK));
+
+ *ret = (TPMS_PCR_SELECTION){
+ .hash = hash_alg,
+ .sizeofSelect = TPM2_PCRS_MAX / 8,
+ .pcrSelect[0] = mask & 0xff,
+ .pcrSelect[1] = (mask >> 8) & 0xff,
+ .pcrSelect[2] = (mask >> 16) & 0xff,
+ };
+}
+
+/* Add all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
+void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
+ assert(a);
+ assert(b);
+ assert(a->hash == b->hash);
+
+ uint32_t maska, maskb;
+ tpm2_tpms_pcr_selection_to_mask(a, &maska);
+ tpm2_tpms_pcr_selection_to_mask(b, &maskb);
+ tpm2_tpms_pcr_selection_from_mask(maska | maskb, a->hash, a);
+}
+
+/* Remove all PCR selections in 'b' from 'a'. Both must have the same hash alg. */
+void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
+ assert(a);
+ assert(b);
+ assert(a->hash == b->hash);
+
+ uint32_t maska, maskb;
+ tpm2_tpms_pcr_selection_to_mask(a, &maska);
+ tpm2_tpms_pcr_selection_to_mask(b, &maskb);
+ tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a);
+}
+
+/* Move all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
+void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b) {
+ if (a == b)
+ return;
+
+ tpm2_tpms_pcr_selection_add(a, b);
+ tpm2_tpms_pcr_selection_from_mask(0, b->hash, b);
+}
+
+#define FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms) \
+ _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, UNIQ)
+#define _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, uniq) \
+ FOREACH_PCR_IN_MASK(pcr, \
+ ({ uint32_t UNIQ_T(_mask, uniq); \
+ tpm2_tpms_pcr_selection_to_mask(tpms, &UNIQ_T(_mask, uniq)); \
+ UNIQ_T(_mask, uniq); \
+ }))
+
+#define FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
+ UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, UNIQ)
+#define UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, uniq) \
+ for (TPML_PCR_SELECTION *UNIQ_T(_tpml, uniq) = (TPML_PCR_SELECTION*)(tpml); \
+ UNIQ_T(_tpml, uniq); UNIQ_T(_tpml, uniq) = NULL) \
+ _FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, UNIQ_T(_tpml, uniq))
+#define _FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
+ for (TPMS_PCR_SELECTION *tpms = tpml->pcrSelections; \
+ (uint32_t)(tpms - tpml->pcrSelections) < tpml->count; \
+ tpms++)
+
+#define FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, tpms, tpml) \
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
+ FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms)
+
+char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s) {
+ assert(s);
+
+ const char *algstr = strna(tpm2_hash_alg_to_string(s->hash));
+
+ uint32_t mask;
+ tpm2_tpms_pcr_selection_to_mask(s, &mask);
+ _cleanup_free_ char *maskstr = tpm2_pcr_mask_to_string(mask);
+ if (!maskstr)
+ return NULL;
+
+ return strjoin(algstr, "(", maskstr, ")");
+}
+
+size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s) {
+ assert(s);
+
+ uint32_t mask;
+ tpm2_tpms_pcr_selection_to_mask(s, &mask);
+ return (size_t)__builtin_popcount(mask);
+}
+
+/* Utility functions for TPML_PCR_SELECTION. */
+
+/* Remove the (0-based) index entry from 'l', shift all following entries, and update the count. */
+static void tpm2_tpml_pcr_selection_remove_index(TPML_PCR_SELECTION *l, uint32_t index) {
+ assert(l);
+ assert(l->count <= sizeof(l->pcrSelections));
+ assert(index < l->count);
+
+ size_t s = l->count - (index + 1);
+ memmove(&l->pcrSelections[index], &l->pcrSelections[index + 1], s * sizeof(l->pcrSelections[0]));
+ l->count--;
+}
+
+/* Get a TPMS_PCR_SELECTION from a TPML_PCR_SELECTION for the given hash alg. Returns NULL if there is no
+ * entry for the hash alg. This guarantees the returned entry contains all the PCR selections for the given
+ * hash alg, which may require modifying the TPML_PCR_SELECTION by removing duplicate entries. */
+static TPMS_PCR_SELECTION *tpm2_tpml_pcr_selection_get_tpms_pcr_selection(
+ TPML_PCR_SELECTION *l,
+ TPMI_ALG_HASH hash_alg) {
+
+ assert(l);
+
+ TPMS_PCR_SELECTION *selection = NULL;
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l)
+ if (s->hash == hash_alg) {
+ selection = s;
+ break;
+ }
+
+ if (!selection)
+ return NULL;
+
+ /* Iterate backwards through the entries, removing any other entries for the hash alg. */
+ for (uint32_t i = l->count - 1; i > 0; i--) {
+ TPMS_PCR_SELECTION *s = &l->pcrSelections[i];
+
+ if (selection == s)
+ break;
+
+ if (s->hash == hash_alg) {
+ tpm2_tpms_pcr_selection_move(selection, s);
+ tpm2_tpml_pcr_selection_remove_index(l, i);
}
+ }
+
+ return selection;
+}
+
+/* Convert a TPML_PCR_SELECTION object to a mask. Returns -ENOENT if 'hash_alg' is not in the object. */
+int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg, uint32_t *ret) {
+ assert(l);
+ assert(ret);
+
+ /* Make a copy, as tpm2_tpml_pcr_selection_get_tpms_pcr_selection() will modify the object if there
+ * are multiple entries with the requested hash alg. */
+ TPML_PCR_SELECTION lcopy = *l;
+
+ TPMS_PCR_SELECTION *s;
+ s = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(&lcopy, hash_alg);
+ if (!s)
+ return SYNTHETIC_ERRNO(ENOENT);
+
+ tpm2_tpms_pcr_selection_to_mask(s, ret);
+ return 0;
+}
+
+/* Convert a mask and hash alg to a TPML_PCR_SELECTION object. */
+void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TPML_PCR_SELECTION *ret) {
+ assert(ret);
+
+ TPMS_PCR_SELECTION s;
+ tpm2_tpms_pcr_selection_from_mask(mask, hash_alg, &s);
+
+ *ret = (TPML_PCR_SELECTION){
+ .count = 1,
+ .pcrSelections[0] = s,
};
}
+/* Combine all duplicate (same hash alg) TPMS_PCR_SELECTION entries in 'l'. */
+static void tpm2_tpml_pcr_selection_cleanup(TPML_PCR_SELECTION *l) {
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l)
+ /* This removes all duplicates for s->hash. */
+ (void) tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash);
+}
+
+/* Add the PCR selections in 's' to the corresponding hash alg TPMS_PCR_SELECTION entry in 'l'. Adds a new
+ * TPMS_PCR_SELECTION entry for the hash alg if needed. This may modify the TPML_PCR_SELECTION by combining
+ * entries with the same hash alg. */
+void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s) {
+ assert(l);
+ assert(s);
+
+ if (tpm2_tpms_pcr_selection_is_empty(s))
+ return;
+
+ TPMS_PCR_SELECTION *selection = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash);
+ if (selection) {
+ tpm2_tpms_pcr_selection_add(selection, s);
+ return;
+ }
+
+ /* It's already broken if the count is higher than the array has size for. */
+ assert(!(l->count > sizeof(l->pcrSelections)));
+
+ /* If full, the cleanup should result in at least one available entry. */
+ if (l->count == sizeof(l->pcrSelections))
+ tpm2_tpml_pcr_selection_cleanup(l);
+
+ assert(l->count < sizeof(l->pcrSelections));
+ l->pcrSelections[l->count++] = *s;
+}
+
+/* Remove the PCR selections in 's' from the corresponding hash alg TPMS_PCR_SELECTION entry in 'l'. This
+ * will combine all entries for 's->hash' in 'l'. */
+void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s) {
+ assert(l);
+ assert(s);
+
+ if (tpm2_tpms_pcr_selection_is_empty(s))
+ return;
+
+ TPMS_PCR_SELECTION *selection = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(l, s->hash);
+ if (selection)
+ tpm2_tpms_pcr_selection_sub(selection, s);
+}
+
+/* Add all PCR selections in 'b' to 'a'. */
+void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) {
+ assert(a);
+ assert(b);
+
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection_b, (TPML_PCR_SELECTION*) b)
+ tpm2_tpml_pcr_selection_add_tpms_pcr_selection(a, selection_b);
+}
+
+/* Remove all PCR selections in 'b' from 'a'. */
+void tpm2_tpml_pcr_selection_sub(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) {
+ assert(a);
+ assert(b);
+
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection_b, (TPML_PCR_SELECTION*) b)
+ tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(a, selection_b);
+}
+
+char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l) {
+ assert(l);
+
+ _cleanup_free_ char *banks = NULL;
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, (TPML_PCR_SELECTION*) l) {
+ if (tpm2_tpms_pcr_selection_is_empty(s))
+ continue;
+
+ _cleanup_free_ char *str = tpm2_tpms_pcr_selection_to_string(s);
+ if (!str || !strextend_with_separator(&banks, ",", str))
+ return NULL;
+ }
+
+ return strjoin("[", strempty(banks), "]");
+}
+
+size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) {
+ assert(l);
+ assert(l->count <= sizeof(l->pcrSelections));
+
+ size_t weight = 0;
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(s, l) {
+ size_t w = tpm2_tpms_pcr_selection_weight(s);
+ assert(weight <= SIZE_MAX - w);
+ weight += w;
+ }
+
+ return weight;
+}
+
static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
if (!DEBUG_LOGGING || !buffer || size == 0)
return;
@@ -562,7 +830,7 @@ static int tpm2_pcr_mask_good(
* actually measure into them, or only into a suboptimal bank. If so, the PCRs should be all zero or
* all 0xFF. Detect that, so that we can warn and maybe pick a better bank. */
- tpm2_pcr_mask_to_selection(mask, bank, &selection);
+ tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
rc = sym_Esys_PCR_Read(
c->esys_context,
@@ -1269,7 +1537,7 @@ static int tpm2_make_policy_session(
/* Put together the PCR policy we want to use */
TPML_PCR_SELECTION pcr_selection;
- tpm2_pcr_mask_to_selection(pubkey_pcr_mask, pcr_bank, &pcr_selection);
+ tpm2_tpml_pcr_selection_from_mask(pubkey_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
rc = sym_Esys_PolicyPCR(
c->esys_context,
session->esys_handle,
@@ -1372,7 +1640,7 @@ static int tpm2_make_policy_session(
log_debug("Configuring hash-based PCR policy.");
TPML_PCR_SELECTION pcr_selection;
- tpm2_pcr_mask_to_selection(hash_pcr_mask, pcr_bank, &pcr_selection);
+ tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
rc = sym_Esys_PolicyPCR(
c->esys_context,
session->esys_handle,
@@ -1989,13 +2257,28 @@ int tpm2_extend_bytes(
}
#endif
-int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
- const char *p = ASSERT_PTR(s);
+char *tpm2_pcr_mask_to_string(uint32_t mask) {
+ _cleanup_free_ char *s = NULL;
+
+ FOREACH_PCR_IN_MASK(n, mask)
+ if (strextendf_with_separator(&s, "+", "%d", n) < 0)
+ return NULL;
+
+ if (!s)
+ return strdup("");
+
+ return TAKE_PTR(s);
+}
+
+int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) {
uint32_t mask = 0;
int r;
- if (isempty(s)) {
- *ret = 0;
+ assert(arg);
+ assert(ret_mask);
+
+ if (isempty(arg)) {
+ *ret_mask = 0;
return 0;
}
@@ -2004,6 +2287,7 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
* /etc/crypttab the "," is already used to separate options, hence a different separator is nice to
* avoid escaping. */
+ const char *p = arg;
for (;;) {
_cleanup_free_ char *pcr = NULL;
unsigned n;
@@ -2012,19 +2296,20 @@ int tpm2_parse_pcrs(const char *s, uint32_t *ret) {
if (r == 0)
break;
if (r < 0)
- return log_error_errno(r, "Failed to parse PCR list: %s", s);
+ return log_error_errno(r, "Failed to parse PCR list: %s", arg);
r = safe_atou(pcr, &n);
if (r < 0)
return log_error_errno(r, "Failed to parse PCR number: %s", pcr);
if (n >= TPM2_PCRS_MAX)
return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
- "PCR number out of range (valid range 0…23): %u", n);
+ "PCR number out of range (valid range 0…%u): %u",
+ TPM2_PCRS_MAX - 1, n);
- mask |= UINT32_C(1) << n;
+ SET_BIT(mask, n);;
}
- *ret = mask;
+ *ret_mask = mask;
return 0;
}
@@ -2389,7 +2674,7 @@ int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask) {
return 0;
}
- r = tpm2_parse_pcrs(arg, &m);
+ r = tpm2_pcr_mask_from_string(arg, &m);
if (r < 0)
return r;
@@ -2445,25 +2730,6 @@ int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pu
return 0;
}
-int pcr_mask_to_string(uint32_t mask, char **ret) {
- _cleanup_free_ char *buf = NULL;
- int r;
-
- assert(ret);
-
- for (unsigned i = 0; i < TPM2_PCRS_MAX; i++) {
- if (!(mask & (UINT32_C(1) << i)))
- continue;
-
- r = strextendf_with_separator(&buf, "+", "%u", i);
- if (r < 0)
- return r;
- }
-
- *ret = TAKE_PTR(buf);
- return 0;
-}
-
#define PBKDF2_HMAC_SHA256_ITERATIONS 10000
/*
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 07a8a89800..c2532c61c2 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -3,6 +3,7 @@
#include <stdbool.h>
+#include "bitfield.h"
#include "json.h"
#include "macro.h"
#include "sha256.h"
@@ -23,6 +24,8 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
return pcr_mask <= TPM2_PCRS_MASK;
}
+#define FOREACH_PCR_IN_MASK(pcr, mask) BIT_FOREACH(pcr, mask)
+
#if HAVE_TPM2
#include <tss2/tss2_esys.h>
@@ -92,8 +95,6 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
-void tpm2_pcr_mask_to_selection(uint32_t mask, uint16_t bank, TPML_PCR_SELECTION *ret);
-
static inline void Esys_Freep(void *p) {
if (*(void**) p)
sym_Esys_Free(*(void**) p);
@@ -104,6 +105,25 @@ int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret)
int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
+void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret);
+void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
+void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
+void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
+void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b);
+char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s);
+size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s);
+#define tpm2_tpms_pcr_selection_is_empty(s) (tpm2_tpms_pcr_selection_weight(s) == 0)
+
+int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t *ret);
+void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPML_PCR_SELECTION *ret);
+void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
+void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
+void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b);
+void tpm2_tpml_pcr_selection_sub(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b);
+char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l);
+size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
+#define tpm2_tpml_pcr_selection_is_empty(l) (tpm2_tpml_pcr_selection_weight(l) == 0)
+
#else /* HAVE_TPM2 */
typedef struct {} Tpm2Context;
typedef struct {} Tpm2Handle;
@@ -112,8 +132,6 @@ typedef struct {} Tpm2Handle;
int tpm2_list_devices(void);
int tpm2_find_device_auto(int log_level, char **ret);
-int tpm2_parse_pcrs(const char *s, uint32_t *ret);
-
int tpm2_make_pcr_json_array(uint32_t pcr_mask, JsonVariant **ret);
int tpm2_parse_pcr_json_array(JsonVariant *v, uint32_t *ret);
@@ -155,6 +173,9 @@ int tpm2_hash_alg_from_string(const char *alg);
const char *tpm2_asym_alg_to_string(uint16_t alg);
int tpm2_asym_alg_from_string(const char *alg);
+char *tpm2_pcr_mask_to_string(uint32_t mask);
+int tpm2_pcr_mask_from_string(const char *arg, uint32_t *mask);
+
typedef struct {
uint32_t search_pcr_mask;
const char *device;
@@ -179,8 +200,6 @@ int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask);
int tpm2_load_pcr_signature(const char *path, JsonVariant **ret);
int tpm2_load_pcr_public_key(const char *path, void **ret_pubkey, size_t *ret_pubkey_size);
-int pcr_mask_to_string(uint32_t mask, char **ret);
-
int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
size_t passlen,
const void *salt,
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 04e08490b3..23277449b5 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -3,29 +3,29 @@
#include "tpm2-util.h"
#include "tests.h"
-static void test_tpm2_parse_pcrs_one(const char *s, uint32_t mask, int ret) {
+static void test_tpm2_pcr_mask_from_string_one(const char *s, uint32_t mask, int ret) {
uint32_t m;
- assert_se(tpm2_parse_pcrs(s, &m) == ret);
+ assert_se(tpm2_pcr_mask_from_string(s, &m) == ret);
if (ret >= 0)
assert_se(m == mask);
}
-TEST(tpm2_parse_pcrs) {
- test_tpm2_parse_pcrs_one("", 0, 0);
- test_tpm2_parse_pcrs_one("0", 1, 0);
- test_tpm2_parse_pcrs_one("1", 2, 0);
- test_tpm2_parse_pcrs_one("0,1", 3, 0);
- test_tpm2_parse_pcrs_one("0+1", 3, 0);
- test_tpm2_parse_pcrs_one("0-1", 0, -EINVAL);
- test_tpm2_parse_pcrs_one("0,1,2", 7, 0);
- test_tpm2_parse_pcrs_one("0+1+2", 7, 0);
- test_tpm2_parse_pcrs_one("0+1,2", 7, 0);
- test_tpm2_parse_pcrs_one("0,1+2", 7, 0);
- test_tpm2_parse_pcrs_one("0,2", 5, 0);
- test_tpm2_parse_pcrs_one("0+2", 5, 0);
- test_tpm2_parse_pcrs_one("foo", 0, -EINVAL);
+TEST(tpm2_mask_from_string) {
+ test_tpm2_pcr_mask_from_string_one("", 0, 0);
+ test_tpm2_pcr_mask_from_string_one("0", 1, 0);
+ test_tpm2_pcr_mask_from_string_one("1", 2, 0);
+ test_tpm2_pcr_mask_from_string_one("0,1", 3, 0);
+ test_tpm2_pcr_mask_from_string_one("0+1", 3, 0);
+ test_tpm2_pcr_mask_from_string_one("0-1", 0, -EINVAL);
+ test_tpm2_pcr_mask_from_string_one("0,1,2", 7, 0);
+ test_tpm2_pcr_mask_from_string_one("0+1+2", 7, 0);
+ test_tpm2_pcr_mask_from_string_one("0+1,2", 7, 0);
+ test_tpm2_pcr_mask_from_string_one("0,1+2", 7, 0);
+ test_tpm2_pcr_mask_from_string_one("0,2", 5, 0);
+ test_tpm2_pcr_mask_from_string_one("0+2", 5, 0);
+ test_tpm2_pcr_mask_from_string_one("foo", 0, -EINVAL);
}
TEST(tpm2_util_pbkdf2_hmac_sha256) {

View File

@ -0,0 +1,363 @@
From cfaacedcf9e263f8291f13f2dde187a46e8a3f31 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 28 Feb 2023 17:16:43 -0500
Subject: [PATCH] test/test-tpm2: add tests for pcr selection functions
(cherry picked from commit e067a49fd1180ff1104b3978c92d11784c67800f)
Related: RHEL-16182
---
src/test/test-tpm2.c | 342 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 342 insertions(+)
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 23277449b5..20baa0f261 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -69,4 +69,346 @@ TEST(tpm2_util_pbkdf2_hmac_sha256) {
}
}
+#if HAVE_TPM2
+
+#define POISON(type) \
+ ({ \
+ type _p; \
+ memset(&_p, 0xaa, sizeof(_p)); \
+ _p; \
+ })
+#define POISON_TPML POISON(TPML_PCR_SELECTION)
+#define POISON_TPMS POISON(TPMS_PCR_SELECTION)
+#define POISON_U32 POISON(uint32_t)
+
+static void assert_tpms_pcr_selection_eq(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b) {
+ assert_se(a);
+ assert_se(b);
+
+ assert_se(a->hash == b->hash);
+ assert_se(a->sizeofSelect == b->sizeofSelect);
+
+ for (size_t i = 0; i < a->sizeofSelect; i++)
+ assert_se(a->pcrSelect[i] == b->pcrSelect[i]);
+}
+
+static void assert_tpml_pcr_selection_eq(TPML_PCR_SELECTION *a, TPML_PCR_SELECTION *b) {
+ assert_se(a);
+ assert_se(b);
+
+ assert_se(a->count == b->count);
+ for (size_t i = 0; i < a->count; i++)
+ assert_tpms_pcr_selection_eq(&a->pcrSelections[i], &b->pcrSelections[i]);
+}
+
+static void verify_tpms_pcr_selection(TPMS_PCR_SELECTION *s, uint32_t mask, TPMI_ALG_HASH hash) {
+ assert_se(s->hash == hash);
+ assert_se(s->sizeofSelect == 3);
+ assert_se(s->pcrSelect[0] == (mask & 0xff));
+ assert_se(s->pcrSelect[1] == ((mask >> 8) & 0xff));
+ assert_se(s->pcrSelect[2] == ((mask >> 16) & 0xff));
+ assert_se(s->pcrSelect[3] == 0);
+
+ uint32_t m = POISON_U32;
+ tpm2_tpms_pcr_selection_to_mask(s, &m);
+ assert_se(m == mask);
+}
+
+static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION s[], size_t count) {
+ assert_se(l->count == count);
+ for (size_t i = 0; i < count; i++) {
+ assert_tpms_pcr_selection_eq(&s[i], &l->pcrSelections[i]);
+
+ uint32_t mask = POISON_U32;
+ TPMI_ALG_HASH hash = l->pcrSelections[i].hash;
+ assert_se(tpm2_tpml_pcr_selection_to_mask(l, hash, &mask) == 0);
+ verify_tpms_pcr_selection(&l->pcrSelections[i], mask, hash);
+ }
+}
+
+static void _test_pcr_selection_mask_hash(uint32_t mask, TPMI_ALG_HASH hash) {
+ TPMS_PCR_SELECTION s = POISON_TPMS;
+ tpm2_tpms_pcr_selection_from_mask(mask, hash, &s);
+ verify_tpms_pcr_selection(&s, mask, hash);
+
+ TPML_PCR_SELECTION l = POISON_TPML;
+ tpm2_tpml_pcr_selection_from_mask(mask, hash, &l);
+ verify_tpml_pcr_selection(&l, &s, 1);
+ verify_tpms_pcr_selection(&l.pcrSelections[0], mask, hash);
+
+ uint32_t test_masks[] = {
+ 0x0, 0x1, 0x100, 0x10000, 0xf0f0f0, 0xaaaaaa, 0xffffff,
+ };
+ for (unsigned i = 0; i < ELEMENTSOF(test_masks); i++) {
+ uint32_t test_mask = test_masks[i];
+
+ TPMS_PCR_SELECTION a = POISON_TPMS, b = POISON_TPMS, test_s = POISON_TPMS;
+ tpm2_tpms_pcr_selection_from_mask(test_mask, hash, &test_s);
+
+ a = s;
+ b = test_s;
+ tpm2_tpms_pcr_selection_add(&a, &b);
+ verify_tpms_pcr_selection(&a, UPDATE_FLAG(mask, test_mask, true), hash);
+ verify_tpms_pcr_selection(&b, test_mask, hash);
+
+ a = s;
+ b = test_s;
+ tpm2_tpms_pcr_selection_sub(&a, &b);
+ verify_tpms_pcr_selection(&a, UPDATE_FLAG(mask, test_mask, false), hash);
+ verify_tpms_pcr_selection(&b, test_mask, hash);
+
+ a = s;
+ b = test_s;
+ tpm2_tpms_pcr_selection_move(&a, &b);
+ verify_tpms_pcr_selection(&a, UPDATE_FLAG(mask, test_mask, true), hash);
+ verify_tpms_pcr_selection(&b, 0, hash);
+ }
+}
+
+TEST(tpms_pcr_selection_mask_and_hash) {
+ TPMI_ALG_HASH HASH_ALGS[] = { TPM2_ALG_SHA1, TPM2_ALG_SHA256, };
+
+ for (unsigned i = 0; i < ELEMENTSOF(HASH_ALGS); i++)
+ for (uint32_t m2 = 0; m2 <= 0xffffff; m2 += 0x30000)
+ for (uint32_t m1 = 0; m1 <= 0xffff; m1 += 0x300)
+ for (uint32_t m0 = 0; m0 <= 0xff; m0 += 0x3)
+ _test_pcr_selection_mask_hash(m0 | m1 | m2, HASH_ALGS[i]);
+}
+
+static void _test_tpms_sw(
+ TPMI_ALG_HASH hash,
+ uint32_t mask,
+ const char *expected_str,
+ size_t expected_weight) {
+
+ TPMS_PCR_SELECTION s = POISON_TPMS;
+ tpm2_tpms_pcr_selection_from_mask(mask, hash, &s);
+
+ _cleanup_free_ char *tpms_str = tpm2_tpms_pcr_selection_to_string(&s);
+ assert_se(streq(tpms_str, expected_str));
+
+ assert_se(tpm2_tpms_pcr_selection_weight(&s) == expected_weight);
+ assert_se(tpm2_tpms_pcr_selection_is_empty(&s) == (expected_weight == 0));
+}
+
+TEST(tpms_pcr_selection_string_and_weight) {
+ TPMI_ALG_HASH sha1 = TPM2_ALG_SHA1, sha256 = TPM2_ALG_SHA256;
+
+ _test_tpms_sw(sha1, 0, "sha1()", 0);
+ _test_tpms_sw(sha1, 1, "sha1(0)", 1);
+ _test_tpms_sw(sha1, 0xf, "sha1(0+1+2+3)", 4);
+ _test_tpms_sw(sha1, 0x00ff00, "sha1(8+9+10+11+12+13+14+15)", 8);
+ _test_tpms_sw(sha1, 0xffffff, "sha1(0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23)", 24);
+ _test_tpms_sw(sha256, 0, "sha256()", 0);
+ _test_tpms_sw(sha256, 1, "sha256(0)", 1);
+ _test_tpms_sw(sha256, 7, "sha256(0+1+2)", 3);
+ _test_tpms_sw(sha256, 0xf00000, "sha256(20+21+22+23)", 4);
+ _test_tpms_sw(sha256, 0xffffff, "sha256(0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23)", 24);
+}
+
+static void _tpml_pcr_selection_add_tpms(TPMS_PCR_SELECTION s[], size_t count, TPML_PCR_SELECTION *ret) {
+ for (size_t i = 0; i < count; i++)
+ tpm2_tpml_pcr_selection_add_tpms_pcr_selection(ret, &s[i]);
+}
+
+static void _tpml_pcr_selection_sub_tpms(TPMS_PCR_SELECTION s[], size_t count, TPML_PCR_SELECTION *ret) {
+ for (size_t i = 0; i < count; i++)
+ tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(ret, &s[i]);
+}
+
+static void _test_tpml_sw(
+ TPMS_PCR_SELECTION s[],
+ size_t count,
+ size_t expected_count,
+ const char *expected_str,
+ size_t expected_weight) {
+
+ TPML_PCR_SELECTION l = {};
+ _tpml_pcr_selection_add_tpms(s, count, &l);
+ assert_se(l.count == expected_count);
+
+ _cleanup_free_ char *tpml_str = tpm2_tpml_pcr_selection_to_string(&l);
+ assert_se(streq(tpml_str, expected_str));
+
+ assert_se(tpm2_tpml_pcr_selection_weight(&l) == expected_weight);
+ assert_se(tpm2_tpml_pcr_selection_is_empty(&l) == (expected_weight == 0));
+}
+
+TEST(tpml_pcr_selection_string_and_weight) {
+ size_t size = 0xaa;
+ TPMI_ALG_HASH sha1 = TPM2_ALG_SHA1,
+ sha256 = TPM2_ALG_SHA256,
+ sha384 = TPM2_ALG_SHA384,
+ sha512 = TPM2_ALG_SHA512;
+ TPMS_PCR_SELECTION s[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, };
+
+ size = 0;
+ tpm2_tpms_pcr_selection_from_mask(0x000002, sha1 , &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0080f0, sha384, &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x010100, sha512, &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &s[size++]);
+ _test_tpml_sw(s,
+ size,
+ /* expected_count= */ 4,
+ "[sha1(1),sha384(4+5+6+7+15),sha512(8+16),sha256(16+17+18+19+20+21+22+23)]",
+ /* expected_weight= */ 16);
+
+ size = 0;
+ tpm2_tpms_pcr_selection_from_mask(0x0403aa, sha512, &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0080f0, sha256, &s[size++]);
+ _test_tpml_sw(s,
+ size,
+ /* expected_count= */ 2,
+ "[sha512(1+3+5+7+8+9+18),sha256(4+5+6+7+15)]",
+ /* expected_weight= */ 12);
+
+ size = 0;
+ /* Empty hashes should be ignored */
+ tpm2_tpms_pcr_selection_from_mask(0x0300ce, sha384, &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0xffffff, sha512, &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x000000, sha1 , &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x330010, sha256, &s[size++]);
+ _test_tpml_sw(s,
+ size,
+ /* expected_count= */ 3,
+ "[sha384(1+2+3+6+7+16+17),sha512(0+1+2+3+4+5+6+7+8+9+10+11+12+13+14+15+16+17+18+19+20+21+22+23),sha256(4+16+17+20+21)]",
+ /* expected_weight= */ 36);
+
+ size = 0;
+ /* Verify same-hash entries are properly combined. */
+ tpm2_tpms_pcr_selection_from_mask(0x000001, sha1 , &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x000001, sha256, &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x000010, sha1 , &s[size++]);
+ tpm2_tpms_pcr_selection_from_mask(0x000010, sha256, &s[size++]);
+ _test_tpml_sw(s,
+ size,
+ /* expected_count= */ 2,
+ "[sha1(0+4),sha256(0+4)]",
+ /* expected_weight= */ 4);
+}
+
+/* Test tpml add/sub by changing the tpms individually */
+static void _test_tpml_addsub_tpms(
+ TPML_PCR_SELECTION *start,
+ TPMS_PCR_SELECTION add[],
+ size_t add_count,
+ TPMS_PCR_SELECTION expected1[],
+ size_t expected1_count,
+ TPMS_PCR_SELECTION sub[],
+ size_t sub_count,
+ TPMS_PCR_SELECTION expected2[],
+ size_t expected2_count) {
+
+ TPML_PCR_SELECTION l = *start;
+
+ _tpml_pcr_selection_add_tpms(add, add_count, &l);
+ verify_tpml_pcr_selection(&l, expected1, expected1_count);
+
+ _tpml_pcr_selection_sub_tpms(sub, sub_count, &l);
+ verify_tpml_pcr_selection(&l, expected2, expected2_count);
+}
+
+/* Test tpml add/sub by creating new tpmls */
+static void _test_tpml_addsub_tpml(
+ TPML_PCR_SELECTION *start,
+ TPMS_PCR_SELECTION add[],
+ size_t add_count,
+ TPMS_PCR_SELECTION expected1[],
+ size_t expected1_count,
+ TPMS_PCR_SELECTION sub[],
+ size_t sub_count,
+ TPMS_PCR_SELECTION expected2[],
+ size_t expected2_count) {
+
+ TPML_PCR_SELECTION l = {};
+ tpm2_tpml_pcr_selection_add(&l, start);
+ assert_tpml_pcr_selection_eq(&l, start);
+
+ TPML_PCR_SELECTION addl = {};
+ _tpml_pcr_selection_add_tpms(add, add_count, &addl);
+ tpm2_tpml_pcr_selection_add(&l, &addl);
+
+ TPML_PCR_SELECTION e1 = {};
+ _tpml_pcr_selection_add_tpms(expected1, expected1_count, &e1);
+ assert_tpml_pcr_selection_eq(&l, &e1);
+
+ TPML_PCR_SELECTION subl = {};
+ _tpml_pcr_selection_add_tpms(sub, sub_count, &subl);
+ tpm2_tpml_pcr_selection_sub(&l, &subl);
+
+ TPML_PCR_SELECTION e2 = {};
+ _tpml_pcr_selection_add_tpms(expected2, expected2_count, &e2);
+ assert_tpml_pcr_selection_eq(&l, &e2);
+}
+
+#define _test_tpml_addsub(...) \
+ ({ \
+ _test_tpml_addsub_tpms(__VA_ARGS__); \
+ _test_tpml_addsub_tpml(__VA_ARGS__); \
+ })
+
+TEST(tpml_pcr_selection_add_sub) {
+ size_t add_count = 0xaa, expected1_count = 0xaa, sub_count = 0xaa, expected2_count = 0xaa;
+ TPMI_ALG_HASH sha1 = TPM2_ALG_SHA1,
+ sha256 = TPM2_ALG_SHA256,
+ sha384 = TPM2_ALG_SHA384,
+ sha512 = TPM2_ALG_SHA512;
+ TPML_PCR_SELECTION l = POISON_TPML;
+ TPMS_PCR_SELECTION add[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, },
+ sub[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, },
+ expected1[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, },
+ expected2[4] = { POISON_TPMS, POISON_TPMS, POISON_TPMS, POISON_TPMS, };
+
+ l = (TPML_PCR_SELECTION){};
+ add_count = 0;
+ expected1_count = 0;
+ sub_count = 0;
+ expected2_count = 0;
+ tpm2_tpms_pcr_selection_from_mask(0x010101, sha256, &add[add_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x101010, sha256, &add[add_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &add[add_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x111111, sha256, &expected1[expected1_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected1[expected1_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x000001, sha256, &sub[sub_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xff0000, sha512, &sub[sub_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x111110, sha256, &expected2[expected2_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected2[expected2_count++]);
+ _test_tpml_addsub(&l,
+ add, add_count,
+ expected1, expected1_count,
+ sub, sub_count,
+ expected2, expected2_count);
+
+ l = (TPML_PCR_SELECTION){
+ .count = 1,
+ .pcrSelections[0].hash = sha1,
+ .pcrSelections[0].sizeofSelect = 3,
+ .pcrSelections[0].pcrSelect[0] = 0xf0,
+ };
+ add_count = 0;
+ expected1_count = 0;
+ sub_count = 0;
+ expected2_count = 0;
+ tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &add[add_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xffff00, sha384, &add[add_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &add[add_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xf00000, sha1 , &add[add_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xf000f0, sha1 , &expected1[expected1_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &expected1[expected1_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xffff00, sha384, &expected1[expected1_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected1[expected1_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x00ffff, sha256, &sub[sub_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xf000f0, sha1 , &expected2[expected2_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xff0000, sha256, &expected2[expected2_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0xffff00, sha384, &expected2[expected2_count++]);
+ tpm2_tpms_pcr_selection_from_mask(0x0000ff, sha512, &expected2[expected2_count++]);
+ _test_tpml_addsub(&l,
+ add, add_count,
+ expected1, expected1_count,
+ sub, sub_count,
+ expected2, expected2_count);
+}
+
+#endif /* HAVE_TPM2 */
+
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -0,0 +1,189 @@
From 8aeeb8bb9c280de69a4d7ae46894304aaad73870 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 21 Feb 2023 16:31:59 -0500
Subject: [PATCH] tpm2: add tpm2_pcr_read()
(cherry picked from commit c57d8bc8717110ff343358be9fdfea1472fc360f)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 129 ++++++++++++++++++++++++++++-------------
1 file changed, 89 insertions(+), 40 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index cf62524e34..722ae3ca9c 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -744,6 +744,14 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) {
return weight;
}
+static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) {
+ if (!DEBUG_LOGGING || !l)
+ return;
+
+ _cleanup_free_ char *s = tpm2_tpml_pcr_selection_to_string(l);
+ log_debug("%s: %s", msg ?: "PCR selection", strna(s));
+}
+
static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
if (!DEBUG_LOGGING || !buffer || size == 0)
return;
@@ -792,26 +800,82 @@ static int tpm2_get_policy_digest(
return 0;
}
-static unsigned find_nth_bit(uint32_t mask, unsigned n) {
- uint32_t bit = 1;
+static int tpm2_pcr_read(
+ Tpm2Context *c,
+ const TPML_PCR_SELECTION *pcr_selection,
+ TPML_PCR_SELECTION *ret_pcr_selection,
+ TPM2B_DIGEST **ret_pcr_values,
+ size_t *ret_pcr_values_size) {
+
+ _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
+ TPML_PCR_SELECTION remaining, total_read = {};
+ size_t pcr_values_size = 0;
+ TSS2_RC rc;
- assert(n < 32);
+ assert(c);
+ assert(pcr_selection);
- /* Returns the bit index of the nth set bit, e.g. mask=0b101001, n=3 → 5 */
+ remaining = *pcr_selection;
+ while (!tpm2_tpml_pcr_selection_is_empty(&remaining)) {
+ _cleanup_(Esys_Freep) TPML_PCR_SELECTION *current_read = NULL;
+ _cleanup_(Esys_Freep) TPML_DIGEST *current_values = NULL;
- for (unsigned i = 0; i < sizeof(mask)*8; i++) {
+ tpm2_log_debug_tpml_pcr_selection(&remaining, "Reading PCR selection");
- if (bit & mask) {
- if (n == 0)
- return i;
+ /* Unfortunately, PCR_Read will not return more than 8 values. */
+ rc = sym_Esys_PCR_Read(
+ c->esys_context,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ &remaining,
+ NULL,
+ &current_read,
+ &current_values);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc));
- n--;
+ if (tpm2_tpml_pcr_selection_is_empty(current_read)) {
+ log_warning("TPM2 refused to read possibly unimplemented PCRs, ignoring.");
+ break;
}
- bit <<= 1;
+ tpm2_tpml_pcr_selection_sub(&remaining, current_read);
+ tpm2_tpml_pcr_selection_add(&total_read, current_read);
+
+ if (!GREEDY_REALLOC(pcr_values, pcr_values_size + current_values->count))
+ return log_oom();
+
+ memcpy_safe(&pcr_values[pcr_values_size], current_values->digests,
+ current_values->count * sizeof(TPM2B_DIGEST));
+ pcr_values_size += current_values->count;
+
+ if (DEBUG_LOGGING) {
+ unsigned i = 0;
+ FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, current_read) {
+ assert(i < current_values->count);
+
+ TPM2B_DIGEST *d = &current_values->digests[i];
+ i++;
+
+ TPML_PCR_SELECTION l;
+ tpm2_tpml_pcr_selection_from_mask(INDEX_TO_MASK(uint32_t, pcr), s->hash, &l);
+
+ _cleanup_free_ char *desc = tpm2_tpml_pcr_selection_to_string(&l);
+ tpm2_log_debug_digest(d, strna(desc));
+ }
+ }
}
- return UINT_MAX;
+ if (ret_pcr_selection)
+ *ret_pcr_selection = total_read;
+ if (ret_pcr_values)
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ if (ret_pcr_values_size)
+ *ret_pcr_values_size = pcr_values_size;
+
+ return 0;
}
static int tpm2_pcr_mask_good(
@@ -819,10 +883,10 @@ static int tpm2_pcr_mask_good(
TPMI_ALG_HASH bank,
uint32_t mask) {
- _cleanup_(Esys_Freep) TPML_DIGEST *pcr_values = NULL;
+ _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION selection;
- bool good = false;
- TSS2_RC rc;
+ size_t pcr_values_size = 0;
+ int r;
assert(c);
@@ -832,38 +896,23 @@ static int tpm2_pcr_mask_good(
tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
- rc = sym_Esys_PCR_Read(
- c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &selection,
- NULL,
- NULL,
- &pcr_values);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &pcr_values_size);
+ if (r < 0)
+ return r;
/* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */
- for (unsigned i = 0; i < pcr_values->count; i++) {
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *h = NULL;
- unsigned j;
+ unsigned i = 0;
+ FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, &selection) {
+ assert(i < pcr_values_size);
- h = hexmem(pcr_values->digests[i].buffer, pcr_values->digests[i].size);
- j = find_nth_bit(mask, i);
- assert(j != UINT_MAX);
-
- log_debug("PCR %u value: %s", j, strna(h));
- }
+ if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) &&
+ !memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size))
+ return true;
- if (!memeqbyte(0x00, pcr_values->digests[i].buffer, pcr_values->digests[i].size) &&
- !memeqbyte(0xFF, pcr_values->digests[i].buffer, pcr_values->digests[i].size))
- good = true;
+ i++;
}
- return good;
+ return false;
}
static int tpm2_bank_has24(const TPMS_PCR_SELECTION *selection) {

View File

@ -0,0 +1,204 @@
From 42b51de62cf4f4bbb92ef63deaa4cd9181f21beb Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 8 Dec 2022 16:57:47 -0500
Subject: [PATCH] tpm2: move openssl-required ifdef code out of policy-building
function
(cherry picked from commit 958982415808eeec956e79c4f7ca030af5af1b71)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 98 +++++++++++++++++++++++++-----------------
1 file changed, 58 insertions(+), 40 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 722ae3ca9c..ea04d0a892 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1262,21 +1262,38 @@ static int tpm2_make_encryption_session(
return 0;
}
+static int openssl_pubkey_to_tpm2_pubkey(
+ const void *pubkey,
+ size_t pubkey_size,
+ TPM2B_PUBLIC *output,
+ void **ret_fp,
+ size_t *ret_fp_size) {
+
#if HAVE_OPENSSL
-static int openssl_pubkey_to_tpm2_pubkey(EVP_PKEY *input, TPM2B_PUBLIC *output) {
#if OPENSSL_VERSION_MAJOR >= 3
_cleanup_(BN_freep) BIGNUM *n = NULL, *e = NULL;
#else
const BIGNUM *n = NULL, *e = NULL;
const RSA *rsa = NULL;
#endif
- int n_bytes, e_bytes;
+ int r, n_bytes, e_bytes;
- assert(input);
+ assert(pubkey);
+ assert(pubkey_size > 0);
assert(output);
/* Converts an OpenSSL public key to a structure that the TPM chip can process. */
+ _cleanup_fclose_ FILE *f = NULL;
+ f = fmemopen((void*) pubkey, pubkey_size, "r");
+ if (!f)
+ return log_oom();
+
+ _cleanup_(EVP_PKEY_freep) EVP_PKEY *input = NULL;
+ input = PEM_read_PUBKEY(f, NULL, NULL, NULL);
+ if (!input)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse PEM public key.");
+
if (EVP_PKEY_base_id(input) != EVP_PKEY_RSA)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Provided public key is not an RSA key.");
@@ -1343,22 +1360,38 @@ static int openssl_pubkey_to_tpm2_pubkey(EVP_PKEY *input, TPM2B_PUBLIC *output)
if (BN_bn2bin(e, (unsigned char*) &output->publicArea.parameters.rsaDetail.exponent) <= 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to convert RSA exponent.");
+ if (ret_fp) {
+ _cleanup_free_ void *fp = NULL;
+ size_t fp_size;
+
+ assert(ret_fp_size);
+
+ r = pubkey_fingerprint(input, EVP_sha256(), &fp, &fp_size);
+ if (r < 0)
+ return log_error_errno(r, "Failed to calculate public key fingerprint: %m");
+
+ *ret_fp = TAKE_PTR(fp);
+ *ret_fp_size = fp_size;
+ }
+
return 0;
+#else /* HAVE_OPENSSL */
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
+#endif
}
static int find_signature(
JsonVariant *v,
- uint16_t pcr_bank,
- uint32_t pcr_mask,
- EVP_PKEY *pk,
+ const TPML_PCR_SELECTION *pcr_selection,
+ const void *fp,
+ size_t fp_size,
const void *policy,
size_t policy_size,
void *ret_signature,
size_t *ret_signature_size) {
- _cleanup_free_ void *fp = NULL;
+#if HAVE_OPENSSL
JsonVariant *b, *i;
- size_t fp_size;
const char *k;
int r;
@@ -1368,6 +1401,12 @@ static int find_signature(
if (!json_variant_is_object(v))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Signature is not a JSON object.");
+ uint16_t pcr_bank = pcr_selection->pcrSelections[0].hash;
+ uint32_t pcr_mask;
+ r = tpm2_tpml_pcr_selection_to_mask(pcr_selection, pcr_bank, &pcr_mask);
+ if (r < 0)
+ return r;
+
k = tpm2_hash_alg_to_string(pcr_bank);
if (!k)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Don't know PCR bank %" PRIu16, pcr_bank);
@@ -1411,12 +1450,6 @@ static int find_signature(
if (r < 0)
return log_error_errno(r, "Failed to decode fingerprint in JSON data: %m");
- if (!fp) {
- r = pubkey_fingerprint(pk, EVP_sha256(), &fp, &fp_size);
- if (r < 0)
- return log_error_errno(r, "Failed to calculate public key fingerprint: %m");
- }
-
if (memcmp_nn(fp, fp_size, fpj_data, fpj_size) != 0)
continue; /* Not for this public key */
@@ -1441,8 +1474,10 @@ static int find_signature(
}
return log_error_errno(SYNTHETIC_ERRNO(ENXIO), "Couldn't find signature for this PCR bank, PCR index and public key.");
-}
+#else /* HAVE_OPENSSL */
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
#endif
+}
static int tpm2_make_policy_session(
Tpm2Context *c,
@@ -1504,21 +1539,6 @@ static int tpm2_make_policy_session(
}
}
-#if HAVE_OPENSSL
- _cleanup_(EVP_PKEY_freep) EVP_PKEY *pk = NULL;
- if (pubkey_size > 0) {
- /* If a pubkey is specified, load it to validate it, even if the PCR mask for this is
- * actually zero, and we are thus not going to use it. */
- _cleanup_fclose_ FILE *f = fmemopen((void*) pubkey, pubkey_size, "r");
- if (!f)
- return log_oom();
-
- pk = PEM_read_PUBKEY(f, NULL, NULL, NULL);
- if (!pk)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse PEM public key.");
- }
-#endif
-
_cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
r = tpm2_handle_new(c, &session);
if (r < 0)
@@ -1541,12 +1561,14 @@ static int tpm2_make_policy_session(
"Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
if (pubkey_pcr_mask != 0) {
-#if HAVE_OPENSSL
+ _cleanup_free_ void *fp = NULL;
+ size_t fp_size = 0;
+ TPM2B_PUBLIC pubkey_tpm2;
+
log_debug("Configuring public key based PCR policy.");
- /* First: load public key into the TPM */
- TPM2B_PUBLIC pubkey_tpm2;
- r = openssl_pubkey_to_tpm2_pubkey(pk, &pubkey_tpm2);
+ /* Convert the PEM key to TPM2 format */
+ r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
if (r < 0)
return r;
@@ -1614,9 +1636,8 @@ static int tpm2_make_policy_session(
r = find_signature(
signature_json,
- pcr_bank,
- pubkey_pcr_mask,
- pk,
+ &pcr_selection,
+ fp, fp_size,
approved_policy->buffer,
approved_policy->size,
&signature_raw,
@@ -1680,9 +1701,6 @@ static int tpm2_make_policy_session(
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
-#else
- return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled.");
-#endif
}
if (hash_pcr_mask != 0) {

View File

@ -0,0 +1,66 @@
From 5f280658b78d0fd80c520da9612043be2fd597a8 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 7 Dec 2022 11:23:59 -0500
Subject: [PATCH] tpm2: add tpm2_is_encryption_session()
(cherry picked from commit e976445d035e21afec2f64a7c825be5df1f664a0)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 16 ++++++++++++++++
src/shared/tpm2-util.h | 1 +
2 files changed, 17 insertions(+)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index ea04d0a892..b4c620ec53 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -46,6 +46,7 @@ TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySes
TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
+TSS2_RC (*sym_Esys_TRSess_GetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION *flags);
TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask);
TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name);
TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
@@ -82,6 +83,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_PolicyPCR),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
+ DLSYM_ARG(Esys_TRSess_GetAttributes),
DLSYM_ARG(Esys_TRSess_SetAttributes),
DLSYM_ARG(Esys_TR_GetName),
DLSYM_ARG(Esys_TR_SetAuth),
@@ -1179,6 +1181,20 @@ static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
sha256_finish_ctx(&hash, auth->buffer);
}
+static bool tpm2_is_encryption_session(Tpm2Context *c, const Tpm2Handle *session) {
+ TPMA_SESSION flags = 0;
+ TSS2_RC rc;
+
+ assert(c);
+ assert(session);
+
+ rc = sym_Esys_TRSess_GetAttributes(c->esys_context, session->esys_handle, &flags);
+ if (rc != TSS2_RC_SUCCESS)
+ return false;
+
+ return (flags & TPMA_SESSION_DECRYPT) && (flags & TPMA_SESSION_ENCRYPT);
+}
+
static int tpm2_make_encryption_session(
Tpm2Context *c,
const Tpm2Handle *primary,
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index c2532c61c2..cc43bbfbfb 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -50,6 +50,7 @@ extern TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR po
extern TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs);
extern TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle);
extern TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType);
+extern TSS2_RC (*sym_Esys_TRSess_GetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION *flags);
extern TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask);
extern TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name);
extern TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue);

View File

@ -0,0 +1,329 @@
From 1d027f4d13ed1c1fbd0766db5b4a544042f1336e Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 7 Dec 2022 11:23:59 -0500
Subject: [PATCH] tpm2: move policy building out of policy session creation
This retains the use of policy sessions instead of trial sessions
in most cases, based on the code comment that some TPMs do not
implement trial sessions correctly. However, it's likely that the
issue was not the TPMs, but our code's incorrect use of PolicyPCR
inside a trial session; we are not providing expected PCR values
with our call to PolicyPCR inside a trial session, but the spec
indicates that in a trial session, the TPM *may* return error if
the expected PCR value(s) are not provided. That may have been the
source of the original confusion about trial sessions.
More details:
https://github.com/systemd/systemd/pull/26357#pullrequestreview-1409983694
Also, future commits will replace the use of trial sessions with
policy calculations, which avoids the problem entirely.
(cherry picked from commit 2cd9d57548b0dadd52523df486d33aa4cf7c3b84)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 199 +++++++++++++++++++++++------------------
1 file changed, 112 insertions(+), 87 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index b4c620ec53..3960c0aed7 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1278,6 +1278,59 @@ static int tpm2_make_encryption_session(
return 0;
}
+static int tpm2_make_policy_session(
+ Tpm2Context *c,
+ const Tpm2Handle *primary,
+ const Tpm2Handle *encryption_session,
+ bool trial,
+ Tpm2Handle **ret_session) {
+
+ static const TPMT_SYM_DEF symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ };
+ TPM2_SE session_type = trial ? TPM2_SE_TRIAL : TPM2_SE_POLICY;
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(primary);
+ assert(encryption_session);
+ assert(ret_session);
+
+ if (!tpm2_is_encryption_session(c, encryption_session))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Missing encryption session");
+
+ log_debug("Starting policy session.");
+
+ _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ r = tpm2_handle_new(c, &session);
+ if (r < 0)
+ return r;
+
+ rc = sym_Esys_StartAuthSession(
+ c->esys_context,
+ primary->esys_handle,
+ ESYS_TR_NONE,
+ encryption_session->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ NULL,
+ session_type,
+ &symmetric,
+ TPM2_ALG_SHA256,
+ &session->esys_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ *ret_session = TAKE_PTR(session);
+
+ return 0;
+}
+
static int openssl_pubkey_to_tpm2_pubkey(
const void *pubkey,
size_t pubkey_size,
@@ -1495,87 +1548,36 @@ static int find_signature(
#endif
}
-static int tpm2_make_policy_session(
+static int tpm2_build_sealing_policy(
Tpm2Context *c,
- const Tpm2Handle *primary,
- const Tpm2Handle *parent_session,
- TPM2_SE session_type,
+ const Tpm2Handle *session,
uint32_t hash_pcr_mask,
- uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */
+ uint16_t pcr_bank,
const void *pubkey,
size_t pubkey_size,
uint32_t pubkey_pcr_mask,
JsonVariant *signature_json,
bool use_pin,
- Tpm2Handle **ret_session,
- TPM2B_DIGEST **ret_policy_digest,
- TPMI_ALG_HASH *ret_pcr_bank) {
+ TPM2B_DIGEST **ret_policy_digest) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
TSS2_RC rc;
int r;
assert(c);
+ assert(session);
assert(pubkey || pubkey_size == 0);
assert(pubkey_pcr_mask == 0 || pubkey_size > 0);
- log_debug("Starting authentication session.");
-
- /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's
- * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR
- * policies (since only then we need to shove PCR values into the policy that don't match current
- * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in
- * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is
- * non-zero, i.e. signed PCR policy is requested.
- *
- * One day we should switch to calculating policy hashes client side when trial mode is requested, to
- * avoid this mess. */
- if (session_type == TPM2_SE_TRIAL && pubkey_pcr_mask == 0)
- session_type = TPM2_SE_POLICY;
+ log_debug("Building sealing policy.");
if ((hash_pcr_mask | pubkey_pcr_mask) != 0) {
- /* We are told to configure a PCR policy of some form, let's determine/validate the PCR bank to use. */
-
- if (pcr_bank != UINT16_MAX) {
- r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask);
- if (r < 0)
- return r;
- if (r == 0)
- log_warning("Selected TPM2 PCRs are not initialized on this system, most likely due to a firmware issue. PCR policy is effectively not enforced. Proceeding anyway.");
- } else {
- /* No bank configured, pick automatically. Some TPM2 devices only can do SHA1. If we
- * detect that use that, but preferably use SHA256 */
- r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank);
- if (r < 0)
- return r;
- }
+ r = tpm2_pcr_mask_good(c, pcr_bank, hash_pcr_mask|pubkey_pcr_mask);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ log_warning("Selected TPM2 PCRs are not initialized on this system.");
}
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
- r = tpm2_handle_new(c, &session);
- if (r < 0)
- return r;
-
- rc = sym_Esys_StartAuthSession(
- c->esys_context,
- primary->esys_handle,
- ESYS_TR_NONE,
- parent_session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- session_type,
- &symmetric,
- TPM2_ALG_SHA256,
- &session->esys_handle);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
-
if (pubkey_pcr_mask != 0) {
_cleanup_free_ void *fp = NULL;
size_t fp_size = 0;
@@ -1756,12 +1758,6 @@ static int tpm2_make_policy_session(
if (r < 0)
return r;
- if (ret_session)
- *ret_session = TAKE_PTR(session);
-
- if (ret_pcr_bank)
- *ret_pcr_bank = pcr_bank;
-
return 0;
}
@@ -1830,33 +1826,57 @@ int tpm2_seal(const char *device,
if (r < 0)
return r;
+ TPMI_ALG_HASH pcr_bank = 0;
+ if (hash_pcr_mask | pubkey_pcr_mask) {
+ /* Some TPM2 devices only can do SHA1. Prefer SHA256 but allow SHA1. */
+ r = tpm2_get_best_pcr_bank(c, hash_pcr_mask|pubkey_pcr_mask, &pcr_bank);
+ if (r < 0)
+ return r;
+ }
+
_cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
r = tpm2_make_primary(c, &primary, 0, &primary_alg);
if (r < 0)
return r;
/* we cannot use the bind key before its created */
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
- r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &session);
+ _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+ r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &encryption_session);
if (r < 0)
return r;
- _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
- TPMI_ALG_HASH pcr_bank;
+ /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's
+ * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR
+ * policies (since only then we need to shove PCR values into the policy that don't match current
+ * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in
+ * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is
+ * non-zero, i.e. signed PCR policy is requested.
+ *
+ * One day we should switch to calculating policy hashes client side when trial mode is requested, to
+ * avoid this mess. */
+ bool trial = (pubkey_pcr_mask != 0);
+
+ _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
r = tpm2_make_policy_session(
c,
primary,
- session,
- TPM2_SE_TRIAL,
+ encryption_session,
+ trial,
+ &policy_session);
+ if (r < 0)
+ return r;
+
+ _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
+ r = tpm2_build_sealing_policy(
+ c,
+ policy_session,
hash_pcr_mask,
- /* pcr_bank= */ UINT16_MAX,
+ pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
/* signature_json= */ NULL,
!!pin,
- /* ret_session= */ NULL,
- &policy_digest,
- &pcr_bank);
+ &policy_digest);
if (r < 0)
return r;
@@ -1897,7 +1917,7 @@ int tpm2_seal(const char *device,
rc = sym_Esys_Create(
c->esys_context,
primary->esys_handle,
- session->esys_handle, /* use HMAC session to enable parameter encryption */
+ encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
ESYS_TR_NONE,
&hmac_sensitive,
@@ -2066,8 +2086,8 @@ int tpm2_unseal(const char *device,
sym_Tss2_RC_Decode(rc));
}
- _cleanup_tpm2_handle_ Tpm2Handle *hmac_session = NULL;
- r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &hmac_session);
+ _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+ r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &encryption_session);
if (r < 0)
return r;
@@ -2077,17 +2097,22 @@ int tpm2_unseal(const char *device,
r = tpm2_make_policy_session(
c,
primary,
- hmac_session,
- TPM2_SE_POLICY,
+ encryption_session,
+ /* trial= */ false,
+ &policy_session);
+ if (r < 0)
+ return r;
+
+ r = tpm2_build_sealing_policy(
+ c,
+ policy_session,
hash_pcr_mask,
pcr_bank,
pubkey, pubkey_size,
pubkey_pcr_mask,
signature,
!!pin,
- &policy_session,
- &policy_digest,
- /* ret_pcr_bank= */ NULL);
+ &policy_digest);
if (r < 0)
return r;
@@ -2105,7 +2130,7 @@ int tpm2_unseal(const char *device,
c->esys_context,
hmac_key->esys_handle,
policy_session->esys_handle,
- hmac_session->esys_handle, /* use HMAC session to enable parameter encryption */
+ encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
&unsealed);
if (rc == TSS2_RC_SUCCESS)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,76 @@
From ec8f1d04f4f10ac1cd2de0ef750199253cba2bf2 Mon Sep 17 00:00:00 2001
From: William Roberts <william.c.roberts@intel.com>
Date: Tue, 4 Apr 2023 11:14:17 -0500
Subject: [PATCH] tpm2: fix nits from PR #26185
Fixes:
- Comment style
- Alignment style
- cleanup macro usage
- incorrect error message[1]
1. Thanks to tempusfugit991@gmail.com for pointing out the error
message mistake.
Signed-off-by: William Roberts <william.c.roberts@intel.com>
(cherry picked from commit 96181b7a893da444fa9adcd1e7c95769d97c2a95)
Related: RHEL-16182
---
src/cryptenroll/cryptenroll-tpm2.c | 2 +-
src/shared/creds-util.c | 6 ++----
src/shared/tpm2-util.c | 4 ++--
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c
index ab43135dc7..a2f57ecff4 100644
--- a/src/cryptenroll/cryptenroll-tpm2.c
+++ b/src/cryptenroll/cryptenroll-tpm2.c
@@ -142,7 +142,7 @@ int enroll_tpm2(struct crypt_device *cd,
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *signature_json = NULL;
_cleanup_(erase_and_freep) char *base64_encoded = NULL;
- _cleanup_(freep) void *srk_buf = NULL;
+ _cleanup_free_ void *srk_buf = NULL;
size_t secret_size, blob_size, hash_size, pubkey_size = 0, srk_buf_size = 0;
_cleanup_free_ void *blob = NULL, *hash = NULL, *pubkey = NULL;
uint16_t pcr_bank, primary_alg;
diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c
index f55c4ac96e..902275215a 100644
--- a/src/shared/creds-util.c
+++ b/src/shared/creds-util.c
@@ -989,10 +989,8 @@ int decrypt_credential_and_warn(
le32toh(z->size));
}
- /*
- * TODO: Add the SRK data to the credential structure so it can be plumbed
- * through and used to verify the TPM session.
- */
+ // TODO: Add the SRK data to the credential structure so it can be plumbed
+ // through and used to verify the TPM session.
r = tpm2_unseal(tpm2_device,
le64toh(t->pcr_mask),
le16toh(t->pcr_bank),
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 55153e79f4..ab88b94f1f 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -556,7 +556,7 @@ static int tpm2_make_primary(
if (use_srk_model) {
r = make_lock_file("/run/systemd/tpm2-srk-init", LOCK_EX, &srk_lock);
if (r < 0)
- return log_error_errno(r, "Failed to take network zone lock: %m");
+ return log_error_errno(r, "Failed to take TPM SRK lock: %m");
}
/* Find existing SRK and use it if present */
@@ -571,7 +571,7 @@ static int tpm2_make_primary(
if (alg != 0 && alg != got_alg)
log_warning("Caller asked for specific algorithm %u, but existing SRK is %u, ignoring",
- alg, got_alg);
+ alg, got_alg);
if (ret_alg)
*ret_alg = alg;

View File

@ -0,0 +1,25 @@
From ee0427588b7052bee4c9fbb42eded8187466f7d1 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 9 Dec 2022 15:05:49 -0500
Subject: [PATCH] tpm2: replace magic number
(cherry picked from commit 1200777b21936bf5647a90504e0ea27e3ec3e42b)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index ab88b94f1f..002e2c01da 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -2097,7 +2097,7 @@ int tpm2_seal(const char *device,
.nameAlg = TPM2_ALG_SHA256,
.objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
- .unique.keyedHash.size = 32,
+ .unique.keyedHash.size = SHA256_DIGEST_SIZE,
.authPolicy = *policy_digest,
},
};

View File

@ -0,0 +1,271 @@
From 6aa706de80fa716b7d54adfc4094884707518d95 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 14 Dec 2022 10:46:13 -0500
Subject: [PATCH] tpm2: add tpm2_digest_*() functions
These functions allow extending (or initializing) a TPM2B_DIGEST with additional
data, using a specified hash operation. This is needed to perform hash
calculations instead of relying on the TPM to perform the calculations in
trial sessions.
(cherry picked from commit da92d39a8577e792075009782d419b423414ad6e)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 86 ++++++++++++++++++++++++++++++++---
src/shared/tpm2-util.h | 13 ++++++
src/test/test-tpm2.c | 100 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 194 insertions(+), 5 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 002e2c01da..d397c505f5 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1384,6 +1384,83 @@ static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
sha256_finish_ctx(&hash, auth->buffer);
}
+/* Hash data into the digest.
+ *
+ * If 'extend' is true, the hashing operation starts with the existing digest hash (and the digest is
+ * required to have a hash and its size must be correct). If 'extend' is false, the digest size is
+ * initialized to the correct size for 'alg' and the hashing operation does not include any existing digest
+ * hash. If 'extend' is false and no data is provided, the digest is initialized to a zero digest.
+ *
+ * On success, the digest hash will be updated with the hashing operation result and the digest size will be
+ * correct for 'alg'.
+ *
+ * This currently only provides SHA256, so 'alg' must be TPM2_ALG_SHA256. */
+int tpm2_digest_many(
+ TPMI_ALG_HASH alg,
+ TPM2B_DIGEST *digest,
+ const struct iovec data[],
+ size_t n_data,
+ bool extend) {
+
+ struct sha256_ctx ctx;
+
+ assert(digest);
+ assert(data || n_data == 0);
+
+ if (alg != TPM2_ALG_SHA256)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Hash algorithm not supported: 0x%x", alg);
+
+ if (extend && digest->size != SHA256_DIGEST_SIZE)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Digest size 0x%x, require 0x%x",
+ digest->size, (unsigned)SHA256_DIGEST_SIZE);
+
+ /* Since we're hardcoding SHA256 (for now), we can check this at compile time. */
+ assert_cc(sizeof(digest->buffer) >= SHA256_DIGEST_SIZE);
+
+ CLEANUP_ERASE(ctx);
+
+ sha256_init_ctx(&ctx);
+
+ if (extend)
+ sha256_process_bytes(digest->buffer, digest->size, &ctx);
+ else {
+ *digest = (TPM2B_DIGEST){ .size = SHA256_DIGEST_SIZE, };
+ if (n_data == 0) /* If not extending and no data, return zero hash */
+ return 0;
+ }
+
+ for (size_t i = 0; i < n_data; i++)
+ sha256_process_bytes(data[i].iov_base, data[i].iov_len, &ctx);
+
+ sha256_finish_ctx(&ctx, digest->buffer);
+
+ return 0;
+}
+
+/* Same as tpm2_digest_many() but data is contained in TPM2B_DIGEST[]. The digests may be any size digests. */
+int tpm2_digest_many_digests(
+ TPMI_ALG_HASH alg,
+ TPM2B_DIGEST *digest,
+ const TPM2B_DIGEST data[],
+ size_t n_data,
+ bool extend) {
+
+ _cleanup_free_ struct iovec *iovecs = NULL;
+
+ assert(data || n_data == 0);
+
+ iovecs = new(struct iovec, n_data);
+ if (!iovecs)
+ return log_oom();
+
+ for (size_t i = 0; i < n_data; i++)
+ iovecs[i] = IOVEC_MAKE((void*) data[i].buffer, data[i].size);
+
+ return tpm2_digest_many(alg, digest, iovecs, n_data, extend);
+}
+
static bool tpm2_is_encryption_session(Tpm2Context *c, const Tpm2Handle *session) {
TPMA_SESSION flags = 0;
TSS2_RC rc;
@@ -1868,11 +1945,10 @@ static int tpm2_build_sealing_policy(
/* TPM2_VerifySignature() will only verify the RSA part of the RSA+SHA256 signature,
* hence we need to do the SHA256 part ourselves, first */
- TPM2B_DIGEST signature_hash = {
- .size = SHA256_DIGEST_SIZE,
- };
- assert(sizeof(signature_hash.buffer) >= SHA256_DIGEST_SIZE);
- sha256_direct(approved_policy->buffer, approved_policy->size, signature_hash.buffer);
+ TPM2B_DIGEST signature_hash = *approved_policy;
+ r = tpm2_digest_rehash(TPM2_ALG_SHA256, &signature_hash);
+ if (r < 0)
+ return r;
TPMT_SIGNATURE policy_signature = {
.sigAlg = TPM2_ALG_RSASSA,
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 3c4d045197..2744cd13bb 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "bitfield.h"
+#include "io-util.h"
#include "json.h"
#include "macro.h"
#include "sha256.h"
@@ -72,6 +73,18 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz
int dlopen_tpm2(void);
+int tpm2_digest_many(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const struct iovec data[], size_t count, bool extend);
+static inline int tpm2_digest_buffer(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const void *data, size_t len, bool extend) {
+ return tpm2_digest_many(alg, digest, &IOVEC_MAKE((void*) data, len), 1, extend);
+}
+int tpm2_digest_many_digests(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const TPM2B_DIGEST data[], size_t count, bool extend);
+static inline int tpm2_digest_rehash(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
+ return tpm2_digest_many(alg, digest, NULL, 0, true);
+}
+static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
+ return tpm2_digest_many(alg, digest, NULL, 0, false);
+}
+
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 2c696e443d..dfbea7b19a 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -1,5 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "hexdecoct.h"
#include "tpm2-util.h"
#include "tests.h"
@@ -500,6 +501,105 @@ TEST(tpm2_get_primary_template) {
}
}
+static bool digest_check(const TPM2B_DIGEST *digest, const char *expect) {
+ _cleanup_free_ char *h = NULL;
+
+ assert_se(digest);
+ assert_se(expect);
+
+ h = hexmem(digest->buffer, digest->size);
+ assert_se(h);
+
+ return streq(expect, h);
+}
+
+static void digest_init_sha256(TPM2B_DIGEST *digest, const char *hash) {
+ _cleanup_free_ void *h = NULL;
+ size_t s = 0;
+
+ assert_se(strlen(hash) == SHA256_DIGEST_SIZE * 2);
+ assert_se(strlen(hash) <= sizeof(digest->buffer) * 2);
+
+ assert_se(unhexmem(hash, strlen(hash), &h, &s) == 0);
+ assert_se(s == SHA256_DIGEST_SIZE);
+
+ memcpy_safe(digest->buffer, h, s);
+ digest->size = s;
+
+ assert_se(digest_check(digest, hash));
+}
+
+TEST(digest_many) {
+ TPM2B_DIGEST d, d0, d1, d2, d3, d4;
+
+ digest_init_sha256(&d0, "0000000000000000000000000000000000000000000000000000000000000000");
+ digest_init_sha256(&d1, "17b7703d9d00776310ba032e88c1a8c2a9c630ebdd799db622f6631530789175");
+ digest_init_sha256(&d2, "12998c017066eb0d2a70b94e6ed3192985855ce390f321bbdb832022888bd251");
+ digest_init_sha256(&d3, "c3a65887fedd3fb4f5d0047e906dff830bcbd1293160909eb4b05f485e7387ad");
+ digest_init_sha256(&d4, "6491fb4bc08fc0b2ef47fc63db57e249917885e69d8c0d99667df83a59107a33");
+
+ /* tpm2_digest_init, tpm2_digest_rehash */
+ d = (TPM2B_DIGEST){ .size = 1, .buffer = { 2, }, };
+ assert_se(tpm2_digest_init(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "0000000000000000000000000000000000000000000000000000000000000000"));
+ assert_se(tpm2_digest_rehash(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925"));
+
+ d = d1;
+ assert_se(tpm2_digest_rehash(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "ab55014b5ace12ba70c3acc887db571585a83539aad3633d252a710f268f405c"));
+ assert_se(tpm2_digest_init(TPM2_ALG_SHA256, &d) == 0);
+ assert_se(digest_check(&d, "0000000000000000000000000000000000000000000000000000000000000000"));
+
+ /* tpm2_digest_many_digests */
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, &d2, 1, false) == 0);
+ assert_se(digest_check(&d, "56571a1be3fbeab18d215f549095915a004b5788ca0d535be668559129a76f25"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, &d2, 1, true) == 0);
+ assert_se(digest_check(&d, "99dedaee8f4d8d10a8be184399fde8740d5e17ff783ee5c288a4486e4ce3a1fe"));
+
+ const TPM2B_DIGEST da1[] = { d2, d3, };
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da1, ELEMENTSOF(da1), false) == 0);
+ assert_se(digest_check(&d, "525aa13ef9a61827778ec3acf16fbb23b65ae8770b8fb2684d3a33f9457dd6d8"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da1, ELEMENTSOF(da1), true) == 0);
+ assert_se(digest_check(&d, "399ca2aa98963d1bd81a2b58a7e5cda24bba1be88fb4da9aa73d97706846566b"));
+
+ const TPM2B_DIGEST da2[] = { d3, d2, d0 };
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da2, ELEMENTSOF(da2), false) == 0);
+ assert_se(digest_check(&d, "b26fd22db74d4cd896bff01c61aa498a575e4a553a7fb5a322a5fee36954313e"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da2, ELEMENTSOF(da2), true) == 0);
+ assert_se(digest_check(&d, "091e79a5b09d4048df49a680f966f3ff67910afe185c3baf9704c9ca45bcf259"));
+
+ const TPM2B_DIGEST da3[] = { d4, d4, d4, d4, d3, d4, d4, d4, d4, };
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da3, ELEMENTSOF(da3), false) == 0);
+ assert_se(digest_check(&d, "8eca947641b6002df79dfb571a7f78b7d0a61370a366f722386dfbe444d18830"));
+ assert_se(tpm2_digest_many_digests(TPM2_ALG_SHA256, &d, da3, ELEMENTSOF(da3), true) == 0);
+ assert_se(digest_check(&d, "f9ba17bc0bbe8794e9bcbf112e4d59a11eb68fffbcd5516a746e4857829dff04"));
+
+ /* tpm2_digest_buffer */
+ const uint8_t b1[] = { 1, 2, 3, 4, };
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b1, ELEMENTSOF(b1), false) == 0);
+ assert_se(digest_check(&d, "9f64a747e1b97f131fabb6b447296c9b6f0201e79fb3c5356e6c77e89b6a806a"));
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b1, ELEMENTSOF(b1), true) == 0);
+ assert_se(digest_check(&d, "ff3bd307b287e9b29bb572f6ccfd19deb0106d0c4c3c5cfe8a1d03a396092ed4"));
+
+ const void *b2 = d2.buffer;
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b2, d2.size, false) == 0);
+ assert_se(digest_check(&d, "56571a1be3fbeab18d215f549095915a004b5788ca0d535be668559129a76f25"));
+ assert_se(tpm2_digest_buffer(TPM2_ALG_SHA256, &d, b2, d2.size, true) == 0);
+ assert_se(digest_check(&d, "99dedaee8f4d8d10a8be184399fde8740d5e17ff783ee5c288a4486e4ce3a1fe"));
+
+ /* tpm2_digest_many */
+ const struct iovec iov1[] = {
+ IOVEC_MAKE((void*) b1, ELEMENTSOF(b1)),
+ IOVEC_MAKE(d2.buffer, d2.size),
+ IOVEC_MAKE(d3.buffer, d3.size),
+ };
+ assert_se(tpm2_digest_many(TPM2_ALG_SHA256, &d, iov1, ELEMENTSOF(iov1), false) == 0);
+ assert_se(digest_check(&d, "cd7bde4a047af976b6f1b282309976229be59f96a78aa186de32a1aee488ab09"));
+ assert_se(tpm2_digest_many(TPM2_ALG_SHA256, &d, iov1, ELEMENTSOF(iov1), true) == 0);
+ assert_se(digest_check(&d, "02ecb0628264235111e0053e271092981c8b15d59cd46617836bee3149a4ecb0"));
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -0,0 +1,65 @@
From 3bd6a10eeaaa863d54573226828f6e0c204442ee Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 14 Dec 2022 10:46:13 -0500
Subject: [PATCH] tpm2: replace hash_pin() with tpm2_digest_*() functions
The hash_pin() function is just a specific use case of the digest functions.
(cherry picked from commit 94a4ff2dc1e753fc5715b5d240092e38456898f0)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 26 ++++++++------------------
1 file changed, 8 insertions(+), 18 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index d397c505f5..f1950189d5 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1369,21 +1369,6 @@ int tpm2_get_good_pcr_banks_strv(
return 0;
}
-static void hash_pin(const char *pin, size_t len, TPM2B_AUTH *auth) {
- struct sha256_ctx hash;
-
- assert(auth);
- assert(pin);
-
- auth->size = SHA256_DIGEST_SIZE;
-
- CLEANUP_ERASE(hash);
-
- sha256_init_ctx(&hash);
- sha256_process_bytes(pin, len, &hash);
- sha256_finish_ctx(&hash, auth->buffer);
-}
-
/* Hash data into the digest.
*
* If 'extend' is true, the hashing operation starts with the existing digest hash (and the digest is
@@ -1507,7 +1492,9 @@ static int tpm2_make_encryption_session(
CLEANUP_ERASE(auth);
- hash_pin(pin, strlen(pin), &auth);
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &auth, pin, strlen(pin), /* extend= */ false);
+ if (r < 0)
+ return r;
rc = sym_Esys_TR_SetAuth(c->esys_context, bind_key->esys_handle, &auth);
if (rc != TSS2_RC_SUCCESS)
@@ -2182,8 +2169,11 @@ int tpm2_seal(const char *device,
.size = sizeof(hmac_sensitive.sensitive),
.sensitive.data.size = 32,
};
- if (pin)
- hash_pin(pin, strlen(pin), &hmac_sensitive.sensitive.userAuth);
+ if (pin) {
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &hmac_sensitive.sensitive.userAuth, pin, strlen(pin), /* extend= */ false);
+ if (r < 0)
+ return r;
+ }
assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);

View File

@ -0,0 +1,120 @@
From 50375de7b310d361a71521c6abf8e4251027dd3b Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 6 Dec 2022 13:16:43 -0500
Subject: [PATCH] tpm2: add tpm2_set_auth()
This provides a function to perform the SetAuth TPM function, which provides
the authValue for a key.
(cherry picked from commit 409a65f82901ace5799da0f22f10056105e062fa)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 65 ++++++++++++++++++++++++------------------
1 file changed, 38 insertions(+), 27 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index f1950189d5..ac8569878c 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1446,6 +1446,31 @@ int tpm2_digest_many_digests(
return tpm2_digest_many(alg, digest, iovecs, n_data, extend);
}
+static int tpm2_set_auth(Tpm2Context *c, const Tpm2Handle *handle, const char *pin) {
+ TPM2B_AUTH auth = {};
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(handle);
+
+ if (!pin)
+ return 0;
+
+ CLEANUP_ERASE(auth);
+
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &auth, pin, strlen(pin), /* extend= */ false);
+ if (r < 0)
+ return r;
+
+ rc = sym_Esys_TR_SetAuth(c->esys_context, handle->esys_handle, &auth);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to load PIN in TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ return 0;
+}
+
static bool tpm2_is_encryption_session(Tpm2Context *c, const Tpm2Handle *session) {
TPMA_SESSION flags = 0;
TSS2_RC rc;
@@ -1464,7 +1489,6 @@ static int tpm2_make_encryption_session(
Tpm2Context *c,
const Tpm2Handle *primary,
const Tpm2Handle *bind_key,
- const char *pin,
Tpm2Handle **ret_session) {
static const TPMT_SYM_DEF symmetric = {
@@ -1480,30 +1504,6 @@ static int tpm2_make_encryption_session(
assert(c);
assert(ret_session);
- /*
- * if a pin is set for the seal object, use it to bind the session
- * key to that object. This prevents active bus interposers from
- * faking a TPM and seeing the unsealed value. An active interposer
- * could fake a TPM, satisfying the encrypted session, and just
- * forward everything to the *real* TPM.
- */
- if (pin) {
- TPM2B_AUTH auth = {};
-
- CLEANUP_ERASE(auth);
-
- r = tpm2_digest_buffer(TPM2_ALG_SHA256, &auth, pin, strlen(pin), /* extend= */ false);
- if (r < 0)
- return r;
-
- rc = sym_Esys_TR_SetAuth(c->esys_context, bind_key->esys_handle, &auth);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(
- SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to load PIN in TPM: %s",
- sym_Tss2_RC_Decode(rc));
- }
-
log_debug("Starting HMAC encryption session.");
/* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which
@@ -2111,7 +2111,7 @@ int tpm2_seal(const char *device,
/* we cannot use the bind key before its created */
_cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
- r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, NULL, &encryption_session);
+ r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, &encryption_session);
if (r < 0)
return r;
@@ -2408,8 +2408,19 @@ int tpm2_unseal(const char *device,
sym_Tss2_RC_Decode(rc));
}
+ /*
+ * if a pin is set for the seal object, use it to bind the session
+ * key to that object. This prevents active bus interposers from
+ * faking a TPM and seeing the unsealed value. An active interposer
+ * could fake a TPM, satisfying the encrypted session, and just
+ * forward everything to the *real* TPM.
+ */
+ r = tpm2_set_auth(c, hmac_key, pin);
+ if (r < 0)
+ return r;
+
_cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
- r = tpm2_make_encryption_session(c, primary, hmac_key, pin, &encryption_session);
+ r = tpm2_make_encryption_session(c, primary, hmac_key, &encryption_session);
if (r < 0)
return r;

View File

@ -0,0 +1,251 @@
From 4f9f6fce5c45c8f9aabe73a428420cfb380e9974 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 14 Dec 2022 10:46:13 -0500
Subject: [PATCH] tpm2: add tpm2_get_name()
This adds functions to get the "name" of a key. The key "name", as defined
by the TPM2 spec, includes its entire public area (with attribute fields),
not only its key fingerprint.
A function is added to calculate the name of a provided key public area,
as well as a function to get the name of a key which is present in the TPM.
(cherry picked from commit dbae4b9535ceb0a94affe34eab700900f4fbd93d)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 115 ++++++++++++++++++++++++++++++++++++++---
src/shared/tpm2-util.h | 4 ++
src/test/test-tpm2.c | 43 +++++++++++++++
3 files changed, 154 insertions(+), 8 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index ac8569878c..629e1bc5ce 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -65,6 +65,8 @@ TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t b
TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
int dlopen_tpm2(void) {
int r;
@@ -114,7 +116,9 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Marshal),
DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Unmarshal),
DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Marshal),
- DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal));
+ DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal),
+ DLSYM_ARG(Tss2_MU_TPMT_HA_Marshal),
+ DLSYM_ARG(Tss2_MU_TPMT_PUBLIC_Marshal));
}
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
@@ -970,6 +974,11 @@ static void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) {
tpm2_log_debug_buffer(digest->buffer, digest->size, msg ?: "Digest");
}
+static void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) {
+ if (name)
+ tpm2_log_debug_buffer(name->name, name->size, msg ?: "Name");
+}
+
static int tpm2_get_policy_digest(
Tpm2Context *c,
const Tpm2Handle *session,
@@ -1815,6 +1824,100 @@ static int find_signature(
#endif
}
+/* Calculates the "name" of a public key.
+ *
+ * As specified in TPM2 spec "Part 1: Architecture", a key's "name" is its nameAlg value followed by a hash
+ * of its TPM2 public area, all properly marshalled. This allows a key's "name" to be dependent not only on
+ * the key fingerprint, but also on the TPM2-specific fields that associated with the key (i.e. all fields in
+ * TPMT_PUBLIC). Note that this means an existing key may not change any of its TPMT_PUBLIC fields, since
+ * that would also change the key name.
+ *
+ * Since we (currently) hardcode to always using SHA256 for hashing, this returns an error if the public key
+ * nameAlg is not TPM2_ALG_SHA256. */
+int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name) {
+ TSS2_RC rc;
+ int r;
+
+ assert(public);
+ assert(ret_name);
+
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support not installed: %m");
+
+ if (public->nameAlg != TPM2_ALG_SHA256)
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Unsupported nameAlg: 0x%x",
+ public->nameAlg);
+
+ _cleanup_free_ uint8_t *buf = NULL;
+ size_t size = 0;
+
+ buf = (uint8_t*) new(TPMT_PUBLIC, 1);
+ if (!buf)
+ return log_oom();
+
+ rc = sym_Tss2_MU_TPMT_PUBLIC_Marshal(public, buf, sizeof(TPMT_PUBLIC), &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc));
+
+ TPM2B_DIGEST digest = {};
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &digest, buf, size, /* extend= */ false);
+ if (r < 0)
+ return r;
+
+ TPMT_HA ha = {
+ .hashAlg = TPM2_ALG_SHA256,
+ };
+ assert(digest.size <= sizeof(ha.digest.sha256));
+ memcpy_safe(ha.digest.sha256, digest.buffer, digest.size);
+
+ TPM2B_NAME name;
+ size = 0;
+ rc = sym_Tss2_MU_TPMT_HA_Marshal(&ha, name.name, sizeof(name.name), &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal key name: %s", sym_Tss2_RC_Decode(rc));
+ name.size = size;
+
+ tpm2_log_debug_name(&name, "Calculated name");
+
+ *ret_name = name;
+
+ return 0;
+}
+
+/* Get the "name" of a key from the TPM.
+ *
+ * The "name" of a key is explained above in tpm2_calculate_name().
+ *
+ * The handle must reference a key already present in the TPM. It may be either a public key only, or a
+ * public/private keypair. */
+static int tpm2_get_name(
+ Tpm2Context *c,
+ const Tpm2Handle *handle,
+ TPM2B_NAME **ret_name) {
+
+ _cleanup_(Esys_Freep) TPM2B_NAME *name = NULL;
+ TSS2_RC rc;
+
+ assert(c);
+ assert(handle);
+ assert(ret_name);
+
+ rc = sym_Esys_TR_GetName(c->esys_context, handle->esys_handle, &name);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ tpm2_log_debug_name(name, "Object name");
+
+ *ret_name = TAKE_PTR(name);
+
+ return 0;
+}
+
static int tpm2_build_sealing_policy(
Tpm2Context *c,
const Tpm2Handle *session,
@@ -1883,13 +1986,9 @@ static int tpm2_build_sealing_policy(
/* Acquire the "name" of what we just loaded */
_cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
- rc = sym_Esys_TR_GetName(
- c->esys_context,
- pubkey_handle->esys_handle,
- &pubkey_name);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get name of public key from TPM: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_get_name(c, pubkey_handle, &pubkey_name);
+ if (r < 0)
+ return r;
/* Put together the PCR policy we want to use */
TPML_PCR_SELECTION pcr_selection;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 2744cd13bb..a23f383e5a 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -70,6 +70,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, ui
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest);
+extern TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
+extern TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
int dlopen_tpm2(void);
@@ -85,6 +87,8 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
return tpm2_digest_many(alg, digest, NULL, 0, false);
}
+int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
+
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index dfbea7b19a..2515f79e57 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -600,6 +600,49 @@ TEST(digest_many) {
assert_se(digest_check(&d, "02ecb0628264235111e0053e271092981c8b15d59cd46617836bee3149a4ecb0"));
}
+static void tpm2b_public_init(TPM2B_PUBLIC *public) {
+ TPMT_PUBLIC tpmt = {
+ .type = TPM2_ALG_RSA,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .parameters.rsaDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ },
+ .scheme.scheme = TPM2_ALG_NULL,
+ .keyBits = 2048,
+ },
+ };
+
+ const char *key = "9ec7341c52093ac40a1965a5df10432513c539adcf905e30577ab6ebc88ffe53cd08cef12ed9bec6125432f4fada3629b8b96d31b8f507aa35029188fe396da823fcb236027f7fbb01b0da3d87be7f999390449ced604bdf7e26c48657cc0671000f1147da195c3861c96642e54427cb7a11572e07567ec3fd6316978abc4bd92b27bb0a0e4958e599804eeb41d682b3b7fc1f960209f80a4fb8a1b64abfd96bf5d554e73cdd6ad1c8becb4fcf5e8f0c3e621d210e5e2f308f6520ad9a966779231b99f06c5989e5a23a9415c8808ab89ce81117632e2f8461cd4428bded40979236aeadafe8de3f51660a45e1dbc87694e6a36360201cca3ff9e7263e712727";
+ _cleanup_free_ void *mem = NULL;
+ size_t len = 0;
+ assert_se(unhexmem(key, strlen(key), &mem, &len) == 0);
+ assert_se(len <= sizeof(tpmt.unique.rsa.buffer));
+ memcpy_safe(tpmt.unique.rsa.buffer, mem, len);
+ tpmt.unique.rsa.size = len;
+
+ public->publicArea = tpmt;
+}
+
+TEST(calculate_name) {
+ TPM2B_PUBLIC public;
+ TPM2B_NAME name;
+
+ tpm2b_public_init(&public);
+ assert_se(tpm2_calculate_name(&public.publicArea, &name) == 0);
+ assert_se(name.size == SHA256_DIGEST_SIZE + 2);
+
+ const char *expect = "000be78f74a470dd92e979ca067cdb2293a35f075e8560b436bd2ccea5da21486a07";
+ _cleanup_free_ char *h = hexmem(name.name, name.size);
+ assert_se(h);
+
+ assert_se(strlen(expect) == strlen(h));
+ assert_se(streq(expect, h));
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -0,0 +1,87 @@
From 9c49d0b64ff66bf4d8199abf7989c0a50bb79757 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 11 May 2023 15:33:31 -0400
Subject: [PATCH] tpm2: rename pcr_values_size vars to n_pcr_values
Using the n_ prefix is more appropriate/conventional than the _size suffix.
No functional change, this is cosmetic only.
(cherry picked from commit c648a4b85e9ef71098afba3c7ac36a31f9372a4d)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 629e1bc5ce..a6fab45898 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1019,11 +1019,11 @@ static int tpm2_pcr_read(
const TPML_PCR_SELECTION *pcr_selection,
TPML_PCR_SELECTION *ret_pcr_selection,
TPM2B_DIGEST **ret_pcr_values,
- size_t *ret_pcr_values_size) {
+ size_t *ret_n_pcr_values) {
_cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION remaining, total_read = {};
- size_t pcr_values_size = 0;
+ size_t n_pcr_values = 0;
TSS2_RC rc;
assert(c);
@@ -1058,12 +1058,12 @@ static int tpm2_pcr_read(
tpm2_tpml_pcr_selection_sub(&remaining, current_read);
tpm2_tpml_pcr_selection_add(&total_read, current_read);
- if (!GREEDY_REALLOC(pcr_values, pcr_values_size + current_values->count))
+ if (!GREEDY_REALLOC(pcr_values, n_pcr_values + current_values->count))
return log_oom();
- memcpy_safe(&pcr_values[pcr_values_size], current_values->digests,
+ memcpy_safe(&pcr_values[n_pcr_values], current_values->digests,
current_values->count * sizeof(TPM2B_DIGEST));
- pcr_values_size += current_values->count;
+ n_pcr_values += current_values->count;
if (DEBUG_LOGGING) {
unsigned i = 0;
@@ -1086,8 +1086,8 @@ static int tpm2_pcr_read(
*ret_pcr_selection = total_read;
if (ret_pcr_values)
*ret_pcr_values = TAKE_PTR(pcr_values);
- if (ret_pcr_values_size)
- *ret_pcr_values_size = pcr_values_size;
+ if (ret_n_pcr_values)
+ *ret_n_pcr_values = n_pcr_values;
return 0;
}
@@ -1099,7 +1099,7 @@ static int tpm2_pcr_mask_good(
_cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION selection;
- size_t pcr_values_size = 0;
+ size_t n_pcr_values = 0;
int r;
assert(c);
@@ -1110,14 +1110,14 @@ static int tpm2_pcr_mask_good(
tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
- r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &pcr_values_size);
+ r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &n_pcr_values);
if (r < 0)
return r;
/* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */
unsigned i = 0;
FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, &selection) {
- assert(i < pcr_values_size);
+ assert(i < n_pcr_values);
if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) &&
!memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size))

View File

@ -0,0 +1,272 @@
From 9ffb6893d40f23d0a1fd59176270892c21b7bda3 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 8 Dec 2022 17:56:11 -0500
Subject: [PATCH] tpm2: add tpm2_policy_pcr()
This adds functions to get the digest for a PolicyPCR operation. For building
a policy hash, this provides a function to calculate the hash; and for building
a policy hash to satisfy the authPolicy for an existing object, this provides a
function to perform PolicyPCR with an existing session.
(cherry picked from commit dcbc4674e3daea2d34d02de5a76d4a19bca7545f)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 119 +++++++++++++++++++++++++++++++----------
src/shared/tpm2-util.h | 3 ++
src/test/test-tpm2.c | 48 +++++++++++++++++
3 files changed, 143 insertions(+), 27 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index a6fab45898..1aa49a7232 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -61,10 +61,12 @@ TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR keyHandle
const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
+TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
@@ -113,10 +115,12 @@ int dlopen_tpm2(void) {
return dlopen_many_sym_or_warn(
&libtss2_mu_dl, "libtss2-mu.so.0", LOG_DEBUG,
+ DLSYM_ARG(Tss2_MU_TPM2_CC_Marshal),
DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Marshal),
DLSYM_ARG(Tss2_MU_TPM2B_PRIVATE_Unmarshal),
DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Marshal),
DLSYM_ARG(Tss2_MU_TPM2B_PUBLIC_Unmarshal),
+ DLSYM_ARG(Tss2_MU_TPML_PCR_SELECTION_Marshal),
DLSYM_ARG(Tss2_MU_TPMT_HA_Marshal),
DLSYM_ARG(Tss2_MU_TPMT_PUBLIC_Marshal));
}
@@ -1918,6 +1922,90 @@ static int tpm2_get_name(
return 0;
}
+/* Extend 'digest' with the PolicyPCR calculated hash. */
+int tpm2_calculate_policy_pcr(
+ const TPML_PCR_SELECTION *pcr_selection,
+ const TPM2B_DIGEST pcr_values[],
+ size_t n_pcr_values,
+ TPM2B_DIGEST *digest) {
+
+ TPM2_CC command = TPM2_CC_PolicyPCR;
+ TSS2_RC rc;
+ int r;
+
+ assert(pcr_selection);
+ assert(pcr_values || n_pcr_values == 0);
+ assert(digest);
+ assert(digest->size == SHA256_DIGEST_SIZE);
+
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support not installed: %m");
+
+ TPM2B_DIGEST hash = {};
+ r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, pcr_values, n_pcr_values, /* extend= */ false);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ uint8_t *buf = NULL;
+ size_t size = 0, maxsize = sizeof(command) + sizeof(*pcr_selection);
+
+ buf = malloc(maxsize);
+ if (!buf)
+ return log_oom();
+
+ rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, maxsize, &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal PolicyPCR command: %s", sym_Tss2_RC_Decode(rc));
+
+ rc = sym_Tss2_MU_TPML_PCR_SELECTION_Marshal(pcr_selection, buf, maxsize, &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal PCR selection: %s", sym_Tss2_RC_Decode(rc));
+
+ struct iovec data[] = {
+ IOVEC_MAKE(buf, size),
+ IOVEC_MAKE(hash.buffer, hash.size),
+ };
+ r = tpm2_digest_many(TPM2_ALG_SHA256, digest, data, ELEMENTSOF(data), /* extend= */ true);
+ if (r < 0)
+ return r;
+
+ tpm2_log_debug_digest(digest, "PolicyPCR calculated digest");
+
+ return 0;
+}
+
+static int tpm2_policy_pcr(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ const TPML_PCR_SELECTION *pcr_selection,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+ TSS2_RC rc;
+
+ assert(c);
+ assert(session);
+ assert(pcr_selection);
+
+ log_debug("Adding PCR hash policy.");
+
+ rc = sym_Esys_PolicyPCR(
+ c->esys_context,
+ session->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ NULL,
+ pcr_selection);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ return tpm2_get_policy_digest(c, session, ret_policy_digest);
+}
+
static int tpm2_build_sealing_policy(
Tpm2Context *c,
const Tpm2Handle *session,
@@ -1993,21 +2081,8 @@ static int tpm2_build_sealing_policy(
/* Put together the PCR policy we want to use */
TPML_PCR_SELECTION pcr_selection;
tpm2_tpml_pcr_selection_from_mask(pubkey_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
- rc = sym_Esys_PolicyPCR(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- &pcr_selection);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
-
- /* Get the policy hash of the PCR policy */
_cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
- r = tpm2_get_policy_digest(c, session, &approved_policy);
+ r = tpm2_policy_pcr(c, session, &pcr_selection, &approved_policy);
if (r < 0)
return r;
@@ -2087,21 +2162,11 @@ static int tpm2_build_sealing_policy(
}
if (hash_pcr_mask != 0) {
- log_debug("Configuring hash-based PCR policy.");
-
TPML_PCR_SELECTION pcr_selection;
tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
- rc = sym_Esys_PolicyPCR(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- &pcr_selection);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add PCR policy to TPM: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_policy_pcr(c, session, &pcr_selection, NULL);
+ if (r < 0)
+ return r;
}
if (use_pin) {
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index a23f383e5a..80c00af141 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -66,10 +66,12 @@ extern TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR ke
extern const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc);
+extern TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest);
+extern TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
extern TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
@@ -88,6 +90,7 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
}
int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
+int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest);
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 2515f79e57..c2e074b5f9 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -643,6 +643,54 @@ TEST(calculate_name) {
assert_se(streq(expect, h));
}
+TEST(calculate_policy_pcr) {
+ TPML_PCR_SELECTION pcr_selection;
+ TPM2B_DIGEST pcr_values[16];
+ TPM2B_DIGEST d;
+ uint32_t pcr_mask;
+
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ pcr_mask = (1<<4) | (1<<7) | (1<<8);
+ tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
+ digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0);
+ assert_se(digest_check(&d, "76532a0e16f7e6bf6b02918c11f75d99d729fab0cc81d0df2c4284a2c4fe6e05"));
+
+ pcr_mask = (1<<4) | (1<<7) | (1<<8);
+ tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
+ digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0);
+ assert_se(digest_check(&d, "97e64bcabb64c1fa4b726528644926c8029f5b4458b0575c98c04fe225629a0b"));
+
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ pcr_mask = 0xffff;
+ tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
+ digest_init_sha256(&pcr_values[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec");
+ digest_init_sha256(&pcr_values[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d");
+ digest_init_sha256(&pcr_values[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed");
+ digest_init_sha256(&pcr_values[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init_sha256(&pcr_values[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init_sha256(&pcr_values[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5");
+ digest_init_sha256(&pcr_values[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init_sha256(&pcr_values[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init_sha256(&pcr_values[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ digest_init_sha256(&pcr_values[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b");
+ digest_init_sha256(&pcr_values[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2");
+ digest_init_sha256(&pcr_values[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44");
+ digest_init_sha256(&pcr_values[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c");
+ digest_init_sha256(&pcr_values[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4");
+ digest_init_sha256(&pcr_values[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e");
+ digest_init_sha256(&pcr_values[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137");
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0);
+ assert_se(digest_check(&d, "22be4f1674f792d6345cea9427701068f0e8d9f42755dcc0e927e545a68f9c13"));
+ assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0);
+ assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -0,0 +1,145 @@
From 4bdaf980b4c5eb519b0c762179015c7b96d51037 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 9 Dec 2022 14:59:05 -0500
Subject: [PATCH] tpm2: add tpm2_policy_auth_value()
This adds functions to get the digest for a PolicyAuthValue operation. For
building a policy hash, this provides a function to calculate the hash; and for
building a policy hash to satisfy the authPolicy for an existing object, this
provides a function to perform PolicyAuthValue with an existing session.
(cherry picked from commit 8a716354bb97c9a220cf95aef0e78f66abd33584)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 75 +++++++++++++++++++++++++++++++++++-------
src/shared/tpm2-util.h | 1 +
src/test/test-tpm2.c | 10 ++++++
3 files changed, 74 insertions(+), 12 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 1aa49a7232..35dfa3f371 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1922,6 +1922,66 @@ static int tpm2_get_name(
return 0;
}
+/* Extend 'digest' with the PolicyAuthValue calculated hash. */
+int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest) {
+ TPM2_CC command = TPM2_CC_PolicyAuthValue;
+ TSS2_RC rc;
+ int r;
+
+ assert(digest);
+ assert(digest->size == SHA256_DIGEST_SIZE);
+
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support not installed: %m");
+
+ uint8_t buf[sizeof(command)];
+ size_t offset = 0;
+
+ rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, sizeof(buf), &offset);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal PolicyAuthValue command: %s", sym_Tss2_RC_Decode(rc));
+
+ if (offset != sizeof(command))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Offset 0x%zx wrong after marshalling PolicyAuthValue command", offset);
+
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, digest, buf, offset, /* extend= */ true);
+ if (r < 0)
+ return r;
+
+ tpm2_log_debug_digest(digest, "PolicyAuthValue calculated digest");
+
+ return 0;
+}
+
+static int tpm2_policy_auth_value(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+ TSS2_RC rc;
+
+ assert(c);
+ assert(session);
+
+ log_debug("Adding authValue policy.");
+
+ rc = sym_Esys_PolicyAuthValue(
+ c->esys_context,
+ session->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to add authValue policy to TPM: %s",
+ sym_Tss2_RC_Decode(rc));
+
+ return tpm2_get_policy_digest(c, session, ret_policy_digest);
+}
+
/* Extend 'digest' with the PolicyPCR calculated hash. */
int tpm2_calculate_policy_pcr(
const TPML_PCR_SELECTION *pcr_selection,
@@ -2170,18 +2230,9 @@ static int tpm2_build_sealing_policy(
}
if (use_pin) {
- log_debug("Configuring PIN policy.");
-
- rc = sym_Esys_PolicyAuthValue(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to add authValue policy to TPM: %s",
- sym_Tss2_RC_Decode(rc));
+ r = tpm2_policy_auth_value(c, session, NULL);
+ if (r < 0)
+ return r;
}
r = tpm2_get_policy_digest(c, session, ret_policy_digest);
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 80c00af141..706d228073 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -90,6 +90,7 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
}
int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
+int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest);
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index c2e074b5f9..3fbb31bae0 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -643,6 +643,16 @@ TEST(calculate_name) {
assert_se(streq(expect, h));
}
+TEST(calculate_policy_auth_value) {
+ TPM2B_DIGEST d;
+
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ assert_se(tpm2_calculate_policy_auth_value(&d) == 0);
+ assert_se(digest_check(&d, "8fcd2169ab92694e0c633f1ab772842b8241bbc20288981fc7ac1eddc1fddb0e"));
+ assert_se(tpm2_calculate_policy_auth_value(&d) == 0);
+ assert_se(digest_check(&d, "759ebd5ed65100e0b4aa2d04b4b789c2672d92ecc9cdda4b5fa16a303132e008"));
+}
+
TEST(calculate_policy_pcr) {
TPML_PCR_SELECTION pcr_selection;
TPM2B_DIGEST pcr_values[16];

View File

@ -0,0 +1,262 @@
From c25de62babe4e0734d769de0250544fe8de83d4a Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 9 Dec 2022 14:49:52 -0500
Subject: [PATCH] tpm2: add tpm2_policy_authorize()
This adds functions to get the digest for a PolicyAuthorize operation. For
building a policy hash, this provides a function to calculate the hash; and for
building a policy hash to satisfy the authPolicy for an existing object, this
provides a function to perform PolicyAuthorize with an existing session.
(cherry picked from commit 5c7852f78c0c2b44be60651430876165a37eea95)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 197 +++++++++++++++++++++++++++++++++++++++++
src/shared/tpm2-util.h | 1 +
src/test/test-tpm2.c | 12 +++
3 files changed, 210 insertions(+)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 35dfa3f371..4be07d8944 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -2066,6 +2066,203 @@ static int tpm2_policy_pcr(
return tpm2_get_policy_digest(c, session, ret_policy_digest);
}
+/* Extend 'digest' with the PolicyAuthorize calculated hash. */
+int tpm2_calculate_policy_authorize(
+ const TPM2B_PUBLIC *public,
+ const TPM2B_DIGEST *policy_ref,
+ TPM2B_DIGEST *digest) {
+
+ TPM2_CC command = TPM2_CC_PolicyAuthorize;
+ TSS2_RC rc;
+ int r;
+
+ assert(public);
+ assert(digest);
+ assert(digest->size == SHA256_DIGEST_SIZE);
+
+ r = dlopen_tpm2();
+ if (r < 0)
+ return log_error_errno(r, "TPM2 support not installed: %m");
+
+ uint8_t buf[sizeof(command)];
+ size_t offset = 0;
+
+ rc = sym_Tss2_MU_TPM2_CC_Marshal(command, buf, sizeof(buf), &offset);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal PolicyAuthorize command: %s", sym_Tss2_RC_Decode(rc));
+
+ if (offset != sizeof(command))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Offset 0x%zx wrong after marshalling PolicyAuthorize command", offset);
+
+ TPM2B_NAME name = {};
+ r = tpm2_calculate_name(&public->publicArea, &name);
+ if (r < 0)
+ return r;
+
+ /* PolicyAuthorize does not use the previous hash value; we must zero and then extend it. */
+ zero(digest->buffer);
+
+ struct iovec data[] = {
+ IOVEC_MAKE(buf, offset),
+ IOVEC_MAKE(name.name, name.size),
+ };
+ r = tpm2_digest_many(TPM2_ALG_SHA256, digest, data, ELEMENTSOF(data), /* extend= */ true);
+ if (r < 0)
+ return r;
+
+ /* PolicyAuthorize requires hashing twice; this is either an extension or rehashing. */
+ if (policy_ref)
+ r = tpm2_digest_many_digests(TPM2_ALG_SHA256, digest, policy_ref, 1, /* extend= */ true);
+ else
+ r = tpm2_digest_rehash(TPM2_ALG_SHA256, digest);
+ if (r < 0)
+ return r;
+
+ tpm2_log_debug_digest(digest, "PolicyAuthorize calculated digest");
+
+ return 0;
+}
+
+static int tpm2_policy_authorize(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ TPML_PCR_SELECTION *pcr_selection,
+ const TPM2B_PUBLIC *public,
+ const void *fp,
+ size_t fp_size,
+ JsonVariant *signature_json,
+ TPM2B_DIGEST **ret_policy_digest) {
+
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(session);
+ assert(pcr_selection);
+ assert(public);
+ assert(fp && fp_size > 0);
+
+ log_debug("Adding PCR signature policy.");
+
+ _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
+ r = tpm2_handle_new(c, &pubkey_handle);
+ if (r < 0)
+ return r;
+
+ /* Load the key into the TPM */
+ rc = sym_Esys_LoadExternal(
+ c->esys_context,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ NULL,
+ public,
+#if HAVE_TSS2_ESYS3
+ /* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
+ * hierarchy, older versions need TPM2_RH_* instead. */
+ ESYS_TR_RH_OWNER,
+#else
+ TPM2_RH_OWNER,
+#endif
+ &pubkey_handle->esys_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ /* Acquire the "name" of what we just loaded */
+ _cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
+ r = tpm2_get_name(c, pubkey_handle, &pubkey_name);
+ if (r < 0)
+ return r;
+
+ /* If we have a signature, proceed with verifying the PCR digest */
+ const TPMT_TK_VERIFIED *check_ticket;
+ _cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL;
+ _cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
+ if (signature_json) {
+ r = tpm2_policy_pcr(
+ c,
+ session,
+ pcr_selection,
+ &approved_policy);
+ if (r < 0)
+ return r;
+
+ _cleanup_free_ void *signature_raw = NULL;
+ size_t signature_size;
+
+ r = find_signature(
+ signature_json,
+ pcr_selection,
+ fp, fp_size,
+ approved_policy->buffer,
+ approved_policy->size,
+ &signature_raw,
+ &signature_size);
+ if (r < 0)
+ return r;
+
+ /* TPM2_VerifySignature() will only verify the RSA part of the RSA+SHA256 signature,
+ * hence we need to do the SHA256 part ourselves, first */
+ TPM2B_DIGEST signature_hash = *approved_policy;
+ r = tpm2_digest_rehash(TPM2_ALG_SHA256, &signature_hash);
+ if (r < 0)
+ return r;
+
+ TPMT_SIGNATURE policy_signature = {
+ .sigAlg = TPM2_ALG_RSASSA,
+ .signature.rsassa = {
+ .hash = TPM2_ALG_SHA256,
+ .sig.size = signature_size,
+ },
+ };
+ if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
+ memcpy(policy_signature.signature.rsassa.sig.buffer, signature_raw, signature_size);
+
+ rc = sym_Esys_VerifySignature(
+ c->esys_context,
+ pubkey_handle->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ &signature_hash,
+ &policy_signature,
+ &check_ticket_buffer);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to validate signature in TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ check_ticket = check_ticket_buffer;
+ } else {
+ /* When enrolling, we pass a NULL ticket */
+ static const TPMT_TK_VERIFIED check_ticket_null = {
+ .tag = TPM2_ST_VERIFIED,
+ .hierarchy = TPM2_RH_OWNER,
+ };
+
+ check_ticket = &check_ticket_null;
+ }
+
+ rc = sym_Esys_PolicyAuthorize(
+ c->esys_context,
+ session->esys_handle,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ approved_policy,
+ /* policyRef= */ &(const TPM2B_NONCE) {},
+ pubkey_name,
+ check_ticket);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ return tpm2_get_policy_digest(c, session, ret_policy_digest);
+}
+
static int tpm2_build_sealing_policy(
Tpm2Context *c,
const Tpm2Handle *session,
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 706d228073..526e2fdfb2 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -91,6 +91,7 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
+int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest);
int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest);
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 3fbb31bae0..0b123c25a7 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -653,6 +653,18 @@ TEST(calculate_policy_auth_value) {
assert_se(digest_check(&d, "759ebd5ed65100e0b4aa2d04b4b789c2672d92ecc9cdda4b5fa16a303132e008"));
}
+TEST(calculate_policy_authorize) {
+ TPM2B_PUBLIC public;
+ TPM2B_DIGEST d;
+
+ tpm2b_public_init(&public);
+ digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
+ assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0);
+ assert_se(digest_check(&d, "95213a3784eaab04f427bc7e8851c2f1df0903be8e42428ec25dcefd907baff1"));
+ assert_se(tpm2_calculate_policy_authorize(&public, NULL, &d) == 0);
+ assert_se(digest_check(&d, "95213a3784eaab04f427bc7e8851c2f1df0903be8e42428ec25dcefd907baff1"));
+}
+
TEST(calculate_policy_pcr) {
TPML_PCR_SELECTION pcr_selection;
TPM2B_DIGEST pcr_values[16];

View File

@ -0,0 +1,230 @@
From dc66857abd6ffdf6414d54b8e95f1da594b7317e Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 9 Feb 2023 10:04:58 -0500
Subject: [PATCH] tpm2: use tpm2_policy_authorize()
This updates the function to build the sealing policy to use the dedicated
function to perform PolicyAuthorize.
This is separate from the previous commit to make each commit easier to read.
(cherry picked from commit 524cef3ff5e52ab8683a5c95c519d598dd3d0726)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 154 ++++++++---------------------------------
1 file changed, 29 insertions(+), 125 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 4be07d8944..2747cf0b53 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -2268,20 +2268,19 @@ static int tpm2_build_sealing_policy(
const Tpm2Handle *session,
uint32_t hash_pcr_mask,
uint16_t pcr_bank,
- const void *pubkey,
- size_t pubkey_size,
+ const TPM2B_PUBLIC *public,
+ const void *fp,
+ size_t fp_size,
uint32_t pubkey_pcr_mask,
JsonVariant *signature_json,
bool use_pin,
TPM2B_DIGEST **ret_policy_digest) {
- TSS2_RC rc;
int r;
assert(c);
assert(session);
- assert(pubkey || pubkey_size == 0);
- assert(pubkey_pcr_mask == 0 || pubkey_size > 0);
+ assert(pubkey_pcr_mask == 0 || public);
log_debug("Building sealing policy.");
@@ -2294,128 +2293,11 @@ static int tpm2_build_sealing_policy(
}
if (pubkey_pcr_mask != 0) {
- _cleanup_free_ void *fp = NULL;
- size_t fp_size = 0;
- TPM2B_PUBLIC pubkey_tpm2;
-
- log_debug("Configuring public key based PCR policy.");
-
- /* Convert the PEM key to TPM2 format */
- r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
- if (r < 0)
- return r;
-
- _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
- r = tpm2_handle_new(c, &pubkey_handle);
- if (r < 0)
- return r;
-
- rc = sym_Esys_LoadExternal(
- c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- &pubkey_tpm2,
-#if HAVE_TSS2_ESYS3
- /* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
- * hierarchy, older versions need TPM2_RH_* instead. */
- ESYS_TR_RH_OWNER,
-#else
- TPM2_RH_OWNER,
-#endif
- &pubkey_handle->esys_handle);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
-
- /* Acquire the "name" of what we just loaded */
- _cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
- r = tpm2_get_name(c, pubkey_handle, &pubkey_name);
- if (r < 0)
- return r;
-
- /* Put together the PCR policy we want to use */
TPML_PCR_SELECTION pcr_selection;
tpm2_tpml_pcr_selection_from_mask(pubkey_pcr_mask, (TPMI_ALG_HASH)pcr_bank, &pcr_selection);
- _cleanup_(Esys_Freep) TPM2B_DIGEST *approved_policy = NULL;
- r = tpm2_policy_pcr(c, session, &pcr_selection, &approved_policy);
+ r = tpm2_policy_authorize(c, session, &pcr_selection, public, fp, fp_size, signature_json, NULL);
if (r < 0)
return r;
-
- /* When we are unlocking and have a signature, let's pass it to the TPM */
- _cleanup_(Esys_Freep) TPMT_TK_VERIFIED *check_ticket_buffer = NULL;
- const TPMT_TK_VERIFIED *check_ticket;
- if (signature_json) {
- _cleanup_free_ void *signature_raw = NULL;
- size_t signature_size;
-
- r = find_signature(
- signature_json,
- &pcr_selection,
- fp, fp_size,
- approved_policy->buffer,
- approved_policy->size,
- &signature_raw,
- &signature_size);
- if (r < 0)
- return r;
-
- /* TPM2_VerifySignature() will only verify the RSA part of the RSA+SHA256 signature,
- * hence we need to do the SHA256 part ourselves, first */
- TPM2B_DIGEST signature_hash = *approved_policy;
- r = tpm2_digest_rehash(TPM2_ALG_SHA256, &signature_hash);
- if (r < 0)
- return r;
-
- TPMT_SIGNATURE policy_signature = {
- .sigAlg = TPM2_ALG_RSASSA,
- .signature.rsassa = {
- .hash = TPM2_ALG_SHA256,
- .sig.size = signature_size,
- },
- };
- if (signature_size > sizeof(policy_signature.signature.rsassa.sig.buffer))
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Signature larger than buffer.");
- memcpy(policy_signature.signature.rsassa.sig.buffer, signature_raw, signature_size);
-
- rc = sym_Esys_VerifySignature(
- c->esys_context,
- pubkey_handle->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &signature_hash,
- &policy_signature,
- &check_ticket_buffer);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to validate signature in TPM: %s", sym_Tss2_RC_Decode(rc));
-
- check_ticket = check_ticket_buffer;
- } else {
- /* When enrolling, we pass a NULL ticket */
- static const TPMT_TK_VERIFIED check_ticket_null = {
- .tag = TPM2_ST_VERIFIED,
- .hierarchy = TPM2_RH_OWNER,
- };
-
- check_ticket = &check_ticket_null;
- }
-
- rc = sym_Esys_PolicyAuthorize(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- approved_policy,
- /* policyRef= */ &(const TPM2B_NONCE) {},
- pubkey_name,
- check_ticket);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to push Authorize policy into TPM: %s", sym_Tss2_RC_Decode(rc));
}
if (hash_pcr_mask != 0) {
@@ -2516,6 +2398,16 @@ int tpm2_seal(const char *device,
return r;
}
+ TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
+ _cleanup_free_ void *fp = NULL;
+ size_t fp_size = 0;
+ if (pubkey) {
+ r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
+ if (r < 0)
+ return r;
+ authorize_key = &pubkey_tpm2;
+ }
+
_cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary);
if (r < 0)
@@ -2554,7 +2446,8 @@ int tpm2_seal(const char *device,
policy_session,
hash_pcr_mask,
pcr_bank,
- pubkey, pubkey_size,
+ authorize_key,
+ fp, fp_size,
pubkey_pcr_mask,
/* signature_json= */ NULL,
!!pin,
@@ -2820,6 +2713,16 @@ int tpm2_unseal(const char *device,
sym_Tss2_RC_Decode(rc));
}
+ TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
+ _cleanup_free_ void *fp = NULL;
+ size_t fp_size = 0;
+ if (pubkey) {
+ r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
+ if (r < 0)
+ return r;
+ authorize_key = &pubkey_tpm2;
+ }
+
/*
* if a pin is set for the seal object, use it to bind the session
* key to that object. This prevents active bus interposers from
@@ -2853,7 +2756,8 @@ int tpm2_unseal(const char *device,
policy_session,
hash_pcr_mask,
pcr_bank,
- pubkey, pubkey_size,
+ authorize_key,
+ fp, fp_size,
pubkey_pcr_mask,
signature,
!!pin,

View File

@ -0,0 +1,208 @@
From 3047d3fed460b6df83cb0713d5ef25169fdebb4e Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 15 Dec 2022 12:56:35 -0500
Subject: [PATCH] tpm2: add tpm2_calculate_sealing_policy()
This adds a function to fully calculate the authPolicy needed to seal a secret,
and updates tpm2_seal() to use the new function instead of a trial policy.
(cherry picked from commit d9a1f1a724a08defb70dbc6f44aa578983a66ac8)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 118 ++++++++++++++++++++++++-----------------
1 file changed, 70 insertions(+), 48 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 2747cf0b53..f638c18223 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -2263,6 +2263,40 @@ static int tpm2_policy_authorize(
return tpm2_get_policy_digest(c, session, ret_policy_digest);
}
+/* Extend 'digest' with the calculated policy hash. */
+static int tpm2_calculate_sealing_policy(
+ const TPML_PCR_SELECTION *hash_pcr_selection,
+ const TPM2B_DIGEST *hash_pcr_values,
+ size_t n_hash_pcr_values,
+ const TPM2B_PUBLIC *public,
+ const char *pin,
+ TPM2B_DIGEST *digest) {
+
+ int r;
+
+ assert(digest);
+
+ if (public) {
+ r = tpm2_calculate_policy_authorize(public, NULL, digest);
+ if (r < 0)
+ return r;
+ }
+
+ if (hash_pcr_selection && !tpm2_tpml_pcr_selection_is_empty(hash_pcr_selection)) {
+ r = tpm2_calculate_policy_pcr(hash_pcr_selection, hash_pcr_values, n_hash_pcr_values, digest);
+ if (r < 0)
+ return r;
+ }
+
+ if (pin) {
+ r = tpm2_calculate_policy_auth_value(digest);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int tpm2_build_sealing_policy(
Tpm2Context *c,
const Tpm2Handle *session,
@@ -2345,7 +2379,6 @@ int tpm2_seal(const char *device,
_cleanup_(erase_and_freep) void *secret = NULL;
_cleanup_free_ void *hash = NULL;
TPM2B_SENSITIVE_CREATE hmac_sensitive;
- TPMI_ALG_PUBLIC primary_alg;
TPM2B_PUBLIC hmac_template;
usec_t start;
TSS2_RC rc;
@@ -2398,59 +2431,37 @@ int tpm2_seal(const char *device,
return r;
}
+ TPML_PCR_SELECTION hash_pcr_selection = {};
+ _cleanup_free_ TPM2B_DIGEST *hash_pcr_values = NULL;
+ size_t n_hash_pcr_values = 0;
+ if (hash_pcr_mask) {
+ /* For now, we just read the current values from the system; we need to be able to specify
+ * expected values, eventually. */
+ tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection);
+ r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values);
+ if (r < 0)
+ return r;
+ }
+
TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
- _cleanup_free_ void *fp = NULL;
- size_t fp_size = 0;
if (pubkey) {
- r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, &fp, &fp_size);
+ r = openssl_pubkey_to_tpm2_pubkey(pubkey, pubkey_size, &pubkey_tpm2, NULL, NULL);
if (r < 0)
return r;
authorize_key = &pubkey_tpm2;
}
- _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
- r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary);
+ TPM2B_DIGEST policy_digest;
+ r = tpm2_digest_init(TPM2_ALG_SHA256, &policy_digest);
if (r < 0)
return r;
- /* we cannot use the bind key before its created */
- _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
- r = tpm2_make_encryption_session(c, primary, &TPM2_HANDLE_NONE, &encryption_session);
- if (r < 0)
- return r;
-
- /* So apparently some TPM implementations don't implement trial mode correctly. To avoid issues let's
- * avoid it when it is easy to. At the moment we only really need trial mode for the signed PCR
- * policies (since only then we need to shove PCR values into the policy that don't match current
- * state anyway), hence if we have none of those we don't need to bother. Hence, let's patch in
- * TPM2_SE_POLICY even if trial mode is requested unless a pubkey PCR mask is specified that is
- * non-zero, i.e. signed PCR policy is requested.
- *
- * One day we should switch to calculating policy hashes client side when trial mode is requested, to
- * avoid this mess. */
- bool trial = (pubkey_pcr_mask != 0);
-
- _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
- r = tpm2_make_policy_session(
- c,
- primary,
- encryption_session,
- trial,
- &policy_session);
- if (r < 0)
- return r;
-
- _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
- r = tpm2_build_sealing_policy(
- c,
- policy_session,
- hash_pcr_mask,
- pcr_bank,
+ r = tpm2_calculate_sealing_policy(
+ &hash_pcr_selection,
+ hash_pcr_values,
+ n_hash_pcr_values,
authorize_key,
- fp, fp_size,
- pubkey_pcr_mask,
- /* signature_json= */ NULL,
- !!pin,
+ pin,
&policy_digest);
if (r < 0)
return r;
@@ -2466,7 +2477,7 @@ int tpm2_seal(const char *device,
.objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
.parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
.unique.keyedHash.size = SHA256_DIGEST_SIZE,
- .authPolicy = *policy_digest,
+ .authPolicy = policy_digest,
},
};
@@ -2490,11 +2501,22 @@ int tpm2_seal(const char *device,
if (r < 0)
return log_error_errno(r, "Failed to generate secret key: %m");
+ _cleanup_tpm2_handle_ Tpm2Handle *primary_handle = NULL;
+ TPMI_ALG_PUBLIC primary_alg;
+ r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary_handle);
+ if (r < 0)
+ return r;
+
+ _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+ r = tpm2_make_encryption_session(c, primary_handle, &TPM2_HANDLE_NONE, &encryption_session);
+ if (r < 0)
+ return r;
+
log_debug("Creating HMAC key.");
rc = sym_Esys_Create(
c->esys_context,
- primary->esys_handle,
+ primary_handle->esys_handle,
encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -2534,7 +2556,7 @@ int tpm2_seal(const char *device,
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc));
- hash = memdup(policy_digest->buffer, policy_digest->size);
+ hash = memdup(policy_digest.buffer, policy_digest.size);
if (!hash)
return log_oom();
@@ -2544,7 +2566,7 @@ int tpm2_seal(const char *device,
*/
if (ret_srk_buf) {
log_debug("Serializing SRK ESYS_TR reference");
- rc = sym_Esys_TR_Serialize(c->esys_context, primary->esys_handle, &srk_buf, &srk_buf_size);
+ rc = sym_Esys_TR_Serialize(c->esys_context, primary_handle->esys_handle, &srk_buf, &srk_buf_size);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to serialize primary key: %s", sym_Tss2_RC_Decode(rc));
@@ -2572,7 +2594,7 @@ int tpm2_seal(const char *device,
*ret_blob = TAKE_PTR(blob);
*ret_blob_size = blob_size;
*ret_pcr_hash = TAKE_PTR(hash);
- *ret_pcr_hash_size = policy_digest->size;
+ *ret_pcr_hash_size = policy_digest.size;
*ret_pcr_bank = pcr_bank;
*ret_primary_alg = primary_alg;

View File

@ -0,0 +1,46 @@
From 31bb91d7c4cc04586865e041a227730b459554c0 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 5 May 2023 19:53:32 -0400
Subject: [PATCH] tpm: remove external calls to dlopen_tpm2()
The calls outside tpm2-util.c are redundant, as tpm2_context_new()
is always called immediately after, which then calls dlopen_tpm2().
(cherry picked from commit 9944909e68e06d903d828aeca4a48abc6285f74e)
Related: RHEL-16182
---
src/boot/pcrphase.c | 4 ----
src/cryptsetup/cryptsetup.c | 4 ----
2 files changed, 8 deletions(-)
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 6c37d34fd6..57e31e6cad 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -340,10 +340,6 @@ static int run(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
- r = dlopen_tpm2();
- if (r < 0)
- return log_error_errno(r, "Failed to load TPM2 libraries: %m");
-
_cleanup_tpm2_context_ Tpm2Context *c = NULL;
r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index caef45637c..b384897e4f 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -837,10 +837,6 @@ static int measure_volume_key(
}
#if HAVE_TPM2
- r = dlopen_tpm2();
- if (r < 0)
- return log_error_errno(r, "Failed to load TPM2 libraries: %m");
-
_cleanup_tpm2_context_ Tpm2Context *c = NULL;
r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)

View File

@ -0,0 +1,198 @@
From 663405d25eb1bcec9694c7c8c43b29801c9d2005 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 5 May 2023 19:49:49 -0400
Subject: [PATCH] tpm2: remove all extern tpm2-tss symbols
These library syms should be restricted to use only in tpm2-util.c,
and all other code should use simpler functions exported in tpm2-util.h.
Also move the Esys_Freep() cleanup function into tpm-util.c, and make
tpm2-tss symbols static.
(cherry picked from commit b57a7b3d9b7e1d4081cd1a4abb0772c3b5d3ccb2)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 91 ++++++++++++++++++++++--------------------
src/shared/tpm2-util.h | 41 -------------------
2 files changed, 48 insertions(+), 84 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index f638c18223..6f62dd609a 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -29,46 +29,46 @@ static void *libtss2_esys_dl = NULL;
static void *libtss2_rc_dl = NULL;
static void *libtss2_mu_dl = NULL;
-TSS2_RC (*sym_Esys_Create)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
-TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, ESYS_TR *objectHandle, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
-TSS2_RC (*sym_Esys_EvictControl)(ESYS_CONTEXT *esysContext, ESYS_TR auth, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPMI_DH_PERSISTENT persistentHandle, ESYS_TR *newObjectHandle);
-void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
-TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
-void (*sym_Esys_Free)(void *ptr) = NULL;
-TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
-TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes) = NULL;
-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;
-TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = NULL;
-TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
-TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName);
-TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
-TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
-TSS2_RC (*sym_Esys_TRSess_GetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION *flags);
-TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask);
-TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name);
-TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle);
-TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object);
-TSS2_RC (*sym_Esys_TR_Serialize)(ESYS_CONTEXT *esys_context, ESYS_TR object, uint8_t **buffer, size_t *buffer_size);
-TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
-TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL;
-TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR keyHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *digest, const TPMT_SIGNATURE *signature, TPMT_TK_VERIFIED **validation);
-
-const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
-
-TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL;
-TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
-TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
-TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+static TSS2_RC (*sym_Esys_Create)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
+static TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, ESYS_TR *objectHandle, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
+static TSS2_RC (*sym_Esys_EvictControl)(ESYS_CONTEXT *esysContext, ESYS_TR auth, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPMI_DH_PERSISTENT persistentHandle, ESYS_TR *newObjectHandle) = NULL;
+static void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
+static TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle) = NULL;
+static void (*sym_Esys_Free)(void *ptr) = NULL;
+static TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData) = NULL;
+static TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes) = NULL;
+static TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL;
+static 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;
+static 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) = NULL;
+static 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) = NULL;
+static 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) = NULL;
+static 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) = NULL;
+static TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3) = NULL;
+static TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = NULL;
+static TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL;
+static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName) = NULL;
+static TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
+static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
+static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
+static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
+static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
+static TSS2_RC (*sym_Esys_TR_Serialize)(ESYS_CONTEXT *esys_context, ESYS_TR object, uint8_t **buffer, size_t *buffer_size) = NULL;
+static TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL;
+static TSS2_RC (*sym_Esys_TRSess_GetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION *flags) = NULL;
+static TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask) = NULL;
+static TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL;
+static TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR keyHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *digest, const TPMT_SIGNATURE *signature, TPMT_TK_VERIFIED **validation) = NULL;
+
+static TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+static TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest) = NULL;
+static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+static TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest) = NULL;
+static TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+static TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+static TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset) = NULL;
+
+static const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL;
int dlopen_tpm2(void) {
int r;
@@ -95,13 +95,13 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_ReadPublic),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
- DLSYM_ARG(Esys_TRSess_GetAttributes),
- DLSYM_ARG(Esys_TRSess_SetAttributes),
+ DLSYM_ARG(Esys_TR_Deserialize),
DLSYM_ARG(Esys_TR_FromTPMPublic),
DLSYM_ARG(Esys_TR_GetName),
- DLSYM_ARG(Esys_TR_Deserialize),
DLSYM_ARG(Esys_TR_Serialize),
DLSYM_ARG(Esys_TR_SetAuth),
+ DLSYM_ARG(Esys_TRSess_GetAttributes),
+ DLSYM_ARG(Esys_TRSess_SetAttributes),
DLSYM_ARG(Esys_Unseal),
DLSYM_ARG(Esys_VerifySignature));
if (r < 0)
@@ -125,6 +125,11 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Tss2_MU_TPMT_PUBLIC_Marshal));
}
+static inline void Esys_Freep(void *p) {
+ if (*(void**) p)
+ sym_Esys_Free(*(void**) p);
+}
+
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
if (!c)
return NULL;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 526e2fdfb2..9bf9e44878 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -39,42 +39,6 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
#include <tss2/tss2_mu.h>
#include <tss2/tss2_rc.h>
-extern TSS2_RC (*sym_Esys_Create)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket);
-extern TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, ESYS_TR *objectHandle, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket);
-extern void (*sym_Esys_Finalize)(ESYS_CONTEXT **context);
-extern TSS2_RC (*sym_Esys_FlushContext)(ESYS_CONTEXT *esysContext, ESYS_TR flushHandle);
-extern void (*sym_Esys_Free)(void *ptr);
-extern TSS2_RC (*sym_Esys_GetCapability)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2_CAP capability, UINT32 property, UINT32 propertyCount, TPMI_YES_NO *moreData, TPMS_CAPABILITY_DATA **capabilityData);
-extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, UINT16 bytesRequested, TPM2B_DIGEST **randomBytes);
-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);
-extern TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest);
-extern TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs);
-extern TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle);
-extern TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType);
-extern TSS2_RC (*sym_Esys_TRSess_GetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION *flags);
-extern TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask);
-extern TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name);
-extern TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue);
-extern TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData);
-extern TSS2_RC (*sym_Esys_VerifySignature)(ESYS_CONTEXT *esysContext, ESYS_TR keyHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *digest, const TPMT_SIGNATURE *signature, TPMT_TK_VERIFIED **validation);
-
-extern const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc);
-
-extern TSS2_RC (*sym_Tss2_MU_TPM2_CC_Marshal)(TPM2_CC src, uint8_t buffer[], size_t buffer_size, size_t *offset);
-extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Marshal)(TPM2B_PRIVATE const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
-extern TSS2_RC (*sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PRIVATE *dest);
-extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Marshal)(TPM2B_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
-extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], size_t buffer_size, size_t *offset, TPM2B_PUBLIC *dest);
-extern TSS2_RC (*sym_Tss2_MU_TPML_PCR_SELECTION_Marshal)(TPML_PCR_SELECTION const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
-extern TSS2_RC (*sym_Tss2_MU_TPMT_HA_Marshal)(TPMT_HA const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
-extern TSS2_RC (*sym_Tss2_MU_TPMT_PUBLIC_Marshal)(TPMT_PUBLIC const *src, uint8_t buffer[], size_t buffer_size, size_t *offset);
-
int dlopen_tpm2(void);
int tpm2_digest_many(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const struct iovec data[], size_t count, bool extend);
@@ -125,11 +89,6 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
-static inline void Esys_Freep(void *p) {
- if (*(void**) p)
- sym_Esys_Free(*(void**) p);
-}
-
int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret);

View File

@ -0,0 +1,274 @@
From ca70ae72c223e6f0bc4b41efee13a847b2968734 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Sun, 1 Jan 2023 20:19:12 -0500
Subject: [PATCH] tpm2: add tpm2_get_capability(), tpm2_cache_capabilities(),
tpm2_capability_pcrs()
This adds a function to query specific capabilities from the TPM. That is then
used in a function to query the allocation of PCRs in the TPM, i.e. which PCR
banks and indexes are available, and caches the PCR allocation when the TPM
context is created.
(cherry picked from commit 3a35d6cdd29f0303b9fffff2f34461b2be0cb1c7)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 159 ++++++++++++++++++++++++++++-------------
src/shared/tpm2-util.h | 3 +
2 files changed, 113 insertions(+), 49 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 6f62dd609a..460ebe62c7 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -130,6 +130,94 @@ static inline void Esys_Freep(void *p) {
sym_Esys_Free(*(void**) p);
}
+/* Get a specific TPM capability (or capabilities).
+ *
+ * Returns 0 if there are no more capability properties of the requested type, or 1 if there are more, or < 0
+ * on any error. Both 0 and 1 indicate this completed successfully, but do not indicate how many capability
+ * properties were provided in 'ret_capability_data'. To find the number of provided properties, check the
+ * specific type's 'count' field (e.g. for TPM2_CAP_ALGS, check ret_capability_data->algorithms.count).
+ *
+ * This calls TPM2_GetCapability() and does not alter the provided data, so it is important to understand how
+ * that TPM function works. It is recommended to check the TCG TPM specification Part 3 ("Commands") section
+ * on TPM2_GetCapability() for full details, but a short summary is: if this returns 0, all available
+ * properties have been provided in ret_capability_data, or no properties were available. If this returns 1,
+ * there are between 1 and "count" properties provided in ret_capability_data, and there are more available.
+ * Note that this may provide less than "count" properties even if the TPM has more available. Also, each
+ * capability category may have more specific requirements than described here; see the spec for exact
+ * details. */
+static int tpm2_get_capability(
+ Tpm2Context *c,
+ TPM2_CAP capability,
+ uint32_t property,
+ uint32_t count,
+ TPMU_CAPABILITIES *ret_capability_data) {
+
+ _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *capabilities = NULL;
+ TPMI_YES_NO more;
+ TSS2_RC rc;
+
+ assert(c);
+
+ log_debug("Getting TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 " count %" PRIu32 ".",
+ capability, property, count);
+
+ rc = sym_Esys_GetCapability(
+ c->esys_context,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ capability,
+ property,
+ count,
+ &more,
+ &capabilities);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to get TPM2 capability 0x%04" PRIx32 " property 0x%04" PRIx32 ": %s",
+ capability, property, sym_Tss2_RC_Decode(rc));
+
+ if (capabilities->capability != capability)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM provided wrong capability: 0x%04" PRIx32 " instead of 0x%04" PRIx32 ".",
+ capabilities->capability, capability);
+
+ if (ret_capability_data)
+ *ret_capability_data = capabilities->data;
+
+ return more == TPM2_YES;
+}
+
+static int tpm2_cache_capabilities(Tpm2Context *c) {
+ TPMU_CAPABILITIES capability;
+ int r;
+
+ assert(c);
+
+ /* Cache the PCR capabilities, which are safe to cache, as the only way they can change is
+ * TPM2_PCR_Allocate(), which changes the allocation after the next _TPM_Init(). If the TPM is
+ * reinitialized while we are using it, all our context and sessions will be invalid, so we can
+ * safely assume the TPM PCR allocation will not change while we are using it. */
+ r = tpm2_get_capability(
+ c,
+ TPM2_CAP_PCRS,
+ /* property= */ 0,
+ /* count= */ 1,
+ &capability);
+ if (r < 0)
+ return r;
+ if (r == 1)
+ /* This should never happen. Part 3 ("Commands") of the TCG TPM2 spec in the section for
+ * TPM2_GetCapability states: "TPM_CAP_PCRS Returns the current allocation of PCR in a
+ * TPML_PCR_SELECTION. The property parameter shall be zero. The TPM will always respond to
+ * this command with the full PCR allocation and moreData will be NO." */
+ log_warning("TPM bug: reported multiple PCR sets; using only first set.");
+ c->capability_pcrs = capability.assignedPCR;
+
+ return 0;
+}
+
+#define tpm2_capability_pcrs(c) ((c)->capability_pcrs)
+
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
if (!c)
return NULL;
@@ -250,6 +338,10 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to start up TPM: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_cache_capabilities(context);
+ if (r < 0)
+ return r;
+
*ret_context = TAKE_PTR(context);
return 0;
@@ -1173,48 +1265,33 @@ static int tpm2_get_best_pcr_bank(
uint32_t pcr_mask,
TPMI_ALG_HASH *ret) {
- _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
+ TPML_PCR_SELECTION pcrs;
TPMI_ALG_HASH supported_hash = 0, hash_with_valid_pcr = 0;
- TPMI_YES_NO more;
- TSS2_RC rc;
int r;
assert(c);
+ assert(ret);
- rc = sym_Esys_GetCapability(
- c->esys_context,
- 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++) {
+ pcrs = tpm2_capability_pcrs(c);
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &pcrs) {
+ TPMI_ALG_HASH hash = selection->hash;
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))
+ if (!IN_SET(hash, TPM2_ALG_SHA256, TPM2_ALG_SHA1))
continue;
- r = tpm2_bank_has24(pcap->data.assignedPCR.pcrSelections + i);
+ r = tpm2_bank_has24(selection);
if (r < 0)
return r;
if (!r)
continue;
- good = tpm2_pcr_mask_good(c, pcap->data.assignedPCR.pcrSelections[i].hash, pcr_mask);
+ good = tpm2_pcr_mask_good(c, hash, pcr_mask);
if (good < 0)
return good;
- if (pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA256) {
+ if (hash == TPM2_ALG_SHA256) {
supported_hash = TPM2_ALG_SHA256;
if (good) {
/* Great, SHA256 is supported and has initialized PCR values, we are done. */
@@ -1222,7 +1299,7 @@ static int tpm2_get_best_pcr_bank(
break;
}
} else {
- assert(pcap->data.assignedPCR.pcrSelections[i].hash == TPM2_ALG_SHA1);
+ assert(hash == TPM2_ALG_SHA1);
if (supported_hash == 0)
supported_hash = TPM2_ALG_SHA1;
@@ -1271,42 +1348,26 @@ int tpm2_get_good_pcr_banks(
TPMI_ALG_HASH **ret) {
_cleanup_free_ TPMI_ALG_HASH *good_banks = NULL, *fallback_banks = NULL;
- _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *pcap = NULL;
+ TPML_PCR_SELECTION pcrs;
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_context,
- 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++) {
+ pcrs = tpm2_capability_pcrs(c);
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &pcrs) {
+ TPMI_ALG_HASH hash = selection->hash;
/* 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);
+ r = tpm2_bank_has24(selection);
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);
+ r = tpm2_pcr_mask_good(c, hash, pcr_mask);
if (r < 0)
return r;
@@ -1317,12 +1378,12 @@ int tpm2_get_good_pcr_banks(
if (!GREEDY_REALLOC(good_banks, n_good_banks+1))
return log_oom();
- good_banks[n_good_banks++] = pcap->data.assignedPCR.pcrSelections[i].hash;
+ good_banks[n_good_banks++] = 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;
+ fallback_banks[n_fallback_banks++] = hash;
}
}
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 9bf9e44878..5e5d9e2604 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -67,6 +67,9 @@ typedef struct {
void *tcti_dl;
TSS2_TCTI_CONTEXT *tcti_context;
ESYS_CONTEXT *esys_context;
+
+ /* Some selected cached capabilities of the TPM */
+ TPML_PCR_SELECTION capability_pcrs;
} Tpm2Context;
int tpm2_context_new(const char *device, Tpm2Context **ret_context);

View File

@ -0,0 +1,272 @@
From 2c78f1e768c75bbea7076fa9242ba484c8f472b5 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 17 Feb 2023 12:59:18 -0500
Subject: [PATCH] tpm2: verify symmetric parms in tpm2_context_new()
This adds tpm2_get_capability_algs(), tpm2_supports_alg(), and
tpm2_test_parms(). These functions allow verifying that the TPM supports
specific algs and parameters.
When creating a new context, this checks if the TPM supports the symmetric algs
we use. If the TPM does not support the symmetric algs and parameters we
require, we log and return error.
(cherry picked from commit a47060bb34c912ea9909fcf617f7b553488b5daf)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 119 ++++++++++++++++++++++++++++++++++++-----
src/shared/tpm2-util.h | 4 ++
src/test/test-tpm2.c | 38 +++++++++++++
3 files changed, 149 insertions(+), 12 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 460ebe62c7..91f66aaaf4 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -49,6 +49,7 @@ static TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySe
static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_PUBLIC **outPublic, TPM2B_NAME **name, TPM2B_NAME **qualifiedName) = NULL;
static TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
+static TSS2_RC (*sym_Esys_TestParms)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPMT_PUBLIC_PARMS *parameters) = NULL;
static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
@@ -95,6 +96,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_ReadPublic),
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
+ DLSYM_ARG(Esys_TestParms),
DLSYM_ARG(Esys_TR_Deserialize),
DLSYM_ARG(Esys_TR_FromTPMPublic),
DLSYM_ARG(Esys_TR_GetName),
@@ -218,6 +220,87 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
#define tpm2_capability_pcrs(c) ((c)->capability_pcrs)
+/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID.
+ *
+ * Returns 1 if the TPM supports the algorithm and the TPMA_ALGORITHM is provided, or 0 if the TPM does not
+ * support the algorithm, or < 0 for any errors. */
+static int tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) {
+ TPMU_CAPABILITIES capability;
+ int r;
+
+ assert(c);
+
+ /* The spec explicitly states the TPM2_ALG_ID should be cast to uint32_t. */
+ r = tpm2_get_capability(c, TPM2_CAP_ALGS, (uint32_t) alg, 1, &capability);
+ if (r < 0)
+ return r;
+
+ TPML_ALG_PROPERTY algorithms = capability.algorithms;
+ if (algorithms.count == 0 || algorithms.algProperties[0].alg != alg) {
+ log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg);
+ return 0;
+ }
+
+ if (ret)
+ *ret = algorithms.algProperties[0].algProperties;
+
+ return 1;
+}
+
+/* Returns 1 if the TPM supports the alg, 0 if the TPM does not support the alg, or < 0 for any error. */
+int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
+ return tpm2_get_capability_alg(c, alg, NULL);
+}
+
+/* Returns 1 if the TPM supports the parms, or 0 if the TPM does not support the parms. */
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms) {
+ TSS2_RC rc;
+
+ assert(c);
+ assert(parms);
+
+ TPMT_PUBLIC_PARMS parameters = {
+ .type = alg,
+ .parameters = *parms,
+ };
+
+ rc = sym_Esys_TestParms(c->esys_context, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, &parameters);
+ if (rc != TSS2_RC_SUCCESS)
+ /* The spec says if the parms are not supported the TPM returns "...the appropriate
+ * unmarshaling error if a parameter is not valid". Since the spec (currently) defines 15
+ * unmarshaling errors, instead of checking for them all here, let's just assume any error
+ * indicates unsupported parms, and log the specific error text. */
+ log_debug("TPM does not support tested parms: %s", sym_Tss2_RC_Decode(rc));
+
+ return rc == TSS2_RC_SUCCESS;
+}
+
+static inline bool tpm2_supports_tpmt_sym_def_object(Tpm2Context *c, const TPMT_SYM_DEF_OBJECT *parameters) {
+ assert(c);
+ assert(parameters);
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = *parameters,
+ };
+
+ return tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms);
+}
+
+static inline bool tpm2_supports_tpmt_sym_def(Tpm2Context *c, const TPMT_SYM_DEF *parameters) {
+ assert(c);
+ assert(parameters);
+
+ /* Unfortunately, TPMT_SYM_DEF and TPMT_SYM_DEF_OBEJECT are separately defined, even though they are
+ * functionally identical. */
+ TPMT_SYM_DEF_OBJECT object = {
+ .algorithm = parameters->algorithm,
+ .keyBits = parameters->keyBits,
+ .mode = parameters->mode,
+ };
+
+ return tpm2_supports_tpmt_sym_def_object(c, &object);
+}
+
static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
if (!c)
return NULL;
@@ -233,6 +316,12 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
DEFINE_TRIVIAL_REF_UNREF_FUNC(Tpm2Context, tpm2_context, tpm2_context_free);
+static const TPMT_SYM_DEF SESSION_TEMPLATE_SYM_AES_128_CFB = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB, /* The spec requires sessions to use CFB. */
+};
+
int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
_cleanup_tpm2_context_ Tpm2Context *context = NULL;
TSS2_RC rc;
@@ -342,6 +431,22 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
if (r < 0)
return r;
+ /* We require AES and CFB support for session encryption. */
+ r = tpm2_supports_alg(context, TPM2_ALG_AES);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
+
+ r = tpm2_supports_alg(context, TPM2_ALG_CFB);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
+
+ if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES-128-CFB.");
+
*ret_context = TAKE_PTR(context);
return 0;
@@ -1570,11 +1675,6 @@ static int tpm2_make_encryption_session(
const Tpm2Handle *bind_key,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT |
TPMA_SESSION_CONTINUESESSION;
TSS2_RC rc;
@@ -1602,7 +1702,7 @@ static int tpm2_make_encryption_session(
ESYS_TR_NONE,
NULL,
TPM2_SE_HMAC,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
@@ -1631,11 +1731,6 @@ static int tpm2_make_policy_session(
bool trial,
Tpm2Handle **ret_session) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
TPM2_SE session_type = trial ? TPM2_SE_TRIAL : TPM2_SE_POLICY;
TSS2_RC rc;
int r;
@@ -1665,7 +1760,7 @@ static int tpm2_make_policy_session(
ESYS_TR_NONE,
NULL,
session_type,
- &symmetric,
+ &SESSION_TEMPLATE_SYM_AES_128_CFB,
TPM2_ALG_SHA256,
&session->esys_handle);
if (rc != TSS2_RC_SUCCESS)
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 5e5d9e2604..764104ed58 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -92,6 +92,10 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
+int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
+
+bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);
+
int tpm2_get_good_pcr_banks(Tpm2Context *c, uint32_t pcr_mask, TPMI_ALG_HASH **ret_banks);
int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 0b123c25a7..130a968273 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -713,6 +713,44 @@ TEST(calculate_policy_pcr) {
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
}
+TEST(tpm_required_tests) {
+ int r;
+
+ _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ r = tpm2_context_new(NULL, &c);
+ if (r < 0) {
+ log_tests_skipped("Could not find TPM");
+ return;
+ }
+
+ TPMU_PUBLIC_PARMS parms = {
+ .symDetail.sym = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ },
+ };
+
+ /* Test with invalid parms */
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_CFB, &parms));
+
+ TPMU_PUBLIC_PARMS invalid_parms = parms;
+ invalid_parms.symDetail.sym.keyBits.aes = 1;
+ assert_se(!tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &invalid_parms));
+
+ /* Test with valid parms */
+ assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms));
+
+ /* Test invalid algs */
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_ERROR) == 0);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_LAST + 1) == 0);
+
+ /* Test valid algs */
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA) == 1);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_AES) == 1);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB) == 1);
+}
+
#endif /* HAVE_TPM2 */
DEFINE_TEST_MAIN(LOG_DEBUG);

View File

@ -0,0 +1,212 @@
From 7d2ce56f8ce505b3976b1c8dd435478c163db964 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 17 May 2023 17:16:23 -0400
Subject: [PATCH] tpm2: replace _cleanup_tpm2_* macros with _cleanup_()
Remove _cleanup_tpm2_context_ and _cleanup_tpm2_handle_ macros, replacing their
use with _cleanup_(tpm2_context_unrefp) and _cleanup_(tpm2_handle_freep),
respectively.
(cherry picked from commit 1dc8f51841f2a552da8924c4d5501c7b1c757ba8)
Related: RHEL-16182
---
src/boot/pcrphase.c | 2 +-
src/cryptsetup/cryptsetup.c | 2 +-
src/shared/tpm2-util.c | 30 +++++++++++++++---------------
src/shared/tpm2-util.h | 2 --
src/test/test-tpm2.c | 2 +-
5 files changed, 18 insertions(+), 20 deletions(-)
diff --git a/src/boot/pcrphase.c b/src/boot/pcrphase.c
index 57e31e6cad..16d71e6a22 100644
--- a/src/boot/pcrphase.c
+++ b/src/boot/pcrphase.c
@@ -340,7 +340,7 @@ static int run(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index b384897e4f..674d222db6 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -837,7 +837,7 @@ static int measure_volume_key(
}
#if HAVE_TPM2
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 91f66aaaf4..bc3ae8340d 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -323,7 +323,7 @@ static const TPMT_SYM_DEF SESSION_TEMPLATE_SYM_AES_128_CFB = {
};
int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
- _cleanup_tpm2_context_ Tpm2Context *context = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *context = NULL;
TSS2_RC rc;
int r;
@@ -469,7 +469,7 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle) {
if (!handle)
return NULL;
- _cleanup_tpm2_context_ Tpm2Context *context = (Tpm2Context*)handle->tpm2_context;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *context = (Tpm2Context*)handle->tpm2_context;
if (context && !handle->keep)
tpm2_handle_flush(context->esys_context, handle->esys_handle);
@@ -477,7 +477,7 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle) {
}
int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle) {
- _cleanup_tpm2_handle_ Tpm2Handle *handle = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
assert(ret_handle);
@@ -754,7 +754,7 @@ static int tpm2_make_primary(
ts = now(CLOCK_MONOTONIC);
- _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *primary = NULL;
r = tpm2_handle_new(c, &primary);
if (r < 0)
return r;
@@ -1688,7 +1688,7 @@ static int tpm2_make_encryption_session(
/* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which
* means that the random salt will be encrypted with the well-known key. That way, only the TPM can
* recover the salt, which is then used for key derivation. */
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *session = NULL;
r = tpm2_handle_new(c, &session);
if (r < 0)
return r;
@@ -1746,7 +1746,7 @@ static int tpm2_make_policy_session(
log_debug("Starting policy session.");
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *session = NULL;
r = tpm2_handle_new(c, &session);
if (r < 0)
return r;
@@ -2307,7 +2307,7 @@ static int tpm2_policy_authorize(
log_debug("Adding PCR signature policy.");
- _cleanup_tpm2_handle_ Tpm2Handle *pubkey_handle = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *pubkey_handle = NULL;
r = tpm2_handle_new(c, &pubkey_handle);
if (r < 0)
return r;
@@ -2579,7 +2579,7 @@ int tpm2_seal(const char *device,
CLEANUP_ERASE(hmac_sensitive);
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(device, &c);
if (r < 0)
return r;
@@ -2662,13 +2662,13 @@ int tpm2_seal(const char *device,
if (r < 0)
return log_error_errno(r, "Failed to generate secret key: %m");
- _cleanup_tpm2_handle_ Tpm2Handle *primary_handle = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
TPMI_ALG_PUBLIC primary_alg;
r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary_handle);
if (r < 0)
return r;
- _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
r = tpm2_make_encryption_session(c, primary_handle, &TPM2_HANDLE_NONE, &encryption_session);
if (r < 0)
return r;
@@ -2829,13 +2829,13 @@ int tpm2_unseal(const char *device,
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to unmarshal public key: %s", sym_Tss2_RC_Decode(rc));
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(device, &c);
if (r < 0)
return r;
/* If their is a primary key we trust, like an SRK, use it */
- _cleanup_tpm2_handle_ Tpm2Handle *primary = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *primary = NULL;
if (srk_buf) {
r = tpm2_handle_new(c, &primary);
@@ -2868,7 +2868,7 @@ int tpm2_unseal(const char *device,
* SRK model, the tpmKey is verified. In the non-srk model, with pin, the bindKey
* provides protections.
*/
- _cleanup_tpm2_handle_ Tpm2Handle *hmac_key = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *hmac_key = NULL;
r = tpm2_handle_new(c, &hmac_key);
if (r < 0)
return r;
@@ -2917,13 +2917,13 @@ int tpm2_unseal(const char *device,
if (r < 0)
return r;
- _cleanup_tpm2_handle_ Tpm2Handle *encryption_session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
r = tpm2_make_encryption_session(c, primary, hmac_key, &encryption_session);
if (r < 0)
return r;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
- _cleanup_tpm2_handle_ Tpm2Handle *policy_session = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
c,
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 764104ed58..a03bee148b 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -76,7 +76,6 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context);
Tpm2Context *tpm2_context_ref(Tpm2Context *context);
Tpm2Context *tpm2_context_unref(Tpm2Context *context);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
-#define _cleanup_tpm2_context_ _cleanup_(tpm2_context_unrefp)
typedef struct {
Tpm2Context *tpm2_context;
@@ -90,7 +89,6 @@ static const Tpm2Handle TPM2_HANDLE_NONE = _tpm2_handle(NULL, ESYS_TR_NONE);
int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
-#define _cleanup_tpm2_handle_ _cleanup_(tpm2_handle_freep)
int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 130a968273..75e207e9d9 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -716,7 +716,7 @@ TEST(calculate_policy_pcr) {
TEST(tpm_required_tests) {
int r;
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
+ _cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(NULL, &c);
if (r < 0) {
log_tests_skipped("Could not find TPM");

View File

@ -0,0 +1,51 @@
From 84aed24cce13f1432d050b72a7717df1098c9381 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 24 Feb 2023 18:20:50 +0100
Subject: [PATCH] tpm2-util: use compound initialization when allocating tpm2
objects
(cherry picked from commit d70e4bc9f1395a5ca48ac4f6b3e71e64029312e1)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index bc3ae8340d..bf36b4de95 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -329,11 +329,13 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
assert(ret_context);
- context = new0(Tpm2Context, 1);
+ context = new(Tpm2Context, 1);
if (!context)
return log_oom();
- context->n_ref = 1;
+ *context = (Tpm2Context) {
+ .n_ref = 1,
+ };
r = dlopen_tpm2();
if (r < 0)
@@ -481,12 +483,14 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle) {
assert(ret_handle);
- handle = new0(Tpm2Handle, 1);
+ handle = new(Tpm2Handle, 1);
if (!handle)
return log_oom();
- handle->tpm2_context = tpm2_context_ref(context);
- handle->esys_handle = ESYS_TR_NONE;
+ *handle = (Tpm2Handle) {
+ .tpm2_context = tpm2_context_ref(context),
+ .esys_handle = ESYS_TR_NONE,
+ };
*ret_handle = TAKE_PTR(handle);

View File

@ -0,0 +1,408 @@
From 88bac2c3213ab4a5036732ff9bc4f5dc2b4287b7 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 17 Feb 2023 12:50:31 -0500
Subject: [PATCH] tpm2: add tpm2_get_capability_handle(),
tpm2_esys_handle_from_tpm_handle()
Add tpm2_get_capability_handle() to query if a "TPM handle" (meaning, a
location/address in TPM storage) is populated in the TPM, and
tpm2_get_capability_handles() to query for a specific number of handles.
Add tpm2_esys_handle_from_tpm_handle() to create an "esys handle" (an opaque
reference for use with the TPM EAPI that represents a TPM handle address) for an
existing TPM handle.
Since the TPM handle already exists in the TPM, this also also requires
updating the cleanup code for Tpm2Handle objects to close the object (free its
resources only from the EAPI code, but leave the handle in the TPM) instead of
flush the object (which frees its EAPI resources and removes it from the TPM).
(cherry picked from commit c8a85240316898a6de95c9b2565edd08f8450182)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 244 ++++++++++++++++++++++++++++++++---------
src/shared/tpm2-util.h | 3 +-
2 files changed, 193 insertions(+), 54 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index bf36b4de95..3278863f4d 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -50,6 +50,7 @@ static TSS2_RC (*sym_Esys_ReadPublic)(ESYS_CONTEXT *esysContext, ESYS_TR objectH
static TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL;
static TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL;
static TSS2_RC (*sym_Esys_TestParms)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPMT_PUBLIC_PARMS *parameters) = NULL;
+static TSS2_RC (*sym_Esys_TR_Close)(ESYS_CONTEXT *esys_context, ESYS_TR *rsrc_handle) = NULL;
static TSS2_RC (*sym_Esys_TR_Deserialize)(ESYS_CONTEXT *esys_context, uint8_t const *buffer, size_t buffer_size, ESYS_TR *esys_handle) = NULL;
static TSS2_RC (*sym_Esys_TR_FromTPMPublic)(ESYS_CONTEXT *esysContext, TPM2_HANDLE tpm_handle, ESYS_TR optionalSession1, ESYS_TR optionalSession2, ESYS_TR optionalSession3, ESYS_TR *object) = NULL;
static TSS2_RC (*sym_Esys_TR_GetName)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_NAME **name) = NULL;
@@ -97,6 +98,7 @@ int dlopen_tpm2(void) {
DLSYM_ARG(Esys_StartAuthSession),
DLSYM_ARG(Esys_Startup),
DLSYM_ARG(Esys_TestParms),
+ DLSYM_ARG(Esys_TR_Close),
DLSYM_ARG(Esys_TR_Deserialize),
DLSYM_ARG(Esys_TR_FromTPMPublic),
DLSYM_ARG(Esys_TR_GetName),
@@ -252,6 +254,84 @@ int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
return tpm2_get_capability_alg(c, alg, NULL);
}
+/* Query the TPM for populated handles.
+ *
+ * This provides an array of handle indexes populated in the TPM, starting at the requested handle. The array will
+ * contain only populated handle addresses (which might not include the requested handle). The number of
+ * handles will be no more than the 'max' number requested. This will not search past the end of the handle
+ * range (i.e. handle & 0xff000000).
+ *
+ * Returns 0 if all populated handles in the range (starting at the requested handle) were provided (or no
+ * handles were in the range), or 1 if there are more populated handles in the range, or < 0 on any error. */
+static int tpm2_get_capability_handles(
+ Tpm2Context *c,
+ TPM2_HANDLE start,
+ size_t max,
+ TPM2_HANDLE **ret_handles,
+ size_t *ret_n_handles) {
+
+ _cleanup_free_ TPM2_HANDLE *handles = NULL;
+ size_t n_handles = 0;
+ TPM2_HANDLE current = start;
+ int r = 0;
+
+ assert(c);
+ assert(ret_handles);
+ assert(ret_n_handles);
+
+ while (max > 0) {
+ TPMU_CAPABILITIES capability;
+ r = tpm2_get_capability(c, TPM2_CAP_HANDLES, current, (uint32_t) max, &capability);
+ if (r < 0)
+ return r;
+
+ TPML_HANDLE handle_list = capability.handles;
+ if (handle_list.count == 0)
+ break;
+
+ assert(handle_list.count <= max);
+
+ if (n_handles > SIZE_MAX - handle_list.count)
+ return log_oom();
+
+ if (!GREEDY_REALLOC(handles, n_handles + handle_list.count))
+ return log_oom();
+
+ memcpy_safe(&handles[n_handles], handle_list.handle, sizeof(handles[0]) * handle_list.count);
+
+ max -= handle_list.count;
+ n_handles += handle_list.count;
+
+ /* Update current to the handle index after the last handle in the list. */
+ current = handles[n_handles - 1] + 1;
+
+ if (r == 0)
+ /* No more handles in this range. */
+ break;
+ }
+
+ *ret_handles = TAKE_PTR(handles);
+ *ret_n_handles = n_handles;
+
+ return r;
+}
+
+#define TPM2_HANDLE_RANGE(h) ((TPM2_HANDLE)((h) & TPM2_HR_RANGE_MASK))
+#define TPM2_HANDLE_TYPE(h) ((TPM2_HT)(TPM2_HANDLE_RANGE(h) >> TPM2_HR_SHIFT))
+
+/* Returns 1 if the handle is populated in the TPM, 0 if not, and < 0 on any error. */
+static int tpm2_get_capability_handle(Tpm2Context *c, TPM2_HANDLE handle) {
+ _cleanup_free_ TPM2_HANDLE *handles = NULL;
+ size_t n_handles = 0;
+ int r;
+
+ r = tpm2_get_capability_handles(c, handle, 1, &handles, &n_handles);
+ if (r < 0)
+ return r;
+
+ return n_handles == 0 ? false : handles[0] == handle;
+}
+
/* Returns 1 if the TPM supports the parms, or 0 if the TPM does not support the parms. */
bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms) {
TSS2_RC rc;
@@ -454,17 +534,25 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
return 0;
}
-static void tpm2_handle_flush(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle) {
+static void tpm2_handle_cleanup(ESYS_CONTEXT *esys_context, ESYS_TR esys_handle, bool flush) {
+ TSS2_RC rc;
+
if (!esys_context || esys_handle == ESYS_TR_NONE)
return;
- TSS2_RC rc = sym_Esys_FlushContext(esys_context, esys_handle);
+ /* Closing the handle removes its reference from the esys_context, but leaves the corresponding
+ * handle in the actual TPM. Flushing the handle removes its reference from the esys_context as well
+ * as removing its corresponding handle from the actual TPM. */
+ if (flush)
+ rc = sym_Esys_FlushContext(esys_context, esys_handle);
+ else
+ rc = sym_Esys_TR_Close(esys_context, &esys_handle);
if (rc != TSS2_RC_SUCCESS) /* We ignore failures here (besides debug logging), since this is called
* in error paths, where we cannot do anything about failures anymore. And
* when it is called in successful codepaths by this time we already did
* what we wanted to do, and got the results we wanted so there's no
* reason to make this fail more loudly than necessary. */
- log_debug("Failed to flush TPM handle, ignoring: %s", sym_Tss2_RC_Decode(rc));
+ log_debug("Failed to %s TPM handle, ignoring: %s", flush ? "flush" : "close", sym_Tss2_RC_Decode(rc));
}
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle) {
@@ -472,8 +560,8 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle) {
return NULL;
_cleanup_(tpm2_context_unrefp) Tpm2Context *context = (Tpm2Context*)handle->tpm2_context;
- if (context && !handle->keep)
- tpm2_handle_flush(context->esys_context, handle->esys_handle);
+ if (context)
+ tpm2_handle_cleanup(context->esys_context, handle->esys_handle, handle->flush);
return mfree(handle);
}
@@ -490,6 +578,7 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle) {
*handle = (Tpm2Handle) {
.tpm2_context = tpm2_context_ref(context),
.esys_handle = ESYS_TR_NONE,
+ .flush = true,
};
*ret_handle = TAKE_PTR(handle);
@@ -497,6 +586,81 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle) {
return 0;
}
+/* Create a Tpm2Handle object that references a pre-existing handle in the TPM, at the TPM2_HANDLE address
+ * provided. This should be used only for persistent, transient, or NV handles. Returns 1 on success, 0 if
+ * the requested handle is not present in the TPM, or < 0 on error. */
+static int tpm2_esys_handle_from_tpm_handle(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ TPM2_HANDLE tpm_handle,
+ Tpm2Handle **ret_handle) {
+
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(tpm_handle > 0);
+ assert(ret_handle);
+
+ /* Let's restrict this, at least for now, to allow only some handle types. */
+ switch (TPM2_HANDLE_TYPE(tpm_handle)) {
+ case TPM2_HT_PERSISTENT:
+ case TPM2_HT_NV_INDEX:
+ case TPM2_HT_TRANSIENT:
+ break;
+ case TPM2_HT_PCR:
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for PCR handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ case TPM2_HT_HMAC_SESSION:
+ case TPM2_HT_POLICY_SESSION:
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for session handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ case TPM2_HT_PERMANENT: /* Permanent handles are defined, e.g. ESYS_TR_RH_OWNER. */
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for permanent handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ default:
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Refusing to create ESYS handle for unknown handle 0x%08" PRIx32 ".",
+ tpm_handle);
+ }
+
+ r = tpm2_get_capability_handle(c, tpm_handle);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ log_debug("TPM handle 0x%08" PRIx32 " not populated.", tpm_handle);
+ *ret_handle = NULL;
+ return 0;
+ }
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_handle_new(c, &handle);
+ if (r < 0)
+ return r;
+
+ /* Since we didn't create this handle in the TPM (this is only creating an ESYS_TR handle for the
+ * pre-existing TPM handle), we shouldn't flush (or evict) it on cleanup. */
+ handle->flush = false;
+
+ rc = sym_Esys_TR_FromTPMPublic(
+ c->esys_context,
+ tpm_handle,
+ session ? session->esys_handle : ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ &handle->esys_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to read public info: %s", sym_Tss2_RC_Decode(rc));
+
+ *ret_handle = TAKE_PTR(handle);
+
+ return 1;
+}
+
#define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
static int tpm2_credit_random(Tpm2Context *c) {
@@ -660,60 +824,32 @@ const TPM2B_PUBLIC *tpm2_get_primary_template(Tpm2SRKTemplateFlags flags) {
*/
static int tpm2_get_srk(
Tpm2Context *c,
+ const Tpm2Handle *session,
TPMI_ALG_PUBLIC *ret_alg,
- Tpm2Handle *ret_primary) {
+ Tpm2Handle **ret_handle) {
- TPMI_YES_NO more_data;
- ESYS_TR primary_tr = ESYS_TR_NONE;
- _cleanup_(Esys_Freep) TPMS_CAPABILITY_DATA *cap_data = NULL;
+ int r;
assert(c);
- assert(ret_primary);
-
- TSS2_RC rc = sym_Esys_GetCapability(c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- TPM2_CAP_HANDLES,
- SRK_HANDLE,
- 1,
- &more_data,
- &cap_data);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to enumerate handles searching for SRK: %s",
- sym_Tss2_RC_Decode(rc));
-
- /* Did Not find SRK, indicate this by returning 0 */
- if (cap_data->data.handles.count == 0 || cap_data->data.handles.handle[0] != SRK_HANDLE) {
- ret_primary->esys_handle = ESYS_TR_NONE;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_esys_handle_from_tpm_handle(c, session, SRK_HANDLE, &handle);
+ if (r < 0)
+ return r;
+ if (r == 0) { /* SRK not found */
if (ret_alg)
- *ret_alg = 0;
+ *ret_alg = TPM2_ALG_ERROR;
+ if (ret_handle)
+ *ret_handle = NULL;
return 0;
}
- log_debug("Found SRK on TPM.");
-
- /* convert the raw handle to an ESYS_TR */
- TPM2_HANDLE handle = cap_data->data.handles.handle[0];
- rc = sym_Esys_TR_FromTPMPublic(c->esys_context,
- handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &primary_tr);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to convert ray handle to ESYS_TR for SRK: %s",
- sym_Tss2_RC_Decode(rc));
-
/* Get the algorithm if the caller wants it */
_cleanup_(Esys_Freep) TPM2B_PUBLIC *out_public = NULL;
if (ret_alg) {
- rc = sym_Esys_ReadPublic(
+ TSS2_RC rc = sym_Esys_ReadPublic(
c->esys_context,
- primary_tr,
+ handle->esys_handle,
ESYS_TR_NONE,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -726,7 +862,8 @@ static int tpm2_get_srk(
sym_Tss2_RC_Decode(rc));
}
- ret_primary->esys_handle = primary_tr;
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(handle);
if (ret_alg)
*ret_alg = out_public->publicArea.type;
@@ -759,9 +896,6 @@ static int tpm2_make_primary(
ts = now(CLOCK_MONOTONIC);
_cleanup_(tpm2_handle_freep) Tpm2Handle *primary = NULL;
- r = tpm2_handle_new(c, &primary);
- if (r < 0)
- return r;
/* we only need the SRK lock when making the SRK since its not atomic, transient
* primary creations don't even matter if they stomp on each other, the TPM will
@@ -776,7 +910,7 @@ static int tpm2_make_primary(
/* Find existing SRK and use it if present */
if (use_srk_model) {
TPMI_ALG_PUBLIC got_alg = TPM2_ALG_NULL;
- r = tpm2_get_srk(c, &got_alg, primary);
+ r = tpm2_get_srk(c, NULL, &got_alg, &primary);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to establish if SRK is present");
@@ -796,6 +930,10 @@ static int tpm2_make_primary(
log_debug("Did not find SRK, generating...");
}
+ r = tpm2_handle_new(c, &primary);
+ if (r < 0)
+ return r;
+
if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
primary_template = tpm2_get_primary_template(base_flags | TPM2_SRK_TEMPLATE_ECC);
@@ -866,7 +1004,7 @@ static int tpm2_make_primary(
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to persist SRK within TPM: %s", sym_Tss2_RC_Decode(rc));
- primary->keep = true;
+ primary->flush = false;
}
if (ret_primary)
@@ -2846,7 +2984,7 @@ int tpm2_unseal(const char *device,
if (r < 0)
return r;
- primary->keep = true;
+ primary->flush = false;
log_debug("Found existing SRK key to use, deserializing ESYS_TR");
rc = sym_Esys_TR_Deserialize(
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index a03bee148b..26d25f7ee7 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -80,7 +80,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Context*, tpm2_context_unref);
typedef struct {
Tpm2Context *tpm2_context;
ESYS_TR esys_handle;
- bool keep;
+
+ bool flush;
} Tpm2Handle;
#define _tpm2_handle(c, h) { .tpm2_context = (c), .esys_handle = (h), }

View File

@ -0,0 +1,129 @@
From 516a4e71a764f4f5e19dd8f2e19fb1a86aa0534b Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 17 May 2023 20:03:00 -0400
Subject: [PATCH] tpm2: add tpm2_read_public()
(cherry picked from commit 98d6a80942337f07183bc4039ce32dc188f4d4cd)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 69 +++++++++++++++++++++++++++---------------
1 file changed, 45 insertions(+), 24 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 3278863f4d..edd871c632 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -721,6 +721,35 @@ static int tpm2_credit_random(Tpm2Context *c) {
return 0;
}
+static int tpm2_read_public(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ const Tpm2Handle *handle,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_NAME **ret_name,
+ TPM2B_NAME **ret_qname) {
+
+ TSS2_RC rc;
+
+ assert(c);
+ assert(handle);
+
+ rc = sym_Esys_ReadPublic(
+ c->esys_context,
+ handle->esys_handle,
+ session ? session->esys_handle : ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ret_public,
+ ret_name,
+ ret_qname);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to read public info: %s", sym_Tss2_RC_Decode(rc));
+
+ return 0;
+}
+
const TPM2B_PUBLIC *tpm2_get_primary_template(Tpm2SRKTemplateFlags flags) {
/*
@@ -825,7 +854,9 @@ const TPM2B_PUBLIC *tpm2_get_primary_template(Tpm2SRKTemplateFlags flags) {
static int tpm2_get_srk(
Tpm2Context *c,
const Tpm2Handle *session,
- TPMI_ALG_PUBLIC *ret_alg,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_NAME **ret_name,
+ TPM2B_NAME **ret_qname,
Tpm2Handle **ret_handle) {
int r;
@@ -837,37 +868,26 @@ static int tpm2_get_srk(
if (r < 0)
return r;
if (r == 0) { /* SRK not found */
- if (ret_alg)
- *ret_alg = TPM2_ALG_ERROR;
+ if (ret_public)
+ *ret_public = NULL;
+ if (ret_name)
+ *ret_name = NULL;
+ if (ret_qname)
+ *ret_qname = NULL;
if (ret_handle)
*ret_handle = NULL;
return 0;
}
- /* Get the algorithm if the caller wants it */
- _cleanup_(Esys_Freep) TPM2B_PUBLIC *out_public = NULL;
- if (ret_alg) {
- TSS2_RC rc = sym_Esys_ReadPublic(
- c->esys_context,
- handle->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &out_public,
- NULL,
- NULL);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to convert ray handle to ESYS_TR for SRK: %s",
- sym_Tss2_RC_Decode(rc));
+ if (ret_public || ret_name || ret_qname) {
+ r = tpm2_read_public(c, session, handle, ret_public, ret_name, ret_qname);
+ if (r < 0)
+ return r;
}
if (ret_handle)
*ret_handle = TAKE_PTR(handle);
- if (ret_alg)
- *ret_alg = out_public->publicArea.type;
-
return 1;
}
@@ -909,14 +929,15 @@ static int tpm2_make_primary(
/* Find existing SRK and use it if present */
if (use_srk_model) {
- TPMI_ALG_PUBLIC got_alg = TPM2_ALG_NULL;
- r = tpm2_get_srk(c, NULL, &got_alg, &primary);
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
+ r = tpm2_get_srk(c, NULL, &primary_public, NULL, NULL, &primary);
if (r < 0)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to establish if SRK is present");
if (r == 1) {
log_debug("Discovered existing SRK");
+ TPMI_ALG_PUBLIC got_alg = primary_public->publicArea.type;
if (alg != 0 && alg != got_alg)
log_warning("Caller asked for specific algorithm %u, but existing SRK is %u, ignoring",
alg, got_alg);

View File

@ -0,0 +1,525 @@
From 66338a6cbd88d00ec0d1588de61aa0fd9bcaa658 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 17 Feb 2023 12:59:18 -0500
Subject: [PATCH] tpm2: add tpm2_get_legacy_template() and
tpm2_get_srk_template()
Add functions to get either the 'legacy' or standard SRK template, for RSA or
ECC. The 'legacy' templates are those used with earlier code, where a transient
key was created to use for tpm sealing; the standard SRK is the persistent
shared key as defined in TCG guidance docs.
This also replaces tpm2_get_primary_template() with the new functions; that
function's use of flags is confusing and unnecessary.
(cherry picked from commit f4f5b3a9de29874bcb2345196eb47ec90d02b67d)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 289 +++++++++++++++++++++++++++--------------
src/shared/tpm2-util.h | 9 --
src/test/test-tpm2.c | 91 -------------
3 files changed, 195 insertions(+), 194 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index edd871c632..fb75f105e5 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -254,6 +254,25 @@ int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
return tpm2_get_capability_alg(c, alg, NULL);
}
+/* Returns 1 if the TPM supports the ECC curve, 0 if not, or < 0 for any error. */
+static int tpm2_supports_ecc_curve(Tpm2Context *c, TPM2_ECC_CURVE curve) {
+ TPMU_CAPABILITIES capability;
+ int r;
+
+ /* The spec explicitly states the TPM2_ECC_CURVE should be cast to uint32_t. */
+ r = tpm2_get_capability(c, TPM2_CAP_ECC_CURVES, (uint32_t) curve, 1, &capability);
+ if (r < 0)
+ return r;
+
+ TPML_ECC_CURVE eccCurves = capability.eccCurves;
+ if (eccCurves.count == 0 || eccCurves.eccCurves[0] != curve) {
+ log_debug("TPM does not support ECC curve 0x%02" PRIx16 ".", curve);
+ return 0;
+ }
+
+ return 1;
+}
+
/* Query the TPM for populated handles.
*
* This provides an array of handle indexes populated in the TPM, starting at the requested handle. The array will
@@ -355,6 +374,13 @@ bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARM
return rc == TSS2_RC_SUCCESS;
}
+static inline bool tpm2_supports_tpmt_public(Tpm2Context *c, const TPMT_PUBLIC *public) {
+ assert(c);
+ assert(public);
+
+ return tpm2_test_parms(c, public->type, &public->parameters);
+}
+
static inline bool tpm2_supports_tpmt_sym_def_object(Tpm2Context *c, const TPMT_SYM_DEF_OBJECT *parameters) {
assert(c);
assert(parameters);
@@ -750,102 +776,168 @@ static int tpm2_read_public(
return 0;
}
-const TPM2B_PUBLIC *tpm2_get_primary_template(Tpm2SRKTemplateFlags flags) {
-
- /*
- * Set up array so flags can be used directly as an input.
- *
- * Templates for SRK come from the spec:
- * - https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf
- *
- * However, note their is some lore here. On Linux, the SRK has it's unique field set to size 0 and
- * on Windows the SRK has their unique data set to keyLen in bytes of zeros.
- */
- assert(flags >= 0);
- assert(flags <= _TPM2_SRK_TEMPLATE_MAX);
-
- static const TPM2B_PUBLIC templ[_TPM2_SRK_TEMPLATE_MAX + 1] = {
- /* index 0 RSA old */
- [0] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
+/* Get one of the legacy primary key templates.
+ *
+ * The legacy templates should only be used for older sealed data that did not use the SRK. Instead of a
+ * persistent SRK, a transient key was created to seal the data and then flushed; and the exact same template
+ * must be used to recreate the same transient key to unseal the data. The alg parameter must be TPM2_ALG_RSA
+ * or TPM2_ALG_ECC. This does not check if the alg is actually supported on this TPM. */
+static int tpm2_get_legacy_template(TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_template) {
+ /* Do not modify. */
+ static const TPMT_PUBLIC legacy_ecc = {
+ .type = TPM2_ALG_ECC,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .parameters.eccDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
},
+ .scheme.scheme = TPM2_ALG_NULL,
+ .curveID = TPM2_ECC_NIST_P256,
+ .kdf.scheme = TPM2_ALG_NULL,
},
- [TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
+ };
+
+ /* Do not modify. */
+ static const TPMT_PUBLIC legacy_rsa = {
+ .type = TPM2_ALG_RSA,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
+ .parameters.rsaDetail = {
+ .symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
},
+ .scheme.scheme = TPM2_ALG_NULL,
+ .keyBits = 2048,
},
- [TPM2_SRK_TEMPLATE_NEW_STYLE] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
- },
+ };
+
+ assert(ret_template);
+
+ if (alg == TPM2_ALG_ECC)
+ *ret_template = legacy_ecc;
+ else if (alg == TPM2_ALG_RSA)
+ *ret_template = legacy_rsa;
+ else
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Unsupported legacy SRK alg: 0x%x", alg);
+
+ return 0;
+}
+
+/* Get a Storage Root Key (SRK) template.
+ *
+ * The SRK template values are recommended by the "TCG TPM v2.0 Provisioning Guidance" document in section
+ * 7.5.1 "Storage Primary Key (SRK) Templates", referencing "TCG EK Credential Profile for TPM Family 2.0".
+ * The EK Credential Profile version 2.0 provides only a single template each for RSA and ECC, while later EK
+ * Credential Profile versions provide more templates, and keep the original templates as "L-1" (for RSA) and
+ * "L-2" (for ECC).
+ *
+ * https://trustedcomputinggroup.org/resource/tcg-tpm-v2-0-provisioning-guidance
+ * https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile
+ *
+ * These templates are only needed to create a new persistent SRK (or a new transient key that is
+ * SRK-compatible). Preferably, the TPM should contain a shared SRK located at the reserved shared SRK handle
+ * (see TPM2_SRK_HANDLE and tpm2_get_srk() below).
+ *
+ * The alg must be TPM2_ALG_RSA or TPM2_ALG_ECC. Returns error if the requested template is not supported on
+ * this TPM. */
+static int tpm2_get_srk_template(Tpm2Context *c, TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_template) {
+ /* The attributes are the same between ECC and RSA templates. This has the changes specified in the
+ * Provisioning Guidance document, specifically:
+ * TPMA_OBJECT_USERWITHAUTH is added.
+ * TPMA_OBJECT_ADMINWITHPOLICY is removed.
+ * TPMA_OBJECT_NODA is added. */
+ TPMA_OBJECT srk_attributes =
+ TPMA_OBJECT_DECRYPT |
+ TPMA_OBJECT_FIXEDPARENT |
+ TPMA_OBJECT_FIXEDTPM |
+ TPMA_OBJECT_NODA |
+ TPMA_OBJECT_RESTRICTED |
+ TPMA_OBJECT_SENSITIVEDATAORIGIN |
+ TPMA_OBJECT_USERWITHAUTH;
+
+ /* The symmetric configuration is the same between ECC and RSA templates. */
+ TPMT_SYM_DEF_OBJECT srk_symmetric = {
+ .algorithm = TPM2_ALG_AES,
+ .keyBits.aes = 128,
+ .mode.aes = TPM2_ALG_CFB,
+ };
+
+ /* Both templates have an empty authPolicy as specified by the Provisioning Guidance document. */
+
+ /* From the EK Credential Profile template "L-2". */
+ TPMT_PUBLIC srk_ecc = {
+ .type = TPM2_ALG_ECC,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = srk_attributes,
+ .parameters.eccDetail = {
+ .symmetric = srk_symmetric,
+ .scheme.scheme = TPM2_ALG_NULL,
+ .curveID = TPM2_ECC_NIST_P256,
+ .kdf.scheme = TPM2_ALG_NULL,
},
- [TPM2_SRK_TEMPLATE_NEW_STYLE|TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
- },
+ };
+
+ /* From the EK Credential Profile template "L-1". */
+ TPMT_PUBLIC srk_rsa = {
+ .type = TPM2_ALG_RSA,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = srk_attributes,
+ .parameters.rsaDetail = {
+ .symmetric = srk_symmetric,
+ .scheme.scheme = TPM2_ALG_NULL,
+ .keyBits = 2048,
},
};
- return &templ[flags];
+ assert(c);
+ assert(ret_template);
+
+ if (alg == TPM2_ALG_ECC) {
+ if (!tpm2_supports_alg(c, TPM2_ALG_ECC))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support ECC.");
+
+ if (!tpm2_supports_ecc_curve(c, srk_ecc.parameters.eccDetail.curveID))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support ECC-NIST-P256 curve.");
+
+ if (!tpm2_supports_tpmt_public(c, &srk_ecc))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support SRK ECC template L-2.");
+
+ *ret_template = srk_ecc;
+ return 0;
+ }
+
+ if (alg == TPM2_ALG_RSA) {
+ if (!tpm2_supports_alg(c, TPM2_ALG_RSA))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support RSA.");
+
+ if (!tpm2_supports_tpmt_public(c, &srk_rsa))
+ return log_debug_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "TPM does not support SRK RSA template L-1.");
+
+ *ret_template = srk_rsa;
+ return 0;
+ }
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported SRK alg: 0x%x.", alg);
}
-/*
- * Why and what is an SRK?
- * TL;DR provides a working space for those without owner auth. The user enrolling
- * the disk may not have access to the TPMs owner hierarchy auth, so they need a
- * working space. This working space is at the defined address of 0x81000001.
- * Details can be found here:
- * - https://trustedcomputinggroup.org/wp-content/uploads/TCG-TPM-v2.0-Provisioning-Guidance-Published-v1r1.pdf
- */
-#define SRK_HANDLE UINT32_C(0x81000001)
+/* The SRK handle is defined in the Provisioning Guidance document (see above) in the table "Reserved Handles
+ * for TPM Provisioning Fundamental Elements". The SRK is useful because it is "shared", meaning it has no
+ * authValue nor authPolicy set, and thus may be used by anyone on the system to generate derived keys or
+ * seal secrets. This is useful if the TPM has an auth (password) set for the 'owner hierarchy', which would
+ * prevent users from generating primary transient keys, unless they knew the owner hierarchy auth. See
+ * the Provisioning Guidance document for more details. */
+#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
/*
* Retrieves the SRK handle if present. Returns 0 if SRK not present, 1 if present
@@ -864,7 +956,7 @@ static int tpm2_get_srk(
assert(c);
_cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
- r = tpm2_esys_handle_from_tpm_handle(c, session, SRK_HANDLE, &handle);
+ r = tpm2_esys_handle_from_tpm_handle(c, session, TPM2_SRK_HANDLE, &handle);
if (r < 0)
return r;
if (r == 0) { /* SRK not found */
@@ -900,8 +992,7 @@ static int tpm2_make_primary(
static const TPM2B_SENSITIVE_CREATE primary_sensitive = {};
static const TPML_PCR_SELECTION creation_pcr = {};
- const TPM2B_PUBLIC *primary_template = NULL;
- Tpm2SRKTemplateFlags base_flags = use_srk_model ? TPM2_SRK_TEMPLATE_NEW_STYLE : 0;
+ TPM2B_PUBLIC primary_template = { .size = sizeof(TPMT_PUBLIC), };
_cleanup_(release_lock_file) LockFile srk_lock = LOCK_FILE_INIT;
TSS2_RC rc;
usec_t ts;
@@ -956,7 +1047,12 @@ static int tpm2_make_primary(
return r;
if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
- primary_template = tpm2_get_primary_template(base_flags | TPM2_SRK_TEMPLATE_ECC);
+ if (use_srk_model)
+ r = tpm2_get_srk_template(c, TPM2_ALG_ECC, &primary_template.publicArea);
+ else
+ r = tpm2_get_legacy_template(TPM2_ALG_ECC, &primary_template.publicArea);
+ if (r < 0)
+ return r;
rc = sym_Esys_CreatePrimary(
c->esys_context,
@@ -965,7 +1061,7 @@ static int tpm2_make_primary(
ESYS_TR_NONE,
ESYS_TR_NONE,
&primary_sensitive,
- primary_template,
+ &primary_template,
NULL,
&creation_pcr,
&primary->esys_handle,
@@ -987,7 +1083,12 @@ static int tpm2_make_primary(
}
if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
- primary_template = tpm2_get_primary_template(base_flags);
+ if (use_srk_model)
+ r = tpm2_get_srk_template(c, TPM2_ALG_RSA, &primary_template.publicArea);
+ else
+ r = tpm2_get_legacy_template(TPM2_ALG_RSA, &primary_template.publicArea);
+ if (r < 0)
+ return r;
rc = sym_Esys_CreatePrimary(
c->esys_context,
@@ -996,7 +1097,7 @@ static int tpm2_make_primary(
ESYS_TR_NONE,
ESYS_TR_NONE,
&primary_sensitive,
- primary_template,
+ &primary_template,
NULL,
&creation_pcr,
&primary->esys_handle,
@@ -1021,7 +1122,7 @@ static int tpm2_make_primary(
if (use_srk_model) {
rc = sym_Esys_EvictControl(c->esys_context, ESYS_TR_RH_OWNER, primary->esys_handle,
- ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, SRK_HANDLE, &primary->esys_handle);
+ ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_SRK_HANDLE, &primary->esys_handle);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to persist SRK within TPM: %s", sym_Tss2_RC_Decode(rc));
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 26d25f7ee7..1f20aadc98 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -13,13 +13,6 @@ typedef enum TPM2Flags {
TPM2_FLAGS_USE_PIN = 1 << 0,
} TPM2Flags;
-
-typedef enum Tpm2SRKTemplateFlags {
- TPM2_SRK_TEMPLATE_ECC = 1 << 0,
- TPM2_SRK_TEMPLATE_NEW_STYLE = 1 << 1,
- _TPM2_SRK_TEMPLATE_MAX = TPM2_SRK_TEMPLATE_NEW_STYLE|TPM2_SRK_TEMPLATE_ECC,
-} Tpm2SRKTemplateFlags;
-
/* 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. This hardcodes our expectation of 24. */
#define TPM2_PCRS_MAX 24U
@@ -119,8 +112,6 @@ char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l);
size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
#define tpm2_tpml_pcr_selection_is_empty(l) (tpm2_tpml_pcr_selection_weight(l) == 0)
-const TPM2B_PUBLIC *tpm2_get_primary_template(Tpm2SRKTemplateFlags flags);
-
#else /* HAVE_TPM2 */
typedef struct {} Tpm2Context;
typedef struct {} Tpm2Handle;
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 75e207e9d9..af06085af6 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -410,97 +410,6 @@ TEST(tpml_pcr_selection_add_sub) {
expected2, expected2_count);
}
-
-/* this test includes TPM2 specific data structures */
-TEST(tpm2_get_primary_template) {
-
- /*
- * Verify that if someone changes the template code, they know they're breaking things.
- * Templates MUST be changed in a backwards compatible way.
- *
- */
- static const TPM2B_PUBLIC templ[] = {
- /* index 0 RSA old */
- [0] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
- },
- },
- /* Index 1 ECC old */
- [TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_USERWITHAUTH,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
- },
- },
- /* index 2 RSA SRK */
- [TPM2_SRK_TEMPLATE_NEW_STYLE] = {
- .publicArea = {
- .type = TPM2_ALG_RSA,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.rsaDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .keyBits = 2048,
- },
- },
- },
- /* Index 3 ECC SRK */
- [TPM2_SRK_TEMPLATE_NEW_STYLE | TPM2_SRK_TEMPLATE_ECC] = {
- .publicArea = {
- .type = TPM2_ALG_ECC,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM|TPMA_OBJECT_FIXEDPARENT|TPMA_OBJECT_SENSITIVEDATAORIGIN|TPMA_OBJECT_RESTRICTED|TPMA_OBJECT_DECRYPT|TPMA_OBJECT_USERWITHAUTH|TPMA_OBJECT_NODA,
- .parameters.eccDetail = {
- .symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- },
- .scheme.scheme = TPM2_ALG_NULL,
- .curveID = TPM2_ECC_NIST_P256,
- .kdf.scheme = TPM2_ALG_NULL,
- },
- },
- },
- };
-
- assert_cc(ELEMENTSOF(templ) == _TPM2_SRK_TEMPLATE_MAX + 1);
-
- for (size_t i = 0; i < ELEMENTSOF(templ); i++) {
- /* the index counter lines up with the flags and the expected template received */
- const TPM2B_PUBLIC *got = tpm2_get_primary_template((Tpm2SRKTemplateFlags)i);
- assert_se(memcmp(&templ[i], got, sizeof(*got)) == 0);
- }
-}
-
static bool digest_check(const TPM2B_DIGEST *digest, const char *expect) {
_cleanup_free_ char *h = NULL;

View File

@ -0,0 +1,105 @@
From 82561d585aa6d081fa5f9810f97664526726408e Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 19 Dec 2022 08:26:32 -0500
Subject: [PATCH] tpm2: add tpm2_load()
This function allows loading an object (e.g. a sealed secret) or key into the
TPM.
(cherry picked from commit d1d0de735da52a7cf5aa5638b07d5fdf4e8b23f2)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 69 +++++++++++++++++++++++++++---------------
1 file changed, 45 insertions(+), 24 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index fb75f105e5..6eb37a87aa 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1484,6 +1484,50 @@ static int tpm2_get_policy_digest(
return 0;
}
+static int tpm2_load(
+ Tpm2Context *c,
+ const Tpm2Handle *parent,
+ const Tpm2Handle *session,
+ const TPM2B_PUBLIC *public,
+ const TPM2B_PRIVATE *private,
+ Tpm2Handle **ret_handle) {
+
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(public);
+ assert(private);
+ assert(ret_handle);
+
+ log_debug("Loading object into TPM.");
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_handle_new(c, &handle);
+ if (r < 0)
+ return r;
+
+ rc = sym_Esys_Load(
+ c->esys_context,
+ parent ? parent->esys_handle : ESYS_TR_RH_OWNER,
+ session ? session->esys_handle : ESYS_TR_PASSWORD,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ private,
+ public,
+ &handle->esys_handle);
+ if (rc == TPM2_RC_LOCKOUT)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
+ "TPM2 device is in dictionary attack lockout mode.");
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to load key into TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ *ret_handle = TAKE_PTR(handle);
+
+ return 0;
+}
+
static int tpm2_pcr_read(
Tpm2Context *c,
const TPML_PCR_SELECTION *pcr_selection,
@@ -3133,33 +3177,10 @@ int tpm2_unseal(const char *device,
* provides protections.
*/
_cleanup_(tpm2_handle_freep) Tpm2Handle *hmac_key = NULL;
- r = tpm2_handle_new(c, &hmac_key);
+ r = tpm2_load(c, primary, NULL, &public, &private, &hmac_key);
if (r < 0)
return r;
- rc = sym_Esys_Load(
- c->esys_context,
- primary->esys_handle,
- ESYS_TR_PASSWORD,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &private,
- &public,
- &hmac_key->esys_handle);
- if (rc != TSS2_RC_SUCCESS) {
- /* If we're in dictionary attack lockout mode, we should see a lockout error here, which we
- * need to translate for the caller. */
- if (rc == TPM2_RC_LOCKOUT)
- return log_error_errno(
- SYNTHETIC_ERRNO(ENOLCK),
- "TPM2 device is in dictionary attack lockout mode.");
- else
- return log_error_errno(
- SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to load HMAC key in TPM: %s",
- sym_Tss2_RC_Decode(rc));
- }
-
TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
_cleanup_free_ void *fp = NULL;
size_t fp_size = 0;

View File

@ -0,0 +1,102 @@
From 7ef2fc501dfd3a989c986839b928ba967c57dca3 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 19 Dec 2022 08:26:32 -0500
Subject: [PATCH] tpm2: add tpm2_load_external()
This allows loading an external object/key (e.g. an openssl public key) into
the TPM.
(cherry picked from commit efe153bdc2e57c0d0f9bc47a4010fc82743764e7)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 66 ++++++++++++++++++++++++++++--------------
1 file changed, 45 insertions(+), 21 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 6eb37a87aa..277cfa1e8e 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1528,6 +1528,50 @@ static int tpm2_load(
return 0;
}
+static int tpm2_load_external(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ const TPM2B_PUBLIC *public,
+ const TPM2B_SENSITIVE *private,
+ Tpm2Handle **ret_handle) {
+
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(ret_handle);
+
+ log_debug("Loading external key into TPM.");
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_handle_new(c, &handle);
+ if (r < 0)
+ return r;
+
+ rc = sym_Esys_LoadExternal(
+ c->esys_context,
+ session ? session->esys_handle : ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ private,
+ public,
+#if HAVE_TSS2_ESYS3
+ /* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
+ * hierarchy, older versions need TPM2_RH_* instead. */
+ ESYS_TR_RH_OWNER,
+#else
+ TPM2_RH_OWNER,
+#endif
+ &handle->esys_handle);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
+
+ *ret_handle = TAKE_PTR(handle);
+
+ return 0;
+}
+
static int tpm2_pcr_read(
Tpm2Context *c,
const TPML_PCR_SELECTION *pcr_selection,
@@ -2616,30 +2660,10 @@ static int tpm2_policy_authorize(
log_debug("Adding PCR signature policy.");
_cleanup_(tpm2_handle_freep) Tpm2Handle *pubkey_handle = NULL;
- r = tpm2_handle_new(c, &pubkey_handle);
+ r = tpm2_load_external(c, NULL, public, NULL, &pubkey_handle);
if (r < 0)
return r;
- /* Load the key into the TPM */
- rc = sym_Esys_LoadExternal(
- c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- public,
-#if HAVE_TSS2_ESYS3
- /* tpm2-tss >= 3.0.0 requires a ESYS_TR_RH_* constant specifying the requested
- * hierarchy, older versions need TPM2_RH_* instead. */
- ESYS_TR_RH_OWNER,
-#else
- TPM2_RH_OWNER,
-#endif
- &pubkey_handle->esys_handle);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to load public key into TPM: %s", sym_Tss2_RC_Decode(rc));
-
/* Acquire the "name" of what we just loaded */
_cleanup_(Esys_Freep) TPM2B_NAME *pubkey_name = NULL;
r = tpm2_get_name(c, pubkey_handle, &pubkey_name);

View File

@ -0,0 +1,106 @@
From 74b814947793983e56fbedcd8b3044924a73a072 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 12 Dec 2022 09:46:04 -0500
Subject: [PATCH] tpm2: move local vars in tpm2_seal() to point of use
No functional change; cosmetic only.
(cherry picked from commit ee6a8713abbe185f7c8aaedbbc06cc27eefe9072)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 28 +++++++++++++---------------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 277cfa1e8e..12a6036b1d 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -2865,17 +2865,7 @@ int tpm2_seal(const char *device,
void **ret_srk_buf,
size_t *ret_srk_buf_size) {
- _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
- _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
- _cleanup_(Esys_Freep) uint8_t *srk_buf = NULL;
- static const TPML_PCR_SELECTION creation_pcr = {};
- _cleanup_(erase_and_freep) void *secret = NULL;
- _cleanup_free_ void *hash = NULL;
- TPM2B_SENSITIVE_CREATE hmac_sensitive;
- TPM2B_PUBLIC hmac_template;
- usec_t start;
TSS2_RC rc;
- size_t srk_buf_size;
int r;
assert(pubkey || pubkey_size == 0);
@@ -2907,9 +2897,7 @@ int tpm2_seal(const char *device,
* is stored in the LUKS2 JSON only in encrypted form with the "primary" key of the TPM2 chip, thus
* binding the unlocking to the TPM2 chip. */
- start = now(CLOCK_MONOTONIC);
-
- CLEANUP_ERASE(hmac_sensitive);
+ usec_t start = now(CLOCK_MONOTONIC);
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(device, &c);
@@ -2962,7 +2950,7 @@ int tpm2_seal(const char *device,
/* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the
* LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it
* because it's a key type that is universally supported and suitable for symmetric binary blobs. */
- hmac_template = (TPM2B_PUBLIC) {
+ TPM2B_PUBLIC hmac_template = {
.size = sizeof(TPMT_PUBLIC),
.publicArea = {
.type = TPM2_ALG_KEYEDHASH,
@@ -2974,10 +2962,13 @@ int tpm2_seal(const char *device,
},
};
- hmac_sensitive = (TPM2B_SENSITIVE_CREATE) {
+ TPM2B_SENSITIVE_CREATE hmac_sensitive = {
.size = sizeof(hmac_sensitive.sensitive),
.sensitive.data.size = 32,
};
+
+ CLEANUP_ERASE(hmac_sensitive);
+
if (pin) {
r = tpm2_digest_buffer(TPM2_ALG_SHA256, &hmac_sensitive.sensitive.userAuth, pin, strlen(pin), /* extend= */ false);
if (r < 0)
@@ -3007,6 +2998,9 @@ int tpm2_seal(const char *device,
log_debug("Creating HMAC key.");
+ static const TPML_PCR_SELECTION creation_pcr = {};
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
+ _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
rc = sym_Esys_Create(
c->esys_context,
primary_handle->esys_handle,
@@ -3026,6 +3020,7 @@ int tpm2_seal(const char *device,
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to generate HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+ _cleanup_(erase_and_freep) void *secret = NULL;
secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
if (!secret)
return log_oom();
@@ -3049,6 +3044,7 @@ int tpm2_seal(const char *device,
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to marshal public key: %s", sym_Tss2_RC_Decode(rc));
+ _cleanup_free_ void *hash = NULL;
hash = memdup(policy_digest.buffer, policy_digest.size);
if (!hash)
return log_oom();
@@ -3057,6 +3053,8 @@ int tpm2_seal(const char *device,
* the raw TPM handle as well as the object name. The object name is used to verify that
* the key we use later is the key we expect to establish the session with.
*/
+ _cleanup_(Esys_Freep) uint8_t *srk_buf = NULL;
+ size_t srk_buf_size = 0;
if (ret_srk_buf) {
log_debug("Serializing SRK ESYS_TR reference");
rc = sym_Esys_TR_Serialize(c->esys_context, primary_handle->esys_handle, &srk_buf, &srk_buf_size);

View File

@ -0,0 +1,28 @@
From 66e049a96b49cfa9b2be4d4c1fc39be6bdf68867 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 8 Jun 2023 14:06:46 -0400
Subject: [PATCH] tpm2: replace magic number in hmac_sensitive initialization
Instead of setting hmac_sensitive.sensitive.data.size to '32' use the actual
hash size as set in the hmac_template.
(cherry picked from commit 180444b8851a8654771361b1494b5db286d8724e)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 12a6036b1d..f7940bcf2e 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -2964,7 +2964,7 @@ int tpm2_seal(const char *device,
TPM2B_SENSITIVE_CREATE hmac_sensitive = {
.size = sizeof(hmac_sensitive.sensitive),
- .sensitive.data.size = 32,
+ .sensitive.data.size = hmac_template.publicArea.unique.keyedHash.size,
};
CLEANUP_ERASE(hmac_sensitive);

View File

@ -0,0 +1,193 @@
From 4d7527b3da486e260cb6fa94a03d9d3e58584ab8 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Mon, 12 Dec 2022 09:46:04 -0500
Subject: [PATCH] tpm2: add tpm2_create()
This allows creating a new object (e.g. sealed secret) or key using the TPM.
Note that the new object/key is not loaded in the TPM after creation.
(cherry picked from commit e3f1f210761de31d262cb701335f4da194ca4ec7)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 125 ++++++++++++++++++++++++++++-------------
1 file changed, 86 insertions(+), 39 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index f7940bcf2e..4759430cb2 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1484,6 +1484,75 @@ static int tpm2_get_policy_digest(
return 0;
}
+static int tpm2_create(
+ Tpm2Context *c,
+ const Tpm2Handle *parent,
+ const Tpm2Handle *session,
+ const TPMT_PUBLIC *template,
+ const TPMS_SENSITIVE_CREATE *sensitive,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_PRIVATE **ret_private) {
+
+ usec_t ts;
+ TSS2_RC rc;
+
+ assert(c);
+ assert(template);
+
+ log_debug("Creating object on TPM.");
+
+ ts = now(CLOCK_MONOTONIC);
+
+ TPM2B_PUBLIC tpm2b_public = {
+ .size = sizeof(*template) - sizeof(template->unique),
+ .publicArea = *template,
+ };
+
+ /* Zero the unique area. */
+ zero(tpm2b_public.publicArea.unique);
+
+ TPM2B_SENSITIVE_CREATE tpm2b_sensitive;
+ if (sensitive)
+ tpm2b_sensitive = (TPM2B_SENSITIVE_CREATE) {
+ .size = sizeof(*sensitive),
+ .sensitive = *sensitive,
+ };
+ else
+ tpm2b_sensitive = (TPM2B_SENSITIVE_CREATE) {};
+
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
+ _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
+ rc = sym_Esys_Create(
+ c->esys_context,
+ parent ? parent->esys_handle : ESYS_TR_RH_OWNER,
+ session ? session->esys_handle : ESYS_TR_PASSWORD,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ &tpm2b_sensitive,
+ &tpm2b_public,
+ /* outsideInfo= */ NULL,
+ &(TPML_PCR_SELECTION) {},
+ &private,
+ &public,
+ /* creationData= */ NULL,
+ /* creationHash= */ NULL,
+ /* creationTicket= */ NULL);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to generate object in TPM: %s",
+ sym_Tss2_RC_Decode(rc));
+
+ log_debug("Successfully created object on TPM in %s.",
+ FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
+
+ if (ret_public)
+ *ret_public = TAKE_PTR(public);
+ if (ret_private)
+ *ret_private = TAKE_PTR(private);
+
+ return 0;
+}
+
static int tpm2_load(
Tpm2Context *c,
const Tpm2Handle *parent,
@@ -2950,38 +3019,34 @@ int tpm2_seal(const char *device,
/* We use a keyed hash object (i.e. HMAC) to store the secret key we want to use for unlocking the
* LUKS2 volume with. We don't ever use for HMAC/keyed hash operations however, we just use it
* because it's a key type that is universally supported and suitable for symmetric binary blobs. */
- TPM2B_PUBLIC hmac_template = {
- .size = sizeof(TPMT_PUBLIC),
- .publicArea = {
- .type = TPM2_ALG_KEYEDHASH,
- .nameAlg = TPM2_ALG_SHA256,
- .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
- .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
- .unique.keyedHash.size = SHA256_DIGEST_SIZE,
- .authPolicy = policy_digest,
- },
+ TPMT_PUBLIC hmac_template = {
+ .type = TPM2_ALG_KEYEDHASH,
+ .nameAlg = TPM2_ALG_SHA256,
+ .objectAttributes = TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT,
+ .parameters.keyedHashDetail.scheme.scheme = TPM2_ALG_NULL,
+ .unique.keyedHash.size = SHA256_DIGEST_SIZE,
+ .authPolicy = policy_digest,
};
- TPM2B_SENSITIVE_CREATE hmac_sensitive = {
- .size = sizeof(hmac_sensitive.sensitive),
- .sensitive.data.size = hmac_template.publicArea.unique.keyedHash.size,
+ TPMS_SENSITIVE_CREATE hmac_sensitive = {
+ .data.size = hmac_template.unique.keyedHash.size,
};
CLEANUP_ERASE(hmac_sensitive);
if (pin) {
- r = tpm2_digest_buffer(TPM2_ALG_SHA256, &hmac_sensitive.sensitive.userAuth, pin, strlen(pin), /* extend= */ false);
+ r = tpm2_digest_buffer(TPM2_ALG_SHA256, &hmac_sensitive.userAuth, pin, strlen(pin), /* extend= */ false);
if (r < 0)
return r;
}
- assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size);
+ assert(sizeof(hmac_sensitive.data.buffer) >= hmac_sensitive.data.size);
(void) tpm2_credit_random(c);
log_debug("Generating secret key data.");
- r = crypto_random_bytes(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
+ r = crypto_random_bytes(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
if (r < 0)
return log_error_errno(r, "Failed to generate secret key: %m");
@@ -2996,32 +3061,14 @@ int tpm2_seal(const char *device,
if (r < 0)
return r;
- log_debug("Creating HMAC key.");
-
- static const TPML_PCR_SELECTION creation_pcr = {};
_cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
_cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
- rc = sym_Esys_Create(
- c->esys_context,
- primary_handle->esys_handle,
- encryption_session->esys_handle, /* use HMAC session to enable parameter encryption */
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &hmac_sensitive,
- &hmac_template,
- NULL,
- &creation_pcr,
- &private,
- &public,
- NULL,
- NULL,
- NULL);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to generate HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc));
+ r = tpm2_create(c, primary_handle, encryption_session, &hmac_template, &hmac_sensitive, &public, &private);
+ if (r < 0)
+ return r;
_cleanup_(erase_and_freep) void *secret = NULL;
- secret = memdup(hmac_sensitive.sensitive.data.buffer, hmac_sensitive.sensitive.data.size);
+ secret = memdup(hmac_sensitive.data.buffer, hmac_sensitive.data.size);
if (!secret)
return log_oom();
@@ -3081,7 +3128,7 @@ int tpm2_seal(const char *device,
}
*ret_secret = TAKE_PTR(secret);
- *ret_secret_size = hmac_sensitive.sensitive.data.size;
+ *ret_secret_size = hmac_sensitive.data.size;
*ret_blob = TAKE_PTR(blob);
*ret_blob_size = blob_size;
*ret_pcr_hash = TAKE_PTR(hash);

View File

@ -0,0 +1,60 @@
From 0bbab32b4f969a62fcc1fc5495017fc49cd30d33 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 8 Jun 2023 13:41:33 -0400
Subject: [PATCH] tpm2: replace tpm2_capability_pcrs() macro with direct
c->capaiblity_pcrs use
(cherry picked from commit 9ea0ffe61264a107b3a1bcb13bef225c85c9239f)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 4759430cb2..cbdae73759 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -220,8 +220,6 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
return 0;
}
-#define tpm2_capability_pcrs(c) ((c)->capability_pcrs)
-
/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID.
*
* Returns 1 if the TPM supports the algorithm and the TPMA_ALGORITHM is provided, or 0 if the TPM does not
@@ -1791,15 +1789,13 @@ static int tpm2_get_best_pcr_bank(
uint32_t pcr_mask,
TPMI_ALG_HASH *ret) {
- TPML_PCR_SELECTION pcrs;
TPMI_ALG_HASH supported_hash = 0, hash_with_valid_pcr = 0;
int r;
assert(c);
assert(ret);
- pcrs = tpm2_capability_pcrs(c);
- FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &pcrs) {
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &c->capability_pcrs) {
TPMI_ALG_HASH hash = selection->hash;
int good;
@@ -1874,15 +1870,13 @@ int tpm2_get_good_pcr_banks(
TPMI_ALG_HASH **ret) {
_cleanup_free_ TPMI_ALG_HASH *good_banks = NULL, *fallback_banks = NULL;
- TPML_PCR_SELECTION pcrs;
size_t n_good_banks = 0, n_fallback_banks = 0;
int r;
assert(c);
assert(ret);
- pcrs = tpm2_capability_pcrs(c);
- FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &pcrs) {
+ FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &c->capability_pcrs) {
TPMI_ALG_HASH hash = selection->hash;
/* Let's see if this bank is superficially OK, i.e. has at least 24 enabled registers */

View File

@ -0,0 +1,135 @@
From 774d759c39336b9650be285a88729cbfb791fce9 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 17 Feb 2023 12:59:18 -0500
Subject: [PATCH] basic/alloc-util: add greedy_realloc_append()
Add function to perform greedy realloc as well as copying the new data into the
newly allocated space.
(cherry picked from commit 3f27ba99542385174a1bc40beb737a8622790912)
Related: RHEL-16182
---
src/basic/alloc-util.c | 27 ++++++++++++++++++++++++
src/basic/alloc-util.h | 4 ++++
src/test/test-alloc-util.c | 43 ++++++++++++++++++++++++++++++++++++--
3 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c
index b030f454b2..e566350ba2 100644
--- a/src/basic/alloc-util.c
+++ b/src/basic/alloc-util.c
@@ -102,3 +102,30 @@ void* greedy_realloc0(
return q;
}
+
+void* greedy_realloc_append(
+ void **p,
+ size_t *n_p,
+ const void *from,
+ size_t n_from,
+ size_t size) {
+
+ uint8_t *q;
+
+ assert(p);
+ assert(n_p);
+ assert(from || n_from == 0);
+
+ if (n_from > SIZE_MAX - *n_p)
+ return NULL;
+
+ q = greedy_realloc(p, *n_p + n_from, size);
+ if (!q)
+ return NULL;
+
+ memcpy_safe(q + *n_p * size, from, n_from * size);
+
+ *n_p += n_from;
+
+ return q;
+}
diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h
index e4c8b71a2b..28d0cf5ea9 100644
--- a/src/basic/alloc-util.h
+++ b/src/basic/alloc-util.h
@@ -146,6 +146,7 @@ static inline void *memdup_suffix0_multiply(const void *p, size_t size, size_t n
void* greedy_realloc(void **p, size_t need, size_t size);
void* greedy_realloc0(void **p, size_t need, size_t size);
+void* greedy_realloc_append(void **p, size_t *n_p, const void *from, size_t n_from, size_t size);
#define GREEDY_REALLOC(array, need) \
greedy_realloc((void**) &(array), (need), sizeof((array)[0]))
@@ -153,6 +154,9 @@ void* greedy_realloc0(void **p, size_t need, size_t size);
#define GREEDY_REALLOC0(array, need) \
greedy_realloc0((void**) &(array), (need), sizeof((array)[0]))
+#define GREEDY_REALLOC_APPEND(array, n_array, from, n_from) \
+ greedy_realloc_append((void**) &(array), (size_t*) &(n_array), (from), (n_from), sizeof((array)[0]))
+
#define alloca0(n) \
({ \
char *_new_; \
diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c
index df6139005f..57cb886c41 100644
--- a/src/test/test-alloc-util.c
+++ b/src/test/test-alloc-util.c
@@ -23,8 +23,8 @@ TEST(alloca) {
}
TEST(GREEDY_REALLOC) {
- _cleanup_free_ int *a = NULL, *b = NULL;
- size_t i, j;
+ _cleanup_free_ int *a = NULL, *b = NULL, *c = NULL;
+ size_t i, j, n_c = 0;
/* Give valgrind a chance to verify our realloc() operations */
@@ -53,6 +53,45 @@ TEST(GREEDY_REALLOC) {
for (j = 30; j < i / 2; j += 7)
assert_se(b[j] == (int) j);
+
+ size_t n_from = 10;
+ int from[n_from];
+ for (i = 0; i < 2048; i++) {
+ for (j = 0; j < n_from; j++)
+ from[j] = n_from * i + j;
+
+ _cleanup_free_ int *before = NULL;
+ size_t n_before = 0;
+ assert_se(GREEDY_REALLOC_APPEND(before, n_before, c, n_c));
+ assert_se(before);
+ assert_se(n_before == n_c);
+ assert_se(memcmp_safe(c, before, n_c) == 0);
+
+ assert_se(GREEDY_REALLOC_APPEND(c, n_c, from, n_from));
+ assert_se(n_c == n_before + n_from);
+ assert_se(MALLOC_ELEMENTSOF(c) >= n_c);
+ assert_se(MALLOC_SIZEOF_SAFE(c) >= n_c * sizeof(int));
+ assert_se(memcmp_safe(c, before, n_before) == 0);
+ assert_se(memcmp_safe(&c[n_before], from, n_from) == 0);
+
+ before = mfree(before);
+ assert_se(!before);
+ n_before = 0;
+ assert_se(GREEDY_REALLOC_APPEND(before, n_before, c, n_c));
+ assert_se(before);
+ assert_se(n_before == n_c);
+ assert_se(memcmp_safe(c, before, n_c) == 0);
+
+ assert_se(GREEDY_REALLOC_APPEND(c, n_c, NULL, 0));
+ assert_se(c);
+ assert_se(n_c == n_before);
+ assert_se(MALLOC_ELEMENTSOF(c) >= n_c);
+ assert_se(MALLOC_SIZEOF_SAFE(c) >= n_c * sizeof(int));
+ assert_se(memcmp_safe(c, before, n_c) == 0);
+ }
+
+ for (j = 0; j < i * n_from; j++)
+ assert_se(c[j] == (int) j);
}
TEST(memdup_multiply_and_greedy_realloc) {

View File

@ -0,0 +1,150 @@
From b5409900792af67d30b80d8088a853c01fdfdc5f Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 17 Feb 2023 12:59:18 -0500
Subject: [PATCH] tpm2: cache the TPM supported commands, add
tpm2_supports_command()
Cache the TPM's supported commands and provide a function to check if a command
is supported.
(cherry picked from commit adbf0c8cfb5d8635133ce9e2be088f9489b54694)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 60 ++++++++++++++++++++++++++++++++++++++++++
src/shared/tpm2-util.h | 3 +++
src/test/test-tpm2.c | 9 +++++++
3 files changed, 72 insertions(+)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index cbdae73759..d38e260f9a 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -191,12 +191,47 @@ static int tpm2_get_capability(
return more == TPM2_YES;
}
+#define TPMA_CC_TO_TPM2_CC(cca) (((cca) & TPMA_CC_COMMANDINDEX_MASK) >> TPMA_CC_COMMANDINDEX_SHIFT)
+
static int tpm2_cache_capabilities(Tpm2Context *c) {
TPMU_CAPABILITIES capability;
int r;
assert(c);
+ /* Cache the command capabilities. The spec isn't actually clear if commands can be added/removed
+ * while running, but that would be crazy, so let's hope it is not possbile. */
+ TPM2_CC current_cc = TPM2_CC_FIRST;
+ for (;;) {
+ r = tpm2_get_capability(
+ c,
+ TPM2_CAP_COMMANDS,
+ current_cc,
+ TPM2_MAX_CAP_CC,
+ &capability);
+ if (r < 0)
+ return r;
+
+ TPML_CCA commands = capability.command;
+
+ /* We should never get 0; the TPM must support some commands, and it must not set 'more' if
+ * there are no more. */
+ assert(commands.count > 0);
+
+ if (!GREEDY_REALLOC_APPEND(
+ c->capability_commands,
+ c->n_capability_commands,
+ commands.commandAttributes,
+ commands.count))
+ return log_oom();
+
+ if (r == 0)
+ break;
+
+ /* Set current_cc to index after last cc the TPM provided */
+ current_cc = TPMA_CC_TO_TPM2_CC(commands.commandAttributes[commands.count - 1]) + 1;
+ }
+
/* Cache the PCR capabilities, which are safe to cache, as the only way they can change is
* TPM2_PCR_Allocate(), which changes the allocation after the next _TPM_Init(). If the TPM is
* reinitialized while we are using it, all our context and sessions will be invalid, so we can
@@ -252,6 +287,29 @@ int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
return tpm2_get_capability_alg(c, alg, NULL);
}
+/* Get the TPMA_CC for a TPM2_CC. Returns true if the TPM supports the command and the TPMA_CC is provided,
+ * otherwise false. */
+static bool tpm2_get_capability_command(Tpm2Context *c, TPM2_CC command, TPMA_CC *ret) {
+ assert(c);
+
+ FOREACH_ARRAY(cca, c->capability_commands, c->n_capability_commands)
+ if (TPMA_CC_TO_TPM2_CC(*cca) == command) {
+ if (ret)
+ *ret = *cca;
+ return true;
+ }
+
+ log_debug("TPM does not support command 0x%04" PRIx32 ".", command);
+ if (ret)
+ *ret = 0;
+
+ return false;
+}
+
+bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command) {
+ return tpm2_get_capability_command(c, command, NULL);
+}
+
/* Returns 1 if the TPM supports the ECC curve, 0 if not, or < 0 for any error. */
static int tpm2_supports_ecc_curve(Tpm2Context *c, TPM2_ECC_CURVE curve) {
TPMU_CAPABILITIES capability;
@@ -415,6 +473,8 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
c->tcti_context = mfree(c->tcti_context);
c->tcti_dl = safe_dlclose(c->tcti_dl);
+ c->capability_commands = mfree(c->capability_commands);
+
return mfree(c);
}
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 1f20aadc98..1ca1a2e503 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -62,6 +62,8 @@ typedef struct {
ESYS_CONTEXT *esys_context;
/* Some selected cached capabilities of the TPM */
+ TPMA_CC *capability_commands;
+ size_t n_capability_commands;
TPML_PCR_SELECTION capability_pcrs;
} Tpm2Context;
@@ -85,6 +87,7 @@ Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
+bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);
bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index af06085af6..dfc8b98e08 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -658,6 +658,15 @@ TEST(tpm_required_tests) {
assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA) == 1);
assert_se(tpm2_supports_alg(c, TPM2_ALG_AES) == 1);
assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB) == 1);
+
+ /* Test invalid commands */
+ assert_se(!tpm2_supports_command(c, TPM2_CC_FIRST - 1));
+ assert_se(!tpm2_supports_command(c, TPM2_CC_LAST + 1));
+
+ /* Test valid commands */
+ assert_se(tpm2_supports_command(c, TPM2_CC_Create));
+ assert_se(tpm2_supports_command(c, TPM2_CC_CreatePrimary));
+ assert_se(tpm2_supports_command(c, TPM2_CC_Unseal));
}
#endif /* HAVE_TPM2 */

View File

@ -0,0 +1,186 @@
From 3fb5fadcb2c26e9e7da5de8f8bda0fa0e987c443 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 14 Jun 2023 13:17:21 -0400
Subject: [PATCH] tpm2: cache TPM algorithms
Cache the supported algorithms when creating a new context.
(cherry picked from commit cbc92a3172609238db572b86fa7da5e543e6a4dd)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 84 +++++++++++++++++++++++++++---------------
src/shared/tpm2-util.h | 4 +-
src/test/test-tpm2.c | 10 ++---
3 files changed, 62 insertions(+), 36 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index d38e260f9a..c05c636745 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -199,6 +199,44 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
assert(c);
+ /* Cache the algorithms. The spec indicates supported algorithms can only be modified during runtime
+ * by the SetAlgorithmSet() command. Unfortunately, the spec doesn't require a TPM reinitialization
+ * after changing the algorithm set (unless the PCR algorithms are changed). However, the spec also
+ * indicates the TPM behavior after SetAlgorithmSet() is "vendor-dependent", giving the example of
+ * flushing sessions and objects, erasing policies, etc. So, if the algorithm set is programatically
+ * changed while we are performing some operation, it's reasonable to assume it will break us even if
+ * we don't cache the algorithms, thus they should be "safe" to cache. */
+ TPM2_ALG_ID current_alg = TPM2_ALG_FIRST;
+ for (;;) {
+ r = tpm2_get_capability(
+ c,
+ TPM2_CAP_ALGS,
+ (uint32_t) current_alg, /* The spec states to cast TPM2_ALG_ID to uint32_t. */
+ TPM2_MAX_CAP_ALGS,
+ &capability);
+ if (r < 0)
+ return r;
+
+ TPML_ALG_PROPERTY algorithms = capability.algorithms;
+
+ /* We should never get 0; the TPM must support some algorithms, and it must not set 'more' if
+ * there are no more. */
+ assert(algorithms.count > 0);
+
+ if (!GREEDY_REALLOC_APPEND(
+ c->capability_algorithms,
+ c->n_capability_algorithms,
+ algorithms.algProperties,
+ algorithms.count))
+ return log_oom();
+
+ if (r == 0)
+ break;
+
+ /* Set current_alg to alg id after last alg id the TPM provided */
+ current_alg = algorithms.algProperties[algorithms.count - 1].alg + 1;
+ }
+
/* Cache the command capabilities. The spec isn't actually clear if commands can be added/removed
* while running, but that would be crazy, so let's hope it is not possbile. */
TPM2_CC current_cc = TPM2_CC_FIRST;
@@ -255,35 +293,26 @@ static int tpm2_cache_capabilities(Tpm2Context *c) {
return 0;
}
-/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID.
- *
- * Returns 1 if the TPM supports the algorithm and the TPMA_ALGORITHM is provided, or 0 if the TPM does not
- * support the algorithm, or < 0 for any errors. */
-static int tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) {
- TPMU_CAPABILITIES capability;
- int r;
-
+/* Get the TPMA_ALGORITHM for a TPM2_ALG_ID. Returns true if the TPM supports the algorithm and the
+ * TPMA_ALGORITHM is provided, otherwise false. */
+static bool tpm2_get_capability_alg(Tpm2Context *c, TPM2_ALG_ID alg, TPMA_ALGORITHM *ret) {
assert(c);
- /* The spec explicitly states the TPM2_ALG_ID should be cast to uint32_t. */
- r = tpm2_get_capability(c, TPM2_CAP_ALGS, (uint32_t) alg, 1, &capability);
- if (r < 0)
- return r;
-
- TPML_ALG_PROPERTY algorithms = capability.algorithms;
- if (algorithms.count == 0 || algorithms.algProperties[0].alg != alg) {
- log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg);
- return 0;
- }
+ FOREACH_ARRAY(alg_prop, c->capability_algorithms, c->n_capability_algorithms)
+ if (alg_prop->alg == alg) {
+ if (ret)
+ *ret = alg_prop->algProperties;
+ return true;
+ }
+ log_debug("TPM does not support alg 0x%02" PRIx16 ".", alg);
if (ret)
- *ret = algorithms.algProperties[0].algProperties;
+ *ret = 0;
- return 1;
+ return false;
}
-/* Returns 1 if the TPM supports the alg, 0 if the TPM does not support the alg, or < 0 for any error. */
-int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
+bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg) {
return tpm2_get_capability_alg(c, alg, NULL);
}
@@ -473,6 +502,7 @@ static Tpm2Context *tpm2_context_free(Tpm2Context *c) {
c->tcti_context = mfree(c->tcti_context);
c->tcti_dl = safe_dlclose(c->tcti_dl);
+ c->capability_algorithms = mfree(c->capability_algorithms);
c->capability_commands = mfree(c->capability_commands);
return mfree(c);
@@ -598,16 +628,10 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
return r;
/* We require AES and CFB support for session encryption. */
- r = tpm2_supports_alg(context, TPM2_ALG_AES);
- if (r < 0)
- return r;
- if (r == 0)
+ if (!tpm2_supports_alg(context, TPM2_ALG_AES))
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support AES.");
- r = tpm2_supports_alg(context, TPM2_ALG_CFB);
- if (r < 0)
- return r;
- if (r == 0)
+ if (!tpm2_supports_alg(context, TPM2_ALG_CFB))
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "TPM does not support CFB.");
if (!tpm2_supports_tpmt_sym_def(context, &SESSION_TEMPLATE_SYM_AES_128_CFB))
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 1ca1a2e503..64a2fd3677 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -62,6 +62,8 @@ typedef struct {
ESYS_CONTEXT *esys_context;
/* Some selected cached capabilities of the TPM */
+ TPMS_ALG_PROPERTY *capability_algorithms;
+ size_t n_capability_algorithms;
TPMA_CC *capability_commands;
size_t n_capability_commands;
TPML_PCR_SELECTION capability_pcrs;
@@ -86,7 +88,7 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
-int tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
+bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);
bool tpm2_test_parms(Tpm2Context *c, TPMI_ALG_PUBLIC alg, const TPMU_PUBLIC_PARMS *parms);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index dfc8b98e08..8fd859b83d 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -651,13 +651,13 @@ TEST(tpm_required_tests) {
assert_se(tpm2_test_parms(c, TPM2_ALG_SYMCIPHER, &parms));
/* Test invalid algs */
- assert_se(tpm2_supports_alg(c, TPM2_ALG_ERROR) == 0);
- assert_se(tpm2_supports_alg(c, TPM2_ALG_LAST + 1) == 0);
+ assert_se(!tpm2_supports_alg(c, TPM2_ALG_ERROR));
+ assert_se(!tpm2_supports_alg(c, TPM2_ALG_LAST + 1));
/* Test valid algs */
- assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA) == 1);
- assert_se(tpm2_supports_alg(c, TPM2_ALG_AES) == 1);
- assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB) == 1);
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_RSA));
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_AES));
+ assert_se(tpm2_supports_alg(c, TPM2_ALG_CFB));
/* Test invalid commands */
assert_se(!tpm2_supports_command(c, TPM2_CC_FIRST - 1));

View File

@ -0,0 +1,117 @@
From 5db840e4bc7b86f7870c153d540ef0f6eddf64c2 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Sun, 1 Jan 2023 23:42:09 -0500
Subject: [PATCH] tpm2: add tpm2_persist_handle()
Add function to convert a transient handle in the TPM into a persistent handle
in the TPM.
(cherry picked from commit d2d29c3be2ff9557d74c7bf852c1423ea6cfa25a)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 83 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 74 insertions(+), 9 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index c05c636745..0c5f3393dd 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -769,6 +769,75 @@ static int tpm2_esys_handle_from_tpm_handle(
return 1;
}
+/* Copy an object in the TPM at a transient location to a persistent location.
+ *
+ * The provided transient handle must exist in the TPM in the transient range. The persistent location may be
+ * 0 or any location in the persistent range. If 0, this will try each handle in the persistent range, in
+ * ascending order, until an available one is found. If non-zero, only the requested persistent location will
+ * be used.
+ *
+ * Returns 1 if the object was successfully persisted, or 0 if there is already a key at the requested
+ * location(s), or < 0 on error. The persistent handle is only provided when returning 1. */
+static int tpm2_persist_handle(
+ Tpm2Context *c,
+ const Tpm2Handle *transient_handle,
+ const Tpm2Handle *session,
+ TPMI_DH_PERSISTENT persistent_location,
+ Tpm2Handle **ret_persistent_handle) {
+
+ /* We don't use TPM2_PERSISTENT_FIRST and TPM2_PERSISTENT_LAST here due to:
+ * https://github.com/systemd/systemd/pull/27713#issuecomment-1591864753 */
+ TPMI_DH_PERSISTENT first = UINT32_C(0x81000000), last = UINT32_C(0x81ffffff);
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(transient_handle);
+
+ /* If persistent location specified, only try that. */
+ if (persistent_location != 0) {
+ if (TPM2_HANDLE_TYPE(persistent_location) != TPM2_HT_PERSISTENT)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Handle not in persistent range: 0x%x", persistent_location);
+
+ first = last = persistent_location;
+ }
+
+ for (TPMI_DH_PERSISTENT requested = first; requested <= last; requested++) {
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *persistent_handle = NULL;
+ r = tpm2_handle_new(c, &persistent_handle);
+ if (r < 0)
+ return r;
+
+ /* Since this is a persistent handle, don't flush it. */
+ persistent_handle->flush = false;
+
+ rc = sym_Esys_EvictControl(
+ c->esys_context,
+ ESYS_TR_RH_OWNER,
+ transient_handle->esys_handle,
+ session ? session->esys_handle : ESYS_TR_PASSWORD,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ requested,
+ &persistent_handle->esys_handle);
+ if (rc == TSS2_RC_SUCCESS) {
+ if (ret_persistent_handle)
+ *ret_persistent_handle = TAKE_PTR(persistent_handle);
+
+ return 1;
+ }
+ if (rc != TPM2_RC_NV_DEFINED)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to persist handle: %s", sym_Tss2_RC_Decode(rc));
+ }
+
+ if (ret_persistent_handle)
+ *ret_persistent_handle = NULL;
+
+ return 0;
+}
+
#define TPM2_CREDIT_RANDOM_FLAG_PATH "/run/systemd/tpm-rng-credited"
static int tpm2_credit_random(Tpm2Context *c) {
@@ -1203,16 +1272,12 @@ static int tpm2_make_primary(
FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
if (use_srk_model) {
- rc = sym_Esys_EvictControl(c->esys_context, ESYS_TR_RH_OWNER, primary->esys_handle,
- ESYS_TR_PASSWORD, ESYS_TR_NONE, ESYS_TR_NONE, TPM2_SRK_HANDLE, &primary->esys_handle);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to persist SRK within TPM: %s", sym_Tss2_RC_Decode(rc));
- primary->flush = false;
- }
-
- if (ret_primary)
+ r = tpm2_persist_handle(c, primary, /* session= */ NULL, TPM2_SRK_HANDLE, ret_primary);
+ if (r < 0)
+ return r;
+ } else if (ret_primary)
*ret_primary = TAKE_PTR(primary);
+
if (ret_alg)
*ret_alg = alg;

View File

@ -0,0 +1,327 @@
From 1228238af33756fa8974840bfa0e65f2ec7bfa71 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 14 Jun 2023 15:49:33 -0400
Subject: [PATCH] tpm2: add tpm2_get_or_create_srk()
Add function to simplify getting the TPM SRK; if one exists, it is provided,
otherwise one is created and then the new SRK provided.
This also add tpm2_create_loaded() and updates tpm2_seal() to use the new
functions instead of tpm2_make_primary().
(cherry picked from commit cea525a902246520d063ab53f667a0f33be650f0)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 241 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 231 insertions(+), 10 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 0c5f3393dd..2d208479c3 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -30,6 +30,7 @@ static void *libtss2_rc_dl = NULL;
static void *libtss2_mu_dl = NULL;
static TSS2_RC (*sym_Esys_Create)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
+static TSS2_RC (*sym_Esys_CreateLoaded)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_TEMPLATE *inPublic, ESYS_TR *objectHandle, TPM2B_PRIVATE **outPrivate, TPM2B_PUBLIC **outPublic) = NULL;
static TSS2_RC (*sym_Esys_CreatePrimary)(ESYS_CONTEXT *esysContext, ESYS_TR primaryHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_SENSITIVE_CREATE *inSensitive, const TPM2B_PUBLIC *inPublic, const TPM2B_DATA *outsideInfo, const TPML_PCR_SELECTION *creationPCR, ESYS_TR *objectHandle, TPM2B_PUBLIC **outPublic, TPM2B_CREATION_DATA **creationData, TPM2B_DIGEST **creationHash, TPMT_TK_CREATION **creationTicket) = NULL;
static TSS2_RC (*sym_Esys_EvictControl)(ESYS_CONTEXT *esysContext, ESYS_TR auth, ESYS_TR objectHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPMI_DH_PERSISTENT persistentHandle, ESYS_TR *newObjectHandle) = NULL;
static void (*sym_Esys_Finalize)(ESYS_CONTEXT **context) = NULL;
@@ -78,6 +79,7 @@ int dlopen_tpm2(void) {
r = dlopen_many_sym_or_warn(
&libtss2_esys_dl, "libtss2-esys.so.0", LOG_DEBUG,
DLSYM_ARG(Esys_Create),
+ DLSYM_ARG(Esys_CreateLoaded),
DLSYM_ARG(Esys_CreatePrimary),
DLSYM_ARG(Esys_EvictControl),
DLSYM_ARG(Esys_Finalize),
@@ -996,7 +998,7 @@ static int tpm2_get_legacy_template(TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_templa
* (see TPM2_SRK_HANDLE and tpm2_get_srk() below).
*
* The alg must be TPM2_ALG_RSA or TPM2_ALG_ECC. Returns error if the requested template is not supported on
- * this TPM. */
+ * this TPM. Also see tpm2_get_best_srk_template() below. */
static int tpm2_get_srk_template(Tpm2Context *c, TPMI_ALG_PUBLIC alg, TPMT_PUBLIC *ret_template) {
/* The attributes are the same between ECC and RSA templates. This has the changes specified in the
* Provisioning Guidance document, specifically:
@@ -1082,6 +1084,16 @@ static int tpm2_get_srk_template(Tpm2Context *c, TPMI_ALG_PUBLIC alg, TPMT_PUBLI
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Unsupported SRK alg: 0x%x.", alg);
}
+/* Get the best supported SRK template. ECC is preferred, then RSA. */
+static int tpm2_get_best_srk_template(Tpm2Context *c, TPMT_PUBLIC *ret_template) {
+ if (tpm2_get_srk_template(c, TPM2_ALG_ECC, ret_template) >= 0 ||
+ tpm2_get_srk_template(c, TPM2_ALG_RSA, ret_template) >= 0)
+ return 0;
+
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "TPM does not support either SRK template L-1 (RSA) or L-2 (ECC).");
+}
+
/* The SRK handle is defined in the Provisioning Guidance document (see above) in the table "Reserved Handles
* for TPM Provisioning Fundamental Elements". The SRK is useful because it is "shared", meaning it has no
* authValue nor authPolicy set, and thus may be used by anyone on the system to generate derived keys or
@@ -1090,10 +1102,8 @@ static int tpm2_get_srk_template(Tpm2Context *c, TPMI_ALG_PUBLIC alg, TPMT_PUBLI
* the Provisioning Guidance document for more details. */
#define TPM2_SRK_HANDLE UINT32_C(0x81000001)
-/*
- * Retrieves the SRK handle if present. Returns 0 if SRK not present, 1 if present
- * and < 0 on error
- */
+/* Get the SRK. Returns 1 if SRK is found, 0 if there is no SRK, or < 0 on error. Also see
+ * tpm2_get_or_create_srk() below. */
static int tpm2_get_srk(
Tpm2Context *c,
const Tpm2Handle *session,
@@ -1134,6 +1144,62 @@ static int tpm2_get_srk(
return 1;
}
+static int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle);
+
+/* Get the SRK, creating one if needed. Returns 0 on success, or < 0 on error. */
+static int tpm2_get_or_create_srk(
+ Tpm2Context *c,
+ const Tpm2Handle *session,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_NAME **ret_name,
+ TPM2B_NAME **ret_qname,
+ Tpm2Handle **ret_handle) {
+
+ int r;
+
+ r = tpm2_get_srk(c, session, ret_public, ret_name, ret_qname, ret_handle);
+ if (r < 0)
+ return r;
+ if (r == 1)
+ return 0;
+
+ /* No SRK, create and persist one */
+ TPMT_PUBLIC template;
+ r = tpm2_get_best_srk_template(c, &template);
+ if (r < 0)
+ return log_error_errno(r, "Could not get best SRK template: %m");
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *transient_handle = NULL;
+ r = tpm2_create_loaded(
+ c,
+ /* parent= */ NULL,
+ session,
+ &template,
+ /* sensitive= */ NULL,
+ /* ret_public= */ NULL,
+ /* ret_private= */ NULL,
+ &transient_handle);
+ if (r < 0)
+ return r;
+
+ /* Try to persist the transient SRK we created. No locking needed; if multiple threads are trying to
+ * persist SRKs concurrently, only one will succeed (r == 1) while the rest will fail (r == 0). In
+ * either case, all threads will get the persistent SRK below. */
+ r = tpm2_persist_handle(c, transient_handle, session, TPM2_SRK_HANDLE, /* ret_persistent_handle= */ NULL);
+ if (r < 0)
+ return r;
+
+ /* The SRK should exist now. */
+ r = tpm2_get_srk(c, session, ret_public, ret_name, ret_qname, ret_handle);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ /* This should never happen. */
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "SRK we just persisted couldn't be found.");
+
+ return 0;
+}
+
static int tpm2_make_primary(
Tpm2Context *c,
TPMI_ALG_PUBLIC alg,
@@ -1788,6 +1854,131 @@ static int tpm2_load_external(
return 0;
}
+/* This calls TPM2_CreateLoaded() directly, without checking if the TPM supports it. Callers should instead
+ * use tpm2_create_loaded(). */
+static int _tpm2_create_loaded(
+ Tpm2Context *c,
+ const Tpm2Handle *parent,
+ const Tpm2Handle *session,
+ const TPMT_PUBLIC *template,
+ const TPMS_SENSITIVE_CREATE *sensitive,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_PRIVATE **ret_private,
+ Tpm2Handle **ret_handle) {
+
+ usec_t ts;
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(template);
+
+ log_debug("Creating loaded object on TPM.");
+
+ ts = now(CLOCK_MONOTONIC);
+
+ /* Copy the input template and zero the unique area. */
+ TPMT_PUBLIC template_copy = *template;
+ zero(template_copy.unique);
+
+ TPM2B_TEMPLATE tpm2b_template;
+ size_t size = 0;
+ rc = sym_Tss2_MU_TPMT_PUBLIC_Marshal(
+ &template_copy,
+ tpm2b_template.buffer,
+ sizeof(tpm2b_template.buffer),
+ &size);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to marshal public key template: %s", sym_Tss2_RC_Decode(rc));
+ assert(size <= UINT16_MAX);
+ tpm2b_template.size = size;
+
+ TPM2B_SENSITIVE_CREATE tpm2b_sensitive;
+ if (sensitive)
+ tpm2b_sensitive = (TPM2B_SENSITIVE_CREATE) {
+ .size = sizeof(*sensitive),
+ .sensitive = *sensitive,
+ };
+ else
+ tpm2b_sensitive = (TPM2B_SENSITIVE_CREATE) {};
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_handle_new(c, &handle);
+ if (r < 0)
+ return r;
+
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
+ _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
+ rc = sym_Esys_CreateLoaded(
+ c->esys_context,
+ parent ? parent->esys_handle : ESYS_TR_RH_OWNER,
+ session ? session->esys_handle : ESYS_TR_PASSWORD,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ &tpm2b_sensitive,
+ &tpm2b_template,
+ &handle->esys_handle,
+ &private,
+ &public);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to generate loaded object in TPM: %s",
+ sym_Tss2_RC_Decode(rc));
+
+ log_debug("Successfully created loaded object on TPM in %s.",
+ FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
+
+ if (ret_public)
+ *ret_public = TAKE_PTR(public);
+ if (ret_private)
+ *ret_private = TAKE_PTR(private);
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(handle);
+
+ return 0;
+}
+
+/* This calls TPM2_CreateLoaded() if the TPM supports it, otherwise it calls TPM2_Create() and TPM2_Load()
+ * separately. */
+static int tpm2_create_loaded(
+ Tpm2Context *c,
+ const Tpm2Handle *parent,
+ const Tpm2Handle *session,
+ const TPMT_PUBLIC *template,
+ const TPMS_SENSITIVE_CREATE *sensitive,
+ TPM2B_PUBLIC **ret_public,
+ TPM2B_PRIVATE **ret_private,
+ Tpm2Handle **ret_handle) {
+
+ int r;
+
+ if (tpm2_supports_command(c, TPM2_CC_CreateLoaded))
+ return _tpm2_create_loaded(c, parent, session, template, sensitive, ret_public, ret_private, ret_handle);
+
+ /* Unfortunately, this TPM doesn't support CreateLoaded (added at spec revision 130) so we need to
+ * create and load manually. */
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
+ _cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
+ r = tpm2_create(c, parent, session, template, sensitive, &public, &private);
+ if (r < 0)
+ return r;
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_load(c, parent, session, public, private, &handle);
+ if (r < 0)
+ return r;
+
+ if (ret_public)
+ *ret_public = TAKE_PTR(public);
+ if (ret_private)
+ *ret_private = TAKE_PTR(private);
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(handle);
+
+ return 0;
+}
+
static int tpm2_pcr_read(
Tpm2Context *c,
const TPML_PCR_SELECTION *pcr_selection,
@@ -3193,11 +3384,41 @@ int tpm2_seal(const char *device,
if (r < 0)
return log_error_errno(r, "Failed to generate secret key: %m");
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
_cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
- TPMI_ALG_PUBLIC primary_alg;
- r = tpm2_make_primary(c, /* alg = */0, !!ret_srk_buf, &primary_alg, &primary_handle);
- if (r < 0)
- return r;
+ if (ret_srk_buf) {
+ r = tpm2_get_or_create_srk(c, NULL, &primary_public, NULL, NULL, &primary_handle);
+ if (r < 0)
+ return r;
+ } else {
+ /* TODO: force all callers to provide ret_srk_buf, so we can stop sealing with the legacy templates. */
+ TPMT_PUBLIC template;
+ r = tpm2_get_legacy_template(TPM2_ALG_ECC, &template);
+ if (r < 0)
+ return log_error_errno(r, "Could not get legacy ECC template: %m");
+
+ if (!tpm2_supports_tpmt_public(c, &template)) {
+ r = tpm2_get_legacy_template(TPM2_ALG_RSA, &template);
+ if (r < 0)
+ return log_error_errno(r, "Could not get legacy RSA template: %m");
+
+ if (!tpm2_supports_tpmt_public(c, &template))
+ return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "TPM does not support either ECC or RSA legacy template.");
+ }
+
+ r = tpm2_create_loaded(
+ c,
+ /* parent= */ NULL,
+ /* session= */ NULL,
+ &template,
+ /* sensitive= */ NULL,
+ &primary_public,
+ /* ret_private= */ NULL,
+ &primary_handle);
+ if (r < 0)
+ return r;
+ }
_cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
r = tpm2_make_encryption_session(c, primary_handle, &TPM2_HANDLE_NONE, &encryption_session);
@@ -3277,7 +3498,7 @@ int tpm2_seal(const char *device,
*ret_pcr_hash = TAKE_PTR(hash);
*ret_pcr_hash_size = policy_digest.size;
*ret_pcr_bank = pcr_bank;
- *ret_primary_alg = primary_alg;
+ *ret_primary_alg = primary_public->publicArea.type;
return 0;
}

View File

@ -0,0 +1,70 @@
From 23ad144b16077c4833f7bcdf3846ebbaa30ae053 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 14 Jun 2023 12:09:35 -0400
Subject: [PATCH] tpm2: move local vars in tpm2_unseal() to point of use
No functional change; cosmetic only.
(cherry picked from commit 98497426d61acc3302505903460abb058142fa0d)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 2d208479c3..92f1fdd962 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -3523,13 +3523,7 @@ int tpm2_unseal(const char *device,
void **ret_secret,
size_t *ret_secret_size) {
- _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
- _cleanup_(erase_and_freep) char *secret = NULL;
- TPM2B_PRIVATE private = {};
- TPM2B_PUBLIC public = {};
- size_t offset = 0;
TSS2_RC rc;
- usec_t start;
int r;
assert(blob);
@@ -3554,10 +3548,12 @@ int tpm2_unseal(const char *device,
* decrypted if the seed and the PCR policy were right ("unsealing"). We then download the result,
* and use it to unlock the LUKS2 volume. */
- start = now(CLOCK_MONOTONIC);
+ usec_t start = now(CLOCK_MONOTONIC);
log_debug("Unmarshalling private part of HMAC key.");
+ TPM2B_PRIVATE private = {};
+ size_t offset = 0;
rc = sym_Tss2_MU_TPM2B_PRIVATE_Unmarshal(blob, blob_size, &offset, &private);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
@@ -3565,6 +3561,7 @@ int tpm2_unseal(const char *device,
log_debug("Unmarshalling public part of HMAC key.");
+ TPM2B_PUBLIC public = {};
rc = sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal(blob, blob_size, &offset, &public);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
@@ -3640,6 +3637,7 @@ int tpm2_unseal(const char *device,
if (r < 0)
return r;
+ _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL;
for (unsigned i = RETRY_UNSEAL_MAX;; i--) {
_cleanup_(tpm2_handle_freep) Tpm2Handle *policy_session = NULL;
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
@@ -3691,6 +3689,7 @@ int tpm2_unseal(const char *device,
log_debug("A PCR value changed during the TPM2 policy session, restarting HMAC key unsealing (%u tries left).", i);
}
+ _cleanup_(erase_and_freep) char *secret = NULL;
secret = memdup(unsealed->buffer, unsealed->size);
explicit_bzero_safe(unsealed->buffer, unsealed->size);
if (!secret)

View File

@ -0,0 +1,256 @@
From 27f6d0b788dd29dfbed8d2b5a9acc8d5bbb04b0e Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 8 Jun 2023 06:55:45 -0400
Subject: [PATCH] tpm2: remove tpm2_make_primary()
Replace use of tpm2_make_primary() with tpm2_create_loaded()
(cherry picked from commit 20988602ff203f6645762ceb8cda70b5f26b0e1d)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 188 ++++++-----------------------------------
1 file changed, 25 insertions(+), 163 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 92f1fdd962..320261afb6 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1200,156 +1200,6 @@ static int tpm2_get_or_create_srk(
return 0;
}
-static int tpm2_make_primary(
- Tpm2Context *c,
- TPMI_ALG_PUBLIC alg,
- bool use_srk_model,
- TPMI_ALG_PUBLIC *ret_alg,
- Tpm2Handle **ret_primary) {
-
- static const TPM2B_SENSITIVE_CREATE primary_sensitive = {};
- static const TPML_PCR_SELECTION creation_pcr = {};
- TPM2B_PUBLIC primary_template = { .size = sizeof(TPMT_PUBLIC), };
- _cleanup_(release_lock_file) LockFile srk_lock = LOCK_FILE_INIT;
- TSS2_RC rc;
- usec_t ts;
- int r;
-
- log_debug("Creating %s on TPM.", use_srk_model ? "SRK" : "Transient Primary Key");
-
- /* So apparently not all TPM2 devices support ECC. ECC is generally preferably, because it's so much
- * faster, noticeably so (~10s vs. ~240ms on my system). Hence, unless explicitly configured let's
- * try to use ECC first, and if that does not work, let's fall back to RSA. */
-
- ts = now(CLOCK_MONOTONIC);
-
- _cleanup_(tpm2_handle_freep) Tpm2Handle *primary = NULL;
-
- /* we only need the SRK lock when making the SRK since its not atomic, transient
- * primary creations don't even matter if they stomp on each other, the TPM will
- * keep kicking back the same key.
- */
- if (use_srk_model) {
- r = make_lock_file("/run/systemd/tpm2-srk-init", LOCK_EX, &srk_lock);
- if (r < 0)
- return log_error_errno(r, "Failed to take TPM SRK lock: %m");
- }
-
- /* Find existing SRK and use it if present */
- if (use_srk_model) {
- _cleanup_(Esys_Freep) TPM2B_PUBLIC *primary_public = NULL;
- r = tpm2_get_srk(c, NULL, &primary_public, NULL, NULL, &primary);
- if (r < 0)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to establish if SRK is present");
- if (r == 1) {
- log_debug("Discovered existing SRK");
-
- TPMI_ALG_PUBLIC got_alg = primary_public->publicArea.type;
- if (alg != 0 && alg != got_alg)
- log_warning("Caller asked for specific algorithm %u, but existing SRK is %u, ignoring",
- alg, got_alg);
-
- if (ret_alg)
- *ret_alg = alg;
- if (ret_primary)
- *ret_primary = TAKE_PTR(primary);
- return 0;
- }
- log_debug("Did not find SRK, generating...");
- }
-
- r = tpm2_handle_new(c, &primary);
- if (r < 0)
- return r;
-
- if (IN_SET(alg, 0, TPM2_ALG_ECC)) {
- if (use_srk_model)
- r = tpm2_get_srk_template(c, TPM2_ALG_ECC, &primary_template.publicArea);
- else
- r = tpm2_get_legacy_template(TPM2_ALG_ECC, &primary_template.publicArea);
- if (r < 0)
- return r;
-
- rc = sym_Esys_CreatePrimary(
- c->esys_context,
- ESYS_TR_RH_OWNER,
- ESYS_TR_PASSWORD,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &primary_sensitive,
- &primary_template,
- NULL,
- &creation_pcr,
- &primary->esys_handle,
- NULL,
- NULL,
- NULL,
- NULL);
-
- if (rc != TSS2_RC_SUCCESS) {
- if (alg != 0)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to generate ECC primary key in TPM: %s", sym_Tss2_RC_Decode(rc));
-
- log_debug("Failed to generate ECC primary key in TPM, trying RSA: %s", sym_Tss2_RC_Decode(rc));
- } else {
- log_debug("Successfully created ECC primary key on TPM.");
- alg = TPM2_ALG_ECC;
- }
- }
-
- if (IN_SET(alg, 0, TPM2_ALG_RSA)) {
- if (use_srk_model)
- r = tpm2_get_srk_template(c, TPM2_ALG_RSA, &primary_template.publicArea);
- else
- r = tpm2_get_legacy_template(TPM2_ALG_RSA, &primary_template.publicArea);
- if (r < 0)
- return r;
-
- rc = sym_Esys_CreatePrimary(
- c->esys_context,
- ESYS_TR_RH_OWNER,
- ESYS_TR_PASSWORD,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &primary_sensitive,
- &primary_template,
- NULL,
- &creation_pcr,
- &primary->esys_handle,
- NULL,
- NULL,
- NULL,
- NULL);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to generate RSA primary key in TPM: %s", sym_Tss2_RC_Decode(rc));
- else if (alg == 0) {
- log_notice("TPM2 chip apparently does not support ECC primary keys, falling back to RSA. "
- "This likely means TPM2 operations will be relatively slow, please be patient.");
- alg = TPM2_ALG_RSA;
- }
-
- log_debug("Successfully created RSA primary key on TPM.");
- }
-
- log_debug("Generating %s on the TPM2 took %s.", use_srk_model ? "SRK" : "Transient Primary Key",
- FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
-
- if (use_srk_model) {
- r = tpm2_persist_handle(c, primary, /* session= */ NULL, TPM2_SRK_HANDLE, ret_primary);
- if (r < 0)
- return r;
- } else if (ret_primary)
- *ret_primary = TAKE_PTR(primary);
-
- if (ret_alg)
- *ret_alg = alg;
-
- return 0;
-}
-
/* Utility functions for TPMS_PCR_SELECTION. */
/* Convert a TPMS_PCR_SELECTION object to a mask. */
@@ -3572,31 +3422,43 @@ int tpm2_unseal(const char *device,
if (r < 0)
return r;
- /* If their is a primary key we trust, like an SRK, use it */
- _cleanup_(tpm2_handle_freep) Tpm2Handle *primary = NULL;
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *primary_handle = NULL;
if (srk_buf) {
-
- r = tpm2_handle_new(c, &primary);
+ r = tpm2_handle_new(c, &primary_handle);
if (r < 0)
return r;
- primary->flush = false;
+ primary_handle->flush = false;
log_debug("Found existing SRK key to use, deserializing ESYS_TR");
rc = sym_Esys_TR_Deserialize(
c->esys_context,
srk_buf,
srk_buf_size,
- &primary->esys_handle);
+ &primary_handle->esys_handle);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to deserialize primary key: %s", sym_Tss2_RC_Decode(rc));
- /* old callers without an SRK still need to create a key */
- } else {
- r = tpm2_make_primary(c, primary_alg, false, NULL, &primary);
+ } else if (primary_alg != 0) {
+ TPMT_PUBLIC template;
+ r = tpm2_get_legacy_template(primary_alg, &template);
+ if (r < 0)
+ return log_error_errno(r, "Could not get legacy template: %m");
+
+ r = tpm2_create_loaded(
+ c,
+ /* parent= */ NULL,
+ /* session= */ NULL,
+ &template,
+ /* sensitive= */ NULL,
+ /* ret_public= */ NULL,
+ /* ret_private= */ NULL,
+ &primary_handle);
if (r < 0)
return r;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "No SRK or primary alg provided.");
log_debug("Loading HMAC key into TPM.");
@@ -3607,7 +3469,7 @@ int tpm2_unseal(const char *device,
* provides protections.
*/
_cleanup_(tpm2_handle_freep) Tpm2Handle *hmac_key = NULL;
- r = tpm2_load(c, primary, NULL, &public, &private, &hmac_key);
+ r = tpm2_load(c, primary_handle, NULL, &public, &private, &hmac_key);
if (r < 0)
return r;
@@ -3633,7 +3495,7 @@ int tpm2_unseal(const char *device,
return r;
_cleanup_(tpm2_handle_freep) Tpm2Handle *encryption_session = NULL;
- r = tpm2_make_encryption_session(c, primary, hmac_key, &encryption_session);
+ r = tpm2_make_encryption_session(c, primary_handle, hmac_key, &encryption_session);
if (r < 0)
return r;
@@ -3643,7 +3505,7 @@ int tpm2_unseal(const char *device,
_cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL;
r = tpm2_make_policy_session(
c,
- primary,
+ primary_handle,
encryption_session,
/* trial= */ false,
&policy_session);

View File

@ -0,0 +1,246 @@
From 41ed51e6fd011244875680580abb892c4cf1fecf Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Thu, 3 Aug 2023 14:44:57 -0400
Subject: [PATCH] tpm2: use CreatePrimary() to create primary keys instead of
Create()
Older versions used CreatePrimary() to create a transient primary key to use
when creating a sealed data object. That was changed in v254 to use Create()
instead, which should result in the same transient key, but it seems some
hardware TPMs refuse to allow using Create() to generate primary keys.
This reverts to using CreatePrimary() to create primary key.
Fixes: #28654
(cherry picked from commit aff853f8ea29f22b28e3b584807893c528227769)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 102 +++++++++++++++++++++++++++++++----------
src/shared/tpm2-util.h | 4 ++
2 files changed, 81 insertions(+), 25 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 320261afb6..e889d4c0fe 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1144,8 +1144,6 @@ static int tpm2_get_srk(
return 1;
}
-static int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle);
-
/* Get the SRK, creating one if needed. Returns 0 on success, or < 0 on error. */
static int tpm2_get_or_create_srk(
Tpm2Context *c,
@@ -1164,20 +1162,18 @@ static int tpm2_get_or_create_srk(
return 0;
/* No SRK, create and persist one */
- TPMT_PUBLIC template;
- r = tpm2_get_best_srk_template(c, &template);
+ TPM2B_PUBLIC template = { .size = sizeof(TPMT_PUBLIC), };
+ r = tpm2_get_best_srk_template(c, &template.publicArea);
if (r < 0)
return log_error_errno(r, "Could not get best SRK template: %m");
_cleanup_(tpm2_handle_freep) Tpm2Handle *transient_handle = NULL;
- r = tpm2_create_loaded(
+ r = tpm2_create_primary(
c,
- /* parent= */ NULL,
session,
&template,
/* sensitive= */ NULL,
/* ret_public= */ NULL,
- /* ret_private= */ NULL,
&transient_handle);
if (r < 0)
return r;
@@ -1547,8 +1543,65 @@ static int tpm2_get_policy_digest(
return 0;
}
-static int tpm2_create(
+int tpm2_create_primary(
Tpm2Context *c,
+ const Tpm2Handle *session,
+ const TPM2B_PUBLIC *template,
+ const TPM2B_SENSITIVE_CREATE *sensitive,
+ TPM2B_PUBLIC **ret_public,
+ Tpm2Handle **ret_handle) {
+
+ usec_t ts;
+ TSS2_RC rc;
+ int r;
+
+ assert(c);
+ assert(template);
+
+ log_debug("Creating primary key on TPM.");
+
+ ts = now(CLOCK_MONOTONIC);
+
+ _cleanup_(tpm2_handle_freep) Tpm2Handle *handle = NULL;
+ r = tpm2_handle_new(c, &handle);
+ if (r < 0)
+ return r;
+
+ _cleanup_(Esys_Freep) TPM2B_PUBLIC *public = NULL;
+ rc = sym_Esys_CreatePrimary(
+ c->esys_context,
+ ESYS_TR_RH_OWNER,
+ session ? session->esys_handle : ESYS_TR_PASSWORD,
+ ESYS_TR_NONE,
+ ESYS_TR_NONE,
+ sensitive ? sensitive : &(TPM2B_SENSITIVE_CREATE) {},
+ template,
+ /* outsideInfo= */ NULL,
+ &(TPML_PCR_SELECTION) {},
+ &handle->esys_handle,
+ &public,
+ /* creationData= */ NULL,
+ /* creationHash= */ NULL,
+ /* creationTicket= */ NULL);
+ if (rc != TSS2_RC_SUCCESS)
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
+ "Failed to generate primary key in TPM: %s",
+ sym_Tss2_RC_Decode(rc));
+
+ log_debug("Successfully created primary key on TPM in %s.",
+ FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - ts, USEC_PER_MSEC));
+
+ if (ret_public)
+ *ret_public = TAKE_PTR(public);
+ if (ret_handle)
+ *ret_handle = TAKE_PTR(handle);
+
+ return 0;
+}
+
+/* Create a TPM object. Do not use this to create primary keys, because some HW TPMs refuse to allow that;
+ * instead use tpm2_create_primary(). */
+int tpm2_create(Tpm2Context *c,
const Tpm2Handle *parent,
const Tpm2Handle *session,
const TPMT_PUBLIC *template,
@@ -1560,6 +1613,7 @@ static int tpm2_create(
TSS2_RC rc;
assert(c);
+ assert(parent);
assert(template);
log_debug("Creating object on TPM.");
@@ -1587,7 +1641,7 @@ static int tpm2_create(
_cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
rc = sym_Esys_Create(
c->esys_context,
- parent ? parent->esys_handle : ESYS_TR_RH_OWNER,
+ parent->esys_handle,
session ? session->esys_handle : ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1721,6 +1775,7 @@ static int _tpm2_create_loaded(
int r;
assert(c);
+ assert(parent);
assert(template);
log_debug("Creating loaded object on TPM.");
@@ -1762,7 +1817,7 @@ static int _tpm2_create_loaded(
_cleanup_(Esys_Freep) TPM2B_PRIVATE *private = NULL;
rc = sym_Esys_CreateLoaded(
c->esys_context,
- parent ? parent->esys_handle : ESYS_TR_RH_OWNER,
+ parent->esys_handle,
session ? session->esys_handle : ESYS_TR_PASSWORD,
ESYS_TR_NONE,
ESYS_TR_NONE,
@@ -1790,8 +1845,9 @@ static int _tpm2_create_loaded(
}
/* This calls TPM2_CreateLoaded() if the TPM supports it, otherwise it calls TPM2_Create() and TPM2_Load()
- * separately. */
-static int tpm2_create_loaded(
+ * separately. Do not use this to create primary keys, because some HW TPMs refuse to allow that; instead use
+ * tpm2_create_primary(). */
+int tpm2_create_loaded(
Tpm2Context *c,
const Tpm2Handle *parent,
const Tpm2Handle *session,
@@ -3242,29 +3298,27 @@ int tpm2_seal(const char *device,
return r;
} else {
/* TODO: force all callers to provide ret_srk_buf, so we can stop sealing with the legacy templates. */
- TPMT_PUBLIC template;
- r = tpm2_get_legacy_template(TPM2_ALG_ECC, &template);
+ TPM2B_PUBLIC template = { .size = sizeof(TPMT_PUBLIC), };
+ r = tpm2_get_legacy_template(TPM2_ALG_ECC, &template.publicArea);
if (r < 0)
return log_error_errno(r, "Could not get legacy ECC template: %m");
- if (!tpm2_supports_tpmt_public(c, &template)) {
- r = tpm2_get_legacy_template(TPM2_ALG_RSA, &template);
+ if (!tpm2_supports_tpmt_public(c, &template.publicArea)) {
+ r = tpm2_get_legacy_template(TPM2_ALG_RSA, &template.publicArea);
if (r < 0)
return log_error_errno(r, "Could not get legacy RSA template: %m");
- if (!tpm2_supports_tpmt_public(c, &template))
+ if (!tpm2_supports_tpmt_public(c, &template.publicArea))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"TPM does not support either ECC or RSA legacy template.");
}
- r = tpm2_create_loaded(
+ r = tpm2_create_primary(
c,
- /* parent= */ NULL,
/* session= */ NULL,
&template,
/* sensitive= */ NULL,
&primary_public,
- /* ret_private= */ NULL,
&primary_handle);
if (r < 0)
return r;
@@ -3440,19 +3494,17 @@ int tpm2_unseal(const char *device,
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to deserialize primary key: %s", sym_Tss2_RC_Decode(rc));
} else if (primary_alg != 0) {
- TPMT_PUBLIC template;
- r = tpm2_get_legacy_template(primary_alg, &template);
+ TPM2B_PUBLIC template = { .size = sizeof(TPMT_PUBLIC), };
+ r = tpm2_get_legacy_template(primary_alg, &template.publicArea);
if (r < 0)
return log_error_errno(r, "Could not get legacy template: %m");
- r = tpm2_create_loaded(
+ r = tpm2_create_primary(
c,
- /* parent= */ NULL,
/* session= */ NULL,
&template,
/* sensitive= */ NULL,
/* ret_public= */ NULL,
- /* ret_private= */ NULL,
&primary_handle);
if (r < 0)
return r;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 64a2fd3677..e059f95790 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -88,6 +88,10 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
+int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_PUBLIC *template, const TPM2B_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, Tpm2Handle **ret_handle);
+int tpm2_create(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private);
+int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle);
+
bool tpm2_supports_alg(Tpm2Context *c, TPM2_ALG_ID alg);
bool tpm2_supports_command(Tpm2Context *c, TPM2_CC command);

View File

@ -0,0 +1,182 @@
From 1609ee25acda161923e179182d6adbdac810993e Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 2 May 2023 11:14:20 +0200
Subject: [PATCH] cryptsetup: downgrade a bunch of log messages that to
LOG_WARNING
In all these cases we ignore the failure, hence per our rule the log
level should be below LOG_ERR. Fix that.
(cherry picked from commit b96cc40a95ccf5bdb61e54f6b361a1c7557ab81a)
Related: RHEL-16182
---
src/cryptsetup/cryptsetup.c | 62 ++++++++++++++-----------------------
1 file changed, 24 insertions(+), 38 deletions(-)
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 674d222db6..96341207b3 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -158,12 +158,12 @@ static int parse_one_option(const char *option) {
r = safe_atou(val, &arg_key_size);
if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
if (arg_key_size % 8) {
- log_error("size= not a multiple of 8, ignoring.");
+ log_warning("size= not a multiple of 8, ignoring.");
return 0;
}
@@ -173,29 +173,25 @@ static int parse_one_option(const char *option) {
r = safe_atou(val, &arg_sector_size);
if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
if (arg_sector_size % 2) {
- log_error("sector-size= not a multiple of 2, ignoring.");
+ log_warning("sector-size= not a multiple of 2, ignoring.");
return 0;
}
- if (arg_sector_size < CRYPT_SECTOR_SIZE || arg_sector_size > CRYPT_MAX_SECTOR_SIZE) {
- log_error("sector-size= is outside of %u and %u, ignoring.", CRYPT_SECTOR_SIZE, CRYPT_MAX_SECTOR_SIZE);
- return 0;
- }
+ if (arg_sector_size < CRYPT_SECTOR_SIZE || arg_sector_size > CRYPT_MAX_SECTOR_SIZE)
+ log_warning("sector-size= is outside of %u and %u, ignoring.", CRYPT_SECTOR_SIZE, CRYPT_MAX_SECTOR_SIZE);
} else if ((val = startswith(option, "key-slot=")) ||
(val = startswith(option, "keyslot="))) {
arg_type = ANY_LUKS;
r = safe_atoi(val, &arg_key_slot);
- if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
- return 0;
- }
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
} else if ((val = startswith(option, "tcrypt-keyfile="))) {
@@ -204,29 +200,25 @@ static int parse_one_option(const char *option) {
if (strv_extend(&arg_tcrypt_keyfiles, val) < 0)
return log_oom();
} else
- log_error("Key file path \"%s\" is not absolute. Ignoring.", val);
+ log_warning("Key file path \"%s\" is not absolute, ignoring.", val);
} else if ((val = startswith(option, "keyfile-size="))) {
r = safe_atou(val, &arg_keyfile_size);
- if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
- return 0;
- }
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
} else if ((val = startswith(option, "keyfile-offset="))) {
r = safe_atou64(val, &arg_keyfile_offset);
- if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
- return 0;
- }
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
} else if ((val = startswith(option, "keyfile-erase="))) {
r = parse_boolean(val);
if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
@@ -258,10 +250,8 @@ static int parse_one_option(const char *option) {
} else if ((val = startswith(option, "tries="))) {
r = safe_atou(val, &arg_tries);
- if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
- return 0;
- }
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
} else if (STR_IN_SET(option, "readonly", "read-only"))
arg_readonly = true;
@@ -314,10 +304,8 @@ static int parse_one_option(const char *option) {
else if ((val = startswith(option, "timeout="))) {
r = parse_sec_fix_0(val, &arg_timeout);
- if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
- return 0;
- }
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
} else if ((val = startswith(option, "offset="))) {
@@ -420,7 +408,7 @@ static int parse_one_option(const char *option) {
r = parse_boolean(val);
if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
@@ -439,7 +427,7 @@ static int parse_one_option(const char *option) {
pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX;
} else if (!TPM2_PCR_VALID(pcr)) {
- log_error("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
+ log_warning("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
return 0;
}
@@ -472,7 +460,7 @@ static int parse_one_option(const char *option) {
r = parse_boolean(val);
if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
@@ -484,7 +472,7 @@ static int parse_one_option(const char *option) {
r = parse_boolean(val);
if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
@@ -495,10 +483,8 @@ static int parse_one_option(const char *option) {
else if ((val = startswith(option, "token-timeout="))) {
r = parse_sec_fix_0(val, &arg_token_timeout_usec);
- if (r < 0) {
- log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
- return 0;
- }
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s, ignoring: %m", option);
} else if (!streq(option, "x-initrd.attach"))
log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);

View File

@ -0,0 +1,148 @@
From 4555de31d96745cabd1362aea7ba3b849429e98b Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 5 May 2023 19:48:14 -0400
Subject: [PATCH] boot/measure: replace TPM PolicyPCR session with calculation
Instead of using a trial policy with a TPM to calculate the measurement hash,
this uses a function to calculate the hash with no TPM needed.
(cherry picked from commit b2efe286587e11e2aa4a6c7e4a2c15da3bb58a2a)
Related: RHEL-16182
---
src/boot/measure.c | 85 +++++++++-------------------------------------
1 file changed, 16 insertions(+), 69 deletions(-)
diff --git a/src/boot/measure.c b/src/boot/measure.c
index 86edf77c52..5ce3049147 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -718,7 +718,6 @@ 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_fclose_ FILE *privkeyf = NULL;
- TSS2_RC rc;
size_t n;
int r;
@@ -787,15 +786,6 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- r = dlopen_tpm2();
- if (r < 0)
- return r;
-
- _cleanup_tpm2_context_ Tpm2Context *c = NULL;
- r = tpm2_context_new(arg_tpm2_device, &c);
- if (r < 0)
- return r;
-
STRV_FOREACH(phase, arg_phase) {
r = measure_phase(pcr_states, n, *phase);
@@ -803,42 +793,8 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
return r;
for (size_t i = 0; i < n; i++) {
- static const TPMT_SYM_DEF symmetric = {
- .algorithm = TPM2_ALG_AES,
- .keyBits.aes = 128,
- .mode.aes = TPM2_ALG_CFB,
- };
PcrState *p = pcr_states + i;
- _cleanup_tpm2_handle_ Tpm2Handle *session = NULL;
- r = tpm2_handle_new(c, &session);
- if (r < 0)
- return r;
-
- rc = sym_Esys_StartAuthSession(
- c->esys_context,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- NULL,
- TPM2_SE_TRIAL,
- &symmetric,
- TPM2_ALG_SHA256,
- &session->esys_handle);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc));
-
- /* Generate a single hash value from the PCRs included in our policy. Given that that's
- * exactly one, the calculation is trivial. */
- TPM2B_DIGEST intermediate_digest = {
- .size = SHA256_DIGEST_SIZE,
- };
- assert(sizeof(intermediate_digest.buffer) >= SHA256_DIGEST_SIZE);
- sha256_direct(p->value, p->value_size, intermediate_digest.buffer);
-
int tpmalg = tpm2_hash_alg_from_string(EVP_MD_name(p->md));
if (tpmalg < 0)
return log_error_errno(tpmalg, "Unsupported PCR bank");
@@ -848,29 +804,20 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
tpmalg,
&pcr_selection);
- rc = sym_Esys_PolicyPCR(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &intermediate_digest,
- &pcr_selection);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to push PCR policy into TPM: %s", sym_Tss2_RC_Decode(rc));
-
- _cleanup_(Esys_Freep) TPM2B_DIGEST *pcr_policy_digest = NULL;
- rc = sym_Esys_PolicyGetDigest(
- c->esys_context,
- session->esys_handle,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- ESYS_TR_NONE,
- &pcr_policy_digest);
- if (rc != TSS2_RC_SUCCESS)
- return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
- "Failed to get policy digest from TPM: %s", sym_Tss2_RC_Decode(rc));
+ TPM2B_DIGEST pcr_values = {
+ .size = p->value_size,
+ };
+ assert(sizeof(pcr_values.buffer) >= p->value_size);
+ memcpy_safe(pcr_values.buffer, p->value, p->value_size);
+
+ TPM2B_DIGEST pcr_policy_digest;
+ r = tpm2_digest_init(TPM2_ALG_SHA256, &pcr_policy_digest);
+ if (r < 0)
+ return r;
+
+ r = tpm2_calculate_policy_pcr(&pcr_selection, &pcr_values, 1, &pcr_policy_digest);
+ if (r < 0)
+ return r;
_cleanup_(EVP_MD_CTX_freep) EVP_MD_CTX* mdctx = NULL;
mdctx = EVP_MD_CTX_new();
@@ -881,7 +828,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to initialize signature context.");
- if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest->buffer, pcr_policy_digest->size) != 1)
+ if (EVP_DigestSignUpdate(mdctx, pcr_policy_digest.buffer, pcr_policy_digest.size) != 1)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to sign data.");
@@ -913,7 +860,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
r = json_build(&bv, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("pcrs", JSON_BUILD_VARIANT(a)), /* PCR mask */
JSON_BUILD_PAIR("pkfp", JSON_BUILD_HEX(pubkey_fp, pubkey_fp_size)), /* SHA256 fingerprint of public key (DER) used for the signature */
- JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest->buffer, pcr_policy_digest->size)), /* TPM2 policy hash that is signed */
+ JSON_BUILD_PAIR("pol", JSON_BUILD_HEX(pcr_policy_digest.buffer, pcr_policy_digest.size)), /* TPM2 policy hash that is signed */
JSON_BUILD_PAIR("sig", JSON_BUILD_BASE64(sig, ss)))); /* signature data */
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");

View File

@ -0,0 +1,77 @@
From 8d11c92de8644e5f090018933bad25be0f2adebb Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Wed, 8 Feb 2023 00:25:00 +0000
Subject: [PATCH] core: imply DeviceAllow=/dev/tpmrm0 with
LoadCredentialEncrypted
If the device access policy is restricted, add implicitly access to the TPM
if at least one encrypted credential needs to be loaded.
Fixes https://github.com/systemd/systemd/issues/26042
(cherry picked from commit 398dc7d39b9a877e71529f0e0b139329e4c6992e)
Related: RHEL-16182
---
man/systemd.exec.xml | 8 +++++++-
src/core/unit.c | 10 ++++++++++
test/units/testsuite-70.sh | 6 ++++++
3 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 29666b102b..4927764b9b 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -3113,7 +3113,13 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
authenticated credentials improves security as credentials are not stored in plaintext and only
authenticated and decrypted into plaintext the moment a service requiring them is started. Moreover,
credentials may be bound to the local hardware and installations, so that they cannot easily be
- analyzed offline, or be generated externally.</para>
+ analyzed offline, or be generated externally. When <varname>DevicePolicy=</varname> is set to
+ <literal>closed</literal> or <literal>strict</literal>, or set to <literal>auto</literal> and
+ <varname>DeviceAllow=</varname> is set, or <varname>PrivateDevices=</varname> is set, then this
+ setting adds <filename>/dev/tpmrm0</filename> with <constant>rw</constant> mode to
+ <varname>DeviceAllow=</varname>. See
+ <citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for the details about <varname>DevicePolicy=</varname> or <varname>DeviceAllow=</varname>.</para>
<para>The credential files/IPC sockets must be accessible to the service manager, but don't have to
be directly accessible to the unit's processes: the credential data is read and copied into separate,
diff --git a/src/core/unit.c b/src/core/unit.c
index c9a42ee3d7..f109d16eb3 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -4191,6 +4191,16 @@ int unit_patch_contexts(Unit *u) {
if (r < 0)
return r;
}
+
+ /* If there are encrypted credentials we might need to access the TPM. */
+ ExecLoadCredential *cred;
+ HASHMAP_FOREACH(cred, ec->load_credentials)
+ if (cred->encrypted) {
+ r = cgroup_add_device_allow(cc, "/dev/tpmrm0", "rw");
+ if (r < 0)
+ return r;
+ break;
+ }
}
}
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
index 3b4d66b686..2c405bccbb 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/testsuite-70.sh
@@ -185,6 +185,12 @@ else
echo "/usr/lib/systemd/systemd-pcrphase or PCR sysfs files not found, skipping PCR extension test case"
fi
+# Ensure that sandboxing doesn't stop creds from being accessible
+echo "test" > /tmp/testdata
+systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2
+systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
+rm /tmp/testdata
+
echo OK >/testok
exit 0

View File

@ -0,0 +1,101 @@
From c42a85ba710d4c0e60a97ecb6003825979351dab Mon Sep 17 00:00:00 2001
From: OMOJOLA JOSHUA DAMILOLA <omojolajoshua@gmail.com>
Date: Mon, 27 Mar 2023 15:24:03 +0000
Subject: [PATCH] added more test cases
(cherry picked from commit e2a4411a2b683e3e5b78c1d4931b5e1029d3ba6e)
Related: RHEL-16182
---
test/units/testsuite-70.sh | 78 ++++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
index 2c405bccbb..19768ef7bf 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/testsuite-70.sh
@@ -191,6 +191,84 @@ systemd-creds encrypt /tmp/testdata /tmp/testdata.encrypted --with-key=tpm2
systemd-run -p PrivateDevices=yes -p LoadCredentialEncrypted=testdata.encrypted:/tmp/testdata.encrypted --pipe --wait systemd-creds cat testdata.encrypted | cmp - /tmp/testdata
rm /tmp/testdata
+# negative tests for cryptenroll
+
+# Prepare a new disk image
+img_2="/var/tmp/file_enroll.txt"
+truncate -s 20M $img_2
+echo -n password >/tmp/password
+cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom $img_2 /tmp/password
+
+#boolean_arguments
+ret="$(! systemd-cryptenroll --fido2-with-client-pin=false 2> >(grep "No block device node specified"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --fido2-with-user-presence=f $img_2 /tmp/foo 2> >(grep "Too many arguments"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --fido2-with-client-pin=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
+test -n "${ret}"
+
+systemd-cryptenroll --fido2-with-client-pin=false $img_2
+
+ret="$(! systemd-cryptenroll --fido2-with-user-presence=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
+test -n "${ret}"
+
+systemd-cryptenroll --fido2-with-user-presence=false $img_2
+
+ret="$(! systemd-cryptenroll --fido2-with-user-verification=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --tpm2-with-pin=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
+test -n "${ret}"
+
+systemd-cryptenroll --fido2-with-user-verification=false $img_2
+
+#arg_enroll_type
+ret="$(! systemd-cryptenroll --recovery-key --password $img_2 2> >(grep "Multiple operations specified at once"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --password --recovery-key $img_2 2> >(grep "Multiple operations specified at once"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --password --fido2-device=auto $img_2 2> >(grep "Multiple operations specified at once"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --password --pkcs11-token-uri=auto $img_2 2> >(grep "Multiple operations specified at once"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --password --tpm2-device=auto $img_2 2> >(grep "Multiple operations specified at once"))"
+test -n "${ret}"
+
+#arg_unlock_type
+ret="$(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto $img_2 2> >(grep "Multiple unlock methods specified at once"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock $img_2 2> >(grep "Multiple unlock methods specified at once"))"
+test -n "${ret}"
+
+#fido2_cred_alg
+ret="$(! systemd-cryptenroll --fido2-credential-algorithm=es512 $img_2 2> >(grep "Failed to parse COSE algorithm"))"
+test -n "${ret}"
+
+#tpm2_errors
+ret="$(! systemd-cryptenroll --tpm2-public-key-pcrs=key $img_2 2> >(grep "Failed to parse PCR number"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --tpm2-pcrs=key $img_2 2> >(grep "Failed to parse PCR number"))"
+test -n "${ret}"
+
+#wipe_slots
+ret="$(! systemd-cryptenroll --wipe-slot $img_2 2> >(grep "Failed to parse slot index"))"
+test -n "${ret}"
+
+ret="$(! systemd-cryptenroll --wipe-slot=10240000 $img_2 2> >(grep "Slot index"))"
+test -n "${ret}"
+
+#fido2_multiple_auto
+ret="$(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto $img_2 2> >(grep "When both enrolling and unlocking with FIDO2 tokens, automatic discovery is unsupported. Please specify device paths for enrolling and unlocking respectively."))"
+test -n "${ret}"
+
echo OK >/testok
exit 0

View File

@ -0,0 +1,112 @@
From 60b259a6c4ddd71a3fe1256271a97c706419b860 Mon Sep 17 00:00:00 2001
From: OMOJOLA JOSHUA DAMILOLA <omojolajoshua@gmail.com>
Date: Thu, 30 Mar 2023 21:36:50 +0000
Subject: [PATCH] test: fixed negative checks in TEST-70-TPM2. Use in-line
error handling rather than redirections. Follow up on #27020
(cherry picked from commit 27d45db38c29d0eb5e2d707507b066438340a792)
Related: RHEL-16182
---
test/units/testsuite-70.sh | 59 +++++++++++++-------------------------
1 file changed, 20 insertions(+), 39 deletions(-)
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
index 19768ef7bf..1bfa14e01a 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/testsuite-70.sh
@@ -200,74 +200,55 @@ echo -n password >/tmp/password
cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom $img_2 /tmp/password
#boolean_arguments
-ret="$(! systemd-cryptenroll --fido2-with-client-pin=false 2> >(grep "No block device node specified"))"
-test -n "${ret}"
+systemd-cryptenroll --fido2-with-client-pin=false && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --fido2-with-user-presence=f $img_2 /tmp/foo 2> >(grep "Too many arguments"))"
-test -n "${ret}"
+systemd-cryptenroll --fido2-with-user-presence=f $img_2 /tmp/foo && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --fido2-with-client-pin=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
-test -n "${ret}"
+systemd-cryptenroll --fido2-with-client-pin=1234 $img_2 && { echo 'unexpected success'; exit 1; }
systemd-cryptenroll --fido2-with-client-pin=false $img_2
-ret="$(! systemd-cryptenroll --fido2-with-user-presence=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
-test -n "${ret}"
+systemd-cryptenroll --fido2-with-user-presence=1234 $img_2 && { echo 'unexpected success'; exit 1; }
systemd-cryptenroll --fido2-with-user-presence=false $img_2
-ret="$(! systemd-cryptenroll --fido2-with-user-verification=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
-test -n "${ret}"
+systemd-cryptenroll --fido2-with-user-verification=1234 $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --tpm2-with-pin=1234 $img_2 2> >(grep "Failed to parse boolean argument"))"
-test -n "${ret}"
+systemd-cryptenroll --tpm2-with-pin=1234 $img_2 && { echo 'unexpected success'; exit 1; }
systemd-cryptenroll --fido2-with-user-verification=false $img_2
#arg_enroll_type
-ret="$(! systemd-cryptenroll --recovery-key --password $img_2 2> >(grep "Multiple operations specified at once"))"
-test -n "${ret}"
+systemd-cryptenroll --recovery-key --password $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --password --recovery-key $img_2 2> >(grep "Multiple operations specified at once"))"
-test -n "${ret}"
+systemd-cryptenroll --password --recovery-key $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --password --fido2-device=auto $img_2 2> >(grep "Multiple operations specified at once"))"
-test -n "${ret}"
+systemd-cryptenroll --password --fido2-device=auto $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --password --pkcs11-token-uri=auto $img_2 2> >(grep "Multiple operations specified at once"))"
-test -n "${ret}"
+systemd-cryptenroll --password --pkcs11-token-uri=auto $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --password --tpm2-device=auto $img_2 2> >(grep "Multiple operations specified at once"))"
-test -n "${ret}"
+systemd-cryptenroll --password --tpm2-device=auto $img_2 && { echo 'unexpected success'; exit 1; }
#arg_unlock_type
-ret="$(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto $img_2 2> >(grep "Multiple unlock methods specified at once"))"
-test -n "${ret}"
+systemd-cryptenroll --unlock-fido2-device=auto --unlock-fido2-device=auto $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock $img_2 2> >(grep "Multiple unlock methods specified at once"))"
-test -n "${ret}"
+systemd-cryptenroll --unlock-fido2-device=auto --unlock-key-file=/tmp/unlock $img_2 && { echo 'unexpected success'; exit 1; }
-#fido2_cred_alg
-ret="$(! systemd-cryptenroll --fido2-credential-algorithm=es512 $img_2 2> >(grep "Failed to parse COSE algorithm"))"
-test -n "${ret}"
+#fido2_cred_algorithm
+systemd-cryptenroll --fido2-credential-algorithm=es512 $img_2 && { echo 'unexpected success'; exit 1; }
#tpm2_errors
-ret="$(! systemd-cryptenroll --tpm2-public-key-pcrs=key $img_2 2> >(grep "Failed to parse PCR number"))"
-test -n "${ret}"
+systemd-cryptenroll --tpm2-public-key-pcrs=key $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --tpm2-pcrs=key $img_2 2> >(grep "Failed to parse PCR number"))"
-test -n "${ret}"
+systemd-cryptenroll --tpm2-pcrs=key $img_2 && { echo 'unexpected success'; exit 1; }
#wipe_slots
-ret="$(! systemd-cryptenroll --wipe-slot $img_2 2> >(grep "Failed to parse slot index"))"
-test -n "${ret}"
+systemd-cryptenroll --wipe-slot $img_2 && { echo 'unexpected success'; exit 1; }
-ret="$(! systemd-cryptenroll --wipe-slot=10240000 $img_2 2> >(grep "Slot index"))"
-test -n "${ret}"
+systemd-cryptenroll --wipe-slot=10240000 $img_2 && { echo 'unexpected success'; exit 1; }
#fido2_multiple_auto
-ret="$(! systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto $img_2 2> >(grep "When both enrolling and unlocking with FIDO2 tokens, automatic discovery is unsupported. Please specify device paths for enrolling and unlocking respectively."))"
-test -n "${ret}"
+systemd-cryptenroll --fido2-device=auto --unlock-fido2-device=auto $img_2 && { echo 'unexpected success'; exit 1; }
echo OK >/testok

View File

@ -0,0 +1,358 @@
From d91f027b44b32703fbd6bcf9a28aadde2549b8fd Mon Sep 17 00:00:00 2001
From: OMOJOLA JOSHUA DAMILOLA <omojolajoshua@gmail.com>
Date: Thu, 30 Mar 2023 07:55:41 +0000
Subject: [PATCH] systemd-cryptenroll: add string aliases for tpm2 PCRs Fixes
#26697. RFE.
(cherry picked from commit 96ead603b80339a4cf047ab2d2ab03d4b26271af)
Related: RHEL-16182
---
man/systemd-cryptenroll.xml | 46 +++++++++++++++++++++++++++++-------
src/basic/string-table.h | 1 +
src/shared/tpm2-util.c | 32 +++++++++++++++++++------
src/shared/tpm2-util.h | 27 +++++++++++++++++++++
src/test/test-tpm2.c | 47 +++++++++++++++++++++++++++++++++++++
test/units/testsuite-70.sh | 8 +++++++
6 files changed, 146 insertions(+), 15 deletions(-)
diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml
index f08d95c6fb..af5269aa7a 100644
--- a/man/systemd-cryptenroll.xml
+++ b/man/systemd-cryptenroll.xml
@@ -225,7 +225,12 @@
<listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the enrollment
requested via <option>--tpm2-device=</option> to. Takes a <literal>+</literal> separated list of
numeric PCR indexes in the range 0…23. If not used, defaults to PCR 7 only. If an empty string is
- specified, binds the enrollment to no PCRs at all. PCRs allow binding the enrollment to specific
+ specified, binds the enrollment to no PCRs at all.
+ Registers may also be specified using string aliases.</para>
+ <para>For instance <option>--tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config</option> to bind to the registers
+ 4, 1, and 5. Check the PCR definitions table below for a full list
+ of available string aliases.
+ PCRs allow binding the enrollment to specific
software versions and system state, so that the enrolled unlocking key is only accessible (may be
"unsealed") if specific trusted software and/or configuration is used.</para>
@@ -239,13 +244,15 @@
<!-- See: https://github.com/tianocore-docs/edk2-TrustedBootChain/blob/main/4_Other_Trusted_Boot_Chains.md -->
<!-- See: https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers -->
- <tgroup cols='2' align='left' colsep='1' rowsep='1'>
+ <tgroup cols='3' align='left' colsep='1' rowsep='1'>
<colspec colname="pcr" />
+ <colspec colname="string_alias" />
<colspec colname="definition" />
<thead>
<row>
<entry>PCR</entry>
+ <entry>alias</entry>
<entry>Explanation</entry>
</row>
</thead>
@@ -253,41 +260,43 @@
<tbody>
<row>
<entry>0</entry>
+ <entry>platform-code</entry>
<entry>Core system firmware executable code; changes on firmware updates</entry>
</row>
<row>
<entry>1</entry>
+ <entry>platform-config</entry>
<entry>Core system firmware data/host platform configuration; typically contains serial and model numbers, changes on basic hardware/CPU/RAM replacements</entry>
</row>
<row>
<entry>2</entry>
+ <entry>external-code</entry>
<entry>Extended or pluggable executable code; includes option ROMs on pluggable hardware</entry>
</row>
<row>
<entry>3</entry>
+ <entry>external-config</entry>
<entry>Extended or pluggable firmware data; includes information about pluggable hardware</entry>
</row>
<row>
<entry>4</entry>
+ <entry>boot-loader-code</entry>
<entry>Boot loader and additional drivers; changes on boot loader updates. The shim project will measure the PE binary it chain loads into this PCR. If the Linux kernel is invoked as UEFI PE binary, it is measured here, too. <citerefentry><refentrytitle>sd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures system extension images read from the ESP here too (see <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).</entry>
</row>
<row>
<entry>5</entry>
+ <entry>boot-loader-config</entry>
<entry>GPT/Partition table; changes when the partitions are added, modified or removed</entry>
</row>
- <row>
- <entry>6</entry>
- <entry>Power state events; changes on system suspend/sleep</entry>
- </row>
-
<row>
<entry>7</entry>
+ <entry>secure-boot-policy</entry>
<entry>Secure boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry>
</row>
@@ -296,39 +305,58 @@
<row>
<entry>9</entry>
+ <entry>kernel-initrd</entry>
<entry>The Linux kernel measures all initrds it receives into this PCR.</entry>
<!-- Strictly speaking only Linux >= 5.17 using the LOAD_FILE2 protocol, see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df -->
</row>
<row>
<entry>10</entry>
+ <entry>ima</entry>
<entry>The IMA project measures its runtime state into this PCR.</entry>
</row>
<row>
<entry>11</entry>
+ <entry>kernel-boot</entry>
<entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initrd. <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures boot phase strings into this PCR at various milestones of the boot process.</entry>
</row>
<row>
<entry>12</entry>
+ <entry>kernel-config</entry>
<entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
</row>
<row>
<entry>13</entry>
+ <entry>sysexts</entry>
<entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it loads and passed to the booted kernel into this PCR.</entry>
</row>
<row>
<entry>14</entry>
+ <entry>shim-policy</entry>
<entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
</row>
<row>
<entry>15</entry>
+ <entry>system-identity</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>
+
+ <row>
+ <entry>16</entry>
+ <entry>debug</entry>
+ <entry>Debug</entry>
+ </row>
+
+ <row>
+ <entry>23</entry>
+ <entry>application-support</entry>
+ <entry>Application Support</entry>
+ </row>
</tbody>
</tgroup>
</table>
@@ -382,7 +410,9 @@
<option>--tpm2-public-key-pcrs=</option>: the former binds decryption to the current, specific PCR
values; the latter binds decryption to any set of PCR values for which a signature by the specified
public key can be provided. The latter is hence more useful in scenarios where software updates shell
- be possible without losing access to all previously encrypted LUKS2 volumes.</para>
+ be possible without losing access to all previously encrypted LUKS2 volumes.
+ Like with <option>--tpm2-pcrs=</option>, string aliases as defined in the table above can also be used
+ to specify the registers, for instance <option>--tpm2-public-key-pcrs=boot-loader-code+system-identity</option>.</para>
<para>The <option>--tpm2-signature=</option> option takes a path to a TPM2 PCR signature file
as generated by the
diff --git a/src/basic/string-table.h b/src/basic/string-table.h
index e3a26a623c..3be70dfade 100644
--- a/src/basic/string-table.h
+++ b/src/basic/string-table.h
@@ -95,6 +95,7 @@ ssize_t string_table_lookup(const char * const *table, size_t len, const char *k
#define DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(name,type,max) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,) \
_DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,)
+#define DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_FALLBACK(name,type,max) _DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_FALLBACK(name,type,max,)
#define DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max) \
_DEFINE_STRING_TABLE_LOOKUP_TO_STRING_FALLBACK(name,type,max,static)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index e889d4c0fe..dd22f94dc0 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -20,6 +20,7 @@
#include "random-util.h"
#include "sha256.h"
#include "stat-util.h"
+#include "string-table.h"
#include "time-util.h"
#include "tpm2-util.h"
#include "virt.h"
@@ -3869,14 +3870,10 @@ int tpm2_pcr_mask_from_string(const char *arg, uint32_t *ret_mask) {
if (r < 0)
return log_error_errno(r, "Failed to parse PCR list: %s", arg);
- r = safe_atou(pcr, &n);
+ r = pcr_index_from_string(pcr);
if (r < 0)
- return log_error_errno(r, "Failed to parse PCR number: %s", pcr);
- if (n >= TPM2_PCRS_MAX)
- return log_error_errno(SYNTHETIC_ERRNO(ERANGE),
- "PCR number out of range (valid range 0…%u): %u",
- TPM2_PCRS_MAX - 1, n);
-
+ return log_error_errno(r, "Failed to parse specified PCR or specified PCR is out of range: %s", pcr);
+ n = r;
SET_BIT(mask, n);;
}
@@ -4373,3 +4370,24 @@ int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
return 0;
}
+
+static const char* const pcr_index_table[_PCR_INDEX_MAX_DEFINED] = {
+ [PCR_PLATFORM_CODE] = "platform-code",
+ [PCR_PLATFORM_CONFIG] = "platform-config",
+ [PCR_EXTERNAL_CODE] = "external-code",
+ [PCR_EXTERNAL_CONFIG] = "external-config",
+ [PCR_BOOT_LOADER_CODE] = "boot-loader-code",
+ [PCR_BOOT_LOADER_CONFIG] = "boot-loader-config",
+ [PCR_SECURE_BOOT_POLICY] = "secure-boot-policy",
+ [PCR_KERNEL_INITRD] = "kernel-initrd",
+ [PCR_IMA] = "ima",
+ [PCR_KERNEL_BOOT] = "kernel-boot",
+ [PCR_KERNEL_CONFIG] = "kernel-config",
+ [PCR_SYSEXTS] = "sysexts",
+ [PCR_SHIM_POLICY] = "shim-policy",
+ [PCR_SYSTEM_IDENTITY] = "system-identity",
+ [PCR_DEBUG] = "debug",
+ [PCR_APPLICATION_SUPPORT] = "application-support",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_FROM_STRING_WITH_FALLBACK(pcr_index, int, TPM2_PCRS_MAX);
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index e059f95790..97dae85fcb 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -190,6 +190,31 @@ typedef enum Tpm2Support {
TPM2_SUPPORT_FULL = TPM2_SUPPORT_FIRMWARE|TPM2_SUPPORT_DRIVER|TPM2_SUPPORT_SYSTEM|TPM2_SUPPORT_SUBSYSTEM,
} Tpm2Support;
+typedef enum PcrIndex {
+/* The following names for PCRs 0…7 are based on the names in the "TCG PC Client Specific Platform Firmware Profile Specification" (https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/) */
+ PCR_PLATFORM_CODE = 0,
+ PCR_PLATFORM_CONFIG = 1,
+ PCR_EXTERNAL_CODE = 2,
+ PCR_EXTERNAL_CONFIG = 3,
+ PCR_BOOT_LOADER_CODE = 4,
+ PCR_BOOT_LOADER_CONFIG = 5,
+ PCR_SECURE_BOOT_POLICY = 7,
+/* The following names for PCRs 9…15 are based on the "Linux TPM PCR Registry"
+(https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/) */
+ PCR_KERNEL_INITRD = 9,
+ PCR_IMA = 10,
+ PCR_KERNEL_BOOT = 11,
+ PCR_KERNEL_CONFIG = 12,
+ PCR_SYSEXTS = 13,
+ PCR_SHIM_POLICY = 14,
+ PCR_SYSTEM_IDENTITY = 15,
+/* As per "TCG PC Client Specific Platform Firmware Profile Specification" again, see above */
+ PCR_DEBUG = 16,
+ PCR_APPLICATION_SUPPORT = 23,
+ _PCR_INDEX_MAX_DEFINED = TPM2_PCRS_MAX,
+ _PCR_INDEX_INVALID = -EINVAL,
+} PcrIndex;
+
Tpm2Support tpm2_support(void);
int tpm2_parse_pcr_argument(const char *arg, uint32_t *mask);
@@ -202,3 +227,5 @@ int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
const void *salt,
size_t saltlen,
uint8_t res[static SHA256_DIGEST_SIZE]);
+
+int pcr_index_from_string(const char *s);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 8fd859b83d..87c8f6f421 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -27,6 +27,53 @@ TEST(tpm2_mask_from_string) {
test_tpm2_pcr_mask_from_string_one("0,2", 5, 0);
test_tpm2_pcr_mask_from_string_one("0+2", 5, 0);
test_tpm2_pcr_mask_from_string_one("foo", 0, -EINVAL);
+ test_tpm2_pcr_mask_from_string_one("7+application-support", 8388736, 0);
+ test_tpm2_pcr_mask_from_string_one("8+boot-loader-code", 272, 0);
+ test_tpm2_pcr_mask_from_string_one("6+boot-loader-code,44", 0, -EINVAL);
+ test_tpm2_pcr_mask_from_string_one("7,shim-policy,4", 16528, 0);
+ test_tpm2_pcr_mask_from_string_one("sysexts,shim-policy+kernel-boot", 26624, 0);
+ test_tpm2_pcr_mask_from_string_one("sysexts,shim+kernel-boot", 0, -EINVAL);
+ test_tpm2_pcr_mask_from_string_one("sysexts+17+23", 8527872, 0);
+ test_tpm2_pcr_mask_from_string_one("debug+24", 16842752, 0);
+}
+
+TEST(pcr_index_from_string) {
+ assert_se(pcr_index_from_string("platform-code") == 0);
+ assert_se(pcr_index_from_string("0") == 0);
+ assert_se(pcr_index_from_string("platform-config") == 1);
+ assert_se(pcr_index_from_string("1") == 1);
+ assert_se(pcr_index_from_string("external-code") == 2);
+ assert_se(pcr_index_from_string("2") == 2);
+ assert_se(pcr_index_from_string("external-config") == 3);
+ assert_se(pcr_index_from_string("3") == 3);
+ assert_se(pcr_index_from_string("boot-loader-code") == 4);
+ assert_se(pcr_index_from_string("4") == 4);
+ assert_se(pcr_index_from_string("boot-loader-config") == 5);
+ assert_se(pcr_index_from_string("5") == 5);
+ assert_se(pcr_index_from_string("secure-boot-policy") == 7);
+ assert_se(pcr_index_from_string("7") == 7);
+ assert_se(pcr_index_from_string("kernel-initrd") == 9);
+ assert_se(pcr_index_from_string("9") == 9);
+ assert_se(pcr_index_from_string("ima") == 10);
+ assert_se(pcr_index_from_string("10") == 10);
+ assert_se(pcr_index_from_string("kernel-boot") == 11);
+ assert_se(pcr_index_from_string("11") == 11);
+ assert_se(pcr_index_from_string("kernel-config") == 12);
+ assert_se(pcr_index_from_string("12") == 12);
+ assert_se(pcr_index_from_string("sysexts") == 13);
+ assert_se(pcr_index_from_string("13") == 13);
+ assert_se(pcr_index_from_string("shim-policy") == 14);
+ assert_se(pcr_index_from_string("14") == 14);
+ assert_se(pcr_index_from_string("system-identity") == 15);
+ assert_se(pcr_index_from_string("15") == 15);
+ assert_se(pcr_index_from_string("debug") == 16);
+ assert_se(pcr_index_from_string("16") == 16);
+ assert_se(pcr_index_from_string("application-support") == 23);
+ assert_se(pcr_index_from_string("23") == 23);
+ assert_se(pcr_index_from_string("hello") == -EINVAL);
+ assert_se(pcr_index_from_string("8") == 8);
+ assert_se(pcr_index_from_string("44") == -EINVAL);
+ assert_se(pcr_index_from_string("-5") == -EINVAL);
}
TEST(tpm2_util_pbkdf2_hmac_sha256) {
diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh
index 1bfa14e01a..5d4b155286 100755
--- a/test/units/testsuite-70.sh
+++ b/test/units/testsuite-70.sh
@@ -242,6 +242,14 @@ systemd-cryptenroll --tpm2-public-key-pcrs=key $img_2 && { echo 'unexpected succ
systemd-cryptenroll --tpm2-pcrs=key $img_2 && { echo 'unexpected success'; exit 1; }
+systemd-cryptenroll --tpm2-pcrs=44+8 $img_2 && { echo 'unexpected success'; exit 1; }
+
+systemd-cryptenroll --tpm2-pcrs=8 $img_2
+
+systemd-cryptenroll --tpm2-pcrs=hello $img_2 && { echo 'unexpected success'; exit 1; }
+
+systemd-cryptenroll --tpm2-pcrs=boot-loader-code+boot-loader-config $img_2
+
#wipe_slots
systemd-cryptenroll --wipe-slot $img_2 && { echo 'unexpected success'; exit 1; }

View File

@ -0,0 +1,32 @@
From 9b0ead2c1db66555e6ca97934085fd3dac397595 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Tue, 9 May 2023 22:17:15 +0200
Subject: [PATCH] cryptenroll: fix an assertion with weak passwords
Passing 0 to log_xxx_errno() leads to an assertion, so let's not do that:
$ NEWPASSWORD="" build-san/systemd-cryptenroll --unlock-key-file=/tmp/password --password "$img"
/tmp/password has 0644 mode that is too permissive, please adjust the ownership and access mode.
Assertion '(_error) != 0' failed at src/cryptenroll/cryptenroll-password.c:164, function enroll_password(). Aborting.
Aborted (core dumped)
(cherry picked from commit 0e43ab6d245a77aab35c7963ec636f37e6103984)
Related: RHEL-16182
---
src/cryptenroll/cryptenroll-password.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c
index 9b7c8b5400..2e2efcf6f6 100644
--- a/src/cryptenroll/cryptenroll-password.c
+++ b/src/cryptenroll/cryptenroll-password.c
@@ -81,7 +81,7 @@ int enroll_password(
if (r < 0)
return log_error_errno(r, "Failed to check password for quality: %m");
if (r == 0)
- log_warning_errno(r, "Specified password does not pass quality checks (%s), proceeding anyway.", error);
+ log_warning("Specified password does not pass quality checks (%s), proceeding anyway.", error);
keyslot = crypt_keyslot_add_by_volume_key(
cd,

View File

@ -0,0 +1,397 @@
From 5d389e870101a3906f45f2f1b276a319d23c7440 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Wed, 5 Apr 2023 09:30:52 +0200
Subject: [PATCH] man/systemd-cryptenroll: update list of PCRs, link to uapi
docs
Entia non sunt multiplicanda praeter necessitatem. We had a list of PCRs in the
man page which was already half out-of-date. Instead, link to web page with the
"authoritative" list. Here, drop the descriptions of what shim and grub do. Instead,
just give some short descriptions and mention what systemd components do.
systemd-pcrmachine.service and systemd-pcrfs@.service are now mentioned too.
https://github.com/uapi-group/specifications/commit/d0e590b1e2648e76ece66157ceade3f45b165b14
extended the table in the specs repo.
https://github.com/uapi-group/specifications/pull/59 adds some more text there
too.
Also, rework the recommendation: hint that PCR 11 is useful, and recommend
binding to policy signatures instead of direct PCR values. This new text is
intentionally vague: doing this correctly is hard, but let's at least not imply
that just binding to PCR 7 is useful in any way.
Also, change "string alias" to "name" in discussion of PCR names.
Inspired by https://discussion.fedoraproject.org/t/future-of-encryption-in-fedora/80397/17
(cherry picked from commit 10fa7251c0d8a465c932f2c4cf4496efb1637458)
Related: RHEL-16182
---
man/systemd-cryptenroll.xml | 336 +++++++++++++++++++-----------------
1 file changed, 177 insertions(+), 159 deletions(-)
diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml
index af5269aa7a..5ddaab40bc 100644
--- a/man/systemd-cryptenroll.xml
+++ b/man/systemd-cryptenroll.xml
@@ -58,6 +58,162 @@
<para>The tool supports only LUKS2 volumes, as it stores token meta-information in the LUKS2 JSON token
area, which is not available in other encryption formats.</para>
+
+ <refsect2>
+ <title>TPM2 PCRs and policies</title>
+
+ <para>PCRs allow binding of the encryption of secrets to specific software versions and system state,
+ so that the enrolled key is only accessible (may be "unsealed") if specific trusted software and/or
+ configuration is used. Such bindings may be created with the option <option>--tpm2-pcrs=</option>
+ described below.</para>
+
+ <para>Secrets may also be bound indirectly: a signed policy for a state of some combination of PCR
+ values is provided, and the secret is bound to the public part of the key used to sign this policy.
+ This means that the owner of a key can generate a sequence of signed policies, for specific software
+ versions and system states, and the secret can be decrypted as long as the machine state matches one of
+ those policies. For example, a vendor may provide such a policy for each kernel+initrd update, allowing
+ users to encrypt secrets so that they can be decrypted when running any kernel+initrd signed by the
+ vendor. Such bindings may be created with the options <option>--tpm2-public-key=</option>,
+ <option>--tpm2-public-key-pcrs=</option>, <option>--tpm2-signature=</option> described below.
+ </para>
+
+ <para>See <ulink url="https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/">Linux TPM
+ PCR Registry</ulink> for an authoritative list of PCRs and how they are updated. The table below
+ contains a quick reference, describing in particular the PCRs modified by systemd.</para>
+
+ <table>
+ <title>Well-known PCR Definitions</title>
+
+ <!-- See: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ -->
+ <!-- See: https://github.com/rhboot/shim/blob/main/README.tpm -->
+ <!-- See: https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html -->
+ <!-- See: https://sourceforge.net/p/linux-ima/wiki/Home/ -->
+ <!-- See: https://github.com/tianocore-docs/edk2-TrustedBootChain/blob/main/4_Other_Trusted_Boot_Chains.md -->
+ <!-- See: https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers -->
+
+ <tgroup cols='3' align='left' colsep='1' rowsep='1'>
+ <colspec colname="pcr" />
+ <colspec colname="name" />
+ <colspec colname="definition" />
+
+ <thead>
+ <row>
+ <entry>PCR</entry>
+ <entry>name</entry>
+ <entry>Explanation</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry>0</entry>
+ <entry>platform-code</entry>
+ <entry>Core system firmware executable code; changes on firmware updates</entry>
+ </row>
+
+ <row>
+ <entry>1</entry>
+ <entry>platform-config</entry>
+ <entry>Core system firmware data/host platform configuration; typically contains serial and model numbers, changes on basic hardware/CPU/RAM replacements</entry>
+ </row>
+
+ <row>
+ <entry>2</entry>
+ <entry>external-code</entry>
+ <entry>Extended or pluggable executable code; includes option ROMs on pluggable hardware</entry>
+ </row>
+
+ <row>
+ <entry>3</entry>
+ <entry>external-config</entry>
+ <entry>Extended or pluggable firmware data; includes information about pluggable hardware</entry>
+ </row>
+
+ <row>
+ <entry>4</entry>
+ <entry>boot-loader-code</entry>
+ <entry>Boot loader and additional drivers, PE binaries invoked by the boot loader; changes on boot loader updates. <citerefentry><refentrytitle>sd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures system extension images read from the ESP here too (see <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).</entry>
+ </row>
+
+ <row>
+ <entry>5</entry>
+ <entry>boot-loader-config</entry>
+ <entry>GPT/Partition table; changes when the partitions are added, modified, or removed</entry>
+ </row>
+
+ <row>
+ <entry>7</entry>
+ <entry>secure-boot-policy</entry>
+ <entry>Secure Boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes.</entry>
+ </row>
+
+ <row>
+ <entry>9</entry>
+ <entry>kernel-initrd</entry>
+ <entry>The Linux kernel measures all initrds it receives into this PCR.</entry>
+ <!-- Strictly speaking only Linux >= 5.17 using the LOAD_FILE2 protocol, see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df -->
+ </row>
+
+ <row>
+ <entry>10</entry>
+ <entry>ima</entry>
+ <entry>The IMA project measures its runtime state into this PCR.</entry>
+ </row>
+
+ <row>
+ <entry>11</entry>
+ <entry>kernel-boot</entry>
+ <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures boot phase strings into this PCR at various milestones of the boot process.</entry>
+ </row>
+
+ <row>
+ <entry>12</entry>
+ <entry>kernel-config</entry>
+ <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR.</entry>
+ </row>
+
+ <row>
+ <entry>13</entry>
+ <entry>sysexts</entry>
+ <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it passes to the booted kernel into this PCR.</entry>
+ </row>
+
+ <row>
+ <entry>14</entry>
+ <entry>shim-policy</entry>
+ <entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
+ </row>
+
+ <row>
+ <entry>15</entry>
+ <entry>system-identity</entry>
+ <entry><citerefentry><refentrytitle>systemd-cryptsetup</refentrytitle><manvolnum>8</manvolnum></citerefentry> optionally measures the volume key of activated LUKS volumes into this PCR. <citerefentry><refentrytitle>systemd-pcrmachine.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures the <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> into this PCR. <citerefentry><refentrytitle>systemd-pcrfs@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures mount points, file system UUIDs, labels, partion UUIDs of the root and <filename>/var/</filename> filesystems into this PCR.</entry>
+ </row>
+
+ <row>
+ <entry>16</entry>
+ <entry>debug</entry>
+ <entry>Debug</entry>
+ </row>
+
+ <row>
+ <entry>23</entry>
+ <entry>application-support</entry>
+ <entry>Application Support</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>In general, encrypted volumes would be bound to some combination of PCRs 7, 11, and 14 (if
+ shim/MOK is used). In order to allow firmware and OS version updates, it is typically not advisable to
+ use PCRs such as 0 and 2, since the program code they cover should already be covered indirectly
+ through the certificates measured into PCR 7. Validation through certificates hashes is typically
+ preferable over validation through direct measurements as it is less brittle in context of OS/firmware
+ updates: the measurements will change on every update, but signatures should remain unchanged. See the
+ <ulink url="https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/">Linux TPM PCR
+ Registry</ulink> for more discussion.</para>
+ </refsect2>
</refsect1>
<refsect1>
@@ -222,154 +378,15 @@
<varlistentry>
<term><option>--tpm2-pcrs=</option><arg rep="repeat">PCR</arg></term>
- <listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the enrollment
- requested via <option>--tpm2-device=</option> to. Takes a <literal>+</literal> separated list of
- numeric PCR indexes in the range 0…23. If not used, defaults to PCR 7 only. If an empty string is
- specified, binds the enrollment to no PCRs at all.
- Registers may also be specified using string aliases.</para>
- <para>For instance <option>--tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config</option> to bind to the registers
- 4, 1, and 5. Check the PCR definitions table below for a full list
- of available string aliases.
- PCRs allow binding the enrollment to specific
- software versions and system state, so that the enrolled unlocking key is only accessible (may be
- "unsealed") if specific trusted software and/or configuration is used.</para>
-
- <table>
- <title>Well-known PCR Definitions</title>
-
- <!-- See: https://trustedcomputinggroup.org/resource/pc-client-specific-platform-firmware-profile-specification/ -->
- <!-- See: https://github.com/rhboot/shim/blob/main/README.tpm -->
- <!-- See: https://www.gnu.org/software/grub/manual/grub/html_node/Measured-Boot.html -->
- <!-- See: https://sourceforge.net/p/linux-ima/wiki/Home/ -->
- <!-- See: https://github.com/tianocore-docs/edk2-TrustedBootChain/blob/main/4_Other_Trusted_Boot_Chains.md -->
- <!-- See: https://wiki.archlinux.org/title/Trusted_Platform_Module#Accessing_PCR_registers -->
-
- <tgroup cols='3' align='left' colsep='1' rowsep='1'>
- <colspec colname="pcr" />
- <colspec colname="string_alias" />
- <colspec colname="definition" />
-
- <thead>
- <row>
- <entry>PCR</entry>
- <entry>alias</entry>
- <entry>Explanation</entry>
- </row>
- </thead>
-
- <tbody>
- <row>
- <entry>0</entry>
- <entry>platform-code</entry>
- <entry>Core system firmware executable code; changes on firmware updates</entry>
- </row>
-
- <row>
- <entry>1</entry>
- <entry>platform-config</entry>
- <entry>Core system firmware data/host platform configuration; typically contains serial and model numbers, changes on basic hardware/CPU/RAM replacements</entry>
- </row>
-
- <row>
- <entry>2</entry>
- <entry>external-code</entry>
- <entry>Extended or pluggable executable code; includes option ROMs on pluggable hardware</entry>
- </row>
-
- <row>
- <entry>3</entry>
- <entry>external-config</entry>
- <entry>Extended or pluggable firmware data; includes information about pluggable hardware</entry>
- </row>
-
- <row>
- <entry>4</entry>
- <entry>boot-loader-code</entry>
- <entry>Boot loader and additional drivers; changes on boot loader updates. The shim project will measure the PE binary it chain loads into this PCR. If the Linux kernel is invoked as UEFI PE binary, it is measured here, too. <citerefentry><refentrytitle>sd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures system extension images read from the ESP here too (see <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry>).</entry>
- </row>
-
- <row>
- <entry>5</entry>
- <entry>boot-loader-config</entry>
- <entry>GPT/Partition table; changes when the partitions are added, modified or removed</entry>
- </row>
-
- <row>
- <entry>7</entry>
- <entry>secure-boot-policy</entry>
- <entry>Secure boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR.</entry>
- </row>
-
- <!-- Grub measures all its commands and the kernel command line into PCR 8… -->
- <!-- Grub measures all files it reads (including kernel image, initrd, …) into PCR 9… -->
-
- <row>
- <entry>9</entry>
- <entry>kernel-initrd</entry>
- <entry>The Linux kernel measures all initrds it receives into this PCR.</entry>
- <!-- Strictly speaking only Linux >= 5.17 using the LOAD_FILE2 protocol, see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f046fff8bc4c4d8f8a478022e76e40b818f692df -->
- </row>
-
- <row>
- <entry>10</entry>
- <entry>ima</entry>
- <entry>The IMA project measures its runtime state into this PCR.</entry>
- </row>
-
- <row>
- <entry>11</entry>
- <entry>kernel-boot</entry>
- <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures the ELF kernel image, embedded initrd and other payload of the PE image it is placed in into this PCR. Unlike PCR 4 (where the same data should be measured into), this PCR value should be easy to pre-calculate, as this only contains static parts of the PE binary. Use this PCR to bind TPM policies to a specific kernel image, possibly with an embedded initrd. <citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry> measures boot phase strings into this PCR at various milestones of the boot process.</entry>
- </row>
-
- <row>
- <entry>12</entry>
- <entry>kernel-config</entry>
- <entry><citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any specified kernel command line into this PCR. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any manually specified kernel command line (i.e. a kernel command line that overrides the one embedded in the unified PE image) and loaded credentials into this PCR. (Note that if <command>systemd-boot</command> and <command>systemd-stub</command> are used in combination the command line might be measured twice!)</entry>
- </row>
-
- <row>
- <entry>13</entry>
- <entry>sysexts</entry>
- <entry><citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> measures any <citerefentry><refentrytitle>systemd-sysext</refentrytitle><manvolnum>8</manvolnum></citerefentry> images it loads and passed to the booted kernel into this PCR.</entry>
- </row>
-
- <row>
- <entry>14</entry>
- <entry>shim-policy</entry>
- <entry>The shim project measures its "MOK" certificates and hashes into this PCR.</entry>
- </row>
-
- <row>
- <entry>15</entry>
- <entry>system-identity</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>
-
- <row>
- <entry>16</entry>
- <entry>debug</entry>
- <entry>Debug</entry>
- </row>
-
- <row>
- <entry>23</entry>
- <entry>application-support</entry>
- <entry>Application Support</entry>
- </row>
- </tbody>
- </tgroup>
- </table>
-
- <para>For most applications it should be sufficient to bind against PCR 7 (and possibly PCR 14, if
- shim/MOK is desired), as this includes measurements of the trusted certificates (and possibly hashes)
- that are used to validate all components of the boot process up to and including the OS kernel. In
- order to simplify firmware and OS version updates it's typically not advisable to include PCRs such
- as 0 and 2 in the binding of the enrollment, since the program code they cover should already be
- protected indirectly through the certificates measured into PCR 7. Validation through these
- certificates is typically preferable over validation through direct measurements as it is less
- brittle in context of OS/firmware updates: the measurements will change on every update, but code
- signatures likely will validate against pre-existing certificates.</para></listitem>
+ <listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind to when
+ enrollment is requested via <option>--tpm2-device=</option>. Takes a list of PCR names or numeric
+ indices in the range 0…23. Multiple PCR indexes are separated by <literal>+</literal>. If not
+ specified, the default is to use PCR 7 only. If an empty string is specified, binds the enrollment to
+ no PCRs at all. See the table above for a list of available PCRs.</para>
+
+ <para>Example: <option>--tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config</option>
+ specifies that PCR registers 4, 1, and 5 should be used.</para>
+ </listitem>
</varlistentry>
<varlistentry>
@@ -410,20 +427,21 @@
<option>--tpm2-public-key-pcrs=</option>: the former binds decryption to the current, specific PCR
values; the latter binds decryption to any set of PCR values for which a signature by the specified
public key can be provided. The latter is hence more useful in scenarios where software updates shell
- be possible without losing access to all previously encrypted LUKS2 volumes.
- Like with <option>--tpm2-pcrs=</option>, string aliases as defined in the table above can also be used
- to specify the registers, for instance <option>--tpm2-public-key-pcrs=boot-loader-code+system-identity</option>.</para>
+ be possible without losing access to all previously encrypted LUKS2 volumes. Like with
+ <option>--tpm2-pcrs=</option>, names defined in the table above can also be used to specify the
+ registers, for instance
+ <option>--tpm2-public-key-pcrs=boot-loader-code+system-identity</option>.</para>
- <para>The <option>--tpm2-signature=</option> option takes a path to a TPM2 PCR signature file
- as generated by the
+ <para>The <option>--tpm2-signature=</option> option takes a path to a TPM2 PCR signature file as
+ generated by the
<citerefentry><refentrytitle>systemd-measure</refentrytitle><manvolnum>1</manvolnum></citerefentry>
- tool. If this this is not specified explicitly a suitable signature file
+ tool. If this is not specified explicitly, a suitable signature file
<filename>tpm2-pcr-signature.json</filename> is searched for in <filename>/etc/systemd/</filename>,
- <filename>/run/systemd/</filename>, <filename>/usr/lib/systemd/</filename> (in this order) and
- used. If a signature file is specified or found it is used to verify if the volume can be unlocked
- with it given the current PCR state, before the new slot is written to disk. This is intended as
- safety net to ensure that access to a volume is not lost if a public key is enrolled for which no
- valid signature for the current PCR state is available. If the supplied signature does not unlock the
+ <filename>/run/systemd/</filename>, <filename>/usr/lib/systemd/</filename> (in this order) and used.
+ If a signature file is specified or found it is used to verify if the volume can be unlocked with it
+ given the current PCR state, before the new slot is written to disk. This is intended as safety net
+ to ensure that access to a volume is not lost if a public key is enrolled for which no valid
+ signature for the current PCR state is available. If the supplied signature does not unlock the
current PCR state and public key combination, no slot is enrolled and the operation will fail. If no
signature file is specified or found no such safety verification is done.</para></listitem>
</varlistentry>

View File

@ -0,0 +1,53 @@
From 73f1ef0adb005695b7feca7e5568a41baf29e6fb Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 1 Aug 2023 14:09:04 -0400
Subject: [PATCH] tpm2: add debug logging to functions converting hash or asym
algs to/from strings or ids
Add debug log message if the algorithm name or id is not known.
(cherry picked from commit 240774f5ce70f0bcbf64999a3db5c25be3f44a9c)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index dd22f94dc0..7387dcc48a 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -4183,6 +4183,7 @@ const char *tpm2_hash_alg_to_string(uint16_t alg) {
return "sha384";
if (alg == TPM2_ALG_SHA512)
return "sha512";
+ log_debug("Unknown hash algorithm id 0x%" PRIx16, alg);
return NULL;
}
@@ -4195,7 +4196,7 @@ int tpm2_hash_alg_from_string(const char *alg) {
return TPM2_ALG_SHA384;
if (strcaseeq_ptr(alg, "sha512"))
return TPM2_ALG_SHA512;
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown hash algorithm name '%s'", alg);
}
const char *tpm2_asym_alg_to_string(uint16_t alg) {
@@ -4203,6 +4204,7 @@ const char *tpm2_asym_alg_to_string(uint16_t alg) {
return "ecc";
if (alg == TPM2_ALG_RSA)
return "rsa";
+ log_debug("Unknown asymmetric algorithm id 0x%" PRIx16, alg);
return NULL;
}
@@ -4211,7 +4213,7 @@ int tpm2_asym_alg_from_string(const char *alg) {
return TPM2_ALG_ECC;
if (strcaseeq_ptr(alg, "rsa"))
return TPM2_ALG_RSA;
- return -EINVAL;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown asymmetric algorithm name '%s'", alg);
}
Tpm2Support tpm2_support(void) {

View File

@ -0,0 +1,51 @@
From c81e096b3856a3b9906d1b1140db39848db02472 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 11 Jul 2023 11:11:59 -0400
Subject: [PATCH] tpm2: add tpm2_hash_alg_to_size()
Add function to get the hash size for a hash algorithm
(cherry picked from commit c9df1fb119b3e57b0468457cc681920f453ff6e7)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 12 ++++++++++++
src/shared/tpm2-util.h | 2 ++
2 files changed, 14 insertions(+)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 7387dcc48a..509dab60f8 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -4174,6 +4174,18 @@ int tpm2_parse_luks2_json(
return 0;
}
+int tpm2_hash_alg_to_size(uint16_t alg) {
+ if (alg == TPM2_ALG_SHA1)
+ return 20;
+ if (alg == TPM2_ALG_SHA256)
+ return 32;
+ if (alg == TPM2_ALG_SHA384)
+ return 48;
+ if (alg == TPM2_ALG_SHA512)
+ return 64;
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown hash algorithm id 0x%" PRIx16, alg);
+}
+
const char *tpm2_hash_alg_to_string(uint16_t alg) {
if (alg == TPM2_ALG_SHA1)
return "sha1";
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 97dae85fcb..affcbea3a1 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -164,6 +164,8 @@ int tpm2_parse_luks2_json(JsonVariant *v, int *ret_keyslot, uint32_t *ret_hash_p
#define TPM2_ALG_RSA 0x1
#endif
+int tpm2_hash_alg_to_size(uint16_t alg);
+
const char *tpm2_hash_alg_to_string(uint16_t alg);
int tpm2_hash_alg_from_string(const char *alg);

View File

@ -0,0 +1,199 @@
From be19ca580bf23d3b6e31c7a030cd3e19c2498f16 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 1 Aug 2023 12:55:17 -0400
Subject: [PATCH] tpm2: change tpm2_tpm*_pcr_selection_to_mask() to return mask
This simplifies use of the functions, as well as avoiding the use of -ENOENT
from tpm2_tpml_pcr_selection_to_mask().
(cherry picked from commit dbaae766c7eaacdfb19ee23600f0f382a16ae33b)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 52 ++++++++++++++++--------------------------
src/shared/tpm2-util.h | 4 ++--
src/test/test-tpm2.c | 8 ++-----
3 files changed, 24 insertions(+), 40 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 509dab60f8..b0a2f715ef 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1200,15 +1200,14 @@ static int tpm2_get_or_create_srk(
/* Utility functions for TPMS_PCR_SELECTION. */
/* Convert a TPMS_PCR_SELECTION object to a mask. */
-void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret) {
+uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s) {
assert(s);
assert(s->sizeofSelect <= sizeof(s->pcrSelect));
- assert(ret);
uint32_t mask = 0;
for (unsigned i = 0; i < s->sizeofSelect; i++)
SET_FLAG(mask, (uint32_t)s->pcrSelect[i] << (i * 8), true);
- *ret = mask;
+ return mask;
}
/* Convert a mask and hash alg to a TPMS_PCR_SELECTION object. */
@@ -1231,25 +1230,27 @@ void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TP
/* Add all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
+ uint32_t maska, maskb;
+
assert(a);
assert(b);
assert(a->hash == b->hash);
- uint32_t maska, maskb;
- tpm2_tpms_pcr_selection_to_mask(a, &maska);
- tpm2_tpms_pcr_selection_to_mask(b, &maskb);
+ maska = tpm2_tpms_pcr_selection_to_mask(a);
+ maskb = tpm2_tpms_pcr_selection_to_mask(b);
tpm2_tpms_pcr_selection_from_mask(maska | maskb, a->hash, a);
}
/* Remove all PCR selections in 'b' from 'a'. Both must have the same hash alg. */
void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
+ uint32_t maska, maskb;
+
assert(a);
assert(b);
assert(a->hash == b->hash);
- uint32_t maska, maskb;
- tpm2_tpms_pcr_selection_to_mask(a, &maska);
- tpm2_tpms_pcr_selection_to_mask(b, &maskb);
+ maska = tpm2_tpms_pcr_selection_to_mask(a);
+ maskb = tpm2_tpms_pcr_selection_to_mask(b);
tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a);
}
@@ -1265,11 +1266,7 @@ void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b)
#define FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms) \
_FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, UNIQ)
#define _FOREACH_PCR_IN_TPMS_PCR_SELECTION(pcr, tpms, uniq) \
- FOREACH_PCR_IN_MASK(pcr, \
- ({ uint32_t UNIQ_T(_mask, uniq); \
- tpm2_tpms_pcr_selection_to_mask(tpms, &UNIQ_T(_mask, uniq)); \
- UNIQ_T(_mask, uniq); \
- }))
+ FOREACH_PCR_IN_MASK(pcr, tpm2_tpms_pcr_selection_to_mask(tpms))
#define FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml) \
UNIQ_FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(tpms, tpml, UNIQ)
@@ -1291,21 +1288,17 @@ char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s) {
const char *algstr = strna(tpm2_hash_alg_to_string(s->hash));
- uint32_t mask;
- tpm2_tpms_pcr_selection_to_mask(s, &mask);
- _cleanup_free_ char *maskstr = tpm2_pcr_mask_to_string(mask);
- if (!maskstr)
+ _cleanup_free_ char *mask = tpm2_pcr_mask_to_string(tpm2_tpms_pcr_selection_to_mask(s));
+ if (!mask)
return NULL;
- return strjoin(algstr, "(", maskstr, ")");
+ return strjoin(algstr, "(", mask, ")");
}
size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s) {
assert(s);
- uint32_t mask;
- tpm2_tpms_pcr_selection_to_mask(s, &mask);
- return (size_t)__builtin_popcount(mask);
+ return (size_t)__builtin_popcount(tpm2_tpms_pcr_selection_to_mask(s));
}
/* Utility functions for TPML_PCR_SELECTION. */
@@ -1356,10 +1349,9 @@ static TPMS_PCR_SELECTION *tpm2_tpml_pcr_selection_get_tpms_pcr_selection(
return selection;
}
-/* Convert a TPML_PCR_SELECTION object to a mask. Returns -ENOENT if 'hash_alg' is not in the object. */
-int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg, uint32_t *ret) {
+/* Convert a TPML_PCR_SELECTION object to a mask. Returns empty mask (i.e. 0) if 'hash_alg' is not in the object. */
+uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash_alg) {
assert(l);
- assert(ret);
/* Make a copy, as tpm2_tpml_pcr_selection_get_tpms_pcr_selection() will modify the object if there
* are multiple entries with the requested hash alg. */
@@ -1368,10 +1360,9 @@ int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH h
TPMS_PCR_SELECTION *s;
s = tpm2_tpml_pcr_selection_get_tpms_pcr_selection(&lcopy, hash_alg);
if (!s)
- return SYNTHETIC_ERRNO(ENOENT);
+ return 0;
- tpm2_tpms_pcr_selection_to_mask(s, ret);
- return 0;
+ return tpm2_tpms_pcr_selection_to_mask(s);
}
/* Convert a mask and hash alg to a TPML_PCR_SELECTION object. */
@@ -2574,10 +2565,7 @@ static int find_signature(
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Signature is not a JSON object.");
uint16_t pcr_bank = pcr_selection->pcrSelections[0].hash;
- uint32_t pcr_mask;
- r = tpm2_tpml_pcr_selection_to_mask(pcr_selection, pcr_bank, &pcr_mask);
- if (r < 0)
- return r;
+ uint32_t pcr_mask = tpm2_tpml_pcr_selection_to_mask(pcr_selection, pcr_bank);
k = tpm2_hash_alg_to_string(pcr_bank);
if (!k)
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index affcbea3a1..2f1eb8a012 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -102,7 +102,7 @@ int tpm2_get_good_pcr_banks_strv(Tpm2Context *c, uint32_t pcr_mask, char ***ret)
int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const void *data, size_t data_size, const void *secret, size_t secret_size);
-void tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s, uint32_t *ret);
+uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s);
void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
@@ -111,7 +111,7 @@ char *tpm2_tpms_pcr_selection_to_string(const TPMS_PCR_SELECTION *s);
size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s);
#define tpm2_tpms_pcr_selection_is_empty(s) (tpm2_tpms_pcr_selection_weight(s) == 0)
-int tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t *ret);
+uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash);
void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPML_PCR_SELECTION *ret);
void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index 87c8f6f421..c61bbf6d94 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -157,9 +157,7 @@ static void verify_tpms_pcr_selection(TPMS_PCR_SELECTION *s, uint32_t mask, TPMI
assert_se(s->pcrSelect[2] == ((mask >> 16) & 0xff));
assert_se(s->pcrSelect[3] == 0);
- uint32_t m = POISON_U32;
- tpm2_tpms_pcr_selection_to_mask(s, &m);
- assert_se(m == mask);
+ assert_se(tpm2_tpms_pcr_selection_to_mask(s) == mask);
}
static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION s[], size_t count) {
@@ -167,10 +165,8 @@ static void verify_tpml_pcr_selection(TPML_PCR_SELECTION *l, TPMS_PCR_SELECTION
for (size_t i = 0; i < count; i++) {
assert_tpms_pcr_selection_eq(&s[i], &l->pcrSelections[i]);
- uint32_t mask = POISON_U32;
TPMI_ALG_HASH hash = l->pcrSelections[i].hash;
- assert_se(tpm2_tpml_pcr_selection_to_mask(l, hash, &mask) == 0);
- verify_tpms_pcr_selection(&l->pcrSelections[i], mask, hash);
+ verify_tpms_pcr_selection(&l->pcrSelections[i], tpm2_tpml_pcr_selection_to_mask(l, hash), hash);
}
}

View File

@ -0,0 +1,134 @@
From 2748f56bf147d5212cada67cba838877f3ce3f5c Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 19 Jul 2023 08:50:06 -0400
Subject: [PATCH] tpm2: add more helper functions for managing
TPML_PCR_SELECTION and TPMS_PCR_SELECTION
Add more functions to help manage these objects.
(cherry picked from commit 13b551744b9df9ea08d7e06dee57a8cea7b48d1b)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 62 +++++++++++++++++++++++++++++++++++-------
src/shared/tpm2-util.h | 6 ++++
2 files changed, 58 insertions(+), 10 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index b0a2f715ef..9c0cad47c6 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1228,30 +1228,45 @@ void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash_alg, TP
};
}
+/* Test if all bits in the mask are set in the TPMS_PCR_SELECTION. */
+bool tpm2_tpms_pcr_selection_has_mask(const TPMS_PCR_SELECTION *s, uint32_t mask) {
+ assert(s);
+
+ return FLAGS_SET(tpm2_tpms_pcr_selection_to_mask(s), mask);
+}
+
+static void tpm2_tpms_pcr_selection_update_mask(TPMS_PCR_SELECTION *s, uint32_t mask, bool b) {
+ assert(s);
+
+ tpm2_tpms_pcr_selection_from_mask(UPDATE_FLAG(tpm2_tpms_pcr_selection_to_mask(s), mask, b), s->hash, s);
+}
+
+/* Add all PCR selections in the mask. */
+void tpm2_tpms_pcr_selection_add_mask(TPMS_PCR_SELECTION *s, uint32_t mask) {
+ tpm2_tpms_pcr_selection_update_mask(s, mask, 1);
+}
+
+/* Remove all PCR selections in the mask. */
+void tpm2_tpms_pcr_selection_sub_mask(TPMS_PCR_SELECTION *s, uint32_t mask) {
+ tpm2_tpms_pcr_selection_update_mask(s, mask, 0);
+}
+
/* Add all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
- uint32_t maska, maskb;
-
assert(a);
assert(b);
assert(a->hash == b->hash);
- maska = tpm2_tpms_pcr_selection_to_mask(a);
- maskb = tpm2_tpms_pcr_selection_to_mask(b);
- tpm2_tpms_pcr_selection_from_mask(maska | maskb, a->hash, a);
+ tpm2_tpms_pcr_selection_add_mask(a, tpm2_tpms_pcr_selection_to_mask(b));
}
/* Remove all PCR selections in 'b' from 'a'. Both must have the same hash alg. */
void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b) {
- uint32_t maska, maskb;
-
assert(a);
assert(b);
assert(a->hash == b->hash);
- maska = tpm2_tpms_pcr_selection_to_mask(a);
- maskb = tpm2_tpms_pcr_selection_to_mask(b);
- tpm2_tpms_pcr_selection_from_mask(maska & ~maskb, a->hash, a);
+ tpm2_tpms_pcr_selection_sub_mask(a, tpm2_tpms_pcr_selection_to_mask(b));
}
/* Move all PCR selections in 'b' to 'a'. Both must have the same hash alg. */
@@ -1426,6 +1441,33 @@ void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const
tpm2_tpms_pcr_selection_sub(selection, s);
}
+/* Test if all bits in the mask for the hash are set in the TPML_PCR_SELECTION. */
+bool tpm2_tpml_pcr_selection_has_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) {
+ assert(l);
+
+ return FLAGS_SET(tpm2_tpml_pcr_selection_to_mask(l, hash), mask);
+}
+
+/* Add the PCR selections in the mask, with the provided hash. */
+void tpm2_tpml_pcr_selection_add_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) {
+ TPMS_PCR_SELECTION tpms;
+
+ assert(l);
+
+ tpm2_tpms_pcr_selection_from_mask(mask, hash, &tpms);
+ tpm2_tpml_pcr_selection_add_tpms_pcr_selection(l, &tpms);
+}
+
+/* Remove the PCR selections in the mask, with the provided hash. */
+void tpm2_tpml_pcr_selection_sub_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask) {
+ TPMS_PCR_SELECTION tpms;
+
+ assert(l);
+
+ tpm2_tpms_pcr_selection_from_mask(mask, hash, &tpms);
+ tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(l, &tpms);
+}
+
/* Add all PCR selections in 'b' to 'a'. */
void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b) {
assert(a);
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 2f1eb8a012..c024245915 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -104,6 +104,9 @@ int tpm2_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const vo
uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s);
void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
+bool tpm2_tpms_pcr_selection_has_mask(const TPMS_PCR_SELECTION *s, uint32_t mask);
+void tpm2_tpms_pcr_selection_add_mask(TPMS_PCR_SELECTION *s, uint32_t mask);
+void tpm2_tpms_pcr_selection_sub_mask(TPMS_PCR_SELECTION *s, uint32_t mask);
void tpm2_tpms_pcr_selection_add(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
void tpm2_tpms_pcr_selection_sub(TPMS_PCR_SELECTION *a, const TPMS_PCR_SELECTION *b);
void tpm2_tpms_pcr_selection_move(TPMS_PCR_SELECTION *a, TPMS_PCR_SELECTION *b);
@@ -113,6 +116,9 @@ size_t tpm2_tpms_pcr_selection_weight(const TPMS_PCR_SELECTION *s);
uint32_t tpm2_tpml_pcr_selection_to_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash);
void tpm2_tpml_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPML_PCR_SELECTION *ret);
+bool tpm2_tpml_pcr_selection_has_mask(const TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask);
+void tpm2_tpml_pcr_selection_add_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask);
+void tpm2_tpml_pcr_selection_sub_mask(TPML_PCR_SELECTION *l, TPMI_ALG_HASH hash, uint32_t mask);
void tpm2_tpml_pcr_selection_add_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
void tpm2_tpml_pcr_selection_sub_tpms_pcr_selection(TPML_PCR_SELECTION *l, const TPMS_PCR_SELECTION *s);
void tpm2_tpml_pcr_selection_add(TPML_PCR_SELECTION *a, const TPML_PCR_SELECTION *b);

View File

@ -0,0 +1,611 @@
From 1f5a825144e492e54b68373d035bd2889bffc81b Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Tue, 11 Jul 2023 21:23:36 -0400
Subject: [PATCH] tpm2: add Tpm2PCRValue struct and associated functions
Add a new struct that can represent a PCR index, hash, and value all
together. This replaces code (e.g. the tpm2_pcr_read() parameters) that
required using both a TPML_PCR_SELECTION as well as array of TPM2B_DIGEST
entries, which was difficult to correlate the selection hash/index to each
digest.
(cherry picked from commit 323eb4803a29a9cc255aa16ef7cab3a00429b146)
Related: RHEL-16182
---
.../cryptsetup-token-systemd-tpm2.c | 2 +-
src/cryptsetup/cryptsetup.c | 2 +-
src/shared/tpm2-util.c | 419 ++++++++++++++++--
src/shared/tpm2-util.h | 25 +-
4 files changed, 399 insertions(+), 49 deletions(-)
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index aab3a4b4c0..94d568c17f 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -278,7 +278,7 @@ _public_ int cryptsetup_token_validate(
}
u = json_variant_unsigned(e);
- if (!TPM2_PCR_VALID(u)) {
+ if (!TPM2_PCR_INDEX_VALID(u)) {
crypt_log_debug(cd, "TPM2 PCR number out of range.");
return 1;
}
diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index 96341207b3..866141ac44 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -426,7 +426,7 @@ static int parse_one_option(const char *option) {
}
pcr = r ? TPM_PCR_INDEX_VOLUME_KEY : UINT_MAX;
- } else if (!TPM2_PCR_VALID(pcr)) {
+ } else if (!TPM2_PCR_INDEX_VALID(pcr)) {
log_warning("Selected TPM index for measurement %u outside of allowed range 0…%u, ignoring.", pcr, TPM2_PCRS_MAX-1);
return 0;
}
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 9c0cad47c6..cef251e69c 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -19,6 +19,7 @@
#include "parse-util.h"
#include "random-util.h"
#include "sha256.h"
+#include "sort-util.h"
#include "stat-util.h"
#include "string-table.h"
#include "time-util.h"
@@ -1516,6 +1517,318 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l) {
return weight;
}
+bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value) {
+ int r;
+
+ assert(pcr_value);
+
+ if (!TPM2_PCR_INDEX_VALID(pcr_value->index)) {
+ log_debug("PCR index %u invalid.", pcr_value->index);
+ return false;
+ }
+
+ /* If it contains a value, the value size must match the hash size. */
+ if (pcr_value->value.size > 0) {
+ r = tpm2_hash_alg_to_size(pcr_value->hash);
+ if (r < 0)
+ return false;
+
+ if ((int) pcr_value->value.size != r) {
+ log_debug("PCR hash 0x%" PRIx16 " expected size %d does not match actual size %" PRIu16 ".",
+ pcr_value->hash, r, pcr_value->value.size);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Verify all entries are valid, and consistent with each other. The requirements for consistency are:
+ *
+ * 1) all entries must be sorted in ascending order (e.g. using tpm2_sort_pcr_values())
+ * 2) all entries must be unique, i.e. there cannot be 2 entries with the same hash and index
+ */
+bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ assert(pcr_values || n_pcr_values == 0);
+
+ for (size_t i = 0; i < n_pcr_values; i++) {
+ const Tpm2PCRValue *v = &pcr_values[i];
+
+ if (!TPM2_PCR_VALUE_VALID(v))
+ return false;
+
+ if (i == 0)
+ continue;
+
+ const Tpm2PCRValue *l = &pcr_values[i - 1];
+
+ /* Hashes must be sorted in ascending order */
+ if (v->hash < l->hash) {
+ log_debug("PCR values not in ascending order, hash %" PRIu16 " is after %" PRIu16 ".",
+ v->hash, l->hash);
+ return false;
+ }
+
+ if (v->hash == l->hash) {
+ /* Indexes (for the same hash) must be sorted in ascending order */
+ if (v->index < l->index) {
+ log_debug("PCR values not in ascending order, hash %" PRIu16 " index %u is after %u.",
+ v->hash, v->index, l->index);
+ return false;
+ }
+
+ /* Indexes (for the same hash) must not be duplicates */
+ if (v->index == l->index) {
+ log_debug("PCR values contain duplicates for hash %" PRIu16 " index %u.",
+ v->hash, v->index);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static int cmp_pcr_values(const Tpm2PCRValue *a, const Tpm2PCRValue *b) {
+ assert(a);
+ assert(b);
+
+ return CMP(a->hash, b->hash) ?: CMP(a->index, b->index);
+}
+
+/* Sort the array of Tpm2PCRValue entries in-place. This sorts first in ascending order of hash algorithm
+ * (sorting simply by the TPM2 hash algorithm number), and then sorting by pcr index. */
+void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ typesafe_qsort(pcr_values, n_pcr_values, cmp_pcr_values);
+}
+
+int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values = 0;
+
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
+
+ FOREACH_PCR_IN_MASK(index, mask)
+ if (!GREEDY_REALLOC_APPEND(
+ pcr_values,
+ n_pcr_values,
+ &TPM2_PCR_VALUE_MAKE(index, hash, {}),
+ 1))
+ return log_oom_debug();
+
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
+
+ return 0;
+}
+
+int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask) {
+ uint32_t mask = 0;
+
+ assert(pcr_values || n_pcr_values == 0);
+ assert(ret_mask);
+
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid PCR values.");
+
+ for (size_t i = 0; i < n_pcr_values; i++)
+ if (pcr_values[i].hash == hash)
+ SET_BIT(mask, pcr_values[i].index);
+
+ *ret_mask = mask;
+
+ return 0;
+}
+
+int tpm2_tpml_pcr_selection_from_pcr_values(
+ const Tpm2PCRValue *pcr_values,
+ size_t n_pcr_values,
+ TPML_PCR_SELECTION *ret_selection,
+ TPM2B_DIGEST **ret_values,
+ size_t *ret_n_values) {
+
+ TPML_PCR_SELECTION selection = {};
+ _cleanup_free_ TPM2B_DIGEST *values = NULL;
+ size_t n_values = 0;
+
+ assert(pcr_values || n_pcr_values == 0);
+
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "PCR values are not valid.");
+
+ for (size_t i = 0; i < n_pcr_values; i++) {
+ unsigned index = pcr_values[i].index;
+ TPMI_ALG_HASH hash = pcr_values[i].hash;
+ const TPM2B_DIGEST *digest = &pcr_values[i].value;
+
+ tpm2_tpml_pcr_selection_add_mask(&selection, hash, INDEX_TO_MASK(uint32_t, index));
+
+ if (!GREEDY_REALLOC_APPEND(values, n_values, digest, 1))
+ return log_oom_debug();
+ }
+
+ if (ret_selection)
+ *ret_selection = selection;
+ if (ret_values)
+ *ret_values = TAKE_PTR(values);
+ if (ret_n_values)
+ *ret_n_values = n_values;
+
+ return 0;
+}
+
+/* Count the number of different hash algorithms for all the entries. */
+int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count) {
+ TPML_PCR_SELECTION selection;
+ int r;
+
+ assert(pcr_values);
+ assert(ret_count);
+
+ r = tpm2_tpml_pcr_selection_from_pcr_values(
+ pcr_values,
+ n_pcr_values,
+ &selection,
+ /* ret_values= */ NULL,
+ /* ret_n_values= */ NULL);
+ if (r < 0)
+ return r;
+
+ *ret_count = selection.count;
+
+ return 0;
+}
+
+/* Parse a string argument into a Tpm2PCRValue object.
+ *
+ * The format is <index>[:hash[=value]] where index is the index number (or name) of the PCR, e.g. 0 (or
+ * platform-code), hash is the name of the hash algorithm (e.g. sha256) and value is the hex hash digest
+ * value, optionally with a leading 0x. This does not check for validity of the fields. */
+int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value) {
+ Tpm2PCRValue pcr_value = {};
+ const char *p = arg;
+ int r;
+
+ assert(arg);
+ assert(ret_pcr_value);
+
+ _cleanup_free_ char *index = NULL;
+ r = extract_first_word(&p, &index, ":", /* flags= */ 0);
+ if (r < 1)
+ return log_error_errno(r, "Could not parse pcr value '%s': %m", p);
+
+ r = pcr_index_from_string(index);
+ if (r < 0)
+ return log_error_errno(r, "Invalid pcr index '%s': %m", index);
+ pcr_value.index = (unsigned) r;
+
+ if (!isempty(p)) {
+ _cleanup_free_ char *hash = NULL;
+ r = extract_first_word(&p, &hash, "=", /* flags= */ 0);
+ if (r < 1)
+ return log_error_errno(r, "Could not parse pcr hash algorithm '%s': %m", p);
+
+ r = tpm2_hash_alg_from_string(hash);
+ if (r < 0)
+ return log_error_errno(r, "Invalid pcr hash algorithm '%s': %m", hash);
+ pcr_value.hash = (TPMI_ALG_HASH) r;
+ }
+
+ if (!isempty(p)) {
+ /* Remove leading 0x if present */
+ p = startswith_no_case(p, "0x") ?: p;
+
+ _cleanup_free_ void *buf = NULL;
+ size_t buf_size = 0;
+ r = unhexmem(p, strlen(p), &buf, &buf_size);
+ if (r < 0)
+ return log_error_errno(r, "Invalid pcr hash value '%s': %m", p);
+
+ pcr_value.value.size = buf_size;
+ assert(sizeof(pcr_value.value.buffer) >= pcr_value.value.size);
+ memcpy(pcr_value.value.buffer, buf, pcr_value.value.size);
+ }
+
+ *ret_pcr_value = pcr_value;
+
+ return 0;
+}
+
+/* Return a string for the PCR value. The format is described in tpm2_pcr_value_from_string(). Note that if
+ * the hash algorithm is not recognized, neither hash name nor hash digest value is included in the
+ * string. This does not check for validity. */
+char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value) {
+ _cleanup_free_ char *index = NULL, *value = NULL;
+ int r;
+
+ r = asprintf(&index, "%u", pcr_value->index);
+ if (r < 0)
+ return NULL;
+
+ const char *hash = tpm2_hash_alg_to_string(pcr_value->hash);
+
+ if (hash && pcr_value->value.size > 0) {
+ value = hexmem(pcr_value->value.buffer, pcr_value->value.size);
+ if (!value)
+ return NULL;
+ }
+
+ return strjoin(index, hash ? ":" : "", hash ?: "", value ? "=" : "", value ?: "");
+}
+
+/* Parse a string argument into an array of Tpm2PCRValue objects.
+ *
+ * The format is zero or more entries separated by ',' or '+'. The format of each entry is described in
+ * tpm2_pcr_value_from_string(). This does not check for validity of the entries. */
+int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values) {
+ const char *p = arg;
+ int r;
+
+ assert(arg);
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
+
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values = 0;
+
+ for (;;) {
+ _cleanup_free_ char *pcr_arg = NULL;
+ r = extract_first_word(&p, &pcr_arg, ",+", /* flags= */ 0);
+ if (r < 0)
+ return log_error_errno(r, "Could not parse pcr values '%s': %m", p);
+ if (r == 0)
+ break;
+
+ Tpm2PCRValue pcr_value;
+ r = tpm2_pcr_value_from_string(pcr_arg, &pcr_value);
+ if (r < 0)
+ return r;
+
+ if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1))
+ return log_oom();
+ }
+
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
+
+ return 0;
+}
+
+/* Return a string representing the array of PCR values. The format is as described in
+ * tpm2_pcr_values_from_string(). This does not check for validity. */
+char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values) {
+ _cleanup_free_ char *s = NULL;
+
+ for (size_t i = 0; i < n_pcr_values; i++) {
+ _cleanup_free_ char *pcrstr = tpm2_pcr_value_to_string(&pcr_values[i]);
+ if (!pcrstr || !strextend_with_separator(&s, "+", pcrstr))
+ return NULL;
+ }
+
+ return s ? TAKE_PTR(s) : strdup("");
+}
+
static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) {
if (!DEBUG_LOGGING || !l)
return;
@@ -1524,6 +1837,14 @@ static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const
log_debug("%s: %s", msg ?: "PCR selection", strna(s));
}
+static void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) {
+ if (!DEBUG_LOGGING || !pcr_value)
+ return;
+
+ _cleanup_free_ char *s = tpm2_pcr_value_to_string(pcr_value);
+ log_debug("%s: %s", msg ?: "PCR value", strna(s));
+}
+
static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
if (!DEBUG_LOGGING || !buffer || size == 0)
return;
@@ -1919,22 +2240,27 @@ int tpm2_create_loaded(
return 0;
}
+/* Read hash values from the specified PCR selection. Provides a Tpm2PCRValue array that contains all
+ * requested PCR values, in the order provided by the TPM. Normally, the provided pcr values will match
+ * exactly what is in the provided selection, but the TPM may ignore some selected PCRs (for example, if an
+ * unimplemented PCR index is requested), in which case those PCRs will be absent from the provided pcr
+ * values. */
static int tpm2_pcr_read(
Tpm2Context *c,
const TPML_PCR_SELECTION *pcr_selection,
- TPML_PCR_SELECTION *ret_pcr_selection,
- TPM2B_DIGEST **ret_pcr_values,
+ Tpm2PCRValue **ret_pcr_values,
size_t *ret_n_pcr_values) {
- _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
- TPML_PCR_SELECTION remaining, total_read = {};
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
size_t n_pcr_values = 0;
TSS2_RC rc;
assert(c);
assert(pcr_selection);
+ assert(ret_pcr_values);
+ assert(ret_n_pcr_values);
- remaining = *pcr_selection;
+ TPML_PCR_SELECTION remaining = *pcr_selection;
while (!tpm2_tpml_pcr_selection_is_empty(&remaining)) {
_cleanup_(Esys_Freep) TPML_PCR_SELECTION *current_read = NULL;
_cleanup_(Esys_Freep) TPML_DIGEST *current_values = NULL;
@@ -1955,44 +2281,39 @@ static int tpm2_pcr_read(
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to read TPM2 PCRs: %s", sym_Tss2_RC_Decode(rc));
+ tpm2_log_debug_tpml_pcr_selection(current_read, "Read PCR selection");
+
if (tpm2_tpml_pcr_selection_is_empty(current_read)) {
log_warning("TPM2 refused to read possibly unimplemented PCRs, ignoring.");
break;
}
- tpm2_tpml_pcr_selection_sub(&remaining, current_read);
- tpm2_tpml_pcr_selection_add(&total_read, current_read);
+ unsigned i = 0;
+ FOREACH_PCR_IN_TPML_PCR_SELECTION(index, tpms, current_read) {
+ assert(i < current_values->count);
+ Tpm2PCRValue pcr_value = {
+ .index = index,
+ .hash = tpms->hash,
+ .value = current_values->digests[i++],
+ };
- if (!GREEDY_REALLOC(pcr_values, n_pcr_values + current_values->count))
- return log_oom();
+ tpm2_log_debug_pcr_value(&pcr_value, /* msg= */ NULL);
- memcpy_safe(&pcr_values[n_pcr_values], current_values->digests,
- current_values->count * sizeof(TPM2B_DIGEST));
- n_pcr_values += current_values->count;
+ if (!GREEDY_REALLOC_APPEND(pcr_values, n_pcr_values, &pcr_value, 1))
+ return log_oom();
+ }
+ assert(i == current_values->count);
- if (DEBUG_LOGGING) {
- unsigned i = 0;
- FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, current_read) {
- assert(i < current_values->count);
+ tpm2_tpml_pcr_selection_sub(&remaining, current_read);
+ }
- TPM2B_DIGEST *d = &current_values->digests[i];
- i++;
+ tpm2_sort_pcr_values(pcr_values, n_pcr_values);
- TPML_PCR_SELECTION l;
- tpm2_tpml_pcr_selection_from_mask(INDEX_TO_MASK(uint32_t, pcr), s->hash, &l);
+ if (!TPM2_PCR_VALUES_VALID(pcr_values, n_pcr_values))
+ return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "PCR values read from TPM are not valid.");
- _cleanup_free_ char *desc = tpm2_tpml_pcr_selection_to_string(&l);
- tpm2_log_debug_digest(d, strna(desc));
- }
- }
- }
-
- if (ret_pcr_selection)
- *ret_pcr_selection = total_read;
- if (ret_pcr_values)
- *ret_pcr_values = TAKE_PTR(pcr_values);
- if (ret_n_pcr_values)
- *ret_n_pcr_values = n_pcr_values;
+ *ret_pcr_values = TAKE_PTR(pcr_values);
+ *ret_n_pcr_values = n_pcr_values;
return 0;
}
@@ -2002,9 +2323,7 @@ static int tpm2_pcr_mask_good(
TPMI_ALG_HASH bank,
uint32_t mask) {
- _cleanup_free_ TPM2B_DIGEST *pcr_values = NULL;
TPML_PCR_SELECTION selection;
- size_t n_pcr_values = 0;
int r;
assert(c);
@@ -2015,22 +2334,18 @@ static int tpm2_pcr_mask_good(
tpm2_tpml_pcr_selection_from_mask(mask, bank, &selection);
- r = tpm2_pcr_read(c, &selection, &selection, &pcr_values, &n_pcr_values);
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values;
+ r = tpm2_pcr_read(c, &selection, &pcr_values, &n_pcr_values);
if (r < 0)
return r;
/* If at least one of the selected PCR values is something other than all 0x00 or all 0xFF we are happy. */
- unsigned i = 0;
- FOREACH_PCR_IN_TPML_PCR_SELECTION(pcr, s, &selection) {
- assert(i < n_pcr_values);
-
- if (!memeqbyte(0x00, pcr_values[i].buffer, pcr_values[i].size) &&
- !memeqbyte(0xFF, pcr_values[i].buffer, pcr_values[i].size))
+ for (unsigned i = 0; i < n_pcr_values; i++)
+ if (!memeqbyte(0x00, pcr_values[i].value.buffer, pcr_values[i].value.size) &&
+ !memeqbyte(0xFF, pcr_values[i].value.buffer, pcr_values[i].value.size))
return true;
- i++;
- }
-
return false;
}
@@ -3254,14 +3569,26 @@ int tpm2_seal(const char *device,
TPML_PCR_SELECTION hash_pcr_selection = {};
_cleanup_free_ TPM2B_DIGEST *hash_pcr_values = NULL;
- size_t n_hash_pcr_values = 0;
+ size_t n_hash_pcr_values;
if (hash_pcr_mask) {
/* For now, we just read the current values from the system; we need to be able to specify
* expected values, eventually. */
tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection);
- r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values);
+
+ _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
+ size_t n_pcr_values;
+ r = tpm2_pcr_read(c, &hash_pcr_selection, &pcr_values, &n_pcr_values);
if (r < 0)
return r;
+
+ r = tpm2_tpml_pcr_selection_from_pcr_values(
+ pcr_values,
+ n_pcr_values,
+ &hash_pcr_selection,
+ &hash_pcr_values,
+ &n_hash_pcr_values);
+ if (r < 0)
+ return log_error_errno(r, "Could not get PCR selection from values: %m");
}
TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index c024245915..2e25866401 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -17,7 +17,7 @@ typedef enum TPM2Flags {
* TPM2 on a Client PC must have at least 24 PCRs. This hardcodes our expectation of 24. */
#define TPM2_PCRS_MAX 24U
#define TPM2_PCRS_MASK ((UINT32_C(1) << TPM2_PCRS_MAX) - 1)
-static inline bool TPM2_PCR_VALID(unsigned pcr) {
+static inline bool TPM2_PCR_INDEX_VALID(unsigned pcr) {
return pcr < TPM2_PCRS_MAX;
}
static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
@@ -88,6 +88,26 @@ int tpm2_handle_new(Tpm2Context *context, Tpm2Handle **ret_handle);
Tpm2Handle *tpm2_handle_free(Tpm2Handle *handle);
DEFINE_TRIVIAL_CLEANUP_FUNC(Tpm2Handle*, tpm2_handle_free);
+typedef struct {
+ unsigned index;
+ TPMI_ALG_HASH hash;
+ TPM2B_DIGEST value;
+} Tpm2PCRValue;
+
+#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) { .index = (i), .hash = (h), .value = ((TPM2B_DIGEST) v), }
+bool TPM2_PCR_VALUE_VALID(const Tpm2PCRValue *pcr_value);
+int tpm2_pcr_value_from_string(const char *arg, Tpm2PCRValue *ret_pcr_value);
+char *tpm2_pcr_value_to_string(const Tpm2PCRValue *pcr_value);
+
+bool TPM2_PCR_VALUES_VALID(const Tpm2PCRValue *pcr_values, size_t n_pcr_values);
+void tpm2_sort_pcr_values(Tpm2PCRValue *pcr_values, size_t n_pcr_values);
+int tpm2_pcr_values_from_mask(uint32_t mask, TPMI_ALG_HASH hash, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
+int tpm2_pcr_values_to_mask(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPMI_ALG_HASH hash, uint32_t *ret_mask);
+int tpm2_pcr_values_from_string(const char *arg, Tpm2PCRValue **ret_pcr_values, size_t *ret_n_pcr_values);
+char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_values);
+int tpm2_pcr_values_hash_count(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, size_t *ret_count);
+int tpm2_tpml_pcr_selection_from_pcr_values(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPML_PCR_SELECTION *ret_selection, TPM2B_DIGEST **ret_values, size_t *ret_n_values);
+
int tpm2_create_primary(Tpm2Context *c, const Tpm2Handle *session, const TPM2B_PUBLIC *template, const TPM2B_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, Tpm2Handle **ret_handle);
int tpm2_create(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private);
int tpm2_create_loaded(Tpm2Context *c, const Tpm2Handle *parent, const Tpm2Handle *session, const TPMT_PUBLIC *template, const TPMS_SENSITIVE_CREATE *sensitive, TPM2B_PUBLIC **ret_public, TPM2B_PRIVATE **ret_private, Tpm2Handle **ret_handle);
@@ -130,6 +150,9 @@ size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
#else /* HAVE_TPM2 */
typedef struct {} Tpm2Context;
typedef struct {} Tpm2Handle;
+typedef struct {} Tpm2PCRValue;
+
+#define TPM2_PCR_VALUE_MAKE(i, h, v) (Tpm2PCRValue) {}
#endif /* HAVE_TPM2 */
int tpm2_list_devices(void);

View File

@ -0,0 +1,76 @@
From 4a7874eaf1810b010fd420ba2690b2f28ca40f15 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 14 Jul 2023 11:38:11 -0400
Subject: [PATCH] tpm2: move declared functions in header lower down
Move some function declarations lower down, below the Tpm2Context and
Tpm2Handle typedefs; later commits will reference the typedefs in some of the
functions, so the typedefs need to come first in the header.
This only moves the declarations, none of the declarations are modified.
(cherry picked from commit e00f46ac7ff47819602c87f5078d90f676e64e1f)
Related: RHEL-16182
---
src/shared/tpm2-util.h | 40 ++++++++++++++++++++--------------------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 2e25866401..52016ca8d7 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -34,26 +34,6 @@ static inline bool TPM2_PCR_MASK_VALID(uint32_t pcr_mask) {
int dlopen_tpm2(void);
-int tpm2_digest_many(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const struct iovec data[], size_t count, bool extend);
-static inline int tpm2_digest_buffer(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const void *data, size_t len, bool extend) {
- return tpm2_digest_many(alg, digest, &IOVEC_MAKE((void*) data, len), 1, extend);
-}
-int tpm2_digest_many_digests(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const TPM2B_DIGEST data[], size_t count, bool extend);
-static inline int tpm2_digest_rehash(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
- return tpm2_digest_many(alg, digest, NULL, 0, true);
-}
-static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
- return tpm2_digest_many(alg, digest, NULL, 0, false);
-}
-
-int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
-int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
-int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest);
-int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest);
-
-int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
-int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
-
typedef struct {
unsigned n_ref;
@@ -147,6 +127,26 @@ char *tpm2_tpml_pcr_selection_to_string(const TPML_PCR_SELECTION *l);
size_t tpm2_tpml_pcr_selection_weight(const TPML_PCR_SELECTION *l);
#define tpm2_tpml_pcr_selection_is_empty(l) (tpm2_tpml_pcr_selection_weight(l) == 0)
+int tpm2_digest_many(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const struct iovec data[], size_t count, bool extend);
+static inline int tpm2_digest_buffer(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const void *data, size_t len, bool extend) {
+ return tpm2_digest_many(alg, digest, &IOVEC_MAKE((void*) data, len), 1, extend);
+}
+int tpm2_digest_many_digests(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest, const TPM2B_DIGEST data[], size_t count, bool extend);
+static inline int tpm2_digest_rehash(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
+ return tpm2_digest_many(alg, digest, NULL, 0, true);
+}
+static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
+ return tpm2_digest_many(alg, digest, NULL, 0, false);
+}
+
+int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
+int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
+int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest);
+int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest);
+
+int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
+int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
+
#else /* HAVE_TPM2 */
typedef struct {} Tpm2Context;
typedef struct {} Tpm2Handle;

View File

@ -0,0 +1,79 @@
From a7151f54a163b9b559b30d31f2d252c0c3b953a7 Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Fri, 14 Jul 2023 07:23:55 -0400
Subject: [PATCH] tpm2: declare tpm2_log_debug_*() functions in tpm2_util.h
Allow other code to use the log debug functions; e.g. they are useful in test
code.
(cherry picked from commit 75de375aafa78ccf2cd93a2c0a15d3bc4b0fe4b7)
Related: RHEL-16182
---
src/shared/tpm2-util.c | 10 +++++-----
src/shared/tpm2-util.h | 6 ++++++
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index cef251e69c..c3e1ca8f3a 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -1829,7 +1829,7 @@ char *tpm2_pcr_values_to_string(const Tpm2PCRValue *pcr_values, size_t n_pcr_val
return s ? TAKE_PTR(s) : strdup("");
}
-static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) {
+void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg) {
if (!DEBUG_LOGGING || !l)
return;
@@ -1837,7 +1837,7 @@ static void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const
log_debug("%s: %s", msg ?: "PCR selection", strna(s));
}
-static void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) {
+void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg) {
if (!DEBUG_LOGGING || !pcr_value)
return;
@@ -1845,7 +1845,7 @@ static void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *
log_debug("%s: %s", msg ?: "PCR value", strna(s));
}
-static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
+void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg) {
if (!DEBUG_LOGGING || !buffer || size == 0)
return;
@@ -1853,12 +1853,12 @@ static void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *m
log_debug("%s: %s", msg ?: "Buffer", strna(h));
}
-static void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) {
+void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg) {
if (digest)
tpm2_log_debug_buffer(digest->buffer, digest->size, msg ?: "Digest");
}
-static void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) {
+void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg) {
if (name)
tpm2_log_debug_buffer(name->name, name->size, msg ?: "Name");
}
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 52016ca8d7..cecf35af4d 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -139,6 +139,12 @@ static inline int tpm2_digest_init(TPMI_ALG_HASH alg, TPM2B_DIGEST *digest) {
return tpm2_digest_many(alg, digest, NULL, 0, false);
}
+void tpm2_log_debug_tpml_pcr_selection(const TPML_PCR_SELECTION *l, const char *msg);
+void tpm2_log_debug_pcr_value(const Tpm2PCRValue *pcr_value, const char *msg);
+void tpm2_log_debug_buffer(const void *buffer, size_t size, const char *msg);
+void tpm2_log_debug_digest(const TPM2B_DIGEST *digest, const char *msg);
+void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg);
+
int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest);

View File

@ -0,0 +1,306 @@
From 8e4e838d22ef7e462fa97f8d77e3d8c3904b2dba Mon Sep 17 00:00:00 2001
From: Dan Streetman <ddstreet@ieee.org>
Date: Wed, 12 Jul 2023 22:14:18 -0400
Subject: [PATCH] tpm2: change tpm2_calculate_policy_pcr(),
tpm2_calculate_sealing_policy() to use Tpm2PCRValue array
An array of Tpm2PCRValue objects effectively replaces a TPML_PCR_SELECTION
object combined with an array of (properly ordered) TPM2B_DIGEST objects.
Also update tpm2_calculate_sealing_policy() pin parameter to boolean use_pin,
since the function does not need to know the pin value, only if a pin is being
used.
(cherry picked from commit 6e8fb3ad5ff2dab03b9e2b189adaf463c06a8101)
Related: RHEL-16182
---
src/boot/measure.c | 15 ++++----
src/shared/tpm2-util.c | 51 ++++++++++++--------------
src/shared/tpm2-util.h | 2 +-
src/test/test-tpm2.c | 82 +++++++++++++++++++++++-------------------
4 files changed, 74 insertions(+), 76 deletions(-)
diff --git a/src/boot/measure.c b/src/boot/measure.c
index 5ce3049147..1d696e1bd9 100644
--- a/src/boot/measure.c
+++ b/src/boot/measure.c
@@ -799,23 +799,20 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
if (tpmalg < 0)
return log_error_errno(tpmalg, "Unsupported PCR bank");
- TPML_PCR_SELECTION pcr_selection;
- tpm2_tpml_pcr_selection_from_mask(1 << TPM_PCR_INDEX_KERNEL_IMAGE,
- tpmalg,
- &pcr_selection);
-
- TPM2B_DIGEST pcr_values = {
+ TPM2B_DIGEST pcr_digest = {
.size = p->value_size,
};
- assert(sizeof(pcr_values.buffer) >= p->value_size);
- memcpy_safe(pcr_values.buffer, p->value, p->value_size);
+ assert(sizeof(pcr_digest.buffer) >= p->value_size);
+ memcpy_safe(pcr_digest.buffer, p->value, p->value_size);
+
+ Tpm2PCRValue pcr_value = TPM2_PCR_VALUE_MAKE(TPM_PCR_INDEX_KERNEL_IMAGE, tpmalg, pcr_digest);
TPM2B_DIGEST pcr_policy_digest;
r = tpm2_digest_init(TPM2_ALG_SHA256, &pcr_policy_digest);
if (r < 0)
return r;
- r = tpm2_calculate_policy_pcr(&pcr_selection, &pcr_values, 1, &pcr_policy_digest);
+ r = tpm2_calculate_policy_pcr(&pcr_value, 1, &pcr_policy_digest);
if (r < 0)
return r;
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index c3e1ca8f3a..50a01f55a6 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -3152,8 +3152,7 @@ static int tpm2_policy_auth_value(
/* Extend 'digest' with the PolicyPCR calculated hash. */
int tpm2_calculate_policy_pcr(
- const TPML_PCR_SELECTION *pcr_selection,
- const TPM2B_DIGEST pcr_values[],
+ const Tpm2PCRValue *pcr_values,
size_t n_pcr_values,
TPM2B_DIGEST *digest) {
@@ -3161,7 +3160,6 @@ int tpm2_calculate_policy_pcr(
TSS2_RC rc;
int r;
- assert(pcr_selection);
assert(pcr_values || n_pcr_values == 0);
assert(digest);
assert(digest->size == SHA256_DIGEST_SIZE);
@@ -3170,13 +3168,20 @@ int tpm2_calculate_policy_pcr(
if (r < 0)
return log_error_errno(r, "TPM2 support not installed: %m");
+ TPML_PCR_SELECTION pcr_selection;
+ _cleanup_free_ TPM2B_DIGEST *values = NULL;
+ size_t n_values;
+ r = tpm2_tpml_pcr_selection_from_pcr_values(pcr_values, n_pcr_values, &pcr_selection, &values, &n_values);
+ if (r < 0)
+ return log_error_errno(r, "Could not convert PCR values to TPML_PCR_SELECTION: %m");
+
TPM2B_DIGEST hash = {};
- r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, pcr_values, n_pcr_values, /* extend= */ false);
+ r = tpm2_digest_many_digests(TPM2_ALG_SHA256, &hash, values, n_values, /* extend= */ false);
if (r < 0)
return r;
_cleanup_free_ uint8_t *buf = NULL;
- size_t size = 0, maxsize = sizeof(command) + sizeof(*pcr_selection);
+ size_t size = 0, maxsize = sizeof(command) + sizeof(pcr_selection);
buf = malloc(maxsize);
if (!buf)
@@ -3187,7 +3192,7 @@ int tpm2_calculate_policy_pcr(
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to marshal PolicyPCR command: %s", sym_Tss2_RC_Decode(rc));
- rc = sym_Tss2_MU_TPML_PCR_SELECTION_Marshal(pcr_selection, buf, maxsize, &size);
+ rc = sym_Tss2_MU_TPML_PCR_SELECTION_Marshal(&pcr_selection, buf, maxsize, &size);
if (rc != TSS2_RC_SUCCESS)
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE),
"Failed to marshal PCR selection: %s", sym_Tss2_RC_Decode(rc));
@@ -3413,15 +3418,15 @@ static int tpm2_policy_authorize(
/* Extend 'digest' with the calculated policy hash. */
static int tpm2_calculate_sealing_policy(
- const TPML_PCR_SELECTION *hash_pcr_selection,
- const TPM2B_DIGEST *hash_pcr_values,
- size_t n_hash_pcr_values,
+ const Tpm2PCRValue *pcr_values,
+ size_t n_pcr_values,
const TPM2B_PUBLIC *public,
- const char *pin,
+ bool use_pin,
TPM2B_DIGEST *digest) {
int r;
+ assert(pcr_values || n_pcr_values == 0);
assert(digest);
if (public) {
@@ -3430,13 +3435,13 @@ static int tpm2_calculate_sealing_policy(
return r;
}
- if (hash_pcr_selection && !tpm2_tpml_pcr_selection_is_empty(hash_pcr_selection)) {
- r = tpm2_calculate_policy_pcr(hash_pcr_selection, hash_pcr_values, n_hash_pcr_values, digest);
+ if (n_pcr_values > 0) {
+ r = tpm2_calculate_policy_pcr(pcr_values, n_pcr_values, digest);
if (r < 0)
return r;
}
- if (pin) {
+ if (use_pin) {
r = tpm2_calculate_policy_auth_value(digest);
if (r < 0)
return r;
@@ -3567,28 +3572,17 @@ int tpm2_seal(const char *device,
return r;
}
- TPML_PCR_SELECTION hash_pcr_selection = {};
- _cleanup_free_ TPM2B_DIGEST *hash_pcr_values = NULL;
+ _cleanup_free_ Tpm2PCRValue *hash_pcr_values = NULL;
size_t n_hash_pcr_values;
if (hash_pcr_mask) {
/* For now, we just read the current values from the system; we need to be able to specify
* expected values, eventually. */
+ TPML_PCR_SELECTION hash_pcr_selection;
tpm2_tpml_pcr_selection_from_mask(hash_pcr_mask, pcr_bank, &hash_pcr_selection);
- _cleanup_free_ Tpm2PCRValue *pcr_values = NULL;
- size_t n_pcr_values;
- r = tpm2_pcr_read(c, &hash_pcr_selection, &pcr_values, &n_pcr_values);
+ r = tpm2_pcr_read(c, &hash_pcr_selection, &hash_pcr_values, &n_hash_pcr_values);
if (r < 0)
return r;
-
- r = tpm2_tpml_pcr_selection_from_pcr_values(
- pcr_values,
- n_pcr_values,
- &hash_pcr_selection,
- &hash_pcr_values,
- &n_hash_pcr_values);
- if (r < 0)
- return log_error_errno(r, "Could not get PCR selection from values: %m");
}
TPM2B_PUBLIC pubkey_tpm2, *authorize_key = NULL;
@@ -3605,11 +3599,10 @@ int tpm2_seal(const char *device,
return r;
r = tpm2_calculate_sealing_policy(
- &hash_pcr_selection,
hash_pcr_values,
n_hash_pcr_values,
authorize_key,
- pin,
+ !!pin,
&policy_digest);
if (r < 0)
return r;
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index cecf35af4d..c6e9339e0e 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -148,7 +148,7 @@ void tpm2_log_debug_name(const TPM2B_NAME *name, const char *msg);
int tpm2_calculate_name(const TPMT_PUBLIC *public, TPM2B_NAME *ret_name);
int tpm2_calculate_policy_auth_value(TPM2B_DIGEST *digest);
int tpm2_calculate_policy_authorize(const TPM2B_PUBLIC *public, const TPM2B_DIGEST *policy_ref, TPM2B_DIGEST *digest);
-int tpm2_calculate_policy_pcr(const TPML_PCR_SELECTION *pcr_selection, const TPM2B_DIGEST pcr_values[], size_t pcr_values_count, TPM2B_DIGEST *digest);
+int tpm2_calculate_policy_pcr(const Tpm2PCRValue *pcr_values, size_t n_pcr_values, TPM2B_DIGEST *digest);
int tpm2_seal(const char *device, uint32_t hash_pcr_mask, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, void **ret_srk_buf, size_t *ret_srk_buf_size);
int tpm2_unseal(const char *device, uint32_t hash_pcr_mask, uint16_t pcr_bank, const void *pubkey, size_t pubkey_size, uint32_t pubkey_pcr_mask, JsonVariant *signature, const char *pin, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, const void *srk_buf, size_t srk_buf_size, void **ret_secret, size_t *ret_secret_size);
diff --git a/src/test/test-tpm2.c b/src/test/test-tpm2.c
index c61bbf6d94..8a4e9f5142 100644
--- a/src/test/test-tpm2.c
+++ b/src/test/test-tpm2.c
@@ -618,50 +618,58 @@ TEST(calculate_policy_authorize) {
}
TEST(calculate_policy_pcr) {
- TPML_PCR_SELECTION pcr_selection;
- TPM2B_DIGEST pcr_values[16];
- TPM2B_DIGEST d;
- uint32_t pcr_mask;
+ TPM2B_DIGEST d, dN[16];
+
+ digest_init_sha256(&dN[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec");
+ digest_init_sha256(&dN[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d");
+ digest_init_sha256(&dN[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed");
+ digest_init_sha256(&dN[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init_sha256(&dN[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
+ digest_init_sha256(&dN[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5");
+ digest_init_sha256(&dN[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
+ digest_init_sha256(&dN[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
+ digest_init_sha256(&dN[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
+ digest_init_sha256(&dN[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b");
+ digest_init_sha256(&dN[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2");
+ digest_init_sha256(&dN[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44");
+ digest_init_sha256(&dN[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c");
+ digest_init_sha256(&dN[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4");
+ digest_init_sha256(&dN[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e");
+ digest_init_sha256(&dN[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137");
digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
- pcr_mask = (1<<4) | (1<<7) | (1<<8);
- tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
- digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
- digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
- digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
- assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0);
+ Tpm2PCRValue v1[] = {
+ TPM2_PCR_VALUE_MAKE(4, TPM2_ALG_SHA256, dN[4]),
+ TPM2_PCR_VALUE_MAKE(7, TPM2_ALG_SHA256, dN[7]),
+ TPM2_PCR_VALUE_MAKE(8, TPM2_ALG_SHA256, dN[8]),
+ };
+ assert_se(tpm2_calculate_policy_pcr(v1, ELEMENTSOF(v1), &d) == 0);
assert_se(digest_check(&d, "76532a0e16f7e6bf6b02918c11f75d99d729fab0cc81d0df2c4284a2c4fe6e05"));
-
- pcr_mask = (1<<4) | (1<<7) | (1<<8);
- tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
- digest_init_sha256(&pcr_values[0], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
- digest_init_sha256(&pcr_values[1], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
- digest_init_sha256(&pcr_values[2], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
- assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 3, &d) == 0);
+ assert_se(tpm2_calculate_policy_pcr(v1, ELEMENTSOF(v1), &d) == 0);
assert_se(digest_check(&d, "97e64bcabb64c1fa4b726528644926c8029f5b4458b0575c98c04fe225629a0b"));
digest_init_sha256(&d, "0000000000000000000000000000000000000000000000000000000000000000");
- pcr_mask = 0xffff;
- tpm2_tpml_pcr_selection_from_mask(pcr_mask, TPM2_ALG_SHA256, &pcr_selection);
- digest_init_sha256(&pcr_values[ 0], "2124793cbbe60c3a8637d3b84a5d054e87c351e1469a285acc04755e8b204dec");
- digest_init_sha256(&pcr_values[ 1], "bf7592f18adcfdc549fc0b94939f5069a24697f9cff4a0dca29014767b97559d");
- digest_init_sha256(&pcr_values[ 2], "4b00cff9dee3a364979b2dc241b34568a8ad49fcf2713df259e47dff8875feed");
- digest_init_sha256(&pcr_values[ 3], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
- digest_init_sha256(&pcr_values[ 4], "368f85b3013041dfe203faaa364f00b07c5da7b1e5f1dbf2efb06fa6b9bd92de");
- digest_init_sha256(&pcr_values[ 5], "c97c40369691c8e4aa78fb3a52655cd193b780a838b8e23f5f476576919db5e5");
- digest_init_sha256(&pcr_values[ 6], "3d458cfe55cc03ea1f443f1562beec8df51c75e14a9fcf9a7234a13f198e7969");
- digest_init_sha256(&pcr_values[ 7], "aa1154c9e0a774854ccbed4c8ce7e9b906b3d700a1a8db1772d0341a62dbe51b");
- digest_init_sha256(&pcr_values[ 8], "cfde439a2c06af3479ca6bdc60429b90553d65300c5cfcc40004a08c6b5ad81a");
- digest_init_sha256(&pcr_values[ 9], "9c2bac22ef5ec84fcdb71c3ebf776cba1247e5da980e5ee08e45666a2edf0b8b");
- digest_init_sha256(&pcr_values[10], "9885873f4d7348199ad286f8f2476d4f866940950f6f9fb9f945ed352dbdcbd2");
- digest_init_sha256(&pcr_values[11], "42400ab950d21aa79d12cc4fdef67d1087a39ad64900619831c0974dbae54e44");
- digest_init_sha256(&pcr_values[12], "767d064382e56ca1ad3bdcc6bc596112e6c2008b593d3570d24c2bfa64c4628c");
- digest_init_sha256(&pcr_values[13], "30c16133175959408c9745d8dafadef5daf4b39cb2be04df0d60089bd46d3cc4");
- digest_init_sha256(&pcr_values[14], "e3991b7ddd47be7e92726a832d6874c5349b52b789fa0db8b558c69fea29574e");
- digest_init_sha256(&pcr_values[15], "852dae3ecb992bdeb13d6002fefeeffdd90feca8b378d56681ef2c885d0e5137");
- assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0);
+ Tpm2PCRValue v2[] = {
+ TPM2_PCR_VALUE_MAKE( 0, TPM2_ALG_SHA256, dN[ 0]),
+ TPM2_PCR_VALUE_MAKE( 1, TPM2_ALG_SHA256, dN[ 1]),
+ TPM2_PCR_VALUE_MAKE( 2, TPM2_ALG_SHA256, dN[ 2]),
+ TPM2_PCR_VALUE_MAKE( 3, TPM2_ALG_SHA256, dN[ 3]),
+ TPM2_PCR_VALUE_MAKE( 4, TPM2_ALG_SHA256, dN[ 4]),
+ TPM2_PCR_VALUE_MAKE( 5, TPM2_ALG_SHA256, dN[ 5]),
+ TPM2_PCR_VALUE_MAKE( 6, TPM2_ALG_SHA256, dN[ 6]),
+ TPM2_PCR_VALUE_MAKE( 7, TPM2_ALG_SHA256, dN[ 7]),
+ TPM2_PCR_VALUE_MAKE( 8, TPM2_ALG_SHA256, dN[ 8]),
+ TPM2_PCR_VALUE_MAKE( 9, TPM2_ALG_SHA256, dN[ 9]),
+ TPM2_PCR_VALUE_MAKE(10, TPM2_ALG_SHA256, dN[10]),
+ TPM2_PCR_VALUE_MAKE(11, TPM2_ALG_SHA256, dN[11]),
+ TPM2_PCR_VALUE_MAKE(12, TPM2_ALG_SHA256, dN[12]),
+ TPM2_PCR_VALUE_MAKE(13, TPM2_ALG_SHA256, dN[13]),
+ TPM2_PCR_VALUE_MAKE(14, TPM2_ALG_SHA256, dN[14]),
+ TPM2_PCR_VALUE_MAKE(15, TPM2_ALG_SHA256, dN[15]),
+ };
+ assert_se(tpm2_calculate_policy_pcr(v2, ELEMENTSOF(v2), &d) == 0);
assert_se(digest_check(&d, "22be4f1674f792d6345cea9427701068f0e8d9f42755dcc0e927e545a68f9c13"));
- assert_se(tpm2_calculate_policy_pcr(&pcr_selection, pcr_values, 16, &d) == 0);
+ assert_se(tpm2_calculate_policy_pcr(v2, ELEMENTSOF(v2), &d) == 0);
assert_se(digest_check(&d, "7481fd1b116078eb3ac2456e4ad542c9b46b9b8eb891335771ca8e7c8f8e4415"));
}

Some files were not shown because too many files have changed in this diff Show More