tpm2-clear: optionally reset TPM during a factory reset

This commit is contained in:
Lennart Poettering 2025-02-20 23:53:24 +01:00
parent daae8f858d
commit 73e53d2ee4
11 changed files with 309 additions and 0 deletions

View File

@ -819,3 +819,18 @@ systemd-networkd, has been changed by another, unrelated process
and will likely result in problems later on.
Value changed to "@NEWVALUE@", which should be "@OURVALUE@".
-- 438188861e0b427a9d638a90487a0ca6
Subject: TPM clear requested
Defined-By: systemd
Support: %SUPPORT_URL%
Documentation: man:systemd-tpm2-clear.service(8)
A request to clear the TPM security chip has been issued to the firmware. This
is typically done as part of a comprehensive factory reset operation, and
ensures that on the next boot process the firmware will reset the TPM, after
interactively requesting confirmation by the user.
Clearing the TPM has the effect of invalidating all keys locked to the TPM,
including full disk encryption keys. Because of that care should be taken that
access to relevant resources is retained via other means.

View File

@ -767,3 +767,10 @@ Tools using the Varlink protocol (such as `varlinkctl`) or sd-bus (such as
`process`, `session`, `user`, `user-session`, or `group`. Controls the kernel
keyring in which `systemd-ask-password` caches the queried password. Defaults
to `user`.
`systemd-tpm2-clear`:
* `SYSTEMD_TPM2_ALLOW_CLEAR` takes a boolean. Overrides the effect of the
`systemd.factory_reset=` kernel command line option: if set to false,
requesting a TPM clearing is skipped, and the command immediately exits
successfully.

View File

@ -752,6 +752,15 @@
<xi:include href="version-info.xml" xpointer="v245"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.tpm2_allow_clear=</varname></term>
<listitem><para>Controls whether to allow clearing of the TPM chip, implemented by
<citerefentry><refentrytitle>systemd-tpm2-clear</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.tpm2_wait=</varname></term>

View File

@ -1142,6 +1142,7 @@ manpages = [
'systemd-tmpfiles-setup-dev.service',
'systemd-tmpfiles-setup.service'],
''],
['systemd-tpm2-clear.service', '8', [], 'ENABLE_BOOTLOADER'],
['systemd-tpm2-generator', '8', [], ''],
['systemd-tpm2-setup.service',
'8',

View File

@ -0,0 +1,90 @@
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-tpm2-clear.service" conditional='ENABLE_BOOTLOADER'
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-tpm2-clear.service</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-tpm2-clear.service</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-tpm2-clear.service</refname>
<refpurpose>Request that the TPM security chip is cleared on next boot</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>systemd-tpm2-clear.service</filename></para>
<para><filename>/usr/lib/systemd/systemd-tpm2-clear</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-tpm2-clear.service</filename> is a service that requests that the TPM is reset by
the PC firmware on the next boot. It makes use of the TPM Physical Presence Interface (PPI). Note that
this service does not immediately execute the clear operation, but simply asks the PC firmware to execute
it at next boot, where the user will be asked for confirmation before the operation is done.</para>
<para><filename>systemd-tpm2-clear.service</filename> is typically hooked into the
<filename>factory-reset.target</filename> unit in order to request the TPM request before an immediate
reboot. See <ulink url="https://systemd.io/FACTORY_RESET">Factory Reset</ulink> for more
information.</para>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>--graceful</option></term>
<listitem><para>Exit cleanly and execute no operation if the system does not possess a TPM
chip.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
</variablelist>
</refsect1>
<refsect1>
<title>Kernel Command Line</title>
<para><filename>systemd-tpm2-clear</filename> understands the following kernel command line
parameters:</para>
<variablelist class='kernel-commandline-options'>
<varlistentry>
<term><varname>systemd.tpm2_allow_clear=</varname></term>
<listitem><para>Takes a boolean argument. If false the service will succeed, but instead of requesting
the TPM clear operation from the PC firmware it will not execute any operation. If not specified
defaults to true.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-tpm2-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-factory-reset-request.service</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@ -33,6 +33,7 @@ enable systemd-pstore.service
enable systemd-resolved.service
enable systemd-sysext.service
enable systemd-timesyncd.service
enable systemd-tpm2-clear.service
enable systemd-userdbd.socket
disable console-getty.service

View File

@ -278,6 +278,8 @@ _SD_BEGIN_DECLARATIONS;
#define SD_MESSAGE_SRK_ENROLLMENT_NEEDS_AUTHORIZATION SD_ID128_MAKE(ad,70,89,f9,28,ac,4f,7e,a0,0c,07,45,7d,47,ba,8a)
#define SD_MESSAGE_SRK_ENROLLMENT_NEEDS_AUTHORIZATION_STR SD_ID128_MAKE_STR(ad,70,89,f9,28,ac,4f,7e,a0,0c,07,45,7d,47,ba,8a)
#define SD_MESSAGE_TPM2_CLEAR_REQUESTED SD_ID128_MAKE(43,81,88,86,1e,0b,42,7a,9d,63,8a,90,48,7a,0c,a6)
#define SD_MESSAGE_TPM2_CLEAR_REQUESTED_STR SD_ID128_MAKE_STR(43,81,88,86,1e,0b,42,7a,9d,63,8a,90,48,7a,0c,a6)
#define SD_MESSAGE_SYSCTL_CHANGED SD_ID128_MAKE(9c,f5,6b,8b,af,95,46,cf,94,78,78,3a,8d,e4,21,13)
#define SD_MESSAGE_SYSCTL_CHANGED_STR SD_ID128_MAKE_STR(9c,f5,6b,8b,af,95,46,cf,94,78,78,3a,8d,e4,21,13)

View File

@ -13,6 +13,13 @@ executables += [
libopenssl,
],
},
libexec_template + {
'name' : 'systemd-tpm2-clear',
'sources' : files('tpm2-clear.c'),
'conditions' : [
'HAVE_TPM2',
],
},
generator_template + {
'name' : 'systemd-tpm2-generator',
'sources' : files('tpm2-generator.c'),

140
src/tpm2-setup/tpm2-clear.c Normal file
View File

@ -0,0 +1,140 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include "sd-messages.h"
#include "build.h"
#include "env-util.h"
#include "fileio.h"
#include "main-func.h"
#include "pretty-print.h"
#include "proc-cmdline.h"
#include "tpm2-util.h"
static bool arg_graceful = false;
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
r = terminal_urlify_man("systemd-tpm2-clear", "8", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...]\n"
"\n%5$sRequest clearing of the TPM2 from PC firmware.%6$s\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --graceful Exit gracefully if no TPM2 device is found\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal());
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_GRACEFUL,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "graceful", no_argument, NULL, ARG_GRACEFUL },
{}
};
int c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
case ARG_VERSION:
return version();
case ARG_GRACEFUL:
arg_graceful = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
if (optind != argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program expects no arguments.");
return 1;
}
static int request_tpm2_clear(void) {
int r, clear = -1;
r = secure_getenv_bool("SYSTEMD_TPM2_ALLOW_CLEAR");
if (r < 0 && r != -ENXIO)
log_warning_errno(r, "Failed to parse $SYSTEMD_TPM2_ALLOW_CLEAR, ignoring: %m");
if (r >= 0)
clear = r;
if (clear < 0) {
bool b;
r = proc_cmdline_get_bool("systemd.tpm2_allow_clear", /* flags= */ 0, &b);
if (r < 0)
return log_debug_errno(r, "Failed to parse systemd.tpm2_allow_clear kernel command line argument: %m");
if (r > 0)
clear = b;
}
if (clear == 0) {
log_info("Clearing TPM2 disabled, exiting early.");
return EXIT_SUCCESS;
}
/* Now issue PPI request */
r = write_string_file("/sys/class/tpm/tpm0/ppi/request", "5", /* flags= */ 0);
if (r < 0)
return log_error_errno(r, "Failed to request TPM2 cleaing via PPI, unable to write to /sys/class/tpm/tpm0/ppi/request: %m");
log_struct(LOG_NOTICE,
"MESSAGE_ID=" SD_MESSAGE_TPM2_CLEAR_REQUESTED_STR,
LOG_MESSAGE("Requested TPM2 clearing via PPI. Firmware will verify with user and clear TPM on reboot."));
return 0;
}
static int run(int argc, char *argv[]) {
int r;
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
/* If we don't fully support the TPM we are unlikely able to reinitialize it after boot, hence don't
* be tempted to reset it in graceful mode. Otherwise we might destroy something without being able
* to rebuild it. */
if (arg_graceful && !tpm2_is_fully_supported()) {
log_notice("No complete TPM2 support detected, exiting gracefully.");
return EXIT_SUCCESS;
}
return request_tpm2_clear();
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

View File

@ -571,6 +571,10 @@ units = [
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
'symlinks' : ['sysinit.target.wants/'],
},
{
'file' : 'systemd-tpm2-clear.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-tpm2-setup.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],

View File

@ -0,0 +1,33 @@
# 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=Issue TPM Clear Request
Documentation=man:systemd-tpm2-clear.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=tpm2.target systemd-pcrphase-factory-reset.service
Before=factory-reset.target shutdown.target
# Note all systems that have a TPM implement the "Physical Presence Interface" (PPI)
ConditionPathExists=/sys/class/tpm/tpm0/ppi/request
# Only do this if we can be reasonably sure people accept our TPM use, which we
# derive here from the fact that UKIs are used. Because if they do they are OK
# with our SRK initialization and our PCR measurements, and hence should also
# be OK with our TPM resets.
ConditionSecurity=measured-uki
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-tpm2-clear --graceful
[Install]
WantedBy=factory-reset.target