]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #28252 from yuwata/journal-open-machine
authorMike Yuan <me@yhndnzj.com>
Wed, 5 Jul 2023 19:08:18 +0000 (03:08 +0800)
committerGitHub <noreply@github.com>
Wed, 5 Jul 2023 19:08:18 +0000 (03:08 +0800)
journal: introduce journal_open_machine()

57 files changed:
TODO
docs/CREDENTIALS.md
hwdb.d/60-keyboard.hwdb
hwdb.d/60-sensor.hwdb
man/directives-template.xml
man/kernel-command-line.xml
man/systemctl.xml
man/systemd-firstboot.xml
man/systemd-fstab-generator.xml
man/systemd-getty-generator.xml
man/systemd-resolved.service.xml
man/systemd-sysctl.service.xml
man/systemd-sysusers.xml
man/systemd-tmpfiles.xml
man/systemd-vconsole-setup.service.xml
man/systemd.exec.xml
man/systemd.generator.xml
man/systemd.system-credentials.xml
man/systemd.xml
src/boot/bless-boot-generator.c
src/core/execute.c
src/core/import-creds.c
src/core/main.c
src/core/transaction.c
src/core/transaction.h
src/core/unit-dependency-atom.c
src/core/unit-dependency-atom.h
src/firstboot/firstboot.c
src/fstab-generator/fstab-generator.c
src/getty-generator/getty-generator.c
src/partition/repart.c
src/rc-local-generator/rc-local-generator.c
src/shared/acl-util.c
src/shared/acl-util.h
src/shared/creds-util.c
src/shared/creds-util.h
src/shared/machine-id-setup.c
src/shared/meson.build
src/shared/mount-util.c
src/shared/mount-util.h
src/system-update-generator/system-update-generator.c
src/sysv-generator/sysv-generator.c
src/test/test-acl-util.c
test/TEST-54-CREDS/test.sh
test/test-functions
test/testsuite-03.units/propagatestopto-and-pullin.target [new file with mode: 0644]
test/testsuite-03.units/propagatestopto-only.target [new file with mode: 0644]
test/testsuite-03.units/sleep-infinity-simple.service [new file with mode: 0644]
test/units/testsuite-03.sh
test/units/testsuite-54.sh
test/units/testsuite-58.sh
units/systemd-backlight@.service.in
units/systemd-boot-update.service
units/systemd-rfkill.service.in
units/systemd-rfkill.socket
units/systemd-update-utmp-runlevel.service.in
units/systemd-update-utmp.service.in

diff --git a/TODO b/TODO
index a3bf2c894b45eb6bbc724782907ed4633213c557..307f25e618ab31cf4072219453ec99fd7e6bc4bf 100644 (file)
--- a/TODO
+++ b/TODO
@@ -131,12 +131,6 @@ Deprecations and removals:
 
 Features:
 
-* use kernel 6.3's "noswap" parameter in tmpfs in place of ramfs for storing
-  credentials.
-
-* import-creds: allocate a non-swap-backed fs for /run/credentials/@system,
-  like we do for services.
-
 * new "systemd-pcrlock" component for dealing with PCR4. Design idea:
   1. define /{etc,usr,var/lib}/pcrlock.d/<component>/<version>.pcrlock
   2. these files contain list of hashes that will be measured when component is
@@ -225,12 +219,10 @@ Features:
   support .microcode in PE add-ons, so that a microcode update can be shipped
   independently of any kernel.
 
-* add clean mechanism concept for passing env/creds from initrd to host on
-  switch root, so that cloud-init and similar have a clean, sane method to pass
-  along the stuff they picked up, without patching any dirs. Maybe add
-  SwitchRootEx() as new bus call that takes these as argument. When adding
-  SwitchRootEx() we should maybe also add a flags param that allows disabling
-  and enabling whether serialization is requested during switch root.
+* Maybe add SwitchRootEx() as new bus call that takes env vars to set for new
+  PID 1 as argument. When adding SwitchRootEx() we should maybe also add a
+  flags param that allows disabling and enabling whether serialization is
+  requested during switch root.
 
 * introduce a .acpitable section for early ACPI table override
 
@@ -249,10 +241,6 @@ Features:
   scenarios. Maybe insist sealing is done additionally against some keypair in
   the TPM to which access is updated on each boot, for the next, or so?
 
