measure: add --append= switch for merging signatures

Often it's useful to add multiple signatures in the signature JSON file
to embedd in a single .pcrsig. (For example, a signature by key X for
boot phase "enter-initrd" and one by key Y for
"enter-initrd:leave-initrd" or so). Make this easy, by adding the
ability to append signatures to a previously generated JSON file.
This commit is contained in:
Lennart Poettering 2022-11-01 14:54:47 +01:00
parent 3bb326c558
commit a5c690a8b5
2 changed files with 37 additions and 1 deletions

View file

@ -182,6 +182,19 @@
<citerefentry><refentrytitle>systemd-pcrphase.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--append=</option><replaceable>PATH</replaceable></term>
<listitem><para>When generating a PCR JSON signature (via the <command>sign</command> command),
combine it with a previously generated PCR JSON signature, and output it as one. The specified path
must refer to a regular file that contains a valid JSON PCR signature object. The specified file is
not modified. It will be read first, then the newly generated signature appended to it, and the
resulting object is written to standard output. Use this to generate a single JSON object consisting
from signatures made with a number of signing keys (for example, to have one key per boot phase). The
command will suppress duplicates: if a specific signature is already included in a JSON signature
object it is not added a second time.</para></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="json" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
<xi:include href="standard-options.xml" xpointer="help" />

View file

@ -33,12 +33,14 @@ static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_PRETTY_AUTO|JSON_FORM
static PagerFlags arg_pager_flags = 0;
static bool arg_current = false;
static char **arg_phase = NULL;
static char *arg_append = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_banks, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_tpm2_device, freep);
STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_public_key, freep);
STATIC_DESTRUCTOR_REGISTER(arg_phase, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_append, freep);
static inline void free_sections(char*(*sections)[_UNIFIED_SECTION_MAX]) {
for (UnifiedSection c = 0; c < _UNIFIED_SECTION_MAX; c++)
@ -73,6 +75,7 @@ static int help(int argc, char *argv[], void *userdata) {
" --public-key=KEY Public key (PEM) to validate against\n"
" --json=MODE Output as JSON\n"
" -j Same as --json=pretty on tty, --json=short otherwise\n"
" --append=PATH Load specified JSON signature, and append new signature to it\n"
"\n%3$sUKI PE Section Options:%4$s %3$sUKI PE Section%4$s\n"
" --linux=PATH Path to Linux kernel image file %7$s .linux\n"
" --osrel=PATH Path to os-release file %7$s .osrel\n"
@ -128,6 +131,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_TPM2_DEVICE,
ARG_JSON,
ARG_PHASE,
ARG_APPEND,
};
static const struct option options[] = {
@ -148,6 +152,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "public-key", required_argument, NULL, ARG_PUBLIC_KEY },
{ "json", required_argument, NULL, ARG_JSON },
{ "phase", required_argument, NULL, ARG_PHASE },
{ "append", required_argument, NULL, ARG_APPEND },
{}
};
@ -254,6 +259,13 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
case ARG_APPEND:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_append);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
@ -623,6 +635,8 @@ static int verb_calculate(int argc, char *argv[], void *userdata) {
if (!arg_sections[UNIFIED_SECTION_LINUX] && !arg_current)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Either --linux= or --current must be specified, refusing.");
if (arg_append)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --append= switch is only supported for 'sign', not 'calculate'.");
assert(!strv_isempty(arg_banks));
assert(!strv_isempty(arg_phase));
@ -728,6 +742,15 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
assert(!strv_isempty(arg_banks));
assert(!strv_isempty(arg_phase));
if (arg_append) {
r = json_parse_file(NULL, arg_append, 0, &v, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse '%s': %m", arg_append);
if (!json_variant_is_object(v))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "File '%s' is not a valid JSON object, refusing.", arg_append);
}
/* When signing we only support JSON output */
arg_json_format_flags &= ~JSON_FORMAT_OFF;
@ -936,7 +959,7 @@ static int verb_sign(int argc, char *argv[], void *userdata) {
_cleanup_(json_variant_unrefp) JsonVariant *av = NULL;
av = json_variant_ref(json_variant_by_key(v, p->bank));
r = json_variant_append_array(&av, bv);
r = json_variant_append_array_nodup(&av, bv);
if (r < 0) {
log_error_errno(r, "Failed to append JSON object: %m");
goto finish;