1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-09 01:18:19 +03:00

pcrextend: make pcrextend tool acccessible via varlink

This is primarily supposed to be a 1st step with varlinkifying our
various command line tools, and excercise in how this might look like
across our codebase one day. However, at AllSystemsGo! 2023 it was
requested that we provide an API to do a PCR measurement along with a
matching event log record, and this provides that.
This commit is contained in:
Lennart Poettering 2023-09-25 16:28:24 +02:00
parent d408a53f78
commit 4e16d5c69e
8 changed files with 223 additions and 30 deletions

View File

@ -21,6 +21,8 @@
#include "pretty-print.h"
#include "tpm2-pcr.h"
#include "tpm2-util.h"
#include "varlink.h"
#include "varlink-io.systemd.PCRExtend.h"
static bool arg_graceful = false;
static char *arg_tpm2_device = NULL;
@ -28,11 +30,14 @@ static char **arg_banks = NULL;
static char *arg_file_system = NULL;
static bool arg_machine_id = false;
static unsigned arg_pcr_index = UINT_MAX;
static bool arg_varlink = false;
STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_file_system, freep);
#define EXTENSION_STRING_SAFE_LIMIT 1024
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@ -165,7 +170,12 @@ static int parse_argv(int argc, char *argv[]) {
if (arg_file_system && arg_machine_id)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--file-system= and --machine-id may not be combined.");
if (arg_pcr_index == UINT_MAX)
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;
else if (arg_pcr_index == UINT_MAX)
arg_pcr_index = (arg_file_system || arg_machine_id) ?
TPM2_PCR_SYSTEM_IDENTITY : /* → PCR 15 */
TPM2_PCR_KERNEL_BOOT; /* → PCR 11 */
@ -257,10 +267,119 @@ static int get_file_system_word(
return 0;
}
static int extend_now(unsigned pcr, const void *data, size_t size, Tpm2UserspaceEventType event) {
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
int r;
r = tpm2_context_new(arg_tpm2_device, &c);
if (r < 0)
return r;
r = determine_banks(c, pcr);
if (r < 0)
return r;
if (strv_isempty(arg_banks)) /* Still none? */
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Found a TPM2 without enabled PCR banks. Can't operate.");
_cleanup_free_ char *joined_banks = NULL;
joined_banks = strv_join(arg_banks, ", ");
if (!joined_banks)
return log_oom();
_cleanup_free_ char *safe = NULL;
if (size > EXTENSION_STRING_SAFE_LIMIT) {
safe = cescape_length(data, EXTENSION_STRING_SAFE_LIMIT);
if (!safe)
return log_oom();
if (!strextend(&safe, "..."))
return log_oom();
} else {
safe = cescape_length(data, size);
if (!safe)
return log_oom();
}
log_debug("Measuring '%s' into PCR index %u, banks %s.", safe, pcr, joined_banks);
r = tpm2_extend_bytes(c, arg_banks, pcr, data, size, /* secret= */ NULL, /* secret_size= */ 0, event, safe);
if (r < 0)
return log_error_errno(r, "Could not extend PCR: %m");
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", pcr, safe, joined_banks),
"MEASURING=%s", safe,
"PCR=%u", pcr,
"BANKS=%s", joined_banks);
return 0;
}
typedef struct MethodExtendParameters {
unsigned pcr;
const char *text;
void *data;
size_t data_size;
} MethodExtendParameters;
static int json_dispatch_binary_data(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
MethodExtendParameters *p = ASSERT_PTR(userdata);
_cleanup_free_ void *d = NULL;
size_t l;
int r;
r = json_variant_unbase64(variant, &d, &l);
if (r < 0)
return json_log(variant, flags, r, "JSON variant is not a base64 string.");
free_and_replace(p->data, d);
p->data_size = l;
return 0;
}
static int vl_method_extend(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
static const JsonDispatch dispatch_table[] = {
{ "pcr", JSON_VARIANT_UNSIGNED, json_dispatch_uint, offsetof(MethodExtendParameters, pcr), JSON_MANDATORY },
{ "text", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodExtendParameters, text), 0 },
{ "data", JSON_VARIANT_STRING, json_dispatch_binary_data, 0, 0 },
{}
};
MethodExtendParameters p = {
.pcr = UINT_MAX,
};
int r;
assert(link);
r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
if (r < 0)
return r;
if (!TPM2_PCR_INDEX_VALID(p.pcr))
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "pcr")));
if (p.text) {
/* Specifying both the text string and the binary data is not allowed */
if (p.data)
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "data")));
r = extend_now(p.pcr, p.text, strlen(p.text), _TPM2_USERSPACE_EVENT_TYPE_INVALID);
} else if (p.data)
r = extend_now(p.pcr, p.data, p.data_size, _TPM2_USERSPACE_EVENT_TYPE_INVALID);
else
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "text")));
if (r < 0)
return r;
return varlink_reply(link, NULL);
}
static int run(int argc, char *argv[]) {
_cleanup_free_ char *joined = NULL, *word = NULL;
_cleanup_free_ char *word = NULL;
Tpm2UserspaceEventType event;
size_t length;
int r;
log_setup();
@ -269,6 +388,30 @@ 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_PCRExtend);
if (r < 0)
return log_error_errno(r, "Failed to add Varlink interface: %m");
r = varlink_server_bind_method(varlink_server, "io.systemd.PCRExtend.Extend", vl_method_extend);
if (r < 0)
return log_error_errno(r, "Failed to bind Varlink method: %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;
}
if (arg_file_system) {
_cleanup_free_ char *normalized = NULL, *normalized_escaped = NULL;
_cleanup_(sd_device_unrefp) sd_device *d = NULL;
@ -348,8 +491,6 @@ static int run(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
length = strlen(word);
/* Skip logic if sd-stub is not used, after all PCR 11 might have a very different purpose then. */
r = efi_measured_uki(LOG_ERR);
if (r < 0)
@ -359,34 +500,10 @@ static int run(int argc, char *argv[]) {
return EXIT_SUCCESS;
}
_cleanup_(tpm2_context_unrefp) Tpm2Context *c = NULL;
r = tpm2_context_new(arg_tpm2_device, &c);
r = extend_now(arg_pcr_index, word, strlen(word), event);
if (r < 0)
return log_error_errno(r, "Failed to create TPM2 context: %m");
r = determine_banks(c, arg_pcr_index);
if (r < 0)
return r;
if (strv_isempty(arg_banks)) /* Still none? */
return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Found a TPM2 without enabled PCR banks. Can't operate.");
joined = strv_join(arg_banks, ", ");
if (!joined)
return log_oom();
log_debug("Measuring '%s' into PCR index %u, banks %s.", word, arg_pcr_index, joined);
r = tpm2_extend_bytes(c, arg_banks, arg_pcr_index, word, length, NULL, 0, event, word);
if (r < 0)
return log_error_errno(r, "Could not extend PCR: %m");
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_TPM_PCR_EXTEND_STR,
LOG_MESSAGE("Extended PCR index %u with '%s' (banks %s).", arg_pcr_index, word, joined),
"MEASURING=%s", word,
"PCR=%u", arg_pcr_index,
"BANKS=%s", joined);
return EXIT_SUCCESS;
}