-* open up creds for uses in generators, and document clearly that encrypted
-  creds are only supported if strictly tpm bound, but not when using the host
-  secret (as that is only available if /var/ is around.
-
 * logind: when logging in, always take an fd to the home dir, to keep the dir
   busy, so that autofs release can never happen. (this is generally a good
   idea, and specifically works around the fact the autofs ignores busy by mount
@@ -819,10 +807,9 @@ Features:
 * Process credentials in:
   • networkd/udevd: add a way to define additional .link, .network, .netdev files
     via the credentials logic.
-  • fstab-generator: allow defining additional fstab-like mounts via
-    credentials (similar: crypttab-generator, verity-generator,
-    integrity-generator)
-  • getty-generator: allow defining additional getty instances via a credential
+  • crypttab-generator: allow defining additional crypttab-like volumes via
+    credentials (similar: verity-generator, integrity-generator). Use
+    fstab-generator logic as inspiration.
   • run-generator: allow defining additional commands to run via a credential
   • resolved: allow defining additional /etc/hosts entries via a credential (it
     might make sense to then synthesize a new combined /etc/hosts file in /run
@@ -837,9 +824,6 @@ Features:
     systemd.homed.register or so with JSON user records to automatically
     register if not registered yet.  Usecase: deploy a system, and add an
     account one can directly log into.
-  • initialize machine ID from systemd credential picked up from the ESP via
-    sd-stub, so that machine ID is stable even on systems where unified kernels
-    are used, and hence kernel cmdline cannot be modified locally
   • in gpt-auto-generator: check partition uuids against such uuids supplied via
     sd-stub credentials. That way, we can support parallel OS installations with
     pre-built kernels.
@@ -948,11 +932,6 @@ Features:
   https://0pointer.net/blog/testing-my-system-code-in-usr-without-modifying-usr.html
   https://0pointer.net/blog/running-an-container-off-the-host-usr.html
 
-* add a clear concept how the initrd can make up credentials on their own to
-  pass to the system when transitioning into the host OS. usecase: things like
-  cloud-init/ignitation and similar can parameterize the host with data they
-  acquire.
-
 * sd-event: compat wd reuse in inotify code: keep a set of removed watch
   descriptors, and clear this set piecemeal when we see the IN_IGNORED event
   for it, or when read() returns EAGAIN or on IN_Q_OVERFLOW. Then, whenever we
@@ -969,7 +948,6 @@ Features:
   - kernel-install should be able to pick up initrd sysexts automatically and
     place them next to EFI kernel, for sd-stub to pick them up.
   - systemd-fstab-generator should look for rootfs device to mount in creds
-  - pid 1 should look for machine ID in creds
   - systemd-resume-generator should look for resume partition uuid in creds
   - sd-stub: automatically pick up microcode from ESP (/loader/microcode/*)
     and synthesize initrd from it, and measure it. Signing is not necessary, as
index 50100cc5b6193b22062bdcf3de392dd7a493974c..9d06c45f1f0576aeadcd849d1a49c734fd13dd92 100644 (file)
@@ -51,9 +51,9 @@ purpose. Specifically, the following features are provided:
    allow it, via `ramfs`.)
 
 7. Credentials may be acquired from a hosting VM hypervisor (SMBIOS OEM strings
-   or qemu `fw_cfg`), a hosting container manager, the kernel command line, or
-   from the UEFI environment and the EFI System Partition (via
-   `systemd-stub`). Such system credentials may then be propagated into
+   or qemu `fw_cfg`), a hosting container manager, the kernel command line,
+   from the initrd, or from the UEFI environment via the EFI System Partition
+   (via `systemd-stub`). Such system credentials may then be propagated into
    individual services as needed.
 
 8. Credentials are an effective way to pass parameters into services that run
@@ -72,19 +72,19 @@ Within unit files, there are four settings to configure service credentials.
 1. `LoadCredential=` may be used to load a credential from disk, from an
    `AF_UNIX` socket, or propagate them from a system credential.
 
-2. `ImportCredential=` may be used to load one or more (encrypted) credentials
-   from disk or from the credential stores.
+2. `ImportCredential=` may be used to load one or more (optionally encrypted)
+   credentials from disk or from the credential stores.
 
-2. `SetCredential=` may be used to set a credential to a literal string encoded
+3. `SetCredential=` may be used to set a credential to a literal string encoded
    in the unit file. Because unit files are world-readable (both on disk and
    via D-Bus), this should only be used for credentials that aren't sensitive,
    e.g. public keys or certificates, but not private keys.
 
-3. `LoadCredentialEncrypted=` is similar to `LoadCredential=` but will load an
+4. `LoadCredentialEncrypted=` is similar to `LoadCredential=` but will load an
    encrypted credential, and decrypt it before passing it to the service. For
    details on credential encryption, see below.
 
-4. `SetCredentialEncrypted=` is similar to `SetCredential=` but expects an
+5. `SetCredentialEncrypted=` is similar to `SetCredential=` but expects an
    encrypted credential to be specified literally. Unlike `SetCredential=` it
    is thus safe to be used even for sensitive information, because even though
    unit files are world readable, the ciphertext included in them cannot be
@@ -153,6 +153,33 @@ credentials directory. For daemons that allow passing credentials via a path
 supplied as environment variable, use the `%d` specifier in the `Environment=`
 setting to build valid paths to specific credentials.
 
+Encrypted credentials are automatically decrypted/authenticated during service
+activation, so that service code only receives plaintext credentials.
+
+## Programming Interface from Generator Code
+
+[Generators](https://www.freedesktop.org/software/systemd/man/systemd.generator.html)
+may generate native unit files from external configuration or system
+parameters, such as system credentials. Note that they run outside of service
+context, and hence will not receive encrypted credentials in plaintext
+form. Specifically, credentials passed into the system in encrypted form will
+be placed as they are in a directory referenced by the
+`$ENCRYPTED_CREDENTIALS_DIRECTORY` environment variable, and those passed in
+plaintext form will be placed in `$CREDENTIALS_DIRECTORY`. Use a command such
+as `systemd-creds --system cat …` to access both forms of credentials, and
+decrypt them if needed (see
+[systemd-creds(1)](https://www.freedesktop.org/software/systemd/man/systemd-creds.html)
+for details.
+
+Note that generators typically run very early during boot (similar to initrd
+code), earlier than the `/var/` file system is necessarily mounted (which is
+where the system's credential encryption secret is located). Thus it's a good
+idea to encrypt credentials with `systemd-creds encrypt --with-key=auto-initrd`
+if they shall be consumed by a generator, to ensure they are locked to the TPM2
+only, not the credentials secret stored below `/var/`.
+
+For further details about encrypted credentials, see below.
+
 ## Tools
 
 The
@@ -194,6 +221,12 @@ cannot be decrypted without access to the TPM2 chip and the aforementioned key
 file `/var/lib/systemd/credential.secret`. Moreover, credentials cannot be
 prepared on a machine other than the local one.
 
+Decryption generally takes place at the moment of service activation. This
+means credentials passed to the system can be either encrypted or plaintext and
+remain that way all the way while they are propagated to their consumers, until
+the moment of service activation when they are decrypted and authenticated, so
+that the service only sees plaintext credentials.
+
 The `systemd-creds` tool provides the commands `encrypt` and `decrypt` to
 encrypt and decrypt/authenticate credentials. Example:
 
@@ -247,7 +280,7 @@ via `systemd` credentials. In particular, it might make sense to boot a
 system with a set of credentials that are then propagated to individual
 services where they are ultimately consumed.
 
-`systemd` supports four ways to pass credentials to systems:
+`systemd` supports five ways to pass credentials to systems:
 
 1. A container manager may set the `$CREDENTIALS_DIRECTORY` environment
    variable for systemd running as PID 1 in the container, the same way as
@@ -255,8 +288,7 @@ services where they are ultimately consumed.
    invokes. [`systemd-nspawn(1)`](https://www.freedesktop.org/software/systemd/man/systemd-nspawn.html#Credentials)'s
    `--set-credential=` and `--load-credential=` switches implement this, in
    order to pass arbitrary credentials from host to container payload. Also see
-   the [Container Interface](CONTAINER_INTERFACE.md)
-   documentation.
+   the [Container Interface](CONTAINER_INTERFACE.md) documentation.
 
 2. Quite similar, VMs can be passed credentials via SMBIOS OEM strings (example
    qemu command line switch `-smbios
@@ -269,16 +301,17 @@ services where they are ultimately consumed.
    three of these specific switches would set credential `foo` to `bar`.)
    Passing credentials via the SMBIOS mechanism is typically preferable over
    `fw_cfg` since it is faster and less specific to the chosen VMM
-   implementation. Moreover, `fw_cfg` has a 55 character limitation
-   on names passed that way. So some settings may not fit.
+   implementation. Moreover, `fw_cfg` has a 55 character limitation on names
+   passed that way. So some settings may not fit.
 
-3. Credentials can also be passed into a system via the kernel command line,
-   via the `systemd.set-credential=` kernel command line option. Note though
-   that any data specified here is visible to any userspace application via
-   `/proc/cmdline`. This is hence typically not useful to pass sensitive
-   information.
+3. Credentials may be passed from the initrd to the host during the initrd →
+   host transition. Provisioning systems that run in the initrd may use this to
+   install credentials on the system. All files placed in
+   `/run/credentials/@initrd/` are imported into the set of file system
+   credentials during the transition. The files (and their directory) are
+   removed once this is completed.
 
-4. Credentials may also be passed from the UEFI environment to userspace, if
+5. Credentials may also be passed from the UEFI environment to userspace, if
    the
    [`systemd-stub`](https://www.freedesktop.org/software/systemd/man/systemd-stub.html)
    UEFI kernel stub is used. This allows placing encrypted credentials in the
@@ -288,6 +321,13 @@ services where they are ultimately consumed.
    initrds, as userspace can place credentials next to these EFI kernels, and
    be sure they can be accessed securely from initrd context.
 
+4. Credentials can also be passed into a system via the kernel command line,
+   via the `systemd.set_credential=` and `systemd.set_credential_binary=`
+   kernel command line options (the latter takes Base64 encoded binary
+   data). Note though that any data specified here is visible to all userspace
+   applications (even unprivileged ones) via `/proc/cmdline`. Typically, this
+   is hence not useful to pass sensitive information, and should be avoided.
+
 Credentials passed to the system may be enumerated/displayed via `systemd-creds
 --system`. They may also be propagated down to services, via the
 `LoadCredential=` setting. Example:
@@ -359,6 +399,9 @@ Various services shipped with `systemd` consume credentials for tweaking behavio
   will look for the credentials `tmpfiles.extra` with arbitrary tmpfiles.d lines.
   Can be encoded in base64 to allow easily passing it on the command line.
 
+* Further well-known credentials are documented in
+  [`systemd.system-credentials(7)`](https://www.freedesktop.org/software/systemd/man/systemd.system-credentials.html).
+
 In future more services are likely to gain support for consuming credentials.
 
 Example:
@@ -427,6 +470,11 @@ READY=1
 From *service* perspective the runtime path to find loaded credentials in is
 provided in the `$CREDENTIALS_DIRECTORY` environment variable.
 
+From *generator* perspective the runtime path to find credentials passed into
+the system in plaintext form in is provided in `$CREDENTIALS_DIRECTORY`, and
+those passed into the system in encrypted form is provided in
+`$ENCRYPTED_CREDENTIALS_DIRECTORY`.
+
 At runtime, credentials passed to the *system* are placed in
 `/run/credentials/@system/` (for regular credentials, such as those passed from
 a container manager or via qemu) and `/run/credentials/@encrypted/` (for
@@ -434,13 +482,14 @@ credentials that must be decrypted/validated before use, such as those from
 `systemd-stub`).
 
 The `ImportCredential=` setting (and the `LoadCredential=` and
-`LoadCredentialEncrypted=` settings when configured with a relative source path)
-will search for the source file to read the credential from automatically. Primarily,
-these credentials are searched among the credentials passed into the system. If
-not found there, they are searched in `/etc/credstore/`, `/run/credstore/`,
+`LoadCredentialEncrypted=` settings when configured with a relative source
+path) will search for the source file to read the credential from
+automatically. Primarily, these credentials are searched among the credentials
+passed into the system. If not found there, they are searched in
+`/etc/credstore/`, `/run/credstore/`,
 `/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
-`/etc/credstore.encrypted/` and similar directories. `ImportCredential` will search
-both the non-encrypted and encrypted directories. These directories are
+`/etc/credstore.encrypted/` and similar directories. `ImportCredential=` will
+search both the non-encrypted and encrypted directories. These directories are
 hence a great place to store credentials to load on the system.
 
 ## Conditionalizing Services
index ce52a3d47dd0abd571f959dff345e6f67e8cb9a9..f3f40ef2ffb8b00f851f9e8a7fac4d8904ea8ce4 100644 (file)
@@ -1010,16 +1010,10 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*IdeaPad*U300s*:pvr*
  KEYBOARD_KEY_ce=f20                                    # micmute
 
 evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPad*Z370*:pvr*
- KEYBOARD_KEY_a0=!mute
- KEYBOARD_KEY_ae=!volumedown
- KEYBOARD_KEY_b0=!volumeup
-
-evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO:*:pvrIdeaPadFlex514IIL05:*
- KEYBOARD_KEY_a0=!mute
- KEYBOARD_KEY_ae=!volumedown
- KEYBOARD_KEY_b0=!volumeup
-
-evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO:*:pvrIdeaPadFlex514ARE05:*
+# Lenovo IdeaPad Flex 5
+evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO:*:pvrIdeaPadFlex5*
+# Lenovo Yoga S940 (9th gen)
+evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO:pn81Q7*:pvrLenovoYogaS940:*
  KEYBOARD_KEY_a0=!mute
  KEYBOARD_KEY_ae=!volumedown
  KEYBOARD_KEY_b0=!volumeup
@@ -1028,15 +1022,6 @@ evdev:atkbd:dmi:*:svnLENOVO:*:pvrLenovoYoga300-11IBR:*
  KEYBOARD_KEY_62=unknown  # Touchpad on, also emitted by "Ideapad extra buttons", ignore
  KEYBOARD_KEY_63=unknown  # Touchpad off, also emitted by "Ideapad extra buttons", ignore
 
-# Fix for volume keys on Lenovo Yoga S940
-# For 10th gen it should be pn81Q8 instead of pn81Q7 but
-# I don't have a device to test
-# perhaps pn81Q* would work for both generations
-evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO:pn81Q7*:pvrLenovoYogaS940:*
- KEYBOARD_KEY_a0=!mute
- KEYBOARD_KEY_ae=!volumedown
- KEYBOARD_KEY_b0=!volumeup
-
 # Lenovo Y50-70
 evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*20378*:pvr*
  KEYBOARD_KEY_f3=f21                                    # Fn+F6 (toggle touchpad)
index d330a05629defa30948daf368709a90a6f26caa7..cd4bee6ea05ea8c015b2222323897cf4ec18dc60 100644 (file)
@@ -127,7 +127,7 @@ sensor:modalias:acpi:SMO8500*:dmi:*:svnARCHOS:pnARCHOS80Cesium:*
  ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
 
 # Archos 101 Cesium Educ
-sensor:modalias:acpi:KIOX000A*:dmi:*svn*ARCHOS:*pn*ARCHOS101CesiumEduc*
+sensor:modalias:acpi:KIOX000A*:dmi:*:svnARCHOS:pnARCHOS101CesiumEduc:*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
 
 #########################################
@@ -389,9 +389,9 @@ sensor:modalias:acpi:INVN6500*:dmi:*svnDell*:pnVenue10Pro5055:*
 sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInternational,LLC.:bvrJP2V*:svnDefaultstring:*
  ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1
 
-# T11 / Juno Tablet 2 
+# T11 / Juno Tablet 2
 sensor:modalias:acpi:NSA2513*:dmi:*:svnDigitek:*
- ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1 
+ ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1
 
 #########################################
 # DEXP
index 114707d2d00017ce190ef55a4c59467870847bf1..0b6ee2192935248282e4160bc686a16d1882dde1 100644 (file)
     <variablelist id='environment-variables' />
   </refsect1>
 
+  <refsect1>
+    <title>System Credentials</title>
+
+    <para>System credentials understood by the system and service manager and various other
+    components:</para>
+
+    <variablelist id='system-credentials' />
+  </refsect1>
+
   <refsect1>
     <title>EFI variables</title>
 
index 93ed4f6acb53d13605d64f46306e99b7be73441d..43247fe5341517c4075666c007b3bf1852a45fe3 100644 (file)
@@ -72,6 +72,7 @@
         <term><varname>systemd.setenv=</varname></term>
         <term><varname>systemd.machine_id=</varname></term>
         <term><varname>systemd.set_credential=</varname></term>
+        <term><varname>systemd.set_credential_binary=</varname></term>
         <term><varname>systemd.import_credentials=</varname></term>
         <term><varname>systemd.reload_limit_interval_sec=</varname></term>
         <term><varname>systemd.reload_limit_burst=</varname></term>
index 2e2daa4526cb5402db269c8d3f2eae52fcf37371..773d5cf4adcdd24b0f4342381892c53b80aa8ad7 100644 (file)
@@ -1950,8 +1950,9 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <literal>isolate</literal>,
         <literal>ignore-dependencies</literal>,
         <literal>ignore-requirements</literal>,
-        <literal>flush</literal>, or
-        <literal>triggering</literal>. Defaults to
+        <literal>flush</literal>,
+        <literal>triggering</literal>, or
+        <literal>restart-dependencies</literal>. Defaults to
         <literal>replace</literal>, except when the
         <command>isolate</command> command is used which implies the
         <literal>isolate</literal> job mode.</para>
@@ -1994,7 +1995,6 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <literal>ignore-dependencies</literal>, but only causes the
         requirement dependencies to be ignored, the ordering
         dependencies will still be honored.</para>
-        </listitem>
 
         <para><literal>triggering</literal> may only be used with
         <command>systemctl stop</command>. In this mode, the specified
@@ -2003,6 +2003,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
         <varname>Triggers=</varname> in <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
         for more information about triggering units.</para>
 
+        <para><literal>restart-dependencies</literal> may only be used with
+        <command>systemctl start</command>. In this mode, dependencies of
+        the specified unit will receive restart propagation, as if a restart
+        job had been enqueued for the unit.</para>
+        </listitem>
       </varlistentry>
 
       <varlistentry>
index becb5f52ace9c51139ac6bed7daf078182ebc897..192b91c2964d2168690d567f84a3414a6ccafadd 100644 (file)
     (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
     details). The following credentials are used when passed in:</para>
 
-    <variablelist>
+    <variablelist class='system-credentials'>
       <varlistentry>
-        <term><literal>passwd.hashed-password.root</literal></term>
-        <term><literal>passwd.plaintext-password.root</literal></term>
+        <term><varname>passwd.hashed-password.root</varname></term>
+        <term><varname>passwd.plaintext-password.root</varname></term>
 
         <listitem><para>A hashed or plaintext version of the root password to use, in place of prompting the
         user. These credentials are equivalent to the same ones defined for the
       </varlistentry>
 
       <varlistentry>
-        <term><literal>passwd.shell.root</literal></term>
+        <term><varname>passwd.shell.root</varname></term>
 
         <listitem><para>Specifies the shell binary to use for the specified account.
         Equivalent to the credential of the same name defined for the
       </varlistentry>
 
       <varlistentry>
-        <term><literal>firstboot.locale</literal></term>
-        <term><literal>firstboot.locale-messages</literal></term>
+        <term><varname>firstboot.locale</varname></term>
+        <term><varname>firstboot.locale-messages</varname></term>
 
         <listitem><para>These credentials specify the locale settings to set during first boot, in place of
         prompting the user.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><literal>firstboot.keymap</literal></term>
+        <term><varname>firstboot.keymap</varname></term>
 
         <listitem><para>This credential specifies the keyboard setting to set during first boot, in place of
         prompting the user.</para>
 
-        <para>Note the relationship to the <literal>vconsole.keymap</literal> credential understood by
+        <para>Note the relationship to the <varname>vconsole.keymap</varname> credential understood by
         <citerefentry><refentrytitle>systemd-vconsole-setup.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>:
         both ultimately affect the same setting, but <varname>firstboot.keymap</varname> is written into
         <filename>/etc/vconsole.conf</filename> on first boot (if not already configured), and then read from
       </varlistentry>
 
       <varlistentry>
-        <term><literal>firstboot.timezone</literal></term>
+        <term><varname>firstboot.timezone</varname></term>
 
         <listitem><para>This credential specifies the system timezone setting to set during first boot, in
         place of prompting the user.</para></listitem>
index e21115f173ffb2397c72c6c8d2dd48847b0da433..46f2390234913af2ac5fc6367c9220ab4f243e5e 100644 (file)
@@ -269,6 +269,21 @@ systemd.swap=/dev/sda2:x-systemd.makefs</programlisting>
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>System Credentials</title>
+
+    <variablelist class='system-credentials'>
+      <varlistentry>
+        <term><varname>fstab.extra</varname></term>
+
+        <listitem><para>This credential may contain addition mounts to establish, in the same format as
+        <citerefentry
+        project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>, with
+        one mount per line. It is read in addition to <filename>/etc/fstab</filename>.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
index a31ed660bb378eaafcbef1a426a3bdcdfdf17457..d2e05dc82fab916e1dc93987745ce701739ce912 100644 (file)
     </variablelist>
   </refsect1>
 
+  <refsect1>
+    <title>System Credentials</title>
+
+    <variablelist class='system-credentials'>
+      <varlistentry>
+        <term><varname>getty.ttys.serial</varname></term>
+        <term><varname>getty.ttys.container</varname></term>
+
+        <listitem><para>These system credentials may be used to spawn additional login prompts on selected
+        TTYs. The two credentials should contain a newline-separated list of TTY names to spawn instances of
+        <filename>serial-getty@.service</filename> (in case of <varname>getty.ttys.serial</varname>) and
+        <filename>container-getty@.service</filename> (in case of <varname>getty.ttys.container</varname>)
+        on.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
   <refsect1>
     <title>See Also</title>
     <para>
       <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
       <citerefentry><refentrytitle>kernel-command-line</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+      <citerefentry><refentrytitle>systemd.system-credentials</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
       <citerefentry project='man-pages'><refentrytitle>agetty</refentrytitle><manvolnum>8</manvolnum></citerefentry>
     </para>
   </refsect1>
index 05d20bbf35502da8731143aa440bce5ec223eeb0..5a87f04832395e5a00eb33fcd46d471280d61c87 100644 (file)
@@ -407,7 +407,7 @@ search foobar.com barbar.com
     (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
     details). The following credentials are used when passed in:</para>
 
-    <variablelist>
+    <variablelist class='system-credentials'>
       <varlistentry>
         <term><varname>network.dns</varname></term>
         <term><varname>network.search_domains</varname></term>
index 4174184c152e63ba8e1cf9dff32b481bb4769789..975ffadefa16e15a6ec3bc97b714e541252fd939 100644 (file)
@@ -89,9 +89,9 @@
     (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
     details). The following credentials are used when passed in:</para>
 
-    <variablelist>
+    <variablelist class='system-credentials'>
       <varlistentry>
-        <term><literal>sysctl.extra</literal></term>
+        <term><varname>sysctl.extra</varname></term>
 
         <listitem><para>The contents of this credential may contain additional lines to operate on. The
         credential contents should follow the same format as any other <filename>sysctl.d/</filename> drop-in
index 34d3cab5c7e6582e913a81bab8afe58d8b04c20b..49f634b18076e2f5f2c1b2ed50c9c40db221e1ce 100644 (file)
     (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
     details). The following credentials are used when passed in:</para>
 
-    <variablelist>
+    <variablelist class='system-credentials'>
       <varlistentry>
-        <term><literal>passwd.hashed-password.<replaceable>user</replaceable></literal></term>
+        <term><varname>passwd.hashed-password.<replaceable>user</replaceable></varname></term>
         <listitem><para>A UNIX hashed password string to use for the specified user, when creating an entry
         for it. This is particularly useful for the <literal>root</literal> user as it allows provisioning
         the default root password to use via a unit file drop-in or from a container manager passing in this
       </varlistentry>
 
       <varlistentry>
-        <term><literal>passwd.plaintext-password.<replaceable>user</replaceable></literal></term>
+        <term><varname>passwd.plaintext-password.<replaceable>user</replaceable></varname></term>
 
         <listitem><para>Similar to <literal>passwd.hashed-password.<replaceable>user</replaceable></literal>
         but expect a literal, plaintext password, which is then automatically hashed before used for the user
       </varlistentry>
 
       <varlistentry>
-        <term><literal>passwd.shell.<replaceable>user</replaceable></literal></term>
+        <term><varname>passwd.shell.<replaceable>user</replaceable></varname></term>
 
         <listitem><para>Specifies the shell binary to use for the specified account when creating it.</para></listitem>
       </varlistentry>
 
       <varlistentry>
-        <term><literal>sysusers.extra</literal></term>
+        <term><varname>sysusers.extra</varname></term>
 
         <listitem><para>The contents of this credential may contain additional lines to operate on. The
         credential contents should follow the same format as any other <filename>sysusers.d/</filename>
index 3a9699ff4b12e832acfd6e9ac9fee9f8dce43461..decd66d5c66730fbf3d20a12ada020a0a67865f5 100644 (file)
     (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
     details). The following credentials are used when passed in:</para>
 
-    <variablelist>
+    <variablelist class='system-credentials'>
       <varlistentry>
-        <term><literal>tmpfiles.extra</literal></term>
+        <term><varname>tmpfiles.extra</varname></term>
 
         <listitem><para> The contents of this credential may contain additional lines to operate on. The
         credential contents should follow the same format as any other <filename>tmpfiles.d/</filename>
index f9f8327a68cdf2566d45ba390edec4b624444cc2..e462ef8f60343f2bb5da8e8345373f757aa2d152 100644 (file)
@@ -57,7 +57,7 @@
     (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
     details). The following credentials are used when passed in:</para>
 
-    <variablelist>
+    <variablelist class='system-credentials'>
       <varlistentry>
         <term><varname>vconsole.keymap</varname></term>
         <term><varname>vconsole.keymap_toggle</varname></term>
index 5a917d83492f0c62c015e605f73893486879d007..e077407367777e1f4d3b86208b23af81cbb6c197 100644 (file)
@@ -3274,18 +3274,21 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
         11) with a prefix of <literal>io.systemd.credential:</literal> or
         <literal>io.systemd.credential.binary:</literal>. In both cases a key/value pair separated by
         <literal>=</literal> is expected, in the latter case the right-hand side is Base64 decoded when
-        parsed (thus permitting binary data to be passed in). Example
-        <ulink url="https://www.qemu.org/docs/master/system/index.html">qemu</ulink>
-        switch: <literal>-smbios
+        parsed (thus permitting binary data to be passed in). Example <ulink
+        url="https://www.qemu.org/docs/master/system/index.html">qemu</ulink> switch: <literal>-smbios
         type=11,value=io.systemd.credential:xx=yy</literal>, or <literal>-smbios
         type=11,value=io.systemd.credential.binary:rick=TmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXA=</literal>. Alternatively,
         use the <command>qemu</command> <literal>fw_cfg</literal> node
-        <literal>opt/io.systemd.credentials/</literal>. Example <command>qemu</command> switch: <literal>-fw_cfg
-        name=opt/io.systemd.credentials/mycred,string=supersecret</literal>. They may also be specified on
-        the kernel command line using the <literal>systemd.set_credential=</literal> switch (see
-        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>) and from
-        the UEFI firmware environment via
-        <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+        <literal>opt/io.systemd.credentials/</literal>. Example <command>qemu</command> switch:
+        <literal>-fw_cfg name=opt/io.systemd.credentials/mycred,string=supersecret</literal>. They may also
+        be passed from the UEFI firmware environment via
+        <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+        from the initrd (see
+        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>), or be
+        specified on the kernel command line using the <literal>systemd.set_credential=</literal> and
+        <literal>systemd.set_credential_binary=</literal> switches (see
+        <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry> – this is
+        not recommended since unprivileged userspace can read the kernel command line). </para>
 
         <para>If referencing an <constant>AF_UNIX</constant> stream socket to connect to, the connection will
         originate from an abstract namespace socket, that includes information about the unit and the
index 5e39c5877fdf08a60c5abbbc53da02a00321b08f..7b2501aa77615bc71999bae3d3e2667ef0a979c1 100644 (file)
         <varname>ConditionArchitecture=</varname> in
         <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>$CREDENTIALS_DIRECTORY</varname></term>
+        <term><varname>$ENCRYPTED_CREDENTIALS_DIRECTORY</varname></term>
+
+        <listitem><para>If set, refers to the directory system credentials have been placed in. Credentials
+        passed into the system in plaintext form will be placed in <varname>$CREDENTIALS_DIRECTORY</varname>,
+        and those passed in in encrypted form will be placed in
+        <varname>$ENCRYPTED_CREDENTIALS_DIRECTORY</varname>. Use the
+        <citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+        command to automatically decrypt/authenticate credentials passed in, if needed. Specifically, use the
+        <command>systemd-creds --system cat</command> command.</para></listitem>
+      </varlistentry>
+
     </variablelist>
   </refsect1>
 
index f75a83cc3fb6a63b4adc167e70bd8df7159dbf6e..0e64b45df0bf62832baf576bc277a941787b5998 100644 (file)
@@ -40,7 +40,7 @@
   <refsect1>
     <title>Well known system credentials</title>
 
-    <variablelist>
+    <variablelist class='system-credentials'>
       <varlistentry>
         <term><varname>firstboot.keymap</varname></term>
         <listitem>
@@ -52,7 +52,7 @@
 
       <varlistentry>
         <term><varname>firstboot.locale</varname></term>
-        <term><varname>firstboot.locale-message</varname></term>
+        <term><varname>firstboot.locale-messages</varname></term>
         <listitem>
           <para>The system locale to set (e.g. <literal>de_DE.UTF-8</literal>). Read by
           <citerefentry><refentrytitle>systemd-firstboot</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>fstab.extra</varname></term>
+
+        <listitem>
+          <para>Additional mounts to establish at boot. For details, see
+          <citerefentry><refentrytitle>systemd-fstab-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>vconsole.keymap</varname></term>
         <term><varname>vconsole.keymap_toggle</varname></term>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>getty.ttys.serial</varname></term>
+        <term><varname>getty.ttys.container</varname></term>
+
+        <listitem><para>Used for spawning additional login prompts, see
+        <citerefentry><refentrytitle>systemd-getty-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry> for details.</para></listitem>
+      </varlistentry>
+
       <varlistentry>
         <term><varname>vmm.notify_socket</varname></term>
         <listitem>
           <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>system.machine_id</varname></term>
+        <listitem>
+          <para>Takes a 128bit ID to initialize the machine ID from (if it is not set yet). Interpreted by
+          the service manager (PID 1). For details see
+          <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 3ceac5f9194599c6ebc5b2c8fc9e9a9941579a23..21c98efecd4dd855180d21e2c635e7461b019770 100644 (file)
 
       <varlistentry>
         <term><varname>systemd.set_credential=</varname></term>
+        <term><varname>systemd.set_credential_binary=</varname></term>
 
         <listitem><para>Sets a system credential, which can then be propagated to system services using the
         <varname>ImportCredential=</varname> or <varname>LoadCredential=</varname> setting, see
         <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
-        details. Takes a pair of credential name and value, separated by a colon. Note that the kernel
-        command line is typically accessible by unprivileged programs in
+        details. Takes a pair of credential name and value, separated by a colon. The
+        <varname>systemd.set_credential=</varname> parameter expects the credential value in literal text
+        form, the <varname>systemd.set_credential_binary=</varname> parameter takes binary data encoded in
+        Base64. Note that the kernel command line is typically accessible by unprivileged programs in
         <filename>/proc/cmdline</filename>. Thus, this mechanism is not suitable for transferring sensitive
         data. Use it only for data that is not sensitive (e.g. public keys/certificates, rather than private
         keys), or in testing/debugging environments.</para>
   <refsect1>
     <title>System credentials</title>
 
-    <para>The service manager when run as PID 1 reads the following system credentials:</para>
+    <para>During initialization the service manager will import credentials from various sources into the
+    system's set of credentials, which can then be propagated into services and consumed by
+    generators:</para>
 
-    <variablelist>
+    <itemizedlist>
+      <listitem><para>When the service manager first initializes it will read system credentials from SMBIOS
+      Type 11 vendor strings
+      <varname>io.systemd.credential:<replaceable>name</replaceable>=<replaceable>value</replaceable></varname>,
+      and
+      <varname>io.systemd.credential.binary:<replaceable>name</replaceable>=<replaceable>value</replaceable></varname>.</para></listitem>
+
+      <listitem><para>At the same time it will import credentials from QEMU <literal>fw_cfg</literal>. (Note
+      that the SMBIOS mechanism is generally preferred, because it is faster and generic.)</para></listitem>
+
+      <listitem><para>Credentials may be passed via the kernel command line, using the
+      <varname>systemd.set-credential=</varname> parameter, see above.</para></listitem>
+
+      <listitem><para>Credentials may be passed from the UEFI environment via
+      <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para></listitem>
+
+      <listitem><para>When the service manager is invoked during the initrd → host transition it will import
+      all files in <filename>/run/credentials/@initrd/</filename> as system credentials.</para></listitem>
+    </itemizedlist>
+
+    <para>Invoke
+    <citerefentry><refentrytitle>systemd-creds</refentrytitle><manvolnum>1</manvolnum></citerefentry> as
+    follows to see the list of credentials passed into the system:</para>
+
+    <programlisting># systemd-creds --system list</programlisting>
+
+    <para>For further information see <ulink url="https://systemd.io/CREDENTIALS">System and Service
+    Credentials</ulink> documentation.</para>
+
+    <para>The service manager when run as PID 1 consumes the following system credentials:</para>
+
+    <variablelist class='system-credentials'>
       <varlistentry>
         <term><varname>vmm.notify_socket</varname></term>
         <listitem>
           notification via VSOCK when a virtual machine has finished booting.</para>
         </listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><varname>system.machine_id</varname></term>
+        <listitem>
+          <para>Takes a 128bit hexadecimal ID to initialize <filename>/etc/machine-id</filename> from, if the
+          file is not set up yet. See
+          <citerefentry><refentrytitle>machine-id</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
+          details.</para>
+        </listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 5120b9622e1fb2d2543a768ac82241d0cdb4ed56..38b2c3ad7cd87ec3375b37270c29c78b393a0581 100644 (file)
@@ -18,9 +18,9 @@
 
 static int run(const char *dest, const char *dest_early, const char *dest_late) {
 
-        if (in_initrd() > 0) {
+        if (in_initrd()) {
                 log_debug("Skipping generator, running in the initrd.");
-                return 0;
+                return EXIT_SUCCESS;
         }
 
         if (detect_container() > 0) {
index d850a68022156935d2c602896fc8783bbfeaafb4..445983bfb62325cce465fb8b158033ab035dc519 100644 (file)
@@ -3182,6 +3182,10 @@ static int acquire_credentials(
         if (dfd < 0)
                 return -errno;
 
+        r = fd_acl_make_writable(dfd); /* Add the "w" bit, if we are reusing an already set up credentials dir where it was unset */
+        if (r < 0)
+                return r;
+
         /* First, load credentials off disk (or acquire via AF_UNIX socket) */
         HASHMAP_FOREACH(lc, context->load_credentials) {
                 _cleanup_close_ int sub_fd = -EBADF;
@@ -3313,8 +3317,9 @@ static int acquire_credentials(
                 left -= add;
         }
 
-        if (fchmod(dfd, 0500) < 0) /* Now take away the "w" bit */
-                return -errno;
+        r = fd_acl_make_read_only(dfd); /* Now take away the "w" bit */
+        if (r < 0)
+                return r;
 
         /* After we created all keys with the right perms, also make sure the credential store as a whole is
          * accessible */
@@ -3384,7 +3389,7 @@ static int setup_credentials_internal(
                         if (r < 0)
                                 return r;
 
-                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL);
+                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
                         if (r < 0)
                                 return r;
 
@@ -3395,57 +3400,34 @@ static int setup_credentials_internal(
 
         if (workspace_mounted < 0) {
                 /* Nothing is mounted on the workspace yet, let's try to mount something now */
-                for (int try = 0;; try++) {
-
-                        if (try == 0) {
-                                /* Try "ramfs" first, since it's not swap backed */
-                                r = mount_nofollow_verbose(LOG_DEBUG, "ramfs", workspace, "ramfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "mode=0700");
-                                if (r >= 0) {
-                                        workspace_mounted = true;
-                                        break;
-                                }
 
-                        } else if (try == 1) {
-                                _cleanup_free_ char *opts = NULL;
+                r = mount_credentials_fs(workspace, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
+                if (r < 0) {
+                        /* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */
+                        r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
+                        if (r < 0) {
+                                if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */
+                                        return r;
 
-                                if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", (size_t) CREDENTIALS_TOTAL_SIZE_MAX) < 0)
-                                        return -ENOMEM;
+                                if (must_mount) /* If we it's not OK to use the plain directory
+                                                 * fallback, propagate all errors too */
+                                        return r;
 
-                                /* Fall back to "tmpfs" otherwise */
-                                r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", workspace, "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, opts);
-                                if (r >= 0) {
-                                        workspace_mounted = true;
-                                        break;
-                                }
+                                /* If we lack privileges to bind mount stuff, then let's gracefully
+                                 * proceed for compat with container envs, and just use the final dir
+                                 * as is. */
 
+                                workspace_mounted = false;
                         } else {
-                                /* If that didn't work, try to make a bind mount from the final to the workspace, so that we can make it writable there. */
-                                r = mount_nofollow_verbose(LOG_DEBUG, final, workspace, NULL, MS_BIND|MS_REC, NULL);
-                                if (r < 0) {
-                                        if (!ERRNO_IS_PRIVILEGE(r)) /* Propagate anything that isn't a permission problem */
-                                                return r;
-
-                                        if (must_mount) /* If we it's not OK to use the plain directory
-                                                         * fallback, propagate all errors too */
-                                                return r;
-
-                                        /* If we lack privileges to bind mount stuff, then let's gracefully
-                                         * proceed for compat with container envs, and just use the final dir
-                                         * as is. */
-
-                                        workspace_mounted = false;
-                                        break;
-                                }
-
                                 /* Make the new bind mount writable (i.e. drop MS_RDONLY) */
-                                r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL);
+                                r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
                                 if (r < 0)
                                         return r;
 
                                 workspace_mounted = true;
-                                break;
                         }
-                }
+                } else
+                        workspace_mounted = true;
         }
 
         assert(!must_mount || workspace_mounted > 0);
@@ -3477,7 +3459,7 @@ static int setup_credentials_internal(
 
                 if (install) {
                         /* Make workspace read-only now, so that any bind mount we make from it defaults to read-only too */
-                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL);
+                        r = mount_nofollow_verbose(LOG_DEBUG, NULL, workspace, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
                         if (r < 0)
                                 return r;
 
index ade509be348c11031465626b6171c2c0961cc9a0..40cbf10dad8d4c74f7607c921fa2c1792cf71a8f 100644 (file)
@@ -70,21 +70,36 @@ static void import_credentials_context_free(ImportCredentialContext *c) {
         c->target_dir_fd = safe_close(c->target_dir_fd);
 }
 
-static int acquire_encrypted_credential_directory(ImportCredentialContext *c) {
+static int acquire_credential_directory(ImportCredentialContext *c, const char *path, bool with_mount) {
         int r;
 
         assert(c);
+        assert(path);
 
         if (c->target_dir_fd >= 0)
                 return c->target_dir_fd;
 
-        r = mkdir_safe_label(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE);
-        if (r < 0)
-                return log_error_errno(r, "Failed to create " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m");
+        r = path_is_mount_point(path, NULL, 0);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        return log_error_errno(r, "Failed to determine if %s is a mount point: %m", path);
+
+                r = mkdir_safe_label(path, 0700, 0, 0, MKDIR_WARN_MODE);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to create %s mount point: %m", path);
 
-        c->target_dir_fd = open(ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+                r = 0; /* Now it exists and is not a mount point */
+        }
+        if (r > 0)
+                /* If already a mount point, then remount writable */
+                (void) mount_nofollow_verbose(LOG_WARNING, NULL, path, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ false), NULL);
+        else if (with_mount)
+                /* If not a mount point yet, and the credentials are not encrypted, then let's try to mount a no-swap fs there */
+                (void) mount_credentials_fs(path, CREDENTIALS_TOTAL_SIZE_MAX, /* ro= */ false);
+
+        c->target_dir_fd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
         if (c->target_dir_fd < 0)
-                return log_error_errno(errno, "Failed to open " ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY ": %m");
+                return log_error_errno(errno, "Failed to open %s: %m", path);
 
         return c->target_dir_fd;
 }
@@ -137,7 +152,7 @@ static int finalize_credentials_dir(const char *dir, const char *envvar) {
         if (r < 0)
                 log_warning_errno(r, "Failed to make '%s' a mount point, ignoring: %m", dir);
         else
-                (void) mount_nofollow_verbose(LOG_WARNING, NULL, dir, NULL, MS_BIND|MS_NODEV|MS_NOEXEC|MS_NOSUID|MS_RDONLY|MS_REMOUNT, NULL);
+                (void) mount_nofollow_verbose(LOG_WARNING, NULL, dir, NULL, MS_BIND|MS_REMOUNT|credentials_fs_mount_flags(/* ro= */ true), NULL);
 
         if (setenv(envvar, dir, /* overwrite= */ true) < 0)
                 return log_error_errno(errno, "Failed to set $%s environment variable: %m", envvar);
@@ -227,7 +242,7 @@ static int import_credentials_boot(void) {
                         if (!credential_size_ok(&context, n, st.st_size))
                                 continue;
 
-                        r = acquire_encrypted_credential_directory(&context);
+                        r = acquire_credential_directory(&context, ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ false);
                         if (r < 0)
                                 return r;
 
@@ -261,48 +276,23 @@ static int import_credentials_boot(void) {
         return 0;
 }
 
-static int acquire_credential_directory(ImportCredentialContext *c) {
-        int r;
-
-        assert(c);
-
-        if (c->target_dir_fd >= 0)
-                return c->target_dir_fd;
-
-        r = path_is_mount_point(SYSTEM_CREDENTIALS_DIRECTORY, NULL, 0);
-        if (r < 0) {
-                if (r != -ENOENT)
-                        return log_error_errno(r, "Failed to determine if " SYSTEM_CREDENTIALS_DIRECTORY " is a mount point: %m");
-
-                r = mkdir_safe_label(SYSTEM_CREDENTIALS_DIRECTORY, 0700, 0, 0, MKDIR_WARN_MODE);
-                if (r < 0)
-                        return log_error_errno(r, "Failed to create " SYSTEM_CREDENTIALS_DIRECTORY " mount point: %m");
-
-                r = 0; /* Now it exists and is not a mount point */
-        }
-        if (r == 0)
-                /* If not a mountpoint yet, try to mount a ramfs there (so that this stuff isn't swapped
-                 * out), but if that doesn't work, let's just use the regular tmpfs it already is. */
-                (void) mount_nofollow_verbose(LOG_WARNING, "ramfs", SYSTEM_CREDENTIALS_DIRECTORY, "ramfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, "mode=0700");
-
-        c->target_dir_fd = open(SYSTEM_CREDENTIALS_DIRECTORY, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
-        if (c->target_dir_fd < 0)
-                return log_error_errno(errno, "Failed to open " SYSTEM_CREDENTIALS_DIRECTORY ": %m");
-
-        return c->target_dir_fd;
-}
-
 static int proc_cmdline_callback(const char *key, const char *value, void *data) {
         ImportCredentialContext *c = ASSERT_PTR(data);
+        _cleanup_free_ void *binary = NULL;
         _cleanup_free_ char *n = NULL;
         _cleanup_close_ int nfd = -EBADF;
-        const char *colon;
+        const char *colon, *d;
+        bool base64;
         size_t l;
         int r;
 
         assert(key);
 
-        if (!proc_cmdline_key_streq(key, "systemd.set_credential"))
+        if (proc_cmdline_key_streq(key, "systemd.set_credential"))
+                base64 = false;
+        else if (proc_cmdline_key_streq(key, "systemd.set_credential_binary"))
+                base64 = true;
+        else
                 return 0;
 
         colon = value ? strchr(value, ':') : NULL;
@@ -321,12 +311,24 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data)
         }
 
         colon++;
-        l = strlen(colon);
+
+        if (base64) {
+                r = unbase64mem(colon, SIZE_MAX, &binary, &l);
+                if (r < 0) {
+                        log_warning_errno(r, "Failed to decode binary credential '%s' data, ignoring: %m", n);
+                        return 0;
+                }
+
+                d = binary;
+        } else {
+                d = colon;
+                l = strlen(colon);
+        }
 
         if (!credential_size_ok(c, n, l))
                 return 0;
 
-        r = acquire_credential_directory(c);
+        r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
         if (r < 0)
                 return r;
 
@@ -336,7 +338,7 @@ static int proc_cmdline_callback(const char *key, const char *value, void *data)
         if (nfd < 0)
                 return nfd;
 
-        r = loop_write(nfd, colon, l, /* do_poll= */ false);
+        r = loop_write(nfd, d, l, /* do_poll= */ false);
         if (r < 0) {
                 (void) unlinkat(c->target_dir_fd, n, 0);
                 return log_error_errno(r, "Failed to write credential: %m");
@@ -433,7 +435,7 @@ static int import_credentials_qemu(ImportCredentialContext *c) {
                         continue;
                 }
 
-                r = acquire_credential_directory(c);
+                r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
                 if (r < 0)
                         return r;
 
@@ -535,7 +537,7 @@ static int parse_smbios_strings(ImportCredentialContext *c, const char *data, si
                 if (!credential_size_ok(c, cn, cdata_len))
                         continue;
 
-                r = acquire_credential_directory(c);
+                r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
                 if (r < 0)
                         return r;
 
@@ -611,27 +613,157 @@ static int import_credentials_smbios(ImportCredentialContext *c) {
         return 0;
 }
 
+static int import_credentials_initrd(ImportCredentialContext *c) {
+        _cleanup_free_ DirectoryEntries *de = NULL;
+        _cleanup_close_ int source_dir_fd = -EBADF;
+        int r;
+
+        assert(c);
+
+        /* This imports credentials from /run/credentials/@initrd/ into our credentials directory and deletes
+         * the source directory afterwards. This is run once after the initrd → host transition. This is
+         * supposed to establish a well-defined avenue for initrd-based host configurators to pass
+         * credentials into the main system. */
+
+        if (in_initrd())
+                return 0;
+
+        source_dir_fd = open("/run/credentials/@initrd", O_RDONLY|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW);
+        if (source_dir_fd < 0) {
+                if (errno == ENOENT)
+                        log_debug_errno(errno, "No credentials passed from initrd.");
+                else
+                        log_warning_errno(errno, "Failed to open '/run/credentials/@initrd', ignoring: %m");
+                return 0;
+        }
+
+        r = readdir_all(source_dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
+        if (r < 0) {
+                log_warning_errno(r, "Failed to read '/run/credentials/@initrd' contents, ignoring: %m");
+                return 0;
+        }
+
+        FOREACH_ARRAY(entry, de->entries, de->n_entries) {
+                _cleanup_close_ int cfd = -EBADF, nfd = -EBADF;
+                const struct dirent *d = *entry;
+                struct stat st;
+
+                if (!credential_name_valid(d->d_name)) {
+                        log_warning("Credential '%s' has invalid name, ignoring.", d->d_name);
+                        continue;
+                }
+
+                cfd = openat(source_dir_fd, d->d_name, O_RDONLY|O_CLOEXEC);
+                if (cfd < 0) {
+                        log_warning_errno(errno, "Failed to open %s, ignoring: %m", d->d_name);
+                        continue;
+                }
+
+                if (fstat(cfd, &st) < 0) {
+                        log_warning_errno(errno, "Failed to stat %s, ignoring: %m", d->d_name);
+                        continue;
+                }
+
+                r = stat_verify_regular(&st);
+                if (r < 0) {
+                        log_warning_errno(r, "Credential file %s is not a regular file, ignoring: %m", d->d_name);
+                        continue;
+                }
+
+                if (!credential_size_ok(c, d->d_name, st.st_size))
+                        continue;
+
+                r = acquire_credential_directory(c, SYSTEM_CREDENTIALS_DIRECTORY, /* with_mount= */ true);
+                if (r < 0)
+                        return r;
+
+                nfd = open_credential_file_for_write(c->target_dir_fd, SYSTEM_CREDENTIALS_DIRECTORY, d->d_name);
+                if (nfd == -EEXIST)
+                        continue;
+                if (nfd < 0)
+                        return nfd;
+
+                r = copy_bytes(cfd, nfd, st.st_size, 0);
+                if (r < 0) {
+                        (void) unlinkat(c->target_dir_fd, d->d_name, 0);
+                        return log_error_errno(r, "Failed to create credential '%s': %m", d->d_name);
+                }
+
+                c->size_sum += st.st_size;
+                c->n_credentials++;
+
+                log_debug("Successfully copied initrd credential '%s'.", d->d_name);
+
+                (void) unlinkat(source_dir_fd, d->d_name, 0);
+        }
+
+        source_dir_fd = safe_close(source_dir_fd);
+
+        if (rmdir("/run/credentials/@initrd") < 0)
+                log_warning_errno(errno, "Failed to remove /run/credentials/@initrd after import, ignoring: %m");
+
+        return 0;
+}
+
 static int import_credentials_trusted(void) {
         _cleanup_(import_credentials_context_free) ImportCredentialContext c = {
                 .target_dir_fd = -EBADF,
         };
-        int q, w, r;
+        int q, w, r, y;
+
+        /* This is invoked during early boot when no credentials have been imported so far. (Specifically, if
+         * the $CREDENTIALS_DIRECTORY or $ENCRYPTED_CREDENTIALS_DIRECTORY environment variables are not set
+         * yet.) */
 
         r = import_credentials_qemu(&c);
         w = import_credentials_smbios(&c);
         q = import_credentials_proc_cmdline(&c);
+        y = import_credentials_initrd(&c);
 
         if (c.n_credentials > 0) {
                 int z;
 
-                log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg.", c.n_credentials);
+                log_debug("Imported %u credentials from kernel command line/smbios/fw_cfg/initrd.", c.n_credentials);
 
                 z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY");
                 if (z < 0)
                         return z;
         }
 
-        return r < 0 ? r : w < 0 ? w : q;
+        return r < 0 ? r : w < 0 ? w : q < 0 ? q : y;
+}
+
+static int merge_credentials_trusted(const char *creds_dir) {
+        _cleanup_(import_credentials_context_free) ImportCredentialContext c = {
+                .target_dir_fd = -EBADF,
+        };
+        int r;
+
+        /* This is invoked after the initrd → host transitions, when credentials already have been imported,
+         * but we might want to import some more from the initrd. */
+
+        if (in_initrd())
+                return 0;
+
+        /* Do not try to merge initrd credentials into foreign credentials directories */
+        if (!path_equal_ptr(creds_dir, SYSTEM_CREDENTIALS_DIRECTORY)) {
+                log_debug("Not importing initrd credentials, as foreign $CREDENTIALS_DIRECTORY has been set.");
+                return 0;
+        }
+
+        r = import_credentials_initrd(&c);
+
+        if (c.n_credentials > 0) {
+                int z;
+
+                log_debug("Merged %u credentials from initrd.", c.n_credentials);
+
+                z = finalize_credentials_dir(SYSTEM_CREDENTIALS_DIRECTORY, "CREDENTIALS_DIRECTORY");
+                if (z < 0)
+                        return z;
+        }
+
+        return r;
 }
 
 static int symlink_credential_dir(const char *envvar, const char *path, const char *where) {
@@ -657,6 +789,79 @@ static int symlink_credential_dir(const char *envvar, const char *path, const ch
         return 0;
 }
 
+static int setenv_notify_socket(void) {
+        _cleanup_free_ char *address = NULL;
+        int r;
+
+        r = read_credential_with_decryption("vmm.notify_socket", (void **)&address, /* ret_size= */ NULL);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to read 'vmm.notify_socket' credential, ignoring: %m");
+
+        if (isempty(address))
+                return 0;
+
+        if (setenv("NOTIFY_SOCKET", address, /* replace= */ 1) < 0)
+                return log_warning_errno(errno, "Failed to set $NOTIFY_SOCKET environment variable, ignoring: %m");
+
+        return 1;
+}
+
+static int report_credentials_per_func(const char *title, int (*get_directory_func)(const char **ret)) {
+        _cleanup_free_ DirectoryEntries *de = NULL;
+        _cleanup_close_ int dir_fd = -EBADF;
+        _cleanup_free_ char *ll = NULL;
+        const char *d = NULL;
+        int r, c = 0;
+
+        assert(title);
+        assert(get_directory_func);
+
+        r = get_directory_func(&d);
+        if (r < 0) {
+                if (r == -ENXIO) /* Env var not set */
+                        return 0;
+
+                return log_warning_errno(r, "Failed to determine %s directory: %m", title);
+        }
+
+        dir_fd = open(d, O_RDONLY|O_DIRECTORY|O_CLOEXEC);
+        if (dir_fd < 0)
+                return log_warning_errno(errno, "Failed to open credentials directory %s: %m", d);
+
+        r = readdir_all(dir_fd, RECURSE_DIR_SORT|RECURSE_DIR_IGNORE_DOT, &de);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to enumerate credentials directory %s: %m", d);
+
+        FOREACH_ARRAY(entry, de->entries, de->n_entries) {
+                const struct dirent *e = *entry;
+
+                if (!credential_name_valid(e->d_name))
+                        continue;
+
+                if (!strextend_with_separator(&ll, ", ", e->d_name))
+                        return log_oom();
+
+                c++;
+        }
+
+        if (ll)
+                log_info("Received %s: %s", title, ll);
+
+        return c;
+}
+
+static void report_credentials(void) {
+        int p, q;
+
+        p = report_credentials_per_func("regular credentials", get_credentials_dir);
+        q = report_credentials_per_func("untrusted credentials", get_encrypted_credentials_dir);
+
+        log_full(p > 0 || q > 0 ? LOG_INFO : LOG_DEBUG,
+                 "Acquired %i regular credentials, %i untrusted credentials.",
+                 p > 0 ? p : 0,
+                 q > 0 ? q : 0);
+}
+
 int import_credentials(void) {
         const char *received_creds_dir = NULL, *received_encrypted_creds_dir = NULL;
         bool envvar_set = false;
@@ -690,6 +895,10 @@ int import_credentials(void) {
                                 r = q;
                 }
 
+                q = merge_credentials_trusted(received_creds_dir);
+                if (r >= 0)
+                        r = q;
+
         } else {
                 _cleanup_free_ char *v = NULL;
 
@@ -713,18 +922,10 @@ int import_credentials(void) {
                         r = q;
         }
 
-        if (r >= 0) {
-                _cleanup_free_ char *address = NULL;
+        report_credentials();
 
-                r = read_credential("vmm.notify_socket", (void **)&address, /* ret_size= */ NULL);
-                if (r < 0 && !IN_SET(r, -ENOENT, -ENXIO))
-                        log_warning_errno(r, "Failed to read 'vmm.notify_socket' credential, ignoring: %m");
-                else if (r >= 0 && !isempty(address)) {
-                        r = setenv("NOTIFY_SOCKET", address, /* replace= */ 1);
-                        if (r < 0)
-                                log_warning_errno(errno, "Failed to set $NOTIFY_SOCKET environment variable, ignoring: %m");
-                }
-        }
+        /* Propagate vmm_notify_socket credential → $NOTIFY_SOCKET env var */
+        (void) setenv_notify_socket();
 
         return r;
 }
index 3f78f035d0e71126e7cb022d1343487b919f3cd6..6290ec131c02ddd4d2fb797ddf78934a2f413f82 100644 (file)
@@ -2224,10 +2224,15 @@ static int initialize_runtime(
                                 return r;
                         }
 
+                        /* Pull credentials from various sources into a common credential directory (we do
+                         * this here, before setting up the machine ID, so that we can use credential info
+                         * for setting up the machine ID) */
+                        (void) import_credentials();
+
                         (void) os_release_status();
                         (void) hostname_setup(true);
                         /* Force transient machine-id on first boot. */
-                        machine_id_setup(NULL, /* force_transient= */ first_boot, arg_machine_id, NULL);
+                        machine_id_setup(/* root= */ NULL, /* force_transient= */ first_boot, arg_machine_id, /* ret_machine_id */ NULL);
                         (void) loopback_setup();
                         bump_unix_max_dgram_qlen();
                         bump_file_max_and_nr_open();
@@ -2306,10 +2311,6 @@ static int initialize_runtime(
         (void) bump_rlimit_nofile(saved_rlimit_nofile);
         (void) bump_rlimit_memlock(saved_rlimit_memlock);
 
-        /* Pull credentials from various sources into a common credential directory */
-        if (arg_runtime_scope == RUNTIME_SCOPE_SYSTEM && !skip_setup)
-                (void) import_credentials();
-
         return 0;
 }
 
index 68614de7a063e751548898adcd452c24a9f9f42b..aedd24949edfaf8ccb49650b166b1c2ec32979d6 100644 (file)
@@ -911,7 +911,6 @@ int transaction_add_job_and_dependencies(
                 sd_bus_error *e) {
 
         bool is_new;
-        Unit *dep;
         Job *ret;
         int r;
 
@@ -982,116 +981,136 @@ int transaction_add_job_and_dependencies(
                 tr->anchor_job = ret;
         }
 
-        if (is_new && !FLAGS_SET(flags, TRANSACTION_IGNORE_REQUIREMENTS) && type != JOB_NOP) {
-                _cleanup_set_free_ Set *following = NULL;
+        if (!is_new || FLAGS_SET(flags, TRANSACTION_IGNORE_REQUIREMENTS) || type == JOB_NOP)
+                return 0;
 
-                /* If we are following some other unit, make sure we add all dependencies of everybody
-                 * following. */
-                if (unit_following_set(ret->unit, &following) > 0)
-                        SET_FOREACH(dep, following) {
-                                r = transaction_add_job_and_dependencies(tr, type, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
-                                if (r < 0) {
-                                        log_unit_full_errno(dep, r == -ERFKILL ? LOG_INFO : LOG_WARNING, r,
-                                                            "Cannot add dependency job, ignoring: %s",
-                                                            bus_error_message(e, r));
-                                        sd_bus_error_free(e);
-                                }
+        _cleanup_set_free_ Set *following = NULL;
+        Unit *dep;
+
+        /* If we are following some other unit, make sure we add all dependencies of everybody following. */
+        if (unit_following_set(ret->unit, &following) > 0)
+                SET_FOREACH(dep, following) {
+                        r = transaction_add_job_and_dependencies(tr, type, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
+                        if (r < 0) {
+                                log_unit_full_errno(dep, r == -ERFKILL ? LOG_INFO : LOG_WARNING, r,
+                                                    "Cannot add dependency job, ignoring: %s",
+                                                    bus_error_message(e, r));
+                                sd_bus_error_free(e);
                         }
+                }
 
-                /* Finally, recursively add in all dependencies. */
-                if (IN_SET(type, JOB_START, JOB_RESTART)) {
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) {
-                                r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
-                                if (r < 0) {
-                                        if (r != -EBADR) /* job type not applicable */
-                                                goto fail;
+        /* Finally, recursively add in all dependencies. */
+        if (IN_SET(type, JOB_START, JOB_RESTART)) {
+                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) {
+                        r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
+                        if (r < 0) {
+                                if (r != -EBADR) /* job type not applicable */
+                                        goto fail;
 
-                                        sd_bus_error_free(e);
-                                }
+                                sd_bus_error_free(e);
                         }
+                }
 
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START_IGNORED) {
-                                r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
-                                if (r < 0) {
-                                        /* unit masked, job type not applicable and unit not found are not
-                                         * considered as errors. */
-                                        log_unit_full_errno(dep,
-                                                            IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
-                                                            r, "Cannot add dependency job, ignoring: %s",
-                                                            bus_error_message(e, r));
-                                        sd_bus_error_free(e);
-                                }
+                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START_IGNORED) {
+                        r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
+                        if (r < 0) {
+                                /* unit masked, job type not applicable and unit not found are not considered
+                                 * as errors. */
+                                log_unit_full_errno(dep,
+                                                    IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
+                                                    r, "Cannot add dependency job, ignoring: %s",
+                                                    bus_error_message(e, r));
+                                sd_bus_error_free(e);
                         }
+                }
 
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_VERIFY) {
-                                r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
-                                if (r < 0) {
-                                        if (r != -EBADR) /* job type not applicable */
-                                                goto fail;
+                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_VERIFY) {
+                        r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
+                        if (r < 0) {
+                                if (r != -EBADR) /* job type not applicable */
+                                        goto fail;
 
-                                        sd_bus_error_free(e);
-                                }
+                                sd_bus_error_free(e);
                         }
+                }
 
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP) {
-                                r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, TRANSACTION_MATTERS | TRANSACTION_CONFLICTS | (flags & TRANSACTION_IGNORE_ORDER), e);
-                                if (r < 0) {
-                                        if (r != -EBADR) /* job type not applicable */
-                                                goto fail;
+                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP) {
+                        r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, TRANSACTION_MATTERS | TRANSACTION_CONFLICTS | (flags & TRANSACTION_IGNORE_ORDER), e);
+                        if (r < 0) {
+                                if (r != -EBADR) /* job type not applicable */
+                                        goto fail;
 
-                                        sd_bus_error_free(e);
-                                }
+                                sd_bus_error_free(e);
                         }
+                }
 
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) {
-                                r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
-                                if (r < 0) {
-                                        log_unit_warning(dep,
-                                                         "Cannot add dependency job, ignoring: %s",
-                                                         bus_error_message(e, r));
-                                        sd_bus_error_free(e);
-                                }
+                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) {
+                        r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
+                        if (r < 0) {
+                                log_unit_warning(dep,
+                                                 "Cannot add dependency job, ignoring: %s",
+                                                 bus_error_message(e, r));
+                                sd_bus_error_free(e);
                         }
                 }
+        }
 
-                _cleanup_set_free_ Set *propagated_restart = NULL;
-
-                if (type == JOB_RESTART || (type == JOB_START && FLAGS_SET(flags, TRANSACTION_PROPAGATE_START_AS_RESTART))) {
+        if (IN_SET(type, JOB_RESTART, JOB_STOP) || (type == JOB_START && FLAGS_SET(flags, TRANSACTION_PROPAGATE_START_AS_RESTART))) {
+                bool is_stop = type == JOB_STOP;
 
+                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, is_stop ? UNIT_ATOM_PROPAGATE_STOP : UNIT_ATOM_PROPAGATE_RESTART) {
                         /* We propagate RESTART only as TRY_RESTART, in order not to start dependencies that
                          * are not around. */
+                        JobType nt;
 
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_RESTART) {
-                                JobType nt;
+                        nt = job_type_collapse(is_stop ? JOB_STOP : JOB_TRY_RESTART, dep);
+                        if (nt == JOB_NOP)
+                                continue;
 
-                                r = set_ensure_put(&propagated_restart, NULL, dep);
-                                if (r < 0)
+                        r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
+                        if (r < 0) {
+                                if (r != -EBADR) /* job type not applicable */
                                         return r;
 
-                                nt = job_type_collapse(JOB_TRY_RESTART, dep);
-                                if (nt == JOB_NOP)
-                                        continue;
-
-                                r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
-                                if (r < 0) {
-                                        if (r != -EBADR) /* job type not applicable */
-                                                return r;
-
-                                        sd_bus_error_free(e);
-                                }
+                                sd_bus_error_free(e);
                         }
                 }
 
-                if (type == JOB_STOP) {
-                        /* The 'stop' part of a restart job is also propagated to units with
-                         * UNIT_ATOM_PROPAGATE_STOP */
+                /* Process UNIT_ATOM_PROPAGATE_STOP_GRACEFUL (PropagatesStopTo=) units. We need to wait until
+                 * all other dependencies are processed, i.e. we're the anchor job or already in the recursion
+                 * that handles it. */
+                if (!by || FLAGS_SET(flags, TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL))
+                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_STOP_GRACEFUL) {
+                                JobType nt = JOB_STOP;
+                                Job *j;
+
+                                j = hashmap_get(tr->jobs, dep);
+                                if (j)
+                                        LIST_FOREACH(transaction, i, j)
+                                                switch (i->type) {
+
+                                                case JOB_STOP:
+                                                case JOB_RESTART:
+                                                        /* Nothing to worry about, an appropriate job is in-place */
+                                                        nt = JOB_NOP;
+                                                        break;
+
+                                                case JOB_START:
+                                                        /* This unit is pulled in by other dependency types in
+                                                         * this transaction. We will run into job type conflict
+                                                         * if we enqueue a stop job, so let's enqueue a restart
+                                                         * job instead. */
+                                                        nt = JOB_RESTART;
+
+                                                default: /* We don't care about others */
+                                                        ;
+
+                                                }
 
-                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_STOP) {
-                                /* Units experienced restart propagation are skipped */
-                                if (set_contains(propagated_restart, dep))
+                                if (nt == JOB_NOP)
                                         continue;
 
-                                r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
+                                r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER) | TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL, e);
                                 if (r < 0) {
                                         if (r != -EBADR) /* job type not applicable */
                                                 return r;
@@ -1099,22 +1118,22 @@ int transaction_add_job_and_dependencies(
                                         sd_bus_error_free(e);
                                 }
                         }
-                }
+        }
 
-                if (type == JOB_RELOAD)
-                        transaction_add_propagate_reload_jobs(tr, ret->unit, ret, flags & TRANSACTION_IGNORE_ORDER);
+        if (type == JOB_RELOAD)
+                transaction_add_propagate_reload_jobs(tr, ret->unit, ret, flags & TRANSACTION_IGNORE_ORDER);
 
-                /* JOB_VERIFY_ACTIVE requires no dependency handling */
-        }
+        /* JOB_VERIFY_ACTIVE requires no dependency handling */
 
         return 0;
+
 fail:
         /* Recursive call failed to add required jobs so let's drop top level job as well. */
         log_unit_debug_errno(unit, r, "Cannot add dependency job to transaction, deleting job %s/%s again: %s",
                              unit->id, job_type_to_string(type), bus_error_message(e, r));
+
         transaction_delete_job(tr, ret, /* delete_dependencies= */ false);
         return r;
-
 }
 
 static bool shall_stop_on_isolate(Transaction *tr, Unit *u) {
index db2d0566499a2f18cb635802286454c57a65e369..151e02dd605e07d6bbcee31b3f3583f5302f4f6d 100644 (file)
@@ -21,13 +21,16 @@ Transaction *transaction_abort_and_free(Transaction *tr);
 DEFINE_TRIVIAL_CLEANUP_FUNC(Transaction*, transaction_abort_and_free);
 
 typedef enum TransactionAddFlags {
-        TRANSACTION_MATTERS                    = 1 << 0,
-        TRANSACTION_CONFLICTS                  = 1 << 1,
-        TRANSACTION_IGNORE_REQUIREMENTS        = 1 << 2,
-        TRANSACTION_IGNORE_ORDER               = 1 << 3,
+        TRANSACTION_MATTERS                         = 1 << 0,
+        TRANSACTION_CONFLICTS                       = 1 << 1,
+        TRANSACTION_IGNORE_REQUIREMENTS             = 1 << 2,
+        TRANSACTION_IGNORE_ORDER                    = 1 << 3,
 
         /* Propagate a START job to other units like a RESTART */
-        TRANSACTION_PROPAGATE_START_AS_RESTART = 1 << 4,
+        TRANSACTION_PROPAGATE_START_AS_RESTART      = 1 << 4,
+
+        /* Indicate that we're in the recursion for processing UNIT_ATOM_PROPAGATE_STOP_GRACEFUL units */
+        TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL = 1 << 5,
 } TransactionAddFlags;
 
 void transaction_add_propagate_reload_jobs(
index 81333523e73865ee8166ae3fe5fc2a33a3040fbc..35b279b5c743967cb291e051a3daf16125a0c2b7 100644 (file)
@@ -80,7 +80,7 @@ static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = {
                                         UNIT_ATOM_PROPAGATE_STOP_FAILURE,
 
         [UNIT_PROPAGATES_STOP_TO]     = UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
-                                        UNIT_ATOM_PROPAGATE_STOP,
+                                        UNIT_ATOM_PROPAGATE_STOP_GRACEFUL,
 
         /* These are simple dependency types: they consist of a single atom only */
         [UNIT_ON_FAILURE]             = UNIT_ATOM_ON_FAILURE,
@@ -196,6 +196,11 @@ UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) {
         case UNIT_ATOM_PROPAGATE_STOP_FAILURE:
                 return UNIT_CONFLICTED_BY;
 
+        case UNIT_ATOM_RETROACTIVE_STOP_ON_STOP |
+                UNIT_ATOM_PROPAGATE_STOP_GRACEFUL:
+        case UNIT_ATOM_PROPAGATE_STOP_GRACEFUL:
+                return UNIT_PROPAGATES_STOP_TO;
+
         /* And now, the simple ones */
 
         case UNIT_ATOM_ON_FAILURE:
index 02532e57d6a17eb2fef0954a39183f61303667f3..96f00ca39a89d802227f513f5d39d196a5b92fc6 100644 (file)
@@ -58,30 +58,33 @@ typedef enum UnitDependencyAtom {
         UNIT_ATOM_PROPAGATE_INACTIVE_START_AS_FAILURE = UINT64_C(1) << 17,
         /* When putting together a transaction, propagate JOB_STOP from our unit to the other. */
         UNIT_ATOM_PROPAGATE_STOP                      = UINT64_C(1) << 18,
+        /* Like UNIT_ATOM_PROPAGATE_STOP, but enqueues a restart job if there's already a start job (avoids
+         * job type conflict). */
+        UNIT_ATOM_PROPAGATE_STOP_GRACEFUL             = UINT64_C(1) << 19,
         /* When putting together a transaction, propagate JOB_RESTART from our unit to the other. */
-        UNIT_ATOM_PROPAGATE_RESTART                   = UINT64_C(1) << 19,
+        UNIT_ATOM_PROPAGATE_RESTART                   = UINT64_C(1) << 20,
 
         /* Add the other unit to the default target dependency queue */
-        UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE = UINT64_C(1) << 20,
+        UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE = UINT64_C(1) << 21,
         /* Recheck default target deps on other units (which are target units) */
-        UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES         = UINT64_C(1) << 21,
+        UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES         = UINT64_C(1) << 22,
 
         /* The remaining atoms map 1:1 to the equally named high-level deps */
-        UNIT_ATOM_ON_FAILURE                          = UINT64_C(1) << 22,
-        UNIT_ATOM_ON_SUCCESS                          = UINT64_C(1) << 23,
-        UNIT_ATOM_ON_FAILURE_OF                       = UINT64_C(1) << 24,
-        UNIT_ATOM_ON_SUCCESS_OF                       = UINT64_C(1) << 25,
-        UNIT_ATOM_BEFORE                              = UINT64_C(1) << 26,
-        UNIT_ATOM_AFTER                               = UINT64_C(1) << 27,
-        UNIT_ATOM_TRIGGERS                            = UINT64_C(1) << 28,
-        UNIT_ATOM_TRIGGERED_BY                        = UINT64_C(1) << 29,
-        UNIT_ATOM_PROPAGATES_RELOAD_TO                = UINT64_C(1) << 30,
-        UNIT_ATOM_JOINS_NAMESPACE_OF                  = UINT64_C(1) << 31,
-        UNIT_ATOM_REFERENCES                          = UINT64_C(1) << 32,
-        UNIT_ATOM_REFERENCED_BY                       = UINT64_C(1) << 33,
-        UNIT_ATOM_IN_SLICE                            = UINT64_C(1) << 34,
-        UNIT_ATOM_SLICE_OF                            = UINT64_C(1) << 35,
-        _UNIT_DEPENDENCY_ATOM_MAX                     = (UINT64_C(1) << 36) - 1,
+        UNIT_ATOM_ON_FAILURE                          = UINT64_C(1) << 23,
+        UNIT_ATOM_ON_SUCCESS                          = UINT64_C(1) << 24,
+        UNIT_ATOM_ON_FAILURE_OF                       = UINT64_C(1) << 25,
+        UNIT_ATOM_ON_SUCCESS_OF                       = UINT64_C(1) << 26,
+        UNIT_ATOM_BEFORE                              = UINT64_C(1) << 27,
+        UNIT_ATOM_AFTER                               = UINT64_C(1) << 28,
+        UNIT_ATOM_TRIGGERS                            = UINT64_C(1) << 29,
+        UNIT_ATOM_TRIGGERED_BY                        = UINT64_C(1) << 30,
+        UNIT_ATOM_PROPAGATES_RELOAD_TO                = UINT64_C(1) << 31,
+        UNIT_ATOM_JOINS_NAMESPACE_OF                  = UINT64_C(1) << 32,
+        UNIT_ATOM_REFERENCES                          = UINT64_C(1) << 33,
+        UNIT_ATOM_REFERENCED_BY                       = UINT64_C(1) << 34,
+        UNIT_ATOM_IN_SLICE                            = UINT64_C(1) << 35,
+        UNIT_ATOM_SLICE_OF                            = UINT64_C(1) << 36,
+        _UNIT_DEPENDENCY_ATOM_MAX                     = (UINT64_C(1) << 37) - 1,
         _UNIT_DEPENDENCY_ATOM_INVALID                 = -EINVAL,
 } UnitDependencyAtom;
 
index 29fc61dd67e2ad6308e997cb9bcebf0d5a22335c..076e06e8215bd1bca81f5c1cc1cf19e3305d6a01 100644 (file)
@@ -334,7 +334,7 @@ static int prompt_locale(int rfd) {
 
         r = read_credential("firstboot.locale-messages", (void**) &arg_locale_messages, NULL);
         if (r < 0)
-                log_debug_errno(r, "Failed to read credential firstboot.locale-message, ignoring: %m");
+                log_debug_errno(r, "Failed to read credential firstboot.locale-messages, ignoring: %m");
         else
                 acquired_from_creds = true;
 
index 23358ae8a2985653f0de40f8b55e718d0d0c1c02..cdcbb0deec8e266e954cc503fbc8127be032af06 100644 (file)
@@ -8,6 +8,7 @@
 #include "bus-error.h"
 #include "bus-locator.h"
 #include "chase.h"
+#include "creds-util.h"
 #include "efi-loader.h"
 #include "env-util.h"
 #include "fd-util.h"
@@ -1281,6 +1282,40 @@ static int add_mounts_from_cmdline(void) {
         return ret;
 }
 
+static int add_mounts_from_creds(void) {
+        _cleanup_free_ void *b = NULL;
+        struct mntent *me;
+        int r, ret = 0;
+        size_t bs;
+
+        r = read_credential_with_decryption(
+                        in_initrd() ? "fstab.extra.initrd" : "fstab.extra",
+                        &b, &bs);
+        if (r <= 0)
+                return r;
+
+        _cleanup_fclose_ FILE *f = NULL;
+        f = fmemopen_unlocked(b, bs, "r");
+        if (!f)
+                return log_oom();
+
+        while ((me = getmntent(f))) {
+                r = parse_fstab_one(
+                                "/run/credentials",
+                                me->mnt_fsname,
+                                me->mnt_dir,
+                                me->mnt_type,
+                                me->mnt_opts,
+                                me->mnt_passno,
+                                /* initrd = */ false,
+                                /* use_swap_enabled = */ true);
+                if (r < 0 && ret >= 0)
+                        ret = r;
+        }
+
+        return ret;
+}
+
 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
         int r;
 
@@ -1466,7 +1501,7 @@ static int run_generator(void) {
         (void) determine_usr();
 
         if (arg_sysroot_check) {
-                r = parse_fstab(true);
+                r = parse_fstab(/* initrd= */ true);
                 if (r == 0)
                         log_debug("Nothing interesting found, not doing daemon-reload.");
                 if (r > 0)
@@ -1496,13 +1531,13 @@ static int run_generator(void) {
         /* Honour /etc/fstab only when that's enabled */
         if (arg_fstab_enabled) {
                 /* Parse the local /etc/fstab, possibly from the initrd */
-                r = parse_fstab(false);
+                r = parse_fstab(/* initrd= */ false);
                 if (r < 0 && ret >= 0)
                         ret = r;
 
                 /* If running in the initrd also parse the /etc/fstab from the host */
                 if (in_initrd())
-                        r = parse_fstab(true);
+                        r = parse_fstab(/* initrd= */ true);
                 else
                         r = generator_enable_remount_fs_service(arg_dest);
                 if (r < 0 && ret >= 0)
@@ -1513,6 +1548,10 @@ static int run_generator(void) {
         if (r < 0 && ret >= 0)
                 ret = r;
 
+        r = add_mounts_from_creds();
+        if (r < 0 && ret >= 0)
+                ret = r;
+
         return ret;
 }
 
index d255e90db52ec722484f8725355d87992d7b8efc..4379b4b6480dadc32aec2ecc456a95500986fde4 100644 (file)
@@ -5,16 +5,18 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "creds-util.h"
 #include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "generator.h"
+#include "initrd-util.h"
 #include "log.h"
 #include "mkdir-label.h"
 #include "parse-util.h"
 #include "path-util.h"
-#include "process-util.h"
 #include "proc-cmdline.h"
+#include "process-util.h"
 #include "strv.h"
 #include "terminal-util.h"
 #include "unit-name.h"
@@ -24,8 +26,7 @@ static const char *arg_dest = NULL;
 static bool arg_enabled = true;
 
 static int add_symlink(const char *fservice, const char *tservice) {
-        char *from, *to;
-        int r;
+        const char *from, *to;
 
         assert(fservice);
         assert(tservice);
@@ -35,8 +36,7 @@ static int add_symlink(const char *fservice, const char *tservice) {
 
         (void) mkdir_parents_label(to, 0755);
 
-        r = symlink(from, to);
-        if (r < 0) {
+        if (symlink(from, to) < 0) {
                 /* In case console=hvc0 is passed this will very likely result in EEXIST */
                 if (errno == EEXIST)
                         return 0;
@@ -141,6 +141,56 @@ static int run_container(void) {
         }
 }
 
+static int add_credential_gettys(void) {
+        static const struct {
+                const char *credential_name;
+                int (*func)(const char *tty);
+        } table[] = {
+                { "getty.ttys.serial",    add_serial_getty     },
+                { "getty.ttys.container", add_container_getty  },
+        };
+        int r;
+
+        FOREACH_ARRAY(t, table, ELEMENTSOF(table)) {
+                _cleanup_free_ char *b = NULL;
+                size_t sz = 0;
+
+                r = read_credential_with_decryption(t->credential_name, (void*) &b, &sz);
+                if (r < 0)
+                        return r;
+                if (r == 0)
+                        continue;
+
+                _cleanup_fclose_ FILE *f = NULL;
+                f = fmemopen_unlocked(b, sz, "r");
+                if (!f)
+                        return log_oom();
+
+                for (;;) {
+                        _cleanup_free_ char *tty = NULL;
+                        char *s;
+
+                        r = read_line(f, PATH_MAX, &tty);
+                        if (r == 0)
+                                break;
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to parse credential %s: %m", t->credential_name);
+                                break;
+                        }
+
+                        s = strstrip(tty);
+                        if (startswith(s, "#"))
+                                continue;
+
+                        r = t->func(s);
+                        if (r < 0)
+                                return r;
+                }
+        }
+
+        return 0;
+}
+
 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
         int r;
 
@@ -163,6 +213,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
 
         assert_se(arg_dest = dest);
 
+        if (in_initrd()) {
+                log_debug("Skipping generator, running in the initrd.");
+                return EXIT_SUCCESS;
+        }
+
         r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
         if (r < 0)
                 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
@@ -183,6 +238,10 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
                 return 0;
         }
 
+        r = add_credential_gettys();
+        if (r < 0)
+                return r;
+
         if (detect_container() > 0)
                 /* Add console shell and look at $container_ttys, but don't do add any
                  * further magic if we are in a container. */
@@ -226,7 +285,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
 
                 p = path_join("/sys/class/tty", j);
                 if (!p)
-                        return -ENOMEM;
+                        return log_oom();
                 if (access(p, F_OK) < 0)
                         continue;
 
index c7791178b47ea1674b4467ea68e4b2372d282c46..fc33a3eb625834dcc42b04390b2eddbb4d7381bb 100644 (file)
@@ -1922,6 +1922,8 @@ static int determine_current_padding(
                 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!");
 
         offset = fdisk_partition_get_end(p);
+        assert(offset < UINT64_MAX);
+        offset++; /* The end is one sector before the next partition or padding. */
         assert(offset < UINT64_MAX / secsz);
         offset *= secsz;
 
@@ -2108,7 +2110,7 @@ static int context_load_partition_table(Context *context) {
                         return log_error_errno(errno, "Failed to stat block device '%s': %m", context->node);
 
                 if (S_ISREG(st.st_mode) && st.st_size == 0) {
-                        /* User the fallback values if we have no better idea */
+                        /* Use the fallback values if we have no better idea */
                         context->sector_size = arg_sector_size ?: 512;
                         context->grain_size = 4096;
                         return /* from_scratch = */ true;
index d8c06b1d2dcdf1bc78d6beb652ffdd931dcea52c..89cc5fadb6ae9c643e3b05c8f62d295fb5724945 100644 (file)
@@ -5,6 +5,7 @@
 #include <unistd.h>
 
 #include "generator.h"
+#include "initrd-util.h"
 #include "log.h"
 #include "mkdir-label.h"
 #include "string-util.h"
@@ -58,6 +59,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
 
         assert_se(arg_dest = dest);
 
+        if (in_initrd()) {
+                log_debug("Skipping generator, running in the initrd.");
+                return EXIT_SUCCESS;
+        }
+
         if (check_executable(RC_LOCAL_PATH) >= 0) {
                 log_debug("Automatically adding rc-local.service.");
 
index 5c0c4e21aa06a5f2a202f2c348538564c89636a4..7bfe02573a3eb5761d5ba2cc5406e704fbf9f783 100644 (file)
@@ -7,10 +7,13 @@
 
 #include "acl-util.h"
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "user-util.h"
 
+#if HAVE_ACL
+
 int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *ret_entry) {
         acl_entry_t i;
         int r;
@@ -489,3 +492,161 @@ int fd_add_uid_acl_permission(
 
         return 0;
 }
+
+int fd_acl_make_read_only(int fd) {
+        _cleanup_(acl_freep) acl_t acl = NULL;
+        bool changed = false;
+        acl_entry_t i;
+        int r;
+
+        assert(fd >= 0);
+
+        /* Safely drops all W bits from all relevant ACL entries of the file, without changing entries which
+         * are masked by the ACL mask */
+
+        acl = acl_get_fd(fd);
+        if (!acl) {
+
+                if (!ERRNO_IS_NOT_SUPPORTED(errno))
+                        return -errno;
+
+                /* No ACLs? Then just update the regular mode_t */
+                return fd_acl_make_read_only_fallback(fd);
+        }
+
+        for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+             r > 0;
+             r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+                acl_permset_t permset;
+                acl_tag_t tag;
+                int b;
+
+                if (acl_get_tag_type(i, &tag) < 0)
+                        return -errno;
+
+                /* These three control the x bits overall (as ACL_MASK affects all remaining tags) */
+                if (!IN_SET(tag, ACL_USER_OBJ, ACL_MASK, ACL_OTHER))
+                        continue;
+
+                if (acl_get_permset(i, &permset) < 0)
+                        return -errno;
+
+                b = acl_get_perm(permset, ACL_WRITE);
+                if (b < 0)
+                        return -errno;
+
+                if (b) {
+                        if (acl_delete_perm(permset, ACL_WRITE) < 0)
+                                return -errno;
+
+                        changed = true;
+                }
+        }
+        if (r < 0)
+                return -errno;
+
+        if (!changed)
+                return 0;
+
+        if (acl_set_fd(fd, acl) < 0) {
+                if (!ERRNO_IS_NOT_SUPPORTED(errno))
+                        return -errno;
+
+                return fd_acl_make_read_only_fallback(fd);
+        }
+
+        return 1;
+}
+
+int fd_acl_make_writable(int fd) {
+        _cleanup_(acl_freep) acl_t acl = NULL;
+        acl_entry_t i;
+        int r;
+
+        /* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
+         * not the obvious inverse of fd_acl_make_read_only() hence!) */
+
+        acl = acl_get_fd(fd);
+        if (!acl) {
+                if (!ERRNO_IS_NOT_SUPPORTED(errno))
+                        return -errno;
+
+                /* No ACLs? Then just update the regular mode_t */
+                return fd_acl_make_writable_fallback(fd);
+        }
+
+        for (r = acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+             r > 0;
+             r = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+                acl_permset_t permset;
+                acl_tag_t tag;
+                int b;
+
+                if (acl_get_tag_type(i, &tag) < 0)
+                        return -errno;
+
+                if (tag != ACL_USER_OBJ)
+                        continue;
+
+                if (acl_get_permset(i, &permset) < 0)
+                        return -errno;
+
+                b = acl_get_perm(permset, ACL_WRITE);
+                if (b < 0)
+                        return -errno;
+
+                if (b)
+                        return 0; /* Already set? Then there's nothing to do. */
+
+                if (acl_add_perm(permset, ACL_WRITE) < 0)
+                        return -errno;
+
+                break;
+        }
+        if (r < 0)
+                return -errno;
+
+        if (acl_set_fd(fd, acl) < 0) {
+                if (!ERRNO_IS_NOT_SUPPORTED(errno))
+                        return -errno;
+
+                return fd_acl_make_writable_fallback(fd);
+        }
+
+        return 1;
+}
+#endif
+
+int fd_acl_make_read_only_fallback(int fd) {
+        struct stat st;
+
+        assert(fd >= 0);
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if ((st.st_mode & 0222) == 0)
+                return 0;
+
+        if (fchmod(fd, st.st_mode & 0555) < 0)
+                return -errno;
+
+        return 1;
+}
+
+int fd_acl_make_writable_fallback(int fd) {
+        struct stat st;
+
+        assert(fd >= 0);
+
+        if (fstat(fd, &st) < 0)
+                return -errno;
+
+        if ((st.st_mode & 0200) != 0) /* already set */
+                return 0;
+
+        if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0)
+                return -errno;
+
+        return 1;
+}
index 978389ed1d7abf35797a54733cb9c90508fa3909..ef315c2f11d295e28dd691ceab1d1bb9e46cd27e 100644 (file)
@@ -4,6 +4,9 @@
 #include <errno.h>
 #include <unistd.h>
 
+int fd_acl_make_read_only_fallback(int fd);
+int fd_acl_make_writable_fallback(int fd);
+
 #if HAVE_ACL
 #include <acl/libacl.h>
 #include <stdbool.h>
@@ -24,6 +27,9 @@ int parse_acl(
 int acls_for_file(const char *path, acl_type_t type, acl_t new, acl_t *ret);
 int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
 
+int fd_acl_make_read_only(int fd);
+int fd_acl_make_writable(int fd);
+
 /* acl_free takes multiple argument types.
  * Multiple cleanup functions are necessary. */
 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(acl_t, acl_free, NULL);
@@ -42,4 +48,13 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gid_t*, acl_free_gid_tp, NULL);
 static inline int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask) {
         return -EOPNOTSUPP;
 }
+
+static inline int fd_acl_make_read_only(int fd) {
+        return fd_acl_make_read_only_fallback(fd);
+}
+
+static inline int fd_acl_make_writable(int fd) {
+        return fd_acl_make_writable_fallback(fd);
+}
+
 #endif
index 83a4c84aa9a4f8a0b12f5ed5166074de5039f881..f54481e9b466cf3fcd556c3218eec7ac3727a1e1 100644 (file)
@@ -125,6 +125,80 @@ int read_credential(const char *name, void **ret, size_t *ret_size) {
                         (char**) ret, ret_size);
 }
 
+int read_credential_with_decryption(const char *name, void **ret, size_t *ret_size) {
+        _cleanup_(erase_and_freep) void *data = NULL;
+        _cleanup_free_ char *fn = NULL;
+        size_t sz = 0;
+        const char *d;
+        int r;
+
+        assert(ret);
+
+        /* Just like read_credential() but will also look for encrypted credentials. Note that services only
+         * receive decrypted credentials, hence use read_credential() for those. This helper here is for
+         * generators, i.e. code that runs outside of service context, and thus has no decrypted credentials
+         * yet.
+         *
+         * Note that read_credential_harder_and_warn() logs on its own, while read_credential() does not!
+         * (It's a lot more complex and error prone given its TPM2 connectivty, and is generally called from
+         * generators only where logging is OK).
+         *
+         * Error handling is also a bit different: if we can't find a credential we'll return 0 and NULL
+         * pointers/zero size, rather than -ENXIO/-ENOENT. */
+
+        if (!credential_name_valid(name))
+                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid credential name: %s", name);
+
+        r = read_credential(name, ret, ret_size);
+        if (r >= 0)
+                return 1; /* found */
+        if (!IN_SET(r, -ENXIO, -ENOENT))
+                return log_error_errno(r, "Failed read unencrypted credential '%s': %m", name);
+
+        r = get_encrypted_credentials_dir(&d);
+        if (r == -ENXIO)
+                goto not_found;
+        if (r < 0)
+                return log_error_errno(r, "Failed to determine encrypted credentials directory: %m");
+
+        fn = path_join(d, name);
+        if (!fn)
+                return log_oom();
+
+        r = read_full_file_full(
+                        AT_FDCWD, fn,
+                        UINT64_MAX, SIZE_MAX,
+                        READ_FULL_FILE_SECURE,
+                        NULL,
+                        (char**) data, &sz);
+        if (r == -ENOENT)
+                goto not_found;
+        if (r < 0)
+                return log_error_errno(r, "Failed to read encrypted credential data: %m");
+
+        r = decrypt_credential_and_warn(
+                        name,
+                        now(CLOCK_REALTIME),
+                        /* tpm2_device = */ NULL,
+                        /* tpm2_signature_path = */ NULL,
+                        data,
+                        sz,
+                        ret,
+                        ret_size);
+        if (r < 0)
+                return r;
+
+        return 1; /* found */
+
+not_found:
+        *ret = NULL;
+
+        if (ret_size)
+                *ret_size = 0;
+
+        return 0; /* not found */
+}
+
 int read_credential_strings_many_internal(
                 const char *first_name, char **first_value,
                 ...) {
index 1742678cb95c3149d31137729d74973974e9c4e5..8fbd61e9fe6b48780e0a1cc145116d27aa8f73fd 100644 (file)
@@ -35,7 +35,8 @@ int get_encrypted_credentials_dir(const char **ret);
 #define SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@system"
 #define ENCRYPTED_SYSTEM_CREDENTIALS_DIRECTORY "/run/credentials/@encrypted"
 
-int read_credential(const char *name, void **ret, size_t *ret_size);
+int read_credential(const char *name, void **ret, size_t *ret_size); /* use in services! */
+int read_credential_with_decryption(const char *name, void **ret, size_t *ret_size); /* use in generators + pid1! */
 
 int read_credential_strings_many_internal(const char *first_name, char **first_value, ...);
 
index f27c3d768bfcd5b7cd0b4102d3f0a6299d55cc91..e059c71105157ccf594161ead0563c659b5dc629 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "alloc-util.h"
 #include "chase.h"
+#include "creds-util.h"
 #include "fd-util.h"
 #include "id128-util.h"
 #include "io-util.h"
 #include "umask-util.h"
 #include "virt.h"
 
+static int acquire_machine_id_from_credential(sd_id128_t *ret) {
+        _cleanup_free_ char *buf = NULL;
+        int r;
+
+        r = read_credential_with_decryption("system.machine_id", (void**) &buf, /* ret_size= */ NULL);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to read system.machine_id credential, ignoring: %m");
+        if (r == 0) /* not found */
+                return -ENXIO;
+
+        r = sd_id128_from_string(buf, ret);
+        if (r < 0)
+                return log_warning_errno(r, "Failed to parse system.machine_id credential, ignoring: %m");
+
+        log_info("Initializing machine ID from credential.");
+        return 0;
+}
+
 static int generate_machine_id(const char *root, sd_id128_t *ret) {
         _cleanup_close_ int fd = -EBADF;
         int r;
@@ -41,6 +60,11 @@ static int generate_machine_id(const char *root, sd_id128_t *ret) {
         }
 
         if (isempty(root) && running_in_chroot() <= 0) {
+                /* Let's use a system credential for the machine ID if we can */
+                r = acquire_machine_id_from_credential(ret);
+                if (r >= 0)
+                        return r;
+
                 /* If that didn't work, see if we are running in a container,
                  * and a machine ID was passed in via $container_uuid the way
                  * libvirt/LXC does it */
index 1e015bd38e5aca23db581699c4b7a9e8476836ae..d643b2bd09358a651e468a88ab3b51d5ce1709aa 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: LGPL-2.1-or-later
 
 shared_sources = files(
+        'acl-util.c',
         'acpi-fpdt.c',
         'apparmor-util.c',
         'ask-password-api.c',
@@ -189,7 +190,6 @@ syscall_list_h = custom_target(
 
 if conf.get('HAVE_ACL') == 1
         shared_sources += files(
-                'acl-util.c',
                 'devnode-acl.c',
         )
 endif
index b65416b03bdf2eb13e55f6cbcdd3eddd2ffba4f2..f0bf821430613ffd6c90314d3d44fc01a6709277 100644 (file)
@@ -1469,3 +1469,62 @@ int trigger_automount_at(int dir_fd, const char *path) {
 
         return 0;
 }
+
+unsigned long credentials_fs_mount_flags(bool ro) {
+        /* A tight set of mount flags for credentials mounts */
+        return MS_NODEV|MS_NOEXEC|MS_NOSUID|ms_nosymfollow_supported()|(ro ? MS_RDONLY : 0);
+}
+
+int mount_credentials_fs(const char *path, size_t size, bool ro) {
+        _cleanup_free_ char *opts = NULL;
+        int r, noswap_supported;
+
+        /* Mounts a file system we can place credentials in, i.e. with tight access modes right from the
+         * beginning, and ideally swapping turned off. In order of preference:
+         *
+         *      1. tmpfs if it supports "noswap"
+         *      2. ramfs
+         *      3. tmpfs if it doesn't support "noswap"
+         */
+
+        noswap_supported = mount_option_supported("tmpfs", "noswap", NULL); /* Check explicitly to avoid kmsg noise */
+        if (noswap_supported > 0) {
+                _cleanup_free_ char *noswap_opts = NULL;
+
+                if (asprintf(&noswap_opts, "mode=0700,nr_inodes=1024,size=%zu,noswap", size) < 0)
+                        return -ENOMEM;
+
+                /* Best case: tmpfs with noswap (needs kernel >= 6.3) */
+
+                r = mount_nofollow_verbose(
+                                LOG_DEBUG,
+                                "tmpfs",
+                                path,
+                                "tmpfs",
+                                credentials_fs_mount_flags(ro),
+                                noswap_opts);
+                if (r >= 0)
+                        return r;
+        }
+
+        r = mount_nofollow_verbose(
+                        LOG_DEBUG,
+                        "ramfs",
+                        path,
+                        "ramfs",
+                        credentials_fs_mount_flags(ro),
+                        "mode=0700");
+        if (r >= 0)
+                return r;
+
+        if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", size) < 0)
+                return -ENOMEM;
+
+        return mount_nofollow_verbose(
+                        LOG_DEBUG,
+                        "tmpfs",
+                        path,
+                        "tmpfs",
+                        credentials_fs_mount_flags(ro),
+                        opts);
+}
index ceb3964a74883ecb0963916f94e2a33394c0329c..7ee6750044bca8e358409d2ad33af589bc07a821 100644 (file)
@@ -141,3 +141,6 @@ int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mo
 int make_mount_point_inode_from_path(const char *source, const char *dest, mode_t mode);
 
 int trigger_automount_at(int dir_fd, const char *path);
+
+unsigned long credentials_fs_mount_flags(bool ro);
+int mount_credentials_fs(const char *path, size_t size, bool ro);
index 83b06902ab3046046d381474c4bb6c4ecfbacb1f..a1782d5c059524fd74329811cc02546c377532bb 100644 (file)
@@ -5,6 +5,7 @@
 
 #include "fs-util.h"
 #include "generator.h"
+#include "initrd-util.h"
 #include "log.h"
 #include "path-util.h"
 #include "proc-cmdline.h"
@@ -61,6 +62,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
 
         assert_se(arg_dest = dest_early);
 
+        if (in_initrd()) {
+                log_debug("Skipping generator, running in the initrd.");
+                return EXIT_SUCCESS;
+        }
+
         r = generate_symlink();
         if (r <= 0)
                 return r;
index 632f4a1aac7a09dd4f88e43c858a588d08ba7198..30f82d573699d3ed4902ede2ea0024f39d82d3f9 100644 (file)
@@ -14,6 +14,7 @@
 #include "generator.h"
 #include "hashmap.h"
 #include "hexdecoct.h"
+#include "initrd-util.h"
 #include "install.h"
 #include "log.h"
 #include "main-func.h"
@@ -899,6 +900,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
         SysvStub *service;
         int r;
 
+        if (in_initrd()) {
+                log_debug("Skipping generator, running in the initrd.");
+                return EXIT_SUCCESS;
+        }
+
         assert_se(arg_dest = dest_late);
 
         r = lookup_paths_init_or_warn(&lp, RUNTIME_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL);
index 093eaaa01b600810c1b7885aacaca0b5d214cb9c..eb9678a7d9478828a5d4235f44e84531c9044e59 100644 (file)
@@ -69,4 +69,62 @@ TEST_RET(add_acls_for_user) {
         return 0;
 }
 
+TEST(fd_acl_make_read_only) {
+        _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-empty.XXXXXX";
+        _cleanup_close_ int fd = -EBADF;
+        const char *cmd;
+        struct stat st;
+
+        fd = mkostemp_safe(fn);
+        assert_se(fd >= 0);
+
+        /* make it more exciting */
+        (void) fd_add_uid_acl_permission(fd, 1, ACL_READ|ACL_WRITE|ACL_EXECUTE);
+
+        assert_se(fstat(fd, &st) >= 0);
+        assert_se((st.st_mode & 0200) == 0200);
+
+        cmd = strjoina("getfacl -p ", fn);
+        assert_se(system(cmd) == 0);
+
+        cmd = strjoina("stat ", fn);
+        assert_se(system(cmd) == 0);
+
+        log_info("read-only");
+        assert_se(fd_acl_make_read_only(fd));
+
+        assert_se(fstat(fd, &st) >= 0);
+        assert_se((st.st_mode & 0222) == 0000);
+
+        cmd = strjoina("getfacl -p ", fn);
+        assert_se(system(cmd) == 0);
+
+        cmd = strjoina("stat ", fn);
+        assert_se(system(cmd) == 0);
+
+        log_info("writable");
+        assert_se(fd_acl_make_writable(fd));
+
+        assert_se(fstat(fd, &st) >= 0);
+        assert_se((st.st_mode & 0222) == 0200);
+
+        cmd = strjoina("getfacl -p ", fn);
+        assert_se(system(cmd) == 0);
+
+        cmd = strjoina("stat ", fn);
+        assert_se(system(cmd) == 0);
+
+        log_info("read-only");
+        assert_se(fd_acl_make_read_only(fd));
+
+        assert_se(fstat(fd, &st) >= 0);
+        assert_se((st.st_mode & 0222) == 0000);
+
+        cmd = strjoina("getfacl -p ", fn);
+        assert_se(system(cmd) == 0);
+
+        cmd = strjoina("stat ", fn);
+        assert_se(system(cmd) == 0);
+}
+
 DEFINE_TEST_MAIN(LOG_INFO);
index 443be8761e0edfbd4d7b447c6cea921b0c9cc99d..c0a9d7a53d506cb33ecdaa1c5a3a267130799990 100755 (executable)
@@ -15,6 +15,8 @@ QEMU_CREDS=(
     "-smbios type=11,value=io.systemd.credential.binary:binarysmbioscredential=bWFnaWNiaW5hcnlkYXRh"
     "-smbios type=11,value=io.systemd.credential.binary:sysusers.extra=dSBjcmVkdGVzdHVzZXIK"
     "-smbios type=11,value=io.systemd.credential.binary:tmpfiles.extra=ZiAvdG1wL3NvdXJjZWRmcm9tY3JlZGVudGlhbCAtIC0gLSAtIHRtcGZpbGVzc2VjcmV0Cg=="
+    "-smbios type=11,value=io.systemd.credential.binary:fstab.extra=aW5qZWN0ZWQgL2luamVjdGVkIHRtcGZzIFgtbW91bnQubWtkaXIgMCAwCg=="
+    "-smbios type=11,value=io.systemd.credential:getty.ttys.container=idontexist"
 )
 QEMU_OPTIONS="${QEMU_OPTIONS:-} ${QEMU_CREDS[*]}"
 
@@ -23,6 +25,7 @@ KERNEL_CREDS=(
     "systemd.set_credential=sysctl.extra:kernel.domainname=sysctltest"
     "systemd.set_credential=login.motd:hello"
     "systemd.set_credential=login.issue:welcome"
+    "systemd.set_credential_binary=waldi:d29vb29mZmZ3dWZmZnd1ZmYK"
     "rd.systemd.import_credentials=no"
 )
 KERNEL_APPEND="${KERNEL_APPEND:-} ${KERNEL_CREDS[*]}"
@@ -38,4 +41,27 @@ test_append_files() {
     generate_module_dependencies
 }
 
+run_qemu_hook() {
+    local td="$WORKDIR"/initrd.extra."$RANDOM"
+    mkdir -m 755 "$td"
+    add_at_exit_handler "rm -rf $td"
+    mkdir -m 755 "$td/etc" "$td"/etc/systemd "$td"/etc/systemd/system "$td"/etc/systemd/system/initrd.target.wants
+
+    cat > "$td"/etc/systemd/system/initrdcred.service <<EOF
+[Unit]
+Description=populate initrd credential dir
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=sh -c "mkdir -m 0755 -p /run/credentials && mkdir -m 0700 /run/credentials/@initrd && umask 0077 && echo guatemala > /run/credentials/@initrd/myinitrdcred"
+EOF
+    ln -s ../initrdcred.service "$td"/etc/systemd/system/initrd.target.wants/initrdcred.service
+
+    ( cd "$td" && find . | cpio -o -H newc -R root:root > "$td".cpio )
+    add_at_exit_handler "rm $td.cpio"
+
+    INITRD_EXTRA="$td.cpio"
+}
+
 do_test "$@"
index 9d0ea73a42192218cf46ae647b85fa91658b48fe..41e1a0d57df48bb8e7b378c086132f6caedd301c 100644 (file)
@@ -428,9 +428,25 @@ qemu_min_version() {
     printf "%s\n%s\n" "$1" "$qemu_ver" | sort -V -C
 }
 
+# Pads a file to multiple of 4 bytes
+pad4_file() {
+    local size
+    size=$(stat -c "%s" "$1")
+    local padded
+    padded=$((((size + 3) / 4) * 4))
+    truncate -s "$padded" "$1"
+}
+
 # Return 0 if qemu did run (then you must check the result state/logs for actual
 # success), or 1 if qemu is not available.
 run_qemu() {
+    if declare -F run_qemu_hook >/dev/null; then
+        if ! run_qemu_hook "${workspace}"; then
+            derror "check_qemu_hook() returned with EC > 0"
+            ret=4
+        fi
+    fi
+
     # If the test provided its own initrd, use it (e.g. TEST-24)
     if [[ -z "$INITRD" && -f "${TESTDIR:?}/initrd.img" ]]; then
         INITRD="$TESTDIR/initrd.img"
@@ -577,7 +593,28 @@ run_qemu() {
     fi
 
     if [[ -n "$INITRD" ]]; then
-        qemu_options+=(-initrd "$INITRD")
+        if [[ -n "$INITRD_EXTRA" ]]; then
+            # An addition initrd has been specified, let's combine it with the main one.
+            local t="$WORKDIR"/initrd.combined."$RANDOM"
+
+            # First, show contents of additional initrd
+            echo "Additional initrd contents:"
+            cpio -tv < "$INITRD_EXTRA"
+
+            # Copy the main initrd
+            zstd -d -c -f "$INITRD" > "$t"
+            add_at_exit_handler "rm $t"
+            # Kernel requires this to be padded to multiple of 4 bytes with zeroes
+            pad4_file "$t"
+
+            # Copy the additional initrd
+            cat "$INITRD_EXTRA" >> "$t"
+            pad4_file "$t"
+
+            qemu_options+=(-initrd "$t")
+        else
+            qemu_options+=(-initrd "$INITRD")
+        fi
     fi
 
     # Let's use KVM if possible
diff --git a/test/testsuite-03.units/propagatestopto-and-pullin.target b/test/testsuite-03.units/propagatestopto-and-pullin.target
new file mode 100644 (file)
index 0000000..8e409af
--- /dev/null
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Requires=sleep-infinity-simple.service
+After=sleep-infinity-simple.service
+PropagatesStopTo=sleep-infinity-simple.service
diff --git a/test/testsuite-03.units/propagatestopto-only.target b/test/testsuite-03.units/propagatestopto-only.target
new file mode 100644 (file)
index 0000000..327b7c1
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+PropagatesStopTo=sleep-infinity-simple.service
diff --git a/test/testsuite-03.units/sleep-infinity-simple.service b/test/testsuite-03.units/sleep-infinity-simple.service
new file mode 100644 (file)
index 0000000..211346d
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+[Unit]
+Description=Sleep infinitely
+
+[Service]
+Type=simple
+ExecStart=/bin/sleep infinity
index 6ed7d8687a1329334dfc5106ba127c85be829901..22c500debf019555586fff06cd1773b9a92e9ada 100755 (executable)
@@ -3,6 +3,9 @@
 set -eux
 set -o pipefail
 
+# shellcheck source=test/units/util.sh
+. "$(dirname "$0")"/util.sh
+
 # Simple test for that daemon-reexec works in container.
 # See: https://github.com/systemd/systemd/pull/23883
 systemctl daemon-reexec
@@ -127,4 +130,19 @@ for i in {0..19}; do
     systemctl start "transaction-cycle$i.service"
 done
 
+# Test PropagatesStopTo= when restart (issue #26839)
+systemctl start propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+
+systemctl restart propagatestopto-and-pullin.target
+systemctl --quiet is-active propagatestopto-and-pullin.target
+systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl start propagatestopto-only.target
+systemctl --quiet is-active propagatestopto-only.target
+systemctl --quiet is-active sleep-infinity-simple.service
+
+systemctl restart propagatestopto-only.target
+assert_rc 3 systemctl --quiet is-active sleep-infinity-simple.service
+
 touch /testok
index 89466a5e33187480f6becc69831db1eba79d747a..6c4e0065009bae139ab516e177de3de2952d4cc3 100755 (executable)
@@ -176,6 +176,7 @@ if systemd-detect-virt -q -c ; then
 elif [ -d /sys/firmware/qemu_fw_cfg/by_name ]; then
     # Verify that passing creds through kernel cmdline works
     [ "$(systemd-creds --system cat kernelcmdlinecred)" = "uff" ]
+    [ "$(systemd-creds --system cat waldi)" = "woooofffwufffwuff" ]
 
     # And that it also works via SMBIOS
     [ "$(systemd-creds --system cat smbioscredential)" = "magicdata" ]
@@ -301,6 +302,18 @@ systemd-run -p DynamicUser=yes -p 'LoadCredential=os:/etc/os-release' \
             --pipe \
             true | cmp /etc/os-release
 
+if ! systemd-detect-virt -q -c ; then
+    # Validate that the credential we inserted via the initrd logic arrived
+    test "$(systemd-creds cat --system myinitrdcred)" = "guatemala"
+
+    # Check that the fstab credential logic worked
+    test -d /injected
+    grep -q /injected /proc/self/mountinfo
+
+    # Make sure the getty generator processed the credentials properly
+    systemctl -P Wants show getty.target | grep -q container-getty@idontexist.service
+fi
+
 systemd-analyze log-level info
 
 echo OK >/testok
index 6ddb96d26e30e07df25ea9a257819c9d05e5e1c1..c7510dd3fcafa6ef70a597e8b3db1371b9c403f4 100755 (executable)
@@ -1021,6 +1021,88 @@ EOF
     systemd-dissect -U "$imgs/mnt"
 }
 
+testcase_free_area_calculation() {
+    local defs imgs output
+
+    if ! command -v mksquashfs >/dev/null; then
+        echo "Skipping free area calculation test without squashfs."
+        return
+    fi
+
+    defs="$(mktemp --directory "/tmp/test-repart.defs.XXXXXXXXXX")"
+    imgs="$(mktemp --directory "/var/tmp/test-repart.imgs.XXXXXXXXXX")"
+    # shellcheck disable=SC2064
+    trap "rm -rf '$defs' '$imgs'" RETURN
+    chmod a+rx "$defs"
+
+    # https://github.com/systemd/systemd/issues/28225
+    echo "*** free area calculation ***"
+
+    tee "$defs/00-ESP.conf" <<EOF
+[Partition]
+Type         = esp
+Label        = ESP
+Format       = vfat
+
+SizeMinBytes = 128M
+SizeMaxBytes = 128M
+
+# Sufficient for testing
+CopyFiles    = /etc:/
+EOF
+
+    tee "$defs/10-os.conf" <<EOF
+[Partition]
+Type           = root-${architecture}
+Label          = test
+Format         = squashfs
+
+Minimize       = best
+# Sufficient for testing
+CopyFiles      = /etc/:/
+
+VerityMatchKey = os
+Verity         = data
+EOF
+
+    tee "$defs/11-os-verity.conf" <<EOF
+[Partition]
+Type           = root-${architecture}-verity
+Label          = test
+
+Minimize       = best
+
+VerityMatchKey = os
+Verity         = hash
+EOF
+
+    # Set sector size for VFAT to 512 bytes because there will not be enough FAT clusters otherwise
+    output1=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
+                                              --definitions="$defs" \
+                                              --seed="$seed" \
+                                              --dry-run=no \
+                                              --empty=create \
+                                              --size=auto \
+                                              --sector-size=4096 \
+                                              --defer-partitions=esp \
+                                              --json=pretty \
+                                              "$imgs/zzz")
+
+    # The second invocation
+    output2=$(SYSTEMD_REPART_MKFS_OPTIONS_VFAT="-S 512" systemd-repart \
+                                              --definitions="$defs" \
+                                              --seed="$seed" \
+                                              --dry-run=no \
+                                              --empty=allow \
+                                              --size=auto \
+                                              --sector-size=4096 \
+                                              --defer-partitions=esp \
+                                              --json=pretty \
+                                              "$imgs/zzz")
+
+    diff -u <(echo "$output1" | grep -E "(offset|raw_size|raw_padding)") <(echo "$output2" | grep -E "(offset|raw_size|raw_padding)")
+}
+
 test_sector() {
     local defs imgs output loop
     local start size ratio
index 4830a6a21774ec35d4b47c4f43f72705ec4b124d..981d0f278ee33919cc76fb4431f22e483dadd251 100644 (file)
@@ -10,6 +10,8 @@
 [Unit]
 Description=Load/Save Screen Backlight Brightness of %i
 Documentation=man:systemd-backlight@.service(8)
+ConditionPathExists=!/etc/initrd-release
+
 DefaultDependencies=no
 Conflicts=shutdown.target
 Before=sysinit.target shutdown.target
index ce9187bbfedd8844ed78a829954018406b4944d3..f234184d78070c88c0a1f196c4eceb7da5efdb31 100644 (file)
@@ -10,6 +10,7 @@
 [Unit]
 Description=Automatic Boot Loader Update
 Documentation=man:bootctl(1)
+ConditionPathExists=!/etc/initrd-release
 
 DefaultDependencies=no
 After=local-fs.target
index 56de1a15379fb4759218959467d3041ce8d8c6d2..4034d7a557f5f0463613de0d2c43d3871d58718a 100644 (file)
@@ -10,6 +10,8 @@
 [Unit]
 Description=Load/Save RF Kill Switch Status
 Documentation=man:systemd-rfkill.service(8)
+ConditionPathExists=!/etc/initrd-release
+
 DefaultDependencies=no
 BindsTo=sys-devices-virtual-misc-rfkill.device
 Conflicts=shutdown.target
index 81c0e8c9c6caaa60152a8d475ef676bfc9db50e0..588ecd26f464b99fd4000bfecc0f5d3d2eae37c6 100644 (file)
@@ -10,6 +10,7 @@
 [Unit]
 Description=Load/Save RF Kill Switch Status /dev/rfkill Watch
 Documentation=man:systemd-rfkill.socket(8)
+ConditionPathExists=!/etc/initrd-release
 
 DefaultDependencies=no
 BindsTo=sys-devices-virtual-misc-rfkill.device
index 11177dc621f850ba19a05737225849120f4c808b..18c92f9b5a4c35c4d2effc027bd34ea5f0b20d83 100644 (file)
@@ -10,6 +10,8 @@
 [Unit]
 Description=Record Runlevel Change in UTMP
 Documentation=man:systemd-update-utmp-runlevel.service(8) man:utmp(5)
+ConditionPathExists=!/etc/initrd-release
+
 DefaultDependencies=no
 RequiresMountsFor=/var/log/wtmp
 Conflicts=shutdown.target
index f1278fae32e4aed7fbd2c0e8dc5c95f048345662..73a848390e95dabcca981272d22840bc1177633c 100644 (file)
@@ -10,6 +10,7 @@
 [Unit]
 Description=Record System Boot/Shutdown in UTMP
 Documentation=man:systemd-update-utmp.service(8) man:utmp(5)
+ConditionPathExists=!/etc/initrd-release
 
 DefaultDependencies=no
 After=systemd-remount-fs.service systemd-tmpfiles-setup.service auditd.service