]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pcrlock: add basic Varlink interface
authorLennart Poettering <lennart@poettering.net>
Fri, 2 Feb 2024 14:17:09 +0000 (15:17 +0100)
committerLennart Poettering <lennart@poettering.net>
Mon, 12 Feb 2024 11:04:18 +0000 (12:04 +0100)
This can be used to make or delete a PCR policy via Varlink. It can also
be used to query the current event log in CEL format.

src/pcrlock/pcrlock.c
src/shared/meson.build
src/shared/varlink-io.systemd.PCRLock.c [new file with mode: 0644]
src/shared/varlink-io.systemd.PCRLock.h [new file with mode: 0644]
src/test/test-varlink-idl.c
units/meson.build
units/systemd-pcrlock.socket [new file with mode: 0644]
units/systemd-pcrlock@.service.in [new file with mode: 0644]

index 016d73cc009c513d0cb86a3112925a5cc10b5ac3..1dd3d86a73c19e39d26b42ff223524dd036fbe62 100644 (file)
@@ -48,6 +48,8 @@
 #include "unaligned.h"
 #include "unit-name.h"
 #include "utf8.h"
+#include "varlink.h"
+#include "varlink-io.systemd.PCRLock.h"
 #include "verbs.h"
 
 static PagerFlags arg_pager_flags = 0;
@@ -65,6 +67,7 @@ static char *arg_policy_path = NULL;
 static bool arg_force = false;
 static BootEntryTokenType arg_entry_token_type = BOOT_ENTRY_TOKEN_AUTO;
 static char *arg_entry_token = NULL;
+static bool arg_varlink = false;
 
 STATIC_DESTRUCTOR_REGISTER(arg_components, strv_freep);
 STATIC_DESTRUCTOR_REGISTER(arg_pcrlock_path, freep);
@@ -4310,7 +4313,7 @@ static int write_boot_policy_file(const char *json_text) {
         return 1;
 }
 
-static int verb_make_policy(int argc, char *argv[], void *userdata) {
+static int make_policy(bool force, bool recovery_pin) {
         int r;
 
         /* Here's how this all works: after predicting all possible PCR values for next boot (with
@@ -4385,11 +4388,11 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
                 if (arg_nv_index != 0 && old_policy.nv_index != arg_nv_index)
                         return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Stored policy references different NV index (0x%x) than specified (0x%x), refusing.", old_policy.nv_index, arg_nv_index);
 
-                if (!arg_force &&
+                if (!force &&
                     old_policy.algorithm == el->primary_algorithm &&
                     tpm2_pcr_prediction_equal(&old_policy.prediction, &new_prediction, el->primary_algorithm)) {
                         log_info("Prediction is identical to current policy, skipping update.");
-                        return EXIT_SUCCESS;
+                        return 0; /* NOP */
                 }
         }
 
@@ -4434,7 +4437,7 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
 
         /* Acquire a recovery PIN, either from the user, or create a randomized one */
         _cleanup_(erase_and_freep) char *pin = NULL;
