summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/CONTAINER_INTERFACE.md15
-rw-r--r--docs/CREDENTIALS.md18
-rw-r--r--man/org.freedesktop.systemd1.xml24
-rw-r--r--man/systemd-ask-password.xml4
-rw-r--r--man/systemd-creds.xml6
-rw-r--r--man/systemd-firstboot.xml4
-rw-r--r--man/systemd-resolved.service.xml4
-rw-r--r--man/systemd-sysctl.service.xml4
-rw-r--r--man/systemd-sysusers.xml4
-rw-r--r--man/systemd-tmpfiles.xml4
-rw-r--r--man/systemd-vconsole-setup.service.xml4
-rw-r--r--man/systemd.exec.xml40
-rw-r--r--man/systemd.link.xml2
-rw-r--r--man/systemd.xml2
-rw-r--r--src/ask-password/ask-password.c4
-rw-r--r--src/core/dbus-execute.c101
-rw-r--r--src/core/execute.c178
-rw-r--r--src/core/execute.h1
-rw-r--r--src/core/load-fragment-gperf.gperf.in1
-rw-r--r--src/core/load-fragment.c106
-rw-r--r--src/core/load-fragment.h3
-rw-r--r--src/shared/bus-unit-util.c11
-rwxr-xr-xtest/units/testsuite-54.sh15
23 files changed, 433 insertions, 122 deletions
diff --git a/docs/CONTAINER_INTERFACE.md b/docs/CONTAINER_INTERFACE.md
index ddeaf8ea4f..2435d4ae97 100644
--- a/docs/CONTAINER_INTERFACE.md
+++ b/docs/CONTAINER_INTERFACE.md
@@ -138,15 +138,16 @@ manager, please consider supporting the following interfaces.
`$container_host_version_id=10`
5. systemd supports passing immutable binary data blobs with limited size and
- restricted access to services via the `LoadCredential=` and `SetCredential=`
- settings. The same protocol may be used to pass credentials from the
- container manager to systemd itself. The credential data should be placed in
- some location (ideally a read-only and non-swappable file system, like
- 'ramfs'), and the absolute path to this directory exported in the
+ restricted access to services via the `ImportCredential=`, `LoadCredential=`
+ and `SetCredential=` settings. The same protocol may be used to pass credentials
+ from the container manager to systemd itself. The credential data should be
+ placed in some location (ideally a read-only and non-swappable file system,
+ like 'ramfs'), and the absolute path to this directory exported in the
`$CREDENTIALS_DIRECTORY` environment variable. If the container managers
does this, the credentials passed to the service manager can be propagated
- to services via `LoadCredential=` (see ...). The container manager can
- choose any path, but `/run/host/credentials` is recommended.
+ to services via `LoadCredential=` or `ImportCredential=` (see ...). The
+ container manager can choose any path, but `/run/host/credentials` is
+ recommended.
## Advanced Integration
diff --git a/docs/CREDENTIALS.md b/docs/CREDENTIALS.md
index 083c7ecc3c..2f6bdd44b2 100644
--- a/docs/CREDENTIALS.md
+++ b/docs/CREDENTIALS.md
@@ -72,6 +72,9 @@ 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. `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,
@@ -323,7 +326,7 @@ systemd-creds --system cat mycred
Or propagated to services further down:
```
-systemd-run -p LoadCredential=mycred -P --wait systemd-creds cat mycred
+systemd-run -p ImportCredential=mycred -P --wait systemd-creds cat mycred
```
## Well-Known Credentials
@@ -430,13 +433,14 @@ a container manager or via qemu) and `/run/credentials/@encrypted/` (for
credentials that must be decrypted/validated before use, such as those from
`systemd-stub`).
-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/`,
+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/`,
`/usr/lib/credstore/`. `LoadCredentialEncrypted=` will also search
-`/etc/credstore.encrypted/` and similar 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
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index 70273bbf64..fc9a79d796 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -3042,6 +3042,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -3627,6 +3629,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -4277,6 +4281,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -5058,6 +5064,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -5655,6 +5663,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -6285,6 +6295,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -6941,6 +6953,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -7466,6 +7480,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -8014,6 +8030,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@@ -8797,6 +8815,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly a(ss) LoadCredentialEncrypted = [...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly as ImportCredential = ['...', ...];
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly as SupplementaryGroups = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s PAMName = '...';
@@ -9308,6 +9328,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<!--property LoadCredentialEncrypted is not documented!-->
+ <!--property ImportCredential is not documented!-->
+
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@@ -9842,6 +9864,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="LoadCredentialEncrypted"/>
+ <variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
diff --git a/man/systemd-ask-password.xml b/man/systemd-ask-password.xml
index 27f77751fe..ffd6c1c4d8 100644
--- a/man/systemd-ask-password.xml
+++ b/man/systemd-ask-password.xml
@@ -141,8 +141,8 @@
<varlistentry>
<term><option>--credential=</option></term>
<listitem><para>Configure a credential to read the password from – if it exists. This may be used in
- conjunction with the <varname>LoadCredential=</varname> and <varname>SetCredential=</varname>
- settings in unit files. See
+ conjunction with the <varname>ImportCredential=</varname>, <varname>LoadCredential=</varname> and
+ <varname>SetCredential=</varname> settings in unit files. See
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details. If not specified, defaults to <literal>password</literal>. This option has no effect if no
credentials directory is passed to the program (i.e. <varname>$CREDENTIALS_DIRECTORY</varname> is not
diff --git a/man/systemd-creds.xml b/man/systemd-creds.xml
index fbe62262af..a5cfe0901a 100644
--- a/man/systemd-creds.xml
+++ b/man/systemd-creds.xml
@@ -38,9 +38,9 @@
processes. They are primarily used for passing cryptographic keys (both public and private) or
certificates, user account information or identity information from the host to services.</para>
- <para>Credentials are configured in unit files via the <varname>LoadCredential=</varname>,
- <varname>SetCredential=</varname>, <varname>LoadCredentialEncrypted=</varname> and
- <varname>SetCredentialEncrypted=</varname> settings, see
+ <para>Credentials are configured in unit files via the <varname>ImportCredential></varname>,
+ <varname>LoadCredential=</varname>, <varname>SetCredential=</varname>,
+ <varname>LoadCredentialEncrypted=</varname> and <varname>SetCredentialEncrypted=</varname> settings, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details.</para>
diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml
index 9984683967..becb5f52ac 100644
--- a/man/systemd-firstboot.xml
+++ b/man/systemd-firstboot.xml
@@ -304,8 +304,8 @@
<title>Credentials</title>
<para><command>systemd-firstboot</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml
index 7cc143fd41..05d20bbf35 100644
--- a/man/systemd-resolved.service.xml
+++ b/man/systemd-resolved.service.xml
@@ -403,8 +403,8 @@ search foobar.com barbar.com
<title>Credentials</title>
<para><command>systemd-resolved</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
diff --git a/man/systemd-sysctl.service.xml b/man/systemd-sysctl.service.xml
index fede8b092d..4174184c15 100644
--- a/man/systemd-sysctl.service.xml
+++ b/man/systemd-sysctl.service.xml
@@ -85,8 +85,8 @@
<title>Credentials</title>
<para><command>systemd-sysctl</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml
index f7ee5e79d9..34d3cab5c7 100644
--- a/man/systemd-sysusers.xml
+++ b/man/systemd-sysusers.xml
@@ -139,8 +139,8 @@
<title>Credentials</title>
<para><command>systemd-sysusers</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname><varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml
index 735f59cc11..3a9699ff4b 100644
--- a/man/systemd-tmpfiles.xml
+++ b/man/systemd-tmpfiles.xml
@@ -246,8 +246,8 @@
<title>Credentials</title>
<para><command>systemd-tmpfiles</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
diff --git a/man/systemd-vconsole-setup.service.xml b/man/systemd-vconsole-setup.service.xml
index 98d9e2ad01..f9f8327a68 100644
--- a/man/systemd-vconsole-setup.service.xml
+++ b/man/systemd-vconsole-setup.service.xml
@@ -53,8 +53,8 @@
<title>Credentials</title>
<para><command>systemd-vconsole-setup</command> supports the service credentials logic as implemented by
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> (see
- <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ <varname>ImportCredential=</varname><varname>LoadCredential=</varname>/<varname>SetCredential=</varname>
+ (see <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
details). The following credentials are used when passed in:</para>
<variablelist>
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 9fb6c9b908..b70b90d667 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -3287,6 +3287,25 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
</varlistentry>
<varlistentry>
+ <term><varname>ImportCredential=</varname><replaceable>GLOB</replaceable></term>
+
+ <listitem><para>Pass one or more credentials to the unit. Takes a credential name for which we'll
+ attempt to find a credential that the service manager itself received under the specified name —
+ which may be used to propagate credentials from an invoking environment (e.g. a container manager
+ that invoked the service manager) into a service. If the credential name is a glob, all credentials
+ matching the glob are passed to the unit. Matching credentials are searched for in the system
+ credentials, the encrypted system credentials, and under <filename>/etc/credstore/</filename>,
+ <filename>/run/credstore/</filename>, <filename>/usr/lib/credstore/</filename>,
+ <filename>/run/credstore.encrypted/</filename>, <filename>/etc/credstore.encrypted/</filename>, and
+ <filename>/usr/lib/credstore.encrypted/</filename> in that order. When multiple credentials of the
+ same name are found, the first one found is used.</para>
+
+ <para>When multiple credentials of the same name are found, credentials found by
+ <varname>LoadCredential=</varname> and <varname>LoadCredentialEncrypted=</varname> take priority over
+ credentials found by <varname>ImportCredential=</varname></para></listitem>.
+ </varlistentry>
+
+ <varlistentry>
<term><varname>SetCredential=</varname><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
<term><varname>SetCredentialEncrypted=</varname><replaceable>ID</replaceable>:<replaceable>VALUE</replaceable></term>
@@ -3307,10 +3326,13 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
directly from plaintext credentials. For further details see
<varname>LoadCredentialEncrypted=</varname> above.</para>
- <para>If a credential of the same ID is listed in both <varname>LoadCredential=</varname> and
- <varname>SetCredential=</varname>, the latter will act as default if the former cannot be
- retrieved. In this case not being able to retrieve the credential from the path specified in
- <varname>LoadCredential=</varname> is not considered fatal.</para></listitem>
+ <para>When multiple credentials of the same name are found, credentials found by
+ <varname>LoadCredential=</varname>, <varname>LoadCredentialEncrypted=</varname> and
+ <varname>ImportCredential=</varname> take priority over credentials found by
+ <varname>SetCredential=</varname>. As such, <varname>SetCredential=</varname> will act as default if
+ no credentials are found by any of the former. In this case not being able to retrieve the credential
+ from the path specified in <varname>LoadCredential=</varname> or
+ <varname>LoadCredentialEncrypted=</varname> is not considered fatal.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
@@ -3492,10 +3514,10 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
<term><varname>$CREDENTIALS_DIRECTORY</varname></term>
<listitem><para>An absolute path to the per-unit directory with credentials configured via
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname>. The directory is marked
- read-only and is placed in unswappable memory (if supported and permitted), and is only accessible to
- the UID associated with the unit via <varname>User=</varname> or <varname>DynamicUser=</varname> (and
- the superuser).</para></listitem>
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname>.
+ The directory is marked read-only and is placed in unswappable memory (if supported and permitted),
+ and is only accessible to the UID associated with the unit via <varname>User=</varname> or
+ <varname>DynamicUser=</varname> (and the superuser).</para></listitem>
</varlistentry>
<varlistentry>
@@ -4184,7 +4206,7 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
<row>
<entry>243</entry>
<entry><constant>EXIT_CREDENTIALS</constant></entry>
- <entry>Failed to set up unit's credentials. See <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> above.</entry>
+ <entry>Failed to set up unit's credentials. See <varname>ImportCredential=</varname>, <varname>LoadCredential=</varname> and <varname>SetCredential=</varname> above.</entry>
</row>
<row>
<entry>245</entry>
diff --git a/man/systemd.link.xml b/man/systemd.link.xml
index cc851d31f9..1384b1acb3 100644
--- a/man/systemd.link.xml
+++ b/man/systemd.link.xml
@@ -623,7 +623,7 @@
credential <literal><replaceable>LINK</replaceable>.link.wol.password</literal> (e.g.,
<literal>60-foo.link.wol.password</literal>), and if the credential not found, then
read from <literal>wol.password</literal>. See
- <varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
+ <varname>ImportCredential=</varname>/<varname>LoadCredential=</varname>/<varname>SetCredential=</varname> in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>1</manvolnum></citerefentry>
for details. The password in the credential, must be 6 bytes in hex format with each
byte separated by a colon (<literal>:</literal>) like an Ethernet MAC address, e.g.,
diff --git a/man/systemd.xml b/man/systemd.xml
index 95dc1fef83..2b1e990846 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -934,7 +934,7 @@
<term><varname>systemd.set_credential=</varname></term>
<listitem><para>Sets a system credential, which can then be propagated to system services using the
- <varname>LoadCredential=</varname> setting, see
+ <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
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
index de41d7b641..b45842f1cb 100644
--- a/src/ask-password/ask-password.c
+++ b/src/ask-password/ask-password.c
@@ -44,8 +44,8 @@ static int help(void) {
" --id=ID Query identifier (e.g. \"cryptsetup:/dev/sda5\")\n"
" --keyname=NAME Kernel key name for caching passwords (e.g. \"cryptsetup\")\n"
" --credential=NAME\n"
- " Credential name for LoadCredential=/SetCredential=\n"
- " credentials\n"
+ " Credential name for ImportCredential=, LoadCredential= or\n"
+ " SetCredential= credentials\n"
" --timeout=SEC Timeout in seconds\n"
" --echo=yes|no|masked\n"
" Control whether to show password while typing (echo)\n"
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index fb22a9769d..57e9eb8254 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -26,6 +26,7 @@
#include "io-util.h"
#include "ioprio-util.h"
#include "journal-file.h"
+#include "load-fragment.h"
#include "memstream-util.h"
#include "missing_ioprio.h"
#include "mountpoint-util.h"
@@ -928,6 +929,36 @@ static int property_get_load_credential(
return sd_bus_message_close_container(reply);
}
+static int property_get_import_credential(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = ASSERT_PTR(userdata);
+ const char *s;
+ int r;
+
+ assert(bus);
+ assert(property);
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "s");
+ if (r < 0)
+ return r;
+
+ SET_FOREACH(s, c->import_credentials) {
+ r = sd_bus_message_append(reply, "s", s);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
static int property_get_root_hash(
sd_bus *bus,
const char *path,
@@ -1281,6 +1312,7 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadCredential", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("LoadCredentialEncrypted", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("ImportCredential", "as", property_get_import_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -2311,41 +2343,54 @@ int bus_exec_context_set_transient_property(
isempty = false;
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *copy = NULL;
- ExecLoadCredential *old;
+ bool encrypted = streq(name, "LoadCredentialEncrypted");
- copy = strdup(source);
- if (!copy)
- return -ENOMEM;
+ r = hashmap_put_credential(&c->load_credentials, id, source, encrypted);
+ if (r < 0)
+ return r;
- old = hashmap_get(c->load_credentials, id);
- if (old) {
- free_and_replace(old->path, copy);
- old->encrypted = streq(name, "LoadCredentialEncrypted");
- } else {
- _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+ (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s:%s", name, id, source);
+ }
+ }
- lc = new(ExecLoadCredential, 1);
- if (!lc)
- return -ENOMEM;
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
- *lc = (ExecLoadCredential) {
- .id = strdup(id),
- .path = TAKE_PTR(copy),
- .encrypted = streq(name, "LoadCredentialEncrypted"),
- };
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) {
+ c->load_credentials = hashmap_free(c->load_credentials);
+ (void) unit_write_settingf(u, flags, name, "%s=", name);
+ }
- if (!lc->id)
- return -ENOMEM;
+ return 1;
- r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
- if (r < 0)
- return r;
+ } else if (streq(name, "ImportCredential")) {
+ bool isempty = true;
- TAKE_PTR(lc);
- }
+ r = sd_bus_message_enter_container(message, 'a', "s");
+ if (r < 0)
+ return r;
- (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s:%s", name, id, source);
+ for (;;) {
+ const char *s;
+
+ r = sd_bus_message_read(message, "s", &s);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (!filename_is_valid(s))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Credential name is invalid: %s", s);
+
+ isempty = false;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ r = set_put_strdup(&c->import_credentials, s);
+ if (r < 0)
+ return r;
+
+ (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, s);
}
}
@@ -2354,7 +2399,7 @@ int bus_exec_context_set_transient_property(
return r;
if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) {
- c->load_credentials = hashmap_free(c->load_credentials);
+ c->import_credentials = set_free(c->import_credentials);
(void) unit_write_settingf(u, flags, name, "%s=", name);
}
diff --git a/src/core/execute.c b/src/core/execute.c
index 2d1538be85..29e06e837e 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -1544,7 +1544,8 @@ bool exec_context_has_credentials(const ExecContext *context) {
assert(context);
return !hashmap_isempty(context->set_credentials) ||
- !hashmap_isempty(context->load_credentials);
+ !hashmap_isempty(context->load_credentials) ||
+ !set_isempty(context->import_credentials);
}
#if HAVE_SECCOMP
@@ -2802,6 +2803,111 @@ static char **credential_search_path(const ExecParameters *params, CredentialSea
return TAKE_PTR(l);
}
+static int maybe_decrypt_and_write_credential(
+ int dir_fd,
+ const char *id,
+ bool encrypted,
+ uid_t uid,
+ bool ownership_ok,
+ const char *data,
+ size_t size,
+ uint64_t *left) {
+
+ _cleanup_free_ void *plaintext = NULL;
+ size_t add;
+ int r;
+
+ if (encrypted) {
+ size_t plaintext_size = 0;
+
+ r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size,
+ &plaintext, &plaintext_size);
+ if (r < 0)
+ return r;
+
+ data = plaintext;
+ size = plaintext_size;
+ }
+
+ add = strlen(id) + size;
+ if (add > *left)
+ return -E2BIG;
+
+ r = write_credential(dir_fd, id, data, size, uid, ownership_ok);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to write credential '%s': %m", id);
+
+ *left -= add;
+ return 0;
+}
+
+static int load_credential_glob(
+ const char *path,
+ bool encrypted,
+ char **search_path,
+ ReadFullFileFlags flags,
+ int write_dfd,
+ uid_t uid,
+ bool ownership_ok,
+ uint64_t *left) {
+
+ int r;
+
+ STRV_FOREACH(d, search_path) {
+ _cleanup_globfree_ glob_t pglob = {};
+ _cleanup_free_ char *j = NULL;
+
+ j = path_join(*d, path);
+ if (!j)
+ return -ENOMEM;
+
+ r = safe_glob(j, 0, &pglob);
+ if (r == -ENOENT)
+ continue;
+ if (r < 0)
+ return r;
+
+ for (unsigned n = 0; n < pglob.gl_pathc; n++) {
+ _cleanup_free_ char *fn = NULL;
+ _cleanup_(erase_and_freep) char *data = NULL;
+ size_t size;
+
+ /* path is absolute, hence pass AT_FDCWD as nop dir fd here */
+ r = read_full_file_full(
+ AT_FDCWD,
+ pglob.gl_pathv[n],
+ UINT64_MAX,
+ encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX,
+ flags,
+ NULL,
+ &data, &size);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to read credential '%s': %m",
+ pglob.gl_pathv[n]);
+
+ r = path_extract_filename(pglob.gl_pathv[n], &fn);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to extract filename from '%s': %m",
+ pglob.gl_pathv[n]);
+
+ r = maybe_decrypt_and_write_credential(
+ write_dfd,
+ fn,
+ encrypted,
+ uid,
+ ownership_ok,
+ data, size,
+ left);
+ if (r == -EEXIST)
+ continue;
+ if (r < 0)
+ return r;
+ }
+ }
+
+ return 0;
+}
+
static int load_credential(
const ExecContext *context,
const ExecParameters *params,
@@ -2821,7 +2927,7 @@ static int load_credential(
_cleanup_free_ char *bindname = NULL;
const char *source = NULL;
bool missing_ok = true;
- size_t size, add, maxsz;
+ size_t size, maxsz;
int r;
assert(context);
@@ -2923,28 +3029,7 @@ static int load_credential(
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m", path);
- if (encrypted) {
- _cleanup_free_ void *plaintext = NULL;
- size_t plaintext_size = 0;
-
- r = decrypt_credential_and_warn(id, now(CLOCK_REALTIME), NULL, NULL, data, size, &plaintext, &plaintext_size);
- if (r < 0)
- return r;
-
- free_and_replace(data, plaintext);
- size = plaintext_size;
- }
-
- add = strlen(id) + size;
- if (add > *left)
- return -E2BIG;
-
- r = write_credential(write_dfd, id, data, size, uid, ownership_ok);
- if (r < 0)
- return log_debug_errno(r, "Failed to write credential '%s': %m", id);
-
- *left -= add;
- return 0;
+ return maybe_decrypt_and_write_credential(write_dfd, id, encrypted, uid, ownership_ok, data, size, left);
}
struct load_cred_args {
@@ -3019,6 +3104,7 @@ static int acquire_credentials(
uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX;
_cleanup_close_ int dfd = -EBADF;
+ const char *ic;
ExecLoadCredential *lc;
ExecSetCredential *sc;
int r;
@@ -3084,8 +3170,47 @@ static int acquire_credentials(
return r;
}
- /* Second, we add in literally specified credentials. If the credentials already exist, we'll not add
- * them, so that they can act as a "default" if the same credential is specified multiple times. */
+ /* Next, look for system credentials and credentials in the credentials store. Note that these do not
+ * override any credentials found earlier. */
+ SET_FOREACH(ic, context->import_credentials) {
+ _cleanup_free_ char **search_path = NULL;
+
+ search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED);
+ if (!search_path)
+ return -ENOMEM;
+
+ r = load_credential_glob(
+ ic,
+ /* encrypted = */ false,
+ search_path,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER,
+ dfd,
+ uid,
+ ownership_ok,
+ &left);
+ if (r < 0)
+ return r;
+
+ search_path = strv_free(search_path);
+ search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED);
+ if (!search_path)
+ return -ENOMEM;
+
+ r = load_credential_glob(
+ ic,
+ /* encrypted = */ true,
+ search_path,
+ READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER|READ_FULL_FILE_UNBASE64,
+ dfd,
+ uid,
+ ownership_ok,
+ &left);
+ if (r < 0)
+ return r;
+ }
+
+ /* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
+ * add them, so that they can act as a "default" if the same credential is specified multiple times. */
HASHMAP_FOREACH(sc, context->set_credentials) {
_cleanup_(erase_and_freep) void *plaintext = NULL;
const char *data;
@@ -5883,6 +6008,7 @@ void exec_context_done(ExecContext *c) {
c->load_credentials = hashmap_free(c->load_credentials);
c->set_credentials = hashmap_free(c->set_credentials);
+ c->import_credentials = set_free(c->import_credentials);
c->root_image_policy = image_policy_free(c->root_image_policy);
c->mount_image_policy = image_policy_free(c->mount_image_policy);
diff --git a/src/core/execute.h b/src/core/execute.h
index 1c8378c8b0..953dc9e7f7 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -361,6 +361,7 @@ struct ExecContext {
Hashmap *set_credentials; /* output id → ExecSetCredential */
Hashmap *load_credentials; /* output id → ExecLoadCredential */
+ Set *import_credentials;
ImagePolicy *root_image_policy, *mount_image_policy, *extension_image_policy;
};
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 64a00fef28..ae318dae89 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -151,6 +151,7 @@
{{type}}.SetCredentialEncrypted, config_parse_set_credential, 1, offsetof({{type}}, exec_context)
{{type}}.LoadCredential, config_parse_load_credential, 0, offsetof({{type}}, exec_context)
{{type}}.LoadCredentialEncrypted, config_parse_load_credential, 1, offsetof({{type}}, exec_context)
+{{type}}.ImportCredential, config_parse_import_credential, 0, offsetof({{type}}, exec_context.import_credentials)
{{type}}.TimeoutCleanSec, config_parse_sec, 0, offsetof({{type}}, exec_context.timeout_clean_usec)
{% if HAVE_PAM %}
{{type}}.PAMName, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.pam_name)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 554180f1e3..6d129af7b2 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -4840,6 +4840,46 @@ int config_parse_set_credential(
return 0;
}
+int hashmap_put_credential(Hashmap **h, const char *id, const char *path, bool encrypted) {
+ ExecLoadCredential *old;
+ int r;
+
+ assert(h);
+ assert(id);
+ assert(path);
+
+ old = hashmap_get(*h, id);
+ if (old) {
+ r = free_and_strdup(&old->path, path);
+ if (r < 0)
+ return r;
+
+ old->encrypted = encrypted;
+ } else {
+ _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+
+ lc = new(ExecLoadCredential, 1);
+ if (!lc)
+ return log_oom();
+
+ *lc = (ExecLoadCredential) {
+ .id = strdup(id),
+ .path = strdup(path),
+ .encrypted = encrypted,
+ };
+ if (!lc->id || !lc->path)
+ return -ENOMEM;
+
+ r = hashmap_ensure_put(h, &exec_load_credential_hash_ops, lc->id, lc);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(lc);
+ }
+
+ return 0;
+}
+
int config_parse_load_credential(
const char *unit,
const char *filename,
@@ -4854,7 +4894,6 @@ int config_parse_load_credential(
_cleanup_free_ char *word = NULL, *k = NULL, *q = NULL;
ExecContext *context = ASSERT_PTR(data);
- ExecLoadCredential *old;
bool encrypted = ltype;
Unit *u = userdata;
const char *p;
@@ -4907,35 +4946,54 @@ int config_parse_load_credential(
}
}
- old = hashmap_get(context->load_credentials, k);
- if (old) {
- free_and_replace(old->path, q);
- old->encrypted = encrypted;
- } else {
- _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL;
+ r = hashmap_put_credential(&context->load_credentials, k, q, encrypted);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store load credential '%s': %m", rvalue);
- lc = new(ExecLoadCredential, 1);
- if (!lc)
- return log_oom();
+ return 0;
+}
- *lc = (ExecLoadCredential) {
- .id = TAKE_PTR(k),
- .path = TAKE_PTR(q),
- .encrypted = encrypted,
- };
+int config_parse_import_credential(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
- r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue);
- return 0;
- }
+ _cleanup_free_ char *s = NULL;
+ Set** import_credentials = ASSERT_PTR(data);
+ Unit *u = userdata;
+ int r;
- TAKE_PTR(lc);
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ /* Empty assignment resets the list */
+ *import_credentials = set_free(*import_credentials);
+ return 0;
+ }
+
+ r = unit_cred_printf(u, rvalue, &s);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s);
+ return 0;
+ }
+ if (!filename_is_valid(s)) {
+ log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", s);
+ return 0;
}
+ r = set_put_strdup(import_credentials, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to store credential name '%s': %m", rvalue);
+
return 0;
}
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 59f02a3207..3cfb50969a 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -12,6 +12,8 @@ int unit_is_likely_recursive_template_dependency(Unit *u, const char *name, cons
int parse_crash_chvt(const char *value, int *data);
int parse_confirm_spawn(const char *value, char **console);
+int hashmap_put_credential(Hashmap **h, const char *id, const char *path, bool encrypted);
+
/* Read service data from .desktop file style configuration fragments */
int unit_load_fragment(Unit *u);
@@ -105,6 +107,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_preserve_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_exec_directories);
CONFIG_PARSER_PROTOTYPE(config_parse_set_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_load_credential);
+CONFIG_PARSER_PROTOTYPE(config_parse_import_credential);
CONFIG_PARSER_PROTOTYPE(config_parse_set_status);
CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv);
CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems);
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index 8b1a353a9b..6e93d0ca43 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1209,6 +1209,17 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return 1;
}
+ if (streq(field, "ImportCredential")) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", field, "as", 0);
+ else
+ r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
if (streq(field, "LogExtraFields")) {
r = sd_bus_message_open_container(m, 'r', "sv");
if (r < 0)
diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh
index d243d93d81..4affd128cc 100755
--- a/test/units/testsuite-54.sh
+++ b/test/units/testsuite-54.sh
@@ -253,6 +253,21 @@ cmp /tmp/ts54-concat <(echo -n abcd)
rm /tmp/ts54-concat
rm -rf /tmp/ts54-creds
+# Check that globs work as expected
+mkdir -p /run/credstore
+echo -n a >/run/credstore/test.creds.first
+echo -n b >/run/credstore/test.creds.second
+mkdir -p /etc/credstore
+echo -n c >/etc/credstore/test.creds.third
+systemd-run -p "ImportCredential=test.creds.*" \
+ -p DynamicUser=1 \
+ --wait \
+ --pipe \
+ cat '${CREDENTIALS_DIRECTORY}/test.creds.first' \
+ '${CREDENTIALS_DIRECTORY}/test.creds.second' \
+ '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat
+cmp /tmp/ts54-concat <(echo -n abc)
+
# Now test encrypted credentials (only supported when built with OpenSSL though)
if systemctl --version | grep -q -- +OPENSSL ; then
echo -n $RANDOM >/tmp/test-54-plaintext