View File

@ -170,6 +170,7 @@ shared_sources = files(
'varlink-io.systemd.c',
'varlink-io.systemd.Journal.c',
'varlink-io.systemd.ManagedOOM.c',
'varlink-io.systemd.PCRExtend.c',
'varlink-io.systemd.Resolve.Monitor.c',
'varlink-io.systemd.Resolve.c',
'varlink-io.systemd.UserDatabase.c',

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "varlink-io.systemd.PCRExtend.h"
static VARLINK_DEFINE_METHOD(
Extend,
VARLINK_DEFINE_INPUT(pcr, VARLINK_INT, 0),
VARLINK_DEFINE_INPUT(text, VARLINK_STRING, VARLINK_NULLABLE),
VARLINK_DEFINE_INPUT(data, VARLINK_STRING, VARLINK_NULLABLE));
VARLINK_DEFINE_INTERFACE(
io_systemd_PCRExtend,
"io.systemd.PCRExtend",
&vl_method_Extend);

View File

@ -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_PCRExtend;

View File

@ -10,6 +10,7 @@
#include "varlink-io.systemd.h"
#include "varlink-io.systemd.Journal.h"
#include "varlink-io.systemd.ManagedOOM.h"
#include "varlink-io.systemd.PCRExtend.h"
#include "varlink-io.systemd.Resolve.Monitor.h"
#include "varlink-io.systemd.Resolve.h"
#include "varlink-io.systemd.UserDatabase.h"
@ -134,6 +135,8 @@ TEST(parse_format) {
print_separator();
test_parse_format_one(&vl_interface_io_systemd);
print_separator();
test_parse_format_one(&vl_interface_io_systemd_PCRExtend);
print_separator();
test_parse_format_one(&vl_interface_xyz_test);
}

View File

@ -430,6 +430,15 @@ units = [
'file' : 'systemd-oomd.socket',
'conditions' : ['ENABLE_OOMD'],
},
{
'file' : 'systemd-pcrextend@.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
},
{
'file' : 'systemd-pcrextend.socket',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
'symlinks' : ['sockets.target.wants/'],
},
{
'file' : 'systemd-pcrfs-root.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],

View File

@ -0,0 +1,24 @@
# 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=TPM2 PCR Extension (Varlink)
Documentation=man:systemd-pcrextend(8)
DefaultDependencies=no
Before=sockets.target
ConditionSecurity=measured-uki
[Socket]
ListenStream=/run/systemd/io.systemd.PCRExtend
FileDescriptorName=varlink
SocketMode=0600
Accept=yes
[Install]
WantedBy=sockets.target

View File

@ -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=TPM2 PCR Extension (Varlink)
Documentation=man:systemd-pcrphase.service(8)
DefaultDependencies=no
Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target
[Service]
Environment=LISTEN_FDNAMES=varlink
ExecStart=-{{LIBEXECDIR}}/systemd-pcrextend