1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-06 00:58:29 +03:00

factory-reset: rework infrastructure, make it work with gpt-auto, and add support for resetting TPM as part of factory reset (#36512)

This commit is contained in:
Lennart Poettering 2025-03-05 15:25:36 +01:00 committed by GitHub
commit c22948f6c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 1642 additions and 54 deletions

6
NEWS
View File

@ -76,6 +76,12 @@ CHANGES WITH 258 in spe:
*now* to include a native systemd unit file instead of a legacy
System V script to retain compatibility with future systemd releases.
* Support for systemd-repart's FactoryReset EFI variable has been
deprecated and support for it will be removed in v260. Use the newer,
more generic FactoryResetRequest variable instead, which can be
managed by "systemd-factory-reset request" and "systemd-factory-reset
complete".
* To work around limitations of X11's keyboard handling systemd's
keyboard mapping hardware database (hwdb.d/60-keyboard.hwdb) so far
mapped the microphone mute and touchpad on/off/toggle keys to the

7
TODO
View File

@ -125,6 +125,9 @@ Deprecations and removals:
* Once baseline is 4.13, remove support for INTERFACE_OLD= checks in "udevadm
trigger"'s waiting logic, since we can then rely on uuid-tagged uevents
* In v260: remove support for deprecated FactoryReset EFI variable in
systemd-repart, replaced by FactoryResetRequest.
Features:
* Maybe rename pkcs7 and public verbs of systemd-keyutil to be more verb like.
@ -830,10 +833,6 @@ Features:
AllowPeerGroup= that installs additional user/group ACL entries on AF_UNIX
sockets.
* systemd-tpm2-setup should probably have a factory reset logic, i.e. when some
kernel command line option is set we reset the TPM (equivalent of tpm2_clear
-c owner? or rather echo 5 >/sys/class/tpm/tpm0/ppi/request?).
* systemd-tpm2-setup should support a mode where we refuse booting if the SRK
changed. (Must be opt-in, to not break systems which are supposed to be
migratable between PCs)

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.

137
docs/FACTORY_RESET.md Normal file
View File

@ -0,0 +1,137 @@
---
title: Factory Reset
category: Booting
layout: default
SPDX-License-Identifier: LGPL-2.1-or-later
---
# Factory Reset
In various scenarios it is important to be able to reset operating systems back
into a "factory state", i.e. where all state, user data and configuration is
reset so that it resembles the system state when it was originally shipped.
systemd natively supports a concept of factory reset, that can both act as a
specific implementation for UEFI based systems, as well as a series of hook
points and a template for implementations on other systems.
Factory reset always takes place during early boot, i.e. from a well-defined
"clean" state. Factory reset operations may be requested from one boot to be
executed on the next.
Specifically, the following concepts are available:
* The `factory-reset.target` unit may be used to request a factory reset
operation and trigger a reboot in order to execute it. It by default executes
three services: `systemd-factory-reset-request.service`,
`systemd-tpm2-clear.service` and `systemd-factory-reset-reboot.service`.
* The
[`systemd-factory-reset-request.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-request.service.html)
unit is typically invoked via `factory-reset.target`. It requests a factory
reset operation for the next boot by setting the `FactoryResetRequest` EFI
variable. The EFI variable contains information about the requesting OS, so
that multi-boot scenarios are somewhat covered.
* The
[`systemd-tpm2-clear.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-tpm2-clear.service.html)
unit can request a TPM2 clear operation from the firmware on the next
boot. It is also invoked via `factory-reset.target`. UEFI firmwares that
support TPMs will ask the user for confirmation and then reset the TPM,
invalidating all prior keys associated with the security chip and generating
a new seed key.
* The
[`systemd-factory-reset-reboot.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-reboot.service.html)
unit automatically reboots the system as part of `factory-reset.target`. It
is ordered after `systemd-tpm2-clear.service` and
`systemd-factory-reset-request.service` in order to initiate the reboot that
is supposed to execute the factory reset operations.
* The `factory-reset-now.target` unit is started at boot whenever a factory
reset is requested for the boot. A factory reset may be requested via a
kernel command line option (`systemd.factory_reset=1`) or via the UEFI
variable `FactoryResetRequest` (see above). The
`systemd-factory-reset-generator` unit generator checks both these conditions
and adds `factory-reset-now.target` to the boot transaction, already in the
initial RAM disk (initrd).
* The
[`systemd-factory-reset-complete.service`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset-complete.service.html)
unit is invoked after `factory-reset-now.target` and marks the factory reset
operation as complete. The boot process then may continue.
* The
[`systemd-repart`](https://www.freedesktop.org/software/systemd/man/latest/systemd-repart.html)
tool can take the factory reset logic into account. Either on explicit
request via the `--factory-reset=` logic, or automatically derived from the
aforementioned kernel command line switch and EFI variable. When invoked for
factory reset it will securely erase all partitions marked for that via the
`FactoryReset=` setting in its partition definition files. Once that is
complete it will execute the usual setup operation, i.e. format new
partitions again.
* The
[`systemd-logind.service(8)`](https://www.freedesktop.org/software/systemd/man/latest/systemd-logind.service.html)
unit supports automatically binding factory reset to special keypresses
(typically long presses), see the
[`logind.conf(5)`](https://www.freedesktop.org/software/systemd/man/latest/logind.conf.html)
man page.
* The
[`systemd-factory-reset`](https://www.freedesktop.org/software/systemd/man/latest/systemd-factory-reset.html)
tool can be used to query the current state of the factory request mechanism,
i.e. whether a factory reset is currently being executed, or if one has been
requested for the next boot.
* The `/run/systemd/io.systemd.FactoryReset` Varlink service provides two IPC
APIs for working with factory reset: it permits querying whether the local
system supports requesting a factory reset by starting
`factory-reset.target`. This may be used by UIs to hide or show in the UI an
interface to request a factory reset. The Varlink IPC service also reports
the current factory reset state, much like the `systemd-factory-reset` tool
mentioned above. This may be used by various early boot services that
potentially intent to reset system state during a factory reset operation.
## Exposure in the UI
If a graphical UI shall expose a factory reset operation it should first check
if requesting a factory reset is supported at all via the Varlink service
mentioned above. Once a factory reset shall be executed it shall ask for
activation of the `factory-reset.target` unit.
Alternatively, `systemd-logind.service`'s hotkey support may be used, for
example to request factory reset if the reboot button is pressed for a long
time.
## Support for non-UEFI Systems
The above is a relatively bespoke solution for EFI systems. It uses EFI
variables as stateful memory to request the factory reset on the next boot.
On non-EFI systems, a different mechanism should be devised. A service
requesting the factory request can then be plugged into
`factory-reset.target`. At boot the request should then be fed back to the
booted kernel via the `systemd.factory_reset=1` kernel command line option, in
order to execute the reset operation.
## Support for Resetting other Resources than Partitions + TPM
By default a factory reset implemented with systemd's tools can reset/erase
partitions (via `systemd-repart`, see above) and reset the TPM (via
`systemd-tpm2-clear.service`, see above).
In some cases other resources shall be reset/erased too. To support that,
define your own service and plug it into `factory-reset-now.target`, ensuring
it is ordered before that.
## Factory Reset via Boot Menu
Factory reset can also be requested via the boot menu. A simple factory reset
(that does not touch the TPM) at boot can be requested via a boot menu item
containing the `systemd.factory_reset=1` kernel command line option. A more
comprehensive factory reset operation (that also erases the TPM) can be
requested by booting with `rd.systemd.unit=factory-reset.target`. Note that the
latter will require one reboot (required since that's how TPM resets work),
while the former will reset state and continue running without an additional
reboot.

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>
@ -761,6 +770,17 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
<varlistentry>
<term><varname>systemd.factory_reset=</varname></term>
<listitem><para>Controls whether to to boot into factory reset mode, implemented by
<citerefentry><refentrytitle>systemd-factory-reset-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
and other tools.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist>
</refsect1>

View File

@ -942,6 +942,14 @@ manpages = [
['30-systemd-environment-d-generator'],
'ENABLE_ENVIRONMENT_D'],
['systemd-escape', '1', [], ''],
['systemd-factory-reset-generator', '8', [], ''],
['systemd-factory-reset',
'8',
['systemd-factory-reset-complete.service',
'systemd-factory-reset-request.service',
'systemd-factory-reset.socket',
'systemd-factory-reset@.service'],
''],
['systemd-firstboot', '1', ['systemd-firstboot.service'], 'ENABLE_FIRSTBOOT'],
['systemd-fsck@.service',
'8',
@ -1134,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,48 @@
<?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-factory-reset-generator">
<refentryinfo>
<title>systemd-factory-reset-generator</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-factory-reset-generator</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-factory-reset-generator</refname>
<refpurpose>Pull <filename>factory-reset-now.target</filename> into the initial boot transaction when factory reset has been requested</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>/usr/lib/systemd/system-generators/systemd-factory-reset-generator</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-factory-reset-generator</filename> is a generator that pulls
<filename>factory-reset-now.target</filename> into the initial boot transaction when the factory reset
operation has been requested, either via the <varname>systemd.factory_reset=</varname> kernel command
line option or via the <varname>FactoryResetRequest</varname> EFI variable.</para>
<para><filename>systemd-factory-reset-generator</filename> implements
<citerefentry><refentrytitle>systemd.generator</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><simplelist type="inline">
<member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd-factory-reset</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><ulink url="https://systemd.io/FACTORY_RESET">Factory Reset</ulink></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@ -0,0 +1,176 @@
<?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-factory-reset"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-factory-reset</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-factory-reset</refentrytitle>
<manvolnum>8</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-factory-reset</refname>
<refname>systemd-factory-reset-request.service</refname>
<refname>systemd-factory-reset-complete.service</refname>
<refname>systemd-factory-reset.socket</refname>
<refname>systemd-factory-reset@.service</refname>
<refpurpose>Request or complete a factory reset operation, or query current factory reset mode</refpurpose>
</refnamediv>
<refsynopsisdiv>
<para><filename>/usr/lib/systemd/systemd-factory-reset</filename></para>
<para><filename>systemd-factory-reset-request.service</filename></para>
<para><filename>systemd-factory-reset-complete.service</filename></para>
<para><filename>systemd-factory-reset.socket</filename></para>
<para><filename>systemd-factory-reset@.service</filename></para>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><filename>systemd-factory-reset</filename> is a tool that can query the current factory reset
state, request factory request operations or complete them.</para>
<para>Some of the functionality is also available via the
<filename>/run/systemd/io.systemd.FactoryReset</filename> Varlink service (implemented via the
<filename>systemd-factory-reset.socket</filename>/<filename>systemd-factory-reset@.service</filename>
units.</para>
<para>See <ulink url="https://systemd.io/FACTORY_RESET">Factory Reset</ulink> for an overview of the
factory reset logic.</para>
</refsect1>
<refsect1>
<title>Commands</title>
<para>The <filename>/usr/lib/systemd/systemd-factory-reset</filename> executable may also be invoked from the
command line, taking one of the following command arguments:</para>
<variablelist>
<varlistentry>
<term><option>status</option></term>
<listitem><para>Report current factory reset state. Reports one of <literal>unsupported</literal> (if
the OS does not support a factory reset logic), <literal>unspecified</literal> (if no factory reset
was requested, but it wasn't turned off explicitly either), <literal>off</literal> (if the factory
reset logic was explicitly turned off via the kernel command line option), <literal>on</literal> (if
the factory reset is currently enabled and executed), <literal>complete</literal> (if the factory
reset logic ran during the current boot but is complete now), <literal>pending</literal> (if a
factory reset has been requested for the next boot).</para>
<para>Returns with an exit status of 0 if the factory reset mechanism is currently not in effect, 10
if a factory reset is currently being executed, or 11 if it is pending for the next boot.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>request</option></term>
<listitem><para>Request a factory reset operation to be executed on next boot.</para>
<para>Note that this is a relatively low-level operation. The primary interface for requesting a
factory reset operation is by starting the <filename>factory-reset.target</filename>
unit.</para>
<para>This sets the <varname>FactoryResetRequested</varname> EFI variable, see below.</para>
<para>This operation is executed when the <filename>systemd-factory-reset-request.service</filename>
unit is started (which is typically one of the services hooked into
and ordered before <filename>factory-reset.target</filename>).</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>cancel</option></term>
<listitem><para>Cancel any previously requested (but not yet executed) factory reset
operation.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>complete</option></term>
<listitem><para>Mark an ongoing factory reset operation as complete.</para>
<para>This operation is executed when the <filename>systemd-factory-reset-complete.service</filename>
unit is started (which is typically one of the services hooked into and ordered after
<filename>factory-reset-now.target</filename>).</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>--retrigger</option></term>
<listitem><para>When used with the <command>complete</command> command retriggers all block devices,
which might result in auto-discovered devices being usable that previously weren't because the factory
reset logic was in place.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--quiet</option></term>
<term><option>-q</option></term>
<listitem><para>Suppresses the state output of <command>status</command>, but still sets the exit
status as documented.</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>EFI Variables</title>
<para>The following EFI variable is set and read by <command>systemd-factory-reset</command>, under the
vendor UUID <literal>8cf2644b-4b0b-428f-9387-6d876050dc67</literal>, for communication between this boot
and the next.</para>
<variablelist class='efi-variables'>
<varlistentry>
<term><varname>FactoryResetRequest</varname></term>
<listitem><para>Set whenever a factory reset is requested from the next boot, deleted once the
factory reset is complete. Contains JSON data describing the requesting OS, in order to avoid
confusion in multi-boot systems.</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-factory-reset-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry></member>
<member><citerefentry><refentrytitle>systemd.special</refentrytitle><manvolnum>7</manvolnum></citerefentry></member>
<member><ulink url="https://systemd.io/FACTORY_RESET">Factory Reset</ulink></member>
</simplelist></para>
</refsect1>
</refentry>

View File

@ -322,16 +322,20 @@
<term><varname>rootflags=</varname></term>
<listitem><para>When <varname>root=</varname> is used with the special value
<literal>gpt-auto</literal>, full automatic discovery of the root
partition based on the GPT partition type is enabled. Any other value disables this
logic.</para>
<literal>gpt-auto</literal>, full automatic discovery of the root partition based on the GPT
partition type is enabled. Use of the root partition is delayed until factory reset mode is left, in
case it is enabled during the current boot. See <ulink url="https://systemd.io/FACTORY_RESET">Factory
Reset</ulink> for more information on that. If <literal>gpt-auto-force</literal> is specified
automatic discovery of the root partition is enabled, ignoring any factory reset mode. Any other
value disables this logic.</para>
<para>If <varname>root=</varname> is not specified at all on the kernel command line automatic
discovery of the root partition via the boot loader reported ESP is also enabled, however in this
case discovery based on the loopback block device <literal>.lo_name</literal> field is not enabled.</para>
discovery of the root partition via the ESP reported by the boot loader is also enabled (taking
factory reset state into account), however in this case discovery based on the loopback block device
<literal>.lo_name</literal> field is not enabled.</para>
<para>The <varname>rootfstype=</varname> and <varname>rootflags=</varname> are used to select the
file system type and options when the root file system is automatically discovered.</para>
<para>The <varname>rootfstype=</varname> and <varname>rootflags=</varname> options are used to select
the file system type and options when the root file system is automatically discovered.</para>
<xi:include href="version-info.xml" xpointer="v242"/></listitem>
</varlistentry>

View File

@ -130,9 +130,9 @@
<command>systemd-repart</command> may be used to erase existing partitions to reset an installation back
to vendor defaults. This mode of operation is used when either the <option>--factory-reset=yes</option>
switch is passed on the tool's command line, or the <option>systemd.factory_reset=yes</option> option is
specified on the kernel command line, or the <varname>FactoryReset</varname> EFI variable (vendor UUID
<constant>8cf2644b-4b0b-428f-9387-6d876050dc67</constant>) is set to "yes". It alters the algorithm above
slightly: between the 3rd and the 4th step above any partition marked explicitly via the
specified on the kernel command line, or the <varname>FactoryResetRequest</varname> EFI variable (vendor
UUID <constant>8cf2644b-4b0b-428f-9387-6d876050dc67</constant>) is set to "yes". It alters the algorithm
above slightly: between the 3rd and the 4th step above any partition marked explicitly via the
<varname>FactoryReset=</varname> boolean is deleted, and the algorithm restarted, thus immediately
re-creating these partitions anew empty.</para>

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

@ -34,6 +34,7 @@
<filename>emergency.target</filename>,
<filename>exit.target</filename>,
<filename>factory-reset.target</filename>,
<filename>factory-reset-now.target</filename>,
<filename>final.target</filename>,
<filename>first-boot-complete.target</filename>,
<filename>getty.target</filename>,
@ -304,11 +305,30 @@
<varlistentry>
<term><filename>factory-reset.target</filename></term>
<listitem>
<para>A special target to trigger a factory reset.</para>
<para>A special target to request a factory reset operation. This will typically persistently
store a request flag for the next boot and then reboot in order to reset the system to factory
state.</para>
<para>See <ulink url="https://systemd.io/FACTORY_RESET">Factory Reset</ulink> for more
information.</para>
<xi:include href="version-info.xml" xpointer="v250"/>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>factory-reset-now.target</filename></term>
<listitem>
<para>A special target that is started on boots that shall execute a factory reset. It may be
used to pull in additional services that shall be invoked during a factory reset operation. It
also acts as ordering barrier: once the target is reached the factory reset state is marked as
"completed".</para>
<para>See <ulink url="https://systemd.io/FACTORY_RESET">Factory Reset</ulink> for more
information.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>final.target</filename></term>
<listitem>

View File

@ -2299,6 +2299,7 @@ subdir('src/detect-virt')
subdir('src/dissect')
subdir('src/environment-d-generator')
subdir('src/escape')
subdir('src/factory-reset')
subdir('src/firstboot')
subdir('src/fsck')
subdir('src/fstab-generator')

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

@ -34,10 +34,25 @@ SUBSYSTEM=="block", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READ
# we are probably still calling mke2fs or mkswap on it.
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
# add symlink to GPT root disk
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root"
# Add symlink to GPT root disk in two flavours: one which takes the factory
# reset state into account, and one which does not. The former is useful for
# wipe-rootfs-on-factory-reset scenarios where we should not be tempted to use
# the root fs before factory reset is complete. The latter is useful for
# wipe-only-/var-on-factory-reset where we should use it (because that's where
# repart.d/ definitions are placed which tell us what to wipe).
SUBSYSTEM!="block", GOTO="gpt_auto_root_end"
ENV{ID_PART_GPT_AUTO_ROOT}!="1", GOTO="gpt_auto_root_end"
IMPORT{builtin}="factory_reset status"
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="gpt-auto-root"
ENV{ID_FS_TYPE}!="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-ignore-factory-reset"
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}!="on", SYMLINK+="gpt-auto-root-luks"
ENV{ID_FS_TYPE}=="crypto_LUKS", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-luks-ignore-factory-reset"
LABEL="gpt_auto_root_end"
# Note we don't need to condition the gpt-auto-root LUKS symlink for
# auto-discovered LUKS devices, because it's sufficient if we do this for the
# underlying partition block device, which is covered by the above.
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", SYMLINK+="gpt-auto-root", IMPORT{builtin}="factory_reset status"
SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{DM_NAME}=="root", ENV{ID_FACTORY_RESET}=="on|complete", SYMLINK+="gpt-auto-root-ignore-factory-reset"
# Ignore raid devices that are not yet assembled and started
SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="md*", TEST!="md/array_state", ENV{SYSTEMD_READY}="0"

View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "factory-reset.h"
#include "generator.h"
#include "special.h"
/* This generator pulls factory-reset-now.target into the initial transaction the kernel command line's
* systemd.factor_reset= variable, or the FactoryResetRequest EFI variable say so. */
static int run(const char *dest, const char *dest_early, const char *dest_late) {
assert(dest_early);
FactoryResetMode f = factory_reset_mode();
if (f < 0)
return log_error_errno(f, "Failed to determine factory reset mode: %m");
if (f != FACTORY_RESET_ON) {
log_debug("Not in factory reset mode, skipping.");
return EXIT_SUCCESS;
}
log_debug("Detected factory reset mode, pulling in factory-reset-now.target.");
/* We pull this in from basic.target so that it ends up in all "regular" boot ups, but not in
* rescue.target or even emergency.target. */
return generator_add_symlink(dest_early, SPECIAL_BASIC_TARGET, "wants", "factory-reset-now.target");
}
DEFINE_MAIN_GENERATOR_FUNCTION(run);

View File

@ -0,0 +1,385 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include "sd-json.h"
#include "sd-varlink.h"
#include "ansi-color.h"
#include "build.h"
#include "device-util.h"
#include "efivars.h"
#include "factory-reset.h"
#include "fs-util.h"
#include "json-util.h"
#include "main-func.h"
#include "os-util.h"
#include "pretty-print.h"
#include "udev-util.h"
#include "varlink-io.systemd.FactoryReset.h"
#include "varlink-util.h"
#include "verbs.h"
static bool arg_retrigger = false;
static bool arg_quiet = false;
static bool arg_varlink = false;
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
r = terminal_urlify_man("systemd-factory-reset", "8", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] COMMAND\n"
"\n%5$sQuery, request, cancel factory reset operation.%6$s\n"
"\n%3$sCommands:%4$s\n"
" status Report current factory reset status\n"
" request Request a factory reset on next boot\n"
" cancel Cancel a prior factory reset request for next boot\n"
" complete Mark a factory reset as complete\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
" --retrigger Retrigger block devices\n"
" -q --quiet Suppress output\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_RETRIGGER,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "retrigger", no_argument, NULL, ARG_RETRIGGER },
{ "quiet", no_argument, NULL, 'q' },
{}
};
int r, c;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "hq", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
case ARG_VERSION:
return version();
case ARG_RETRIGGER:
arg_retrigger = true;
break;
case 'q':
arg_quiet = true;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
r = sd_varlink_invocation(SD_VARLINK_ALLOW_ACCEPT);
if (r < 0)
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
if (r > 0)
arg_varlink = true;
return 1;
}
static int verb_status(int argc, char *argv[], void *userdata) {
static const int exit_status_table[_FACTORY_RESET_MODE_MAX] = {
/* Report current mode also as via exit status, but only return a subset of states */
[FACTORY_RESET_UNSUPPORTED] = EXIT_SUCCESS,
[FACTORY_RESET_UNSPECIFIED] = EXIT_SUCCESS,
[FACTORY_RESET_OFF] = EXIT_SUCCESS,
[FACTORY_RESET_ON] = 10,
[FACTORY_RESET_COMPLETE] = EXIT_SUCCESS,
[FACTORY_RESET_PENDING] = 11,
};
FactoryResetMode f = factory_reset_mode();
if (f < 0)
return log_error_errno(f, "Failed to determine factory reset mode: %m");
if (!arg_quiet)
puts(factory_reset_mode_to_string(f));
return exit_status_table[f];
}
static int verb_request(int argc, char *argv[], void *userdata) {
int r;
FactoryResetMode f = factory_reset_mode();
if (f < 0)
return log_error_errno(f, "Failed to determine current factory reset mode: %m");
if (f == FACTORY_RESET_ON)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "System is currently in factory reset mode, refusing to request another one.");
if (f == FACTORY_RESET_PENDING) {
if (!arg_quiet)
log_info("Factory reset already requested, skipping.");
return 0;
}
if (!is_efi_boot())
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Not an EFI boot, requesting factory reset via EFI variable not supported.");
_cleanup_free_ char *id = NULL, *image_id = NULL, *version_id = NULL, *image_version = NULL;
r = parse_os_release(
/* root= */ NULL,
"ID", &id,
"IMAGE_ID", &image_id,
"VERSION_ID", &version_id,
"IMAGE_VERSION", &image_version);
if (r < 0)
return log_error_errno(r, "Failed to parse os-release: %m");
if (!id)
return log_error_errno(SYNTHETIC_ERRNO(EBADR), "os-release data lacks ID= field, refusing.");
sd_id128_t boot_id;
r = sd_id128_get_boot(&boot_id);
if (r < 0)
return log_error_errno(r, "Failed to get boot ID: %m");
/* NB: we don't really use the version fields for anything on the parsing side, because we want to
* allow some flexbility between OS/image versions that request the factory reset and that execute
* it. However, we include it nonetheless to make things more clearly debuggable. */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
r = sd_json_buildo(
&v,
SD_JSON_BUILD_PAIR_STRING("osReleaseId", id),
JSON_BUILD_PAIR_STRING_NON_EMPTY("osReleaseVersionId", version_id),
JSON_BUILD_PAIR_STRING_NON_EMPTY("osReleaseImageId", image_id),
JSON_BUILD_PAIR_STRING_NON_EMPTY("osReleaseImageVersion", image_version),
SD_JSON_BUILD_PAIR_ID128("bootId", boot_id));
if (r < 0)
return log_error_errno(r, "Failed to build JSON object: %m");
_cleanup_free_ char *formatted = NULL;
r = sd_json_variant_format(v, /* flags= */ 0, &formatted);
if (r < 0)
return log_error_errno(r, "Failed to format JSON object: %m");
r = efi_set_variable_string(EFI_SYSTEMD_VARIABLE_STR("FactoryResetRequest"), formatted);
if (r < 0)
return log_error_errno(r, "Failed to set EFI variable FactoryResetRequest: %m");
log_debug("Set EFI variable FactoryResetRequest to '%s'.", formatted);
if (!arg_quiet)
log_info("Factory reset requested.");
return 0;
}
static int verb_cancel(int argc, char *argv[], void *userdata) {
int r;
FactoryResetMode f = factory_reset_mode();
if (f < 0)
return log_error_errno(f, "Failed to determine current factory reset mode: %m");
if (f == FACTORY_RESET_ON)
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "System already executing factory reset, cannot cancel.");
if (f != FACTORY_RESET_PENDING) {
if (!arg_quiet)
log_info("No factory reset has been requested, cannot cancel, skipping.");
return 0;
}
if (!is_efi_boot()) {
if (!arg_quiet)
log_info("Not an EFI boot, cannot remove FactoryResetMode EFI variable, not cancelling.");
return 0;
}
r = efi_set_variable(EFI_SYSTEMD_VARIABLE_STR("FactoryResetRequest"), /* value= */ NULL, /* size= */ 0);
if (r < 0)
return log_error_errno(r, "Failed to remove FactoryResetRequest EFI variable: %m");
if (!arg_quiet)
log_info("Factory reset cancelled.");
return 0;
}
static int retrigger_block_devices(void) {
int r;
/* Let's retrigger block devices after factory reset is complete: it's quite likely that some
* partitions went away or got recreated, and will only be considered relevant once factory reset
* mode is left. For example, /dev/disk/gpt-auto-root is like that: it is only created once factory
* reset mode is complete. */
if (!udev_available()) {
if (!arg_quiet)
log_info("Skipping triggering of block devices, as udev is not available.");
return 0;
}
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
r = sd_device_enumerator_new(&e);
if (r < 0)
return log_error_errno(r, "Failed to allocate device enumerator: %m");
r = sd_device_enumerator_allow_uninitialized(e);
if (r < 0)
return log_error_errno(r, "Failed to enable enumeration of uninitialized devices: %m");
r = sd_device_enumerator_add_match_subsystem(e, "block", /* match = */ true);
if (r < 0)
return log_error_errno(r, "Failed to filter device enumeration by 'block' subsystem: %m");
if (!arg_quiet)
log_info("Retriggering block devices.");
FOREACH_DEVICE(e, d) {
r = sd_device_trigger(d, SD_DEVICE_CHANGE);
if (r < 0)
/* Devices can appear anytime, let's not loudly log about that. */
log_device_full_errno(
d,
ERRNO_IS_DEVICE_ABSENT(r) ? LOG_DEBUG : LOG_WARNING,
r,
"Failed to trigger block device, ignoring: %m");
}
return 0;
}
static int verb_complete(int argc, char *argv[], void *userdata) {
int r;
FactoryResetMode f = factory_reset_mode();
if (f < 0)
return log_error_errno(f, "Failed to dermine factory reset mode: %m");
log_debug("Current factory reset mode is: %s", factory_reset_mode_to_string(f));
if (f != FACTORY_RESET_ON) {
if (!arg_quiet)
log_info("Attempted to leave factory reset mode, even though we are not in factory reset mode. Ignoring.");
return 0;
}
if (is_efi_boot()) {
r = efi_set_variable(EFI_SYSTEMD_VARIABLE_STR("FactoryResetRequest"), /* value= */ NULL, /* size= */ 0);
if (r < 0)
log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r,
"Failed to remove FactoryResetRequest EFI variable: %m");
}
r = touch("/run/systemd/factory-reset-complete");
if (r < 0)
return log_error_errno(r, "Failed to create /run/systemd/factory-reset-complete file: %m");
if (!arg_quiet)
log_info("Successfully left factory reset mode.");
if (arg_retrigger)
(void) retrigger_block_devices();
return 0;
}
static int vl_method_get_factory_reset_mode(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
int r;
assert(parameters);
r = sd_varlink_dispatch(link, parameters, /* table= */ NULL, /* userdata= */ NULL);
if (r != 0)
return r;
FactoryResetMode f = factory_reset_mode();
if (f < 0)
return f;
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_STRING("mode", factory_reset_mode_to_string(f)));
}
static int vl_method_can_request_factory_reset(sd_varlink *link, sd_json_variant *parameters, sd_varlink_method_flags_t flags, void *userdata) {
int r;
assert(parameters);
r = sd_varlink_dispatch(link, parameters, /* table= */ NULL, /* userdata= */ NULL);
if (r != 0)
return r;
return sd_varlink_replybo(link, SD_JSON_BUILD_PAIR_BOOLEAN("supported", is_efi_boot()));
}
static int varlink_service(void) {
int r;
/* Invocation as Varlink service */
_cleanup_(sd_varlink_server_unrefp) sd_varlink_server *varlink_server = NULL;
r = varlink_server_new(&varlink_server, /* flags= */ 0, /* userdata= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to allocate Varlink server: %m");
r = sd_varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_FactoryReset);
if (r < 0)
return log_error_errno(r, "Failed to add Varlink interface: %m");
r = sd_varlink_server_bind_method_many(
varlink_server,
"io.systemd.FactoryReset.GetFactoryResetMode", vl_method_get_factory_reset_mode,
"io.systemd.FactoryReset.CanRequestFactoryReset", vl_method_can_request_factory_reset);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink methods: %m");
r = sd_varlink_server_loop_auto(varlink_server);
if (r < 0)
return log_error_errno(r, "Failed to run Varlink event loop: %m");
return EXIT_SUCCESS;
}
static int run(int argc, char *argv[]) {
static const Verb verbs[] = {
{ "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
{ "request", VERB_ANY, 1, 0, verb_request },
{ "cancel", VERB_ANY, 1, 0, verb_cancel },
{ "complete", VERB_ANY, 1, 0, verb_complete },
{}
};
int r;
log_setup();
r = parse_argv(argc, argv);
if (r <= 0)
return r;
if (arg_varlink)
return varlink_service();
return dispatch_verb(argc, argv, verbs, /* userdata= */ NULL);
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

View File

@ -0,0 +1,12 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
executables += [
libexec_template + {
'name' : 'systemd-factory-reset',
'sources' : files('factory-reset-tool.c'),
},
generator_template + {
'name' : 'systemd-factory-reset-generator',
'sources' : files('factory-reset-generator.c'),
},
]

View File

@ -1119,7 +1119,7 @@ static bool validate_root_or_usr_mount_source(const char *what, const char *swit
return false;
}
if (streq(what, "gpt-auto")) {
if (parse_gpt_auto_root(what) > 0) {
/* This is handled by gpt-auto-generator */
log_debug("Skipping %s directory handling, as gpt-auto was requested.", switch_name);
return false;

View File

@ -17,6 +17,7 @@
#include "dissect-image.h"
#include "dropin.h"
#include "efi-loader.h"
#include "factory-reset.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@ -39,7 +40,7 @@
static const char *arg_dest = NULL;
static bool arg_enabled = true;
static int arg_root_enabled = -1; /* tristate */
static GptAutoRoot arg_auto_root = _GPT_AUTO_ROOT_INVALID;
static bool arg_swap_enabled = true;
static char *arg_root_fstype = NULL;
static char *arg_root_options = NULL;
@ -47,7 +48,6 @@ static int arg_root_rw = -1;
static ImagePolicy *arg_image_policy = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
@ -661,7 +661,19 @@ 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", arg_root_options, /* rw= */ true, /* require= */ false, /* measure= */ true, NULL);
const char *bdev = "/dev/gpt-auto-root-luks";
if (arg_auto_root == GPT_AUTO_ROOT_FORCE) {
/* Similar logic as in add_root_mount(), see below */
FactoryResetMode f = factory_reset_mode();
if (f < 0)
log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
bdev = "/dev/gpt-auto-root-luks-ignore-factory-reset";
}
return add_cryptsetup("root", bdev, arg_root_options, /* rw= */ true, /* require= */ false, /* measure= */ true, NULL);
#else
return 0;
#endif
@ -674,11 +686,11 @@ static int add_root_mount(void) {
int r;
/* Explicitly disabled? Then exit immediately */
if (arg_root_enabled == 0)
if (arg_auto_root == GPT_AUTO_ROOT_OFF)
return 0;
/* Neither explicitly enabled nor disabled? Then decide based on the EFI partition variables to be set */
if (arg_root_enabled < 0) {
if (arg_auto_root < 0) {
if (!is_efi_boot()) {
log_debug("Not an EFI boot, not creating root mount.");
return 0;
@ -695,10 +707,27 @@ static int add_root_mount(void) {
}
/* OK, we shall look for a root device, so let's wait for a root device to show up. A udev rule will
* create the link for us under the right name. */
* create the link for us under the right name.
*
* There are two distinct names: the /dev/gpt-auto-root-ignore-factory-reset symlink is created for
* the root partition if factory reset mode is enabled or complete, and the /dev/gpt-auto-root
* symlink is only created if factory reset mode is off or already complete (thus taking factory
* reset state into account). In scenarios where the root disk is partially reformatted during
* factory reset the latter is the link to use, otherwise the former (so that we don't accidentally
* mount a root partition too early that is about to be wiped and replaced by another one). */
const char *bdev = "/dev/gpt-auto-root";
if (arg_auto_root == GPT_AUTO_ROOT_FORCE) {
FactoryResetMode f = factory_reset_mode();
if (f < 0)
log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
bdev = "/dev/gpt-auto-root-ignore-factory-reset";
}
if (in_initrd()) {
r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
r = generator_write_initrd_root_device_deps(arg_dest, bdev);
if (r < 0)
return 0;
@ -727,7 +756,7 @@ static int add_root_mount(void) {
return add_mount(
"root",
"/dev/gpt-auto-root",
bdev,
in_initrd() ? "/sysroot" : "/",
arg_root_fstype,
/* rw= */ arg_root_rw > 0,
@ -909,16 +938,11 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
/* Disable root disk logic if there's a root= value
* specified (unless it happens to be "gpt-auto") */
/* Disable root disk logic if there's a root= value specified (unless it happens to be
* "gpt-auto" or "gpt-auto-force") */
if (streq(value, "gpt-auto")) {
arg_root_enabled = true;
log_debug("Enabling root partition auto-detection, root= is explicitly set to 'gpt_auto'.");
} else {
arg_root_enabled = false;
log_debug("Disabling root partition auto-detection, root= is neither unset, nor set to 'gpt-auto'.");
}
arg_auto_root = parse_gpt_auto_root(value);
assert(arg_auto_root >= 0);
} else if (streq(key, "roothash")) {
@ -927,7 +951,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
/* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
arg_root_enabled = false;
arg_auto_root = GPT_AUTO_ROOT_OFF;
log_debug("Disabling root partition auto-detection, roothash= is set.");
} else if (streq(key, "rootfstype")) {

View File

@ -33,6 +33,7 @@
#include "dirent-util.h"
#include "efivars.h"
#include "errno-util.h"
#include "factory-reset.h"
#include "fd-util.h"
#include "fdisk-util.h"
#include "fileio.h"
@ -8712,23 +8713,20 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY *
}
static int parse_proc_cmdline_factory_reset(void) {
bool b;
int r;
if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
return 0;
if (!in_initrd()) /* Never honour kernel command line factory reset request outside of the initrd */
return 0;
r = proc_cmdline_get_bool("systemd.factory_reset", /* flags = */ 0, &b);
if (r < 0)
return log_error_errno(r, "Failed to parse systemd.factory_reset kernel command line argument: %m");
if (r > 0) {
arg_factory_reset = b;
FactoryResetMode f = factory_reset_mode();
if (f < 0)
return log_error_errno(f, "Failed to determine factory reset status: %m");
if (f != FACTORY_RESET_UNSPECIFIED) {
arg_factory_reset = f == FACTORY_RESET_ON;
if (b)
log_notice("Honouring factory reset requested via kernel command line.");
if (arg_factory_reset)
log_notice("Honouring factory reset requested via kernel command line or EFI variable.");
}
return 0;
@ -8738,6 +8736,10 @@ static int parse_efi_variable_factory_reset(void) {
_cleanup_free_ char *value = NULL;
int r;
/* NB: This is legacy, people should move to the newer FactoryResetRequest variable! */
// FIXME: Remove this in v260
if (arg_factory_reset >= 0) /* Never override what is specified on the process command line */
return 0;
@ -8751,6 +8753,8 @@ static int parse_efi_variable_factory_reset(void) {
return log_error_errno(r, "Failed to read EFI variable FactoryReset: %m");
}
log_warning("Warning, EFI variable FactoryReset is in use, please migrate to use FactoryResetRequest instead, support will be removed in v260!");
r = parse_boolean(value);
if (r < 0)
return log_error_errno(r, "Failed to parse EFI variable FactoryReset: %m");
@ -8765,6 +8769,8 @@ static int parse_efi_variable_factory_reset(void) {
static int remove_efi_variable_factory_reset(void) {
int r;
// FIXME: Remove this in v260, see above
r = efi_set_variable(EFI_SYSTEMD_VARIABLE_STR("FactoryReset"), NULL, 0);
if (r < 0) {
if (r == -ENOENT || ERRNO_IS_NOT_SUPPORTED(r))

125
src/shared/factory-reset.c Normal file
View File

@ -0,0 +1,125 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-json.h"
#include "efivars.h"
#include "env-util.h"
#include "factory-reset.h"
#include "os-util.h"
#include "proc-cmdline.h"
#include "string-table.h"
static bool factory_reset_supported(void) {
int r;
r = secure_getenv_bool("SYSTEMD_FACTORY_RESET_SUPPORTED");
if (r >= 0)
return r;
if (r != -ENXIO)
log_debug_errno(r, "Unable to parse $SYSTEMD_FACTORY_RESET_SUPPORTED, ignoring: %m");
return true;
}
static FactoryResetMode factory_reset_mode_efi_variable(void) {
int r;
if (!is_efi_boot()) {
log_debug("Not booted in EFI mode, not checking FactoryResetRequest variable.");
return FACTORY_RESET_UNSPECIFIED;
}
_cleanup_free_ char *req_str = NULL;
r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE_STR("FactoryResetRequest"), &req_str);
if (r == -ENOENT) {
log_debug_errno(r, "EFI variable FactoryResetRequest is not set, skipping.");
return FACTORY_RESET_UNSPECIFIED;
}
if (r < 0)
return log_debug_errno(r, "Failed to get EFI variable FactoryResetRequest: %m");
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
r = sd_json_parse(req_str, /* flags= */ 0, &v, /* ret_line= */ NULL, /* ret_column= */ NULL);
if (r < 0) {
log_debug_errno(r, "EFI variable FactoryResetRequest set to invalid JSON, ignoring: %m");
return FACTORY_RESET_UNSPECIFIED;
}
struct {
const char *id;
const char *image_id;
sd_id128_t boot_id;
} req = {};
static const sd_json_dispatch_field dispatch_table[] = {
{ "osReleaseId", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(req, id), SD_JSON_MANDATORY },
{ "osReleaseImageId", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(req, image_id), 0 },
{ "bootId", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, voffsetof(req, boot_id), SD_JSON_MANDATORY },
{},
};
r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &req);
if (r < 0) {
log_debug_errno(r, "Unable to dispatch EFI variable FactoryResetRequest, ignoring: %m");
return FACTORY_RESET_UNSPECIFIED;
}
_cleanup_free_ char *id = NULL, *image_id = NULL;
r = parse_os_release(
/* root= */ NULL,
"ID", &id,
"IMAGE_ID", &image_id);
if (r < 0)
return log_debug_errno(r, "Failed to parse os-release: %m");
if (!streq_ptr(req.id, id) || !streq_ptr(req.image_id, image_id)) {
log_debug("FactoryResetRequest EFI variable set, but not for us, ignoring.");
return FACTORY_RESET_UNSPECIFIED;
}
sd_id128_t boot_id;
r = sd_id128_get_boot(&boot_id);
if (r < 0)
return log_debug_errno(r, "Failed to query boot ID: %m");
if (sd_id128_equal(req.boot_id, boot_id)) {
/* NB: if the boot ID in the EFI variable matches our *current* one, then the request is not
* intended for us, but for the *next* boot. */
log_debug("EFI variable FactoryResetRequest set for next boot.");
return FACTORY_RESET_PENDING;
}
return FACTORY_RESET_ON;
}
FactoryResetMode factory_reset_mode(void) {
int r;
if (!factory_reset_supported())
return FACTORY_RESET_UNSUPPORTED;
/* First check if we already completed a factory reset in this boot */
if (access("/run/systemd/factory-reset-complete", F_OK) >= 0)
return FACTORY_RESET_COMPLETE;
if (errno != ENOENT)
return log_debug_errno(errno, "Can't determine if /run/systemd/factory-reset-complete exists: %m");
bool b;
r = proc_cmdline_get_bool("systemd.factory_reset", /* flags= */ 0, &b);
if (r < 0)
return log_debug_errno(r, "Failed to parse systemd.factory_reset kernel command line argument: %m");
if (r == 0) /* Check EFI variable in case kernel cmdline switch is not specified */
return factory_reset_mode_efi_variable();
return b ? FACTORY_RESET_ON : FACTORY_RESET_OFF; /* Honour if explicitly turned off or on via kernel cmdline */
}
static const char* const factory_reset_mode_table[_FACTORY_RESET_MODE_MAX] = {
[FACTORY_RESET_UNSUPPORTED] = "unsupported",
[FACTORY_RESET_UNSPECIFIED] = "unspecified",
[FACTORY_RESET_OFF] = "off",
[FACTORY_RESET_ON] = "on",
[FACTORY_RESET_COMPLETE] = "complete",
[FACTORY_RESET_PENDING] = "pending",
};
DEFINE_STRING_TABLE_LOOKUP(factory_reset_mode, FactoryResetMode);

View File

@ -0,0 +1,24 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <errno.h>
#include "errno-list.h"
#include "macro.h"
typedef enum FactoryResetMode {
FACTORY_RESET_UNSUPPORTED, /* feature not available on this OS */
FACTORY_RESET_UNSPECIFIED, /* not specified on the kernel cmdline, nor via EFI variable */
FACTORY_RESET_OFF, /* explicitly turned off on kernel cmdline */
FACTORY_RESET_ON, /* turned on via kernel cmdline or EFI variable */
FACTORY_RESET_COMPLETE, /* turned on via kernel cmdline or EFI variable, but marked as complete now */
FACTORY_RESET_PENDING, /* marked for next boot via EFI variable, but not in effect on this boot */
_FACTORY_RESET_MODE_MAX,
_FACTORY_RESET_MODE_INVALID = -EINVAL,
_FACTORY_RESET_MODE_ERRNO_MAX = -ERRNO_MAX,
} FactoryResetMode;
FactoryResetMode factory_reset_mode(void);
const char* factory_reset_mode_to_string(FactoryResetMode) _const_;
FactoryResetMode factory_reset_mode_from_string(const char *s) _pure_;

View File

@ -1030,3 +1030,22 @@ bool generator_soft_rebooted(void) {
return (cached = (u > 0));
}
GptAutoRoot parse_gpt_auto_root(const char *value) {
assert(value);
/* Parses the 'gpt-auto'/'gpt-auto-root' parameters to root= */
if (streq(value, "gpt-auto")) {
log_debug("Enabling root partition auto-detection (respecting factory reset mode), root= is explicitly set to 'gpt-auto'.");
return GPT_AUTO_ROOT_ON;
}
if (streq(value, "gpt-auto-force")) {
log_debug("Enabling root partition auto-detection (ignoring factory reset mode), root= is explicitly set to 'gpt-auto-force'.");
return GPT_AUTO_ROOT_FORCE;
}
log_debug("Disabling root partition auto-detection, root= is neither unset, nor set to 'gpt-auto' or 'gpt-auto-force'.");
return GPT_AUTO_ROOT_OFF;
}

View File

@ -3,6 +3,7 @@
#include <stdio.h>
#include "errno-list.h"
#include "macro.h"
#include "main-func.h"
@ -114,3 +115,13 @@ bool generator_soft_rebooted(void);
argv[argc == 4 ? 3 : 1]), \
exit_failure_if_negative, \
exit_failure_if_negative)
typedef enum GptAutoRoot {
GPT_AUTO_ROOT_OFF = 0, /* root= set to something else */
GPT_AUTO_ROOT_ON, /* root= set explicitly to "gpt-auto" */
GPT_AUTO_ROOT_FORCE, /* root= set explicitly to "gpt-auto-force" → ignores factory reset mode */
_GPT_AUTO_ROOT_MAX,
_GPT_AUTO_ROOT_INVALID = -EINVAL,
} GptAutoRoot;
GptAutoRoot parse_gpt_auto_root(const char *value);

View File

@ -70,6 +70,7 @@ shared_sources = files(
'exec-util.c',
'exit-status.c',
'extension-util.c',
'factory-reset.c',
'fdset.c',
'fido2-util.c',
'find-esp.c',
@ -183,6 +184,7 @@ shared_sources = files(
'varlink-io.systemd.AskPassword.c',
'varlink-io.systemd.BootControl.c',
'varlink-io.systemd.Credentials.c',
'varlink-io.systemd.FactoryReset.c',
'varlink-io.systemd.Hostname.c',
'varlink-io.systemd.Import.c',
'varlink-io.systemd.Journal.c',

View File

@ -0,0 +1,39 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "varlink-io.systemd.FactoryReset.h"
#include "sd-varlink-idl.h"
static SD_VARLINK_DEFINE_ENUM_TYPE(
FactoryResetMode,
SD_VARLINK_FIELD_COMMENT("Factory reset is not supported on this OS."),
SD_VARLINK_DEFINE_ENUM_VALUE(unsupported),
SD_VARLINK_FIELD_COMMENT("Factory reset not requested."),
SD_VARLINK_DEFINE_ENUM_VALUE(unspecified),
SD_VARLINK_FIELD_COMMENT("Factory reset explicitly turned off."),
SD_VARLINK_DEFINE_ENUM_VALUE(off),
SD_VARLINK_FIELD_COMMENT("Factory reset is currently being executed."),
SD_VARLINK_DEFINE_ENUM_VALUE(on),
SD_VARLINK_FIELD_COMMENT("Factory reset has been completed during the current boot."),
SD_VARLINK_DEFINE_ENUM_VALUE(complete),
SD_VARLINK_FIELD_COMMENT("Factory reset has been requested for the next boot."),
SD_VARLINK_DEFINE_ENUM_VALUE(pending));
static SD_VARLINK_DEFINE_METHOD(
GetFactoryResetMode,
SD_VARLINK_FIELD_COMMENT("The current factory reset mode"),
SD_VARLINK_DEFINE_OUTPUT_BY_TYPE(mode, FactoryResetMode, 0));
static SD_VARLINK_DEFINE_METHOD(
CanRequestFactoryReset,
SD_VARLINK_DEFINE_OUTPUT(supported, SD_VARLINK_BOOL, 0));
SD_VARLINK_DEFINE_INTERFACE(
io_systemd_FactoryReset,
"io.systemd.FactoryReset",
SD_VARLINK_INTERFACE_COMMENT("APIs to query factory reset status"),
SD_VARLINK_SYMBOL_COMMENT("Encodes the current factory reset status"),
&vl_type_FactoryResetMode,
SD_VARLINK_SYMBOL_COMMENT("Report the current factory reset status"),
&vl_method_GetFactoryResetMode,
SD_VARLINK_SYMBOL_COMMENT("Returns whether requesting a factory reset is available (by invoking the factory-reset.target unit)."),
&vl_method_CanRequestFactoryReset);

View File

@ -0,0 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-varlink-idl.h"
extern const sd_varlink_interface vl_interface_io_systemd_FactoryReset;

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

@ -10,9 +10,10 @@
#include "tests.h"
#include "varlink-idl-util.h"
#include "varlink-io.systemd.h"
#include "varlink-io.systemd.BootControl.h"
#include "varlink-io.systemd.AskPassword.h"
#include "varlink-io.systemd.BootControl.h"
#include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.FactoryReset.h"
#include "varlink-io.systemd.Import.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.Login.h"
@ -203,6 +204,8 @@ TEST(parse_format) {
print_separator();
test_parse_format_one(&vl_interface_io_systemd_Login);
print_separator();
test_parse_format_one(&vl_interface_io_systemd_FactoryReset);
print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}

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

@ -20,7 +20,9 @@ udevadm_sources = files(
libudevd_core_sources = files(
'net/link-config.c',
'udev-builtin.c',
'udev-builtin-btrfs.c',
'udev-builtin-factory_reset.c',
'udev-builtin-hwdb.c',
'udev-builtin-input_id.c',
'udev-builtin-keyboard.c',
@ -29,7 +31,6 @@ libudevd_core_sources = files(
'udev-builtin-net_setup_link.c',
'udev-builtin-path_id.c',
'udev-builtin-usb_id.c',
'udev-builtin.c',
'udev-config.c',
'udev-ctrl.c',
'udev-dump.c',

View File

@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "device-util.h"
#include "factory-reset.h"
#include "udev-builtin.h"
/* Sometimes it is relevant in udev rules to know whether factory reset is currently in effect or not. Report
* the current state at moment of probing as a udev property. This can be used to create certain device node
* symlinks only once factory reset is complete, or even mark whole devices as SYSTEMD_READY=0 as long as
* factory reset is still ongoing. */
static int builtin_factory_reset(UdevEvent *event, int argc, char *argv[]) {
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
if (argc != 2 || !streq(argv[1], "status"))
return log_device_warning_errno(
dev, SYNTHETIC_ERRNO(EINVAL), "%s: expected: status", argv[0]);
/* Report factory reset mode at the moment of probing a device. */
FactoryResetMode f = factory_reset_mode();
if (f < 0) {
log_device_debug_errno(dev, f, "Unable to detect factory reset mode, ignoring: %m");
return 0;
}
return udev_builtin_add_property(event, "ID_FACTORY_RESET", factory_reset_mode_to_string(f));
}
const UdevBuiltin udev_builtin_factory_reset = {
.name = "factory_reset",
.cmd = builtin_factory_reset,
.help = "Factory Reset Mode",
.run_once = true,
};

View File

@ -15,6 +15,7 @@ static const UdevBuiltin *const builtins[_UDEV_BUILTIN_MAX] = {
[UDEV_BUILTIN_BLKID] = &udev_builtin_blkid,
#endif
[UDEV_BUILTIN_BTRFS] = &udev_builtin_btrfs,
[UDEV_BUILTIN_FACTORY_RESET] = &udev_builtin_factory_reset,
[UDEV_BUILTIN_HWDB] = &udev_builtin_hwdb,
[UDEV_BUILTIN_INPUT_ID] = &udev_builtin_input_id,
[UDEV_BUILTIN_KEYBOARD] = &udev_builtin_keyboard,

View File

@ -37,6 +37,7 @@ typedef struct UdevBuiltin {
extern const UdevBuiltin udev_builtin_blkid;
#endif
extern const UdevBuiltin udev_builtin_btrfs;
extern const UdevBuiltin udev_builtin_factory_reset;
extern const UdevBuiltin udev_builtin_hwdb;
extern const UdevBuiltin udev_builtin_input_id;
extern const UdevBuiltin udev_builtin_keyboard;

View File

@ -40,6 +40,7 @@ typedef enum UdevBuiltinCommand {
UDEV_BUILTIN_BLKID,
#endif
UDEV_BUILTIN_BTRFS,
UDEV_BUILTIN_FACTORY_RESET,
UDEV_BUILTIN_HWDB,
UDEV_BUILTIN_INPUT_ID,
UDEV_BUILTIN_KEYBOARD,

View File

@ -0,0 +1,13 @@
# 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=Factory Reset Execution
Documentation=man:systemd.special(7)
Wants=systemd-factory-reset-complete.service

View File

@ -8,5 +8,7 @@
# (at your option) any later version.
[Unit]
Description=Factory Reset
Description=Factory Reset Initiation
Documentation=man:systemd.special(7)
Wants=systemd-factory-reset-reboot.service
Before=systemd-factory-reset-reboot.service

View File

@ -37,6 +37,7 @@ units = [
{ 'file' : 'emergency.target' },
{ 'file' : 'exit.target' },
{ 'file' : 'factory-reset.target' },
{ 'file' : 'factory-reset-now.target' },
{ 'file' : 'final.target' },
{ 'file' : 'first-boot-complete.target' },
{ 'file' : 'getty-pre.target' },
@ -322,6 +323,19 @@ units = [
},
{ 'file' : 'systemd-creds@.service' },
{ 'file' : 'systemd-exit.service' },
{
'file' : 'systemd-factory-reset@.service.in',
},
{
'file' : 'systemd-factory-reset.socket',
'symlinks' : ['sockets.target.wants/'],
},
{ 'file' : 'systemd-factory-reset-complete.service.in' },
{ 'file' : 'systemd-factory-reset-reboot.service' },
{
'file' : 'systemd-factory-reset-request.service.in',
'symlinks' : ['factory-reset.target.wants/'],
},
{
'file' : 'systemd-firstboot.service',
'conditions' : ['ENABLE_FIRSTBOOT'],
@ -557,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,22 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Mark the Factory Reset as Completed
Documentation=man:systemd-factory-reset-complete.service(8)
DefaultDependencies=no
Requires=factory-reset-now.target
After=factory-reset-now.target
Conflicts=shutdown.target
Before=shutdown.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-factory-reset complete --retrigger

View File

@ -0,0 +1,17 @@
# 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=Reboot to Execute Factory Reset
Documentation=man:systemd.special(7)
DefaultDependencies=no
After=factory-reset.target
Conflicts=shutdown.target
Before=shutdown.target
SuccessAction=reboot

View File

@ -0,0 +1,22 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Request Factory Reset on Next Boot
Documentation=man:systemd-factory-reset-request.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-pcrphase-factory-reset.service
Before=factory-reset.target shutdown.target
ConditionFirmware=uefi
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart={{LIBEXECDIR}}/systemd-factory-reset request

View File

@ -0,0 +1,24 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
[Unit]
Description=Factory Reset Management
Documentation=man:systemd-factory-reset.service(8)
DefaultDependencies=no
Before=sockets.target
[Socket]
ListenStream=/run/systemd/io.systemd.FactoryReset
FileDescriptorName=varlink
SocketMode=0666
Accept=yes
MaxConnectionsPerSource=16
[Install]
WantedBy=sockets.target

View File

@ -0,0 +1,18 @@
# 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=Factory Reset Management (Varlink)
Documentation=man:systemd-factory-reset@.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
Before=shutdown.target
[Service]
ExecStart=-{{LIBEXECDIR}}/systemd-factory-reset

View File

@ -22,7 +22,7 @@ ConditionDirectoryNotEmpty=|/sysusr/usr/local/lib/repart.d
DefaultDependencies=no
Wants=modprobe@loop.service modprobe@dm_mod.service
After=initrd-usr-fs.target modprobe@loop.service modprobe@dm_mod.service systemd-tpm2-setup-early.service
Before=initrd-root-fs.target
Before=initrd-root-fs.target factory-reset-now.target
Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target

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

View File

@ -12,5 +12,5 @@ Description=Trusted Platform Module
Documentation=man:systemd.special(7)
# Make this a synchronization point on the first TPM device found
After=dev-tpmrm0.device
Wants=dev-tpmrm0.device
After=dev-tpmrm0.device dev-tpm0.device
Wants=dev-tpmrm0.device dev-tpm0.device