-        if (arg_recovery_pin) {
+        if (recovery_pin) {
                 r = getenv_steal_erase("PIN", &pin);
                 if (r < 0)
                         return log_error_errno(r, "Failed to acquire PIN from environment: %m");
@@ -4712,7 +4715,11 @@ static int verb_make_policy(int argc, char *argv[], void *userdata) {
 
         log_info("Overall time spent: %s", FORMAT_TIMESPAN(usec_sub_unsigned(now(CLOCK_MONOTONIC), start_usec), 1));
 
-        return 0;
+        return 1; /* installed new policy */
+}
+
+static int verb_make_policy(int argc, char *argv[], void *userdata) {
+        return make_policy(arg_force, arg_recovery_pin);
 }
 
 static int undefine_policy_nv_index(
@@ -4768,7 +4775,7 @@ static int undefine_policy_nv_index(
         return 0;
 }
 
-static int verb_remove_policy(int argc, char *argv[], void *userdata) {
+static int remove_policy(void) {
         int ret = 0, r;
 
         _cleanup_(tpm2_pcrlock_policy_done) Tpm2PCRLockPolicy policy = {};
@@ -4807,6 +4814,10 @@ static int verb_remove_policy(int argc, char *argv[], void *userdata) {
         return ret;
 }
 
+static int verb_remove_policy(int argc, char *argv[], void *userdata) {
+        return remove_policy();
+}
+
 static int help(int argc, char *argv[], void *userdata) {
         _cleanup_free_ char *link = NULL;
         int r;
@@ -5082,6 +5093,14 @@ static int parse_argv(int argc, char *argv[]) {
                         return log_oom();
         }
 
+        r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
+        if (r < 0)
+                return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
+        if (r > 0) {
+                arg_varlink = true;
+                arg_pager_flags |= PAGER_DISABLE;
+        }
+
         return 1;
 }
 
@@ -5124,6 +5143,88 @@ static int pcrlock_main(int argc, char *argv[]) {
         return dispatch_verb(argc, argv, verbs, NULL);
 }
 
+static int vl_method_read_event_log(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        _cleanup_(event_log_freep) EventLog *el = NULL;
+        uint64_t recnum = 0;
+        int r;
+
+        assert(link);
+
+        if (json_variant_elements(parameters) > 0)
+                return varlink_error_invalid_parameter(link, parameters);
+
+        el = event_log_new();
+        if (!el)
+                return log_oom();
+
+        r = event_log_load(el);
+        if (r < 0)
+                return r;
+
+        _cleanup_(json_variant_unrefp) JsonVariant *rec_cel = NULL;
+
+        FOREACH_ARRAY(rr, el->records, el->n_records) {
+
+                if (rec_cel) {
+                        r = varlink_notifyb(link,
+                                            JSON_BUILD_OBJECT(JSON_BUILD_PAIR_VARIANT("record", rec_cel)));
+                        if (r < 0)
+                                return r;
+
+                        rec_cel = json_variant_unref(rec_cel);
+                }
+
+                r = event_log_record_to_cel(*rr, &recnum, &rec_cel);
+                if (r < 0)
+                        return r;
+        }
+
+        return varlink_replyb(link,
+                              JSON_BUILD_OBJECT(JSON_BUILD_PAIR_CONDITION(rec_cel, "record", JSON_BUILD_VARIANT(rec_cel))));
+}
+
+typedef struct MethodMakePolicyParameters {
+        bool force;
+} MethodMakePolicyParameters;
+
+static int vl_method_make_policy(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        static const JsonDispatch dispatch_table[] = {
+                { "force", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(MethodMakePolicyParameters, force), 0 },
+                {}
+        };
+        MethodMakePolicyParameters p = {};
+        int r;
+
+        assert(link);
+
+        r = varlink_dispatch(link, parameters, dispatch_table, &p);
+        if (r != 0)
+                return r;
+
+        r = make_policy(p.force, /* recovery_key= */ false);
+        if (r < 0)
+                return r;
+        if (r == 0)
+                return varlink_error(link, "io.systemd.PCRLock.NoChange", NULL);
+
+        return varlink_reply(link, NULL);
+}
+
+static int vl_method_remove_policy(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
+        int r;
+
+        assert(link);
+
+        if (json_variant_elements(parameters) > 0)
+                return varlink_error_invalid_parameter(link, parameters);
+
+        r = remove_policy();
+        if (r < 0)
+                return r;
+
+        return varlink_reply(link, NULL);
+}
+
 static int run(int argc, char *argv[]) {
         int r;
 
@@ -5133,6 +5234,34 @@ 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);
+                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_PCRLock);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to add Varlink interface: %m");
+
+                r = varlink_server_bind_method_many(
+                                varlink_server,
+                                "io.systemd.PCRLock.ReadEventLog", vl_method_read_event_log,
+                                "io.systemd.PCRLock.MakePolicy",   vl_method_make_policy,
+                                "io.systemd.PCRLock.RemovePolicy", vl_method_remove_policy);
+                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 EXIT_SUCCESS;
+        }
+
         return pcrlock_main(argc, argv);
 }
 
index dc9adeddc14bab428a016ed5cf5cde7d5998b38b..81de6708f01d6f3668fa1fd320d4c8ca96bf8166 100644 (file)
@@ -180,6 +180,7 @@ shared_sources = files(
         'varlink-io.systemd.ManagedOOM.c',
         'varlink-io.systemd.Network.c',
         'varlink-io.systemd.PCRExtend.c',
+        'varlink-io.systemd.PCRLock.c',
         'varlink-io.systemd.Resolve.c',
         'varlink-io.systemd.Resolve.Monitor.c',
         'varlink-io.systemd.UserDatabase.c',
diff --git a/src/shared/varlink-io.systemd.PCRLock.c b/src/shared/varlink-io.systemd.PCRLock.c
new file mode 100644 (file)
index 0000000..3b2c408
--- /dev/null
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "varlink-io.systemd.PCRLock.h"
+
+static VARLINK_DEFINE_METHOD(
+                ReadEventLog);
+
+static VARLINK_DEFINE_METHOD(
+                MakePolicy,
+                VARLINK_DEFINE_INPUT(force, VARLINK_BOOL, VARLINK_NULLABLE));
+
+static VARLINK_DEFINE_METHOD(
+                RemovePolicy);
+
+VARLINK_DEFINE_ERROR(
+                NoChange);
+
+VARLINK_DEFINE_INTERFACE(
+                io_systemd_PCRLock,
+                "io.systemd.PCRLock",
+                &vl_method_ReadEventLog,
+                &vl_method_MakePolicy,
+                &vl_method_RemovePolicy,
+                &vl_error_NoChange);
diff --git a/src/shared/varlink-io.systemd.PCRLock.h b/src/shared/varlink-io.systemd.PCRLock.h
new file mode 100644 (file)
index 0000000..687f09e
--- /dev/null
@@ -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_PCRLock;
index d322c02c55222e40de9521a06f4d4bfb2452942d..e5708b73b546a13352b11129db1aba19d381bb92 100644 (file)
@@ -13,6 +13,7 @@
 #include "varlink-io.systemd.ManagedOOM.h"
 #include "varlink-io.systemd.Network.h"
 #include "varlink-io.systemd.PCRExtend.h"
+#include "varlink-io.systemd.PCRLock.h"
 #include "varlink-io.systemd.Resolve.Monitor.h"
 #include "varlink-io.systemd.Resolve.h"
 #include "varlink-io.systemd.UserDatabase.h"
@@ -143,6 +144,8 @@ TEST(parse_format) {
         print_separator();
         test_parse_format_one(&vl_interface_io_systemd_PCRExtend);
         print_separator();
+        test_parse_format_one(&vl_interface_io_systemd_PCRLock);
+        print_separator();
         test_parse_format_one(&vl_interface_io_systemd_service);
         print_separator();
         test_parse_format_one(&vl_interface_io_systemd_sysext);
index efd2eac58356d3cf085e2a28bfa7c377a88d7309..acfd8d1dcbee4d66cb13c75afd2b0ff0682fd8f7 100644 (file)
@@ -519,6 +519,15 @@ units = [
           'file' : 'systemd-pcrlock-firmware-config.service.in',
           'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
         },
+        {
+          'file' : 'systemd-pcrlock@.service.in',
+          'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
+        },
+        {
+          'file' : 'systemd-pcrlock.socket',
+          'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
+          'symlinks' : ['sockets.target.wants/'],
+        },
         {
           'file' : 'systemd-portabled.service.in',
           'conditions' : ['ENABLE_PORTABLED'],
diff --git a/units/systemd-pcrlock.socket b/units/systemd-pcrlock.socket
new file mode 100644 (file)
index 0000000..2143147
--- /dev/null
@@ -0,0 +1,25 @@
+#  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=Make TPM2 PCR Policy (Varlink)
+Documentation=man:systemd-pcrlock(8)
+DefaultDependencies=no
+After=tpm2.target
+Before=sockets.target
+ConditionSecurity=measured-uki
+
+[Socket]
+ListenStream=/run/systemd/io.systemd.PCRLock
+FileDescriptorName=varlink
+SocketMode=0600
+Accept=yes
+
+[Install]
+WantedBy=sockets.target
diff --git a/units/systemd-pcrlock@.service.in b/units/systemd-pcrlock@.service.in
new file mode 100644 (file)
index 0000000..50a0a3d
--- /dev/null
@@ -0,0 +1,21 @@
+#  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=Make TPM2 PCR Policy (Varlink)
+Documentation=man:systemd-pcrlock(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-tpm2-setup.service
+Before=sysinit.target shutdown.target
+After=systemd-remount-fs.service var.mount
+
+[Service]
+Environment=LISTEN_FDNAMES=varlink
+ExecStart={{LIBEXECDIR}}/systemd-pcrlock --location=770