summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2023-11-23 22:22:27 +0100
committerLennart Poettering <lennart@poettering.net>2023-12-21 19:19:12 +0100
commit644f19c75c40e74e83e1e5f0f0b2eb5195f9d09b (patch)
treef879232f8c1bf3066ded5f636ac698c4e2200ac9
parentvarlink: add helper varlink_error_invalid_parameter_name() (diff)
downloadsystemd-644f19c75c40e74e83e1e5f0f0b2eb5195f9d09b.tar.xz
systemd-644f19c75c40e74e83e1e5f0f0b2eb5195f9d09b.zip
creds: add varlink API for encrypting/decrypting credentials
-rw-r--r--src/creds/creds.c180
-rw-r--r--src/shared/meson.build1
-rw-r--r--src/shared/varlink-io.systemd.Credentials.c34
-rw-r--r--src/shared/varlink-io.systemd.Credentials.h6
-rw-r--r--src/test/test-varlink-idl.c3
-rw-r--r--units/meson.build5
-rw-r--r--units/systemd-creds.socket23
-rw-r--r--units/systemd-creds@.service19
8 files changed, 271 insertions, 0 deletions
diff --git a/src/creds/creds.c b/src/creds/creds.c
index 10d117118f..c9bf2e1e36 100644
--- a/src/creds/creds.c
+++ b/src/creds/creds.c
@@ -24,6 +24,9 @@
#include "terminal-util.h"
#include "tpm2-pcr.h"
#include "tpm2-util.h"
+#include "user-util.h"
+#include "varlink.h"
+#include "varlink-io.systemd.Credentials.h"
#include "verbs.h"
typedef enum TranscodeMode {
@@ -54,6 +57,7 @@ static usec_t arg_timestamp = USEC_INFINITY;
static usec_t arg_not_after = USEC_INFINITY;
static bool arg_pretty = false;
static bool arg_quiet = false;
+static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_signature, freep);
@@ -933,6 +937,11 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_tpm2_public_key_pcr_mask == UINT32_MAX)
arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_BOOT;
+ r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
+ if (r < 0)
+ return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+ arg_varlink = r;
+
return 1;
}
@@ -952,6 +961,150 @@ static int creds_main(int argc, char *argv[]) {
return dispatch_verb(argc, argv, verbs, NULL);
}
+typedef struct MethodEncryptParameters {
+ const char *name;
+ const char *text;
+ struct iovec data;
+ uint64_t timestamp;
+ uint64_t not_after;
+} MethodEncryptParameters;
+
+static void method_encrypt_parameters_done(MethodEncryptParameters *p) {
+ assert(p);
+
+ iovec_done_erase(&p->data);
+}
+
+static int vl_method_encrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, name), 0 },
+ { "text", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodEncryptParameters, text), 0 },
+ { "data", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodEncryptParameters, data), 0 },
+ { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, timestamp), 0 },
+ { "notAfter", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodEncryptParameters, not_after), 0 },
+ {}
+ };
+ _cleanup_(method_encrypt_parameters_done) MethodEncryptParameters p = {
+ .timestamp = UINT64_MAX,
+ .not_after = UINT64_MAX,
+ };
+ _cleanup_(iovec_done) struct iovec output = {};
+ int r;
+
+ assert(link);
+
+ json_variant_sensitive(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.name && !credential_name_valid(p.name))
+ return varlink_error_invalid_parameter_name(link, "name");
+ /* Specifying both or neither the text string and the binary data is not allowed */
+ if (!!p.text == !!p.data.iov_base)
+ return varlink_error_invalid_parameter_name(link, "data");
+ if (p.timestamp == UINT64_MAX)
+ p.timestamp = now(CLOCK_REALTIME);
+ if (p.not_after != UINT64_MAX && p.not_after < p.timestamp)
+ return varlink_error_invalid_parameter_name(link, "notAfter");
+
+ r = encrypt_credential_and_warn(
+ arg_with_key,
+ p.name,
+ p.timestamp,
+ p.not_after,
+ arg_tpm2_device,
+ arg_tpm2_pcr_mask,
+ arg_tpm2_public_key,
+ arg_tpm2_public_key_pcr_mask,
+ p.text ?: p.data.iov_base, p.text ? strlen(p.text) : p.data.iov_len,
+ &output.iov_base, &output.iov_len);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+ r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("blob", &output)));
+ if (r < 0)
+ return r;
+
+ /* Let's also mark the (theoretically encrypted) reply as sensitive, in case the NULL encryption scheme was used. */
+ json_variant_sensitive(reply);
+
+ return varlink_reply(link, reply);
+}
+
+typedef struct MethodDecryptParameters {
+ const char *name;
+ struct iovec blob;
+ uint64_t timestamp;
+} MethodDecryptParameters;
+
+static void method_decrypt_parameters_done(MethodDecryptParameters *p) {
+ assert(p);
+
+ iovec_done_erase(&p->blob);
+}
+
+static int vl_method_decrypt(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+
+ static const JsonDispatch dispatch_table[] = {
+ { "name", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodDecryptParameters, name), 0 },
+ { "blob", JSON_VARIANT_STRING, json_dispatch_unbase64_iovec, offsetof(MethodDecryptParameters, blob), 0 },
+ { "timestamp", _JSON_VARIANT_TYPE_INVALID, json_dispatch_uint64, offsetof(MethodDecryptParameters, timestamp), 0 },
+ {}
+ };
+ _cleanup_(method_decrypt_parameters_done) MethodDecryptParameters p = {
+ .timestamp = UINT64_MAX,
+ };
+ _cleanup_(iovec_done_erase) struct iovec output = {};
+ int r;
+
+ assert(link);
+
+ /* Let's also mark the (theoretically encrypted) input as sensitive, in case the NULL encryption scheme was used. */
+ json_variant_sensitive(parameters);
+
+ r = varlink_dispatch(link, parameters, dispatch_table, &p);
+ if (r != 0)
+ return r;
+
+ if (p.name && !credential_name_valid(p.name))
+ return varlink_error_invalid_parameter_name(link, "name");
+ if (!p.blob.iov_base)
+ return varlink_error_invalid_parameter_name(link, "blob");
+ if (p.timestamp == UINT64_MAX)
+ p.timestamp = now(CLOCK_REALTIME);
+
+ r = decrypt_credential_and_warn(
+ p.name,
+ p.timestamp,
+ arg_tpm2_device,
+ arg_tpm2_signature,
+ p.blob.iov_base, p.blob.iov_len,
+ &output.iov_base, &output.iov_len);
+ if (r == -EBADMSG)
+ return varlink_error(link, "io.systemd.Credentials.BadFormat", NULL);
+ if (r == -EREMOTE)
+ return varlink_error(link, "io.systemd.Credentials.NameMismatch", NULL);
+ if (r == -ESTALE)
+ return varlink_error(link, "io.systemd.Credentials.TimeMismatch", NULL);
+ if (r < 0)
+ return r;
+
+ _cleanup_(json_variant_unrefp) JsonVariant *reply = NULL;
+
+ r = json_build(&reply, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_IOVEC_BASE64("data", &output)));
+ if (r < 0)
+ return r;
+
+ json_variant_sensitive(reply);
+
+ return varlink_reply(link, reply);
+}
+
static int run(int argc, char *argv[]) {
int r;
@@ -961,6 +1114,33 @@ static int run(int argc, char *argv[]) {
if (r <= 0)
return r;
+ if (arg_varlink) {
+ _cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
+
+ /* Invocation as Varlink service */
+
+ r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY|VARLINK_SERVER_INHERIT_USERDATA);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate Varlink server: %m");
+
+ r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_Credentials);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+ r = varlink_server_bind_method_many(
+ varlink_server,
+ "io.systemd.Credentials.Encrypt", vl_method_encrypt,
+ "io.systemd.Credentials.Decrypt", vl_method_decrypt);
+ if (r < 0)
+ return log_error_errno(r, "Failed to bind Varlink methods: %m");
+
+ r = varlink_server_loop_auto(varlink_server);
+ if (r < 0)
+ return log_error_errno(r, "Failed to run Varlink event loop: %m");
+
+ return 0;
+ }
+
return creds_main(argc, argv);
}
diff --git a/src/shared/meson.build b/src/shared/meson.build
index b24a541de5..1ff2a2d995 100644
--- a/src/shared/meson.build
+++ b/src/shared/meson.build
@@ -172,6 +172,7 @@ shared_sources = files(
'varlink.c',
'varlink-idl.c',
'varlink-io.systemd.c',
+ 'varlink-io.systemd.Credentials.c',
'varlink-io.systemd.Journal.c',
'varlink-io.systemd.ManagedOOM.c',
'varlink-io.systemd.PCRExtend.c',
diff --git a/src/shared/varlink-io.systemd.Credentials.c b/src/shared/varlink-io.systemd.Credentials.c
new file mode 100644
index 0000000000..b827337eed
--- /dev/null
+++ b/src/shared/varlink-io.systemd.Credentials.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.Credentials.h"
+
+static VARLINK_DEFINE_METHOD(
+ Encrypt,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(text, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(data, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(notAfter, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT(blob, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_METHOD(
+ Decrypt,
+ VARLINK_DEFINE_INPUT(name, VARLINK_STRING, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(blob, VARLINK_STRING, 0),
+ VARLINK_DEFINE_INPUT(timestamp, VARLINK_INT, VARLINK_NULLABLE),
+ VARLINK_DEFINE_INPUT(allowInteractiveAuthentication, VARLINK_BOOL, VARLINK_NULLABLE),
+ VARLINK_DEFINE_OUTPUT(data, VARLINK_STRING, 0));
+
+static VARLINK_DEFINE_ERROR(BadFormat);
+static VARLINK_DEFINE_ERROR(NameMismatch);
+static VARLINK_DEFINE_ERROR(TimeMismatch);
+
+VARLINK_DEFINE_INTERFACE(
+ io_systemd_Credentials,
+ "io.systemd.Credentials",
+ &vl_method_Encrypt,
+ &vl_method_Decrypt,
+ &vl_error_BadFormat,
+ &vl_error_NameMismatch,
+ &vl_error_TimeMismatch);
diff --git a/src/shared/varlink-io.systemd.Credentials.h b/src/shared/varlink-io.systemd.Credentials.h
new file mode 100644
index 0000000000..c0ecc3d840
--- /dev/null
+++ b/src/shared/varlink-io.systemd.Credentials.h
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "varlink-idl.h"
+
+extern const VarlinkInterface vl_interface_io_systemd_Credentials;
diff --git a/src/test/test-varlink-idl.c b/src/test/test-varlink-idl.c
index cbdb9c64fb..6aa1fa6b1d 100644
--- a/src/test/test-varlink-idl.c
+++ b/src/test/test-varlink-idl.c
@@ -8,6 +8,7 @@
#include "varlink.h"
#include "varlink-idl.h"
#include "varlink-io.systemd.h"
+#include "varlink-io.systemd.Credentials.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.ManagedOOM.h"
#include "varlink-io.systemd.PCRExtend.h"
@@ -143,6 +144,8 @@ TEST(parse_format) {
print_separator();
test_parse_format_one(&vl_interface_io_systemd_sysext);
print_separator();
+ test_parse_format_one(&vl_interface_io_systemd_Credentials);
+ print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}
diff --git a/units/meson.build b/units/meson.build
index 8542245239..9d3604951d 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -280,6 +280,11 @@ units = [
'file' : 'systemd-coredump@.service.in',
'conditions' : ['ENABLE_COREDUMP'],
},
+ {
+ 'file' : 'systemd-creds.socket',
+ 'symlinks' : ['sockets.target.wants/'],
+ },
+ { 'file' : 'systemd-creds@.service' },
{ 'file' : 'systemd-exit.service' },
{
'file' : 'systemd-firstboot.service',
diff --git a/units/systemd-creds.socket b/units/systemd-creds.socket
new file mode 100644
index 0000000000..794fac19a8
--- /dev/null
+++ b/units/systemd-creds.socket
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Before=sockets.target
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.Credentials
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
+
+[Install]
+WantedBy=sockets.target
diff --git a/units/systemd-creds@.service b/units/systemd-creds@.service
new file mode 100644
index 0000000000..37cdd319f6
--- /dev/null
+++ b/units/systemd-creds@.service
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=Credential Encryption/Decryption (Varlink)
+Documentation=man:systemd-creds(1)
+DefaultDependencies=no
+Conflicts=shutdown.target initrd-switch-root.target
+Before=shutdown.target initrd-switch-root.target
+
+[Service]
+Environment=LISTEN_FDNAMES=varlink
+ExecStart=-systemd-creds