diff --git a/docs/CREDENTIALS.md b/docs/CREDENTIALS.md
index da5152c164..9d06c45f1f 100644
--- a/docs/CREDENTIALS.md
+++ b/docs/CREDENTIALS.md
@@ -322,10 +322,11 @@ services where they are ultimately consumed.
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=` kernel command line option. 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.
+ 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
diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml
index 93ed4f6acb..43247fe534 100644
--- a/man/kernel-command-line.xml
+++ b/man/kernel-command-line.xml
@@ -72,6 +72,7 @@
systemd.setenv=
systemd.machine_id=
systemd.set_credential=
+ systemd.set_credential_binary=
systemd.import_credentials=
systemd.reload_limit_interval_sec=
systemd.reload_limit_burst=
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index ccec6ec423..e077407367 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -3285,7 +3285,8 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
systemd-stub7,
from the initrd (see
systemd1), or be
- specified on the kernel command line using the systemd.set_credential= switch (see
+ specified on the kernel command line using the systemd.set_credential= and
+ systemd.set_credential_binary= switches (see
systemd1 – this is
not recommended since unprivileged userspace can read the kernel command line).
diff --git a/man/systemd.xml b/man/systemd.xml
index 754aadbb6a..21c98efecd 100644
--- a/man/systemd.xml
+++ b/man/systemd.xml
@@ -932,12 +932,15 @@
systemd.set_credential=
+ systemd.set_credential_binary=
Sets a system credential, which can then be propagated to system services using the
ImportCredential= or LoadCredential= setting, see
systemd.exec5 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
+ systemd.set_credential= parameter expects the credential value in literal text
+ form, the systemd.set_credential_binary= parameter takes binary data encoded in
+ Base64. Note that the kernel command line is typically accessible by unprivileged programs in
/proc/cmdline. 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.
diff --git a/src/core/import-creds.c b/src/core/import-creds.c
index 6bf6ded44c..40cbf10dad 100644
--- a/src/core/import-creds.c
+++ b/src/core/import-creds.c
@@ -278,15 +278,21 @@ static int import_credentials_boot(void) {
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;
@@ -305,7 +311,19 @@ 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;
@@ -320,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");
diff --git a/test/TEST-54-CREDS/test.sh b/test/TEST-54-CREDS/test.sh
index 5588242230..c0a9d7a53d 100755
--- a/test/TEST-54-CREDS/test.sh
+++ b/test/TEST-54-CREDS/test.sh
@@ -25,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[*]}"
diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh
index 8ba327a3b1..6c4e006500 100755
--- a/test/units/testsuite-54.sh
+++ b/test/units/testsuite-54.sh
@@ -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" ]