From: Lennart Poettering Date: Thu, 20 Feb 2025 22:53:24 +0000 (+0100) Subject: tpm2-clear: optionally reset TPM during a factory reset X-Git-Tag: v258-rc1~1176^2~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=73e53d2ee442896dc54e7a64e0a24ef235e8aff5;p=thirdparty%2Fsystemd.git tpm2-clear: optionally reset TPM during a factory reset --- diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index 801bda09395..610b6f5fabe 100644 --- a/catalog/systemd.catalog.in +++ b/catalog/systemd.catalog.in @@ -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. diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 0976623be9c..4b2289e6466 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -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. diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 8e21ecda39a..3a4619edeef 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -752,6 +752,15 @@ + + systemd.tpm2_allow_clear= + + Controls whether to allow clearing of the TPM chip, implemented by + systemd-tpm2-clear8. + + + + systemd.tpm2_wait= diff --git a/man/rules/meson.build b/man/rules/meson.build index 7edbbf7fad9..2b06442d62d 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -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', diff --git a/man/systemd-tpm2-clear.service.xml b/man/systemd-tpm2-clear.service.xml new file mode 100644 index 00000000000..e400e74e34c --- /dev/null +++ b/man/systemd-tpm2-clear.service.xml @@ -0,0 +1,90 @@ + + + + + + + + systemd-tpm2-clear.service + systemd + + + + systemd-tpm2-clear.service + 8 + + + + systemd-tpm2-clear.service + Request that the TPM security chip is cleared on next boot + + + + systemd-tpm2-clear.service + /usr/lib/systemd/systemd-tpm2-clear + + + + Description + + systemd-tpm2-clear.service 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. + + systemd-tpm2-clear.service is typically hooked into the + factory-reset.target unit in order to request the TPM request before an immediate + reboot. See Factory Reset for more + information. + + + + Options + + The following options are understood: + + + + + + Exit cleanly and execute no operation if the system does not possess a TPM + chip. + + + + + + + + + + + Kernel Command Line + + systemd-tpm2-clear understands the following kernel command line + parameters: + + + + systemd.tpm2_allow_clear= + + 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. + + + + + + + + See Also + + systemd1 + systemd-tpm2-setup.service8 + systemd-factory-reset-request.service8 + + + diff --git a/presets/90-systemd.preset b/presets/90-systemd.preset index a12be9eba72..9c13e9c3de5 100644 --- a/presets/90-systemd.preset +++ b/presets/90-systemd.preset @@ -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 diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index cc30add2007..60b28087f6f 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -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) diff --git a/src/tpm2-setup/meson.build b/src/tpm2-setup/meson.build index 11427d5e3ad..6cfafe7cc5a 100644 --- a/src/tpm2-setup/meson.build +++ b/src/tpm2-setup/meson.build @@ -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'), diff --git a/src/tpm2-setup/tpm2-clear.c b/src/tpm2-setup/tpm2-clear.c new file mode 100644 index 00000000000..330d5cbd59d --- /dev/null +++ b/src/tpm2-setup/tpm2-clear.c @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#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); diff --git a/units/meson.build b/units/meson.build index 551c9f77c9f..ae13f85adeb 100644 --- a/units/meson.build +++ b/units/meson.build @@ -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'], diff --git a/units/systemd-tpm2-clear.service.in b/units/systemd-tpm2-clear.service.in new file mode 100644 index 00000000000..a47d99ac8e7 --- /dev/null +++ b/units/systemd-tpm2-clear.service.in @@ -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