From: Philip Withnall Date: Tue, 4 Aug 2026 15:52:53 +0000 (+0100) Subject: ot-builtin-summary: Verify signature when viewing a summary file X-Git-Tag: archive/raspbian/2026.4-1+rpi1^2~9^2~1^2~1^2~1 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=d180a0e5ba038e975c9f4c5f951bbeb7dfcb9051;p=ostree.git ot-builtin-summary: Verify signature when viewing a summary file So now we can run `ostree summary --repo . --view --gpg-homedir /path/to/servers/gpg-homedir` and it’ll verify the GPG signatures. That’s not so useful for verification per-se, since if you’re running this on a server you’d expect the signatures you’ve generated to verify correctly. It’s more useful to see how many signatures are on the `summary` and which subkeys were used to generate them, when messing around with multiple signing keys. Aside from this, there is no way (that I know of) to inspect the signatures on a summary file without checking out a copy of the repository and hoping that the client has got a full copy of the keyring containing all relevant subkeys. The verification output is very similar to that of `ostree show`. Note that signatures are not verified if running with `--raw`, `--list-metadata-keys` or `--print-metadata-key`, since clients may be parsing the output of those commands. Signed-off-by: Philip Withnall --- diff --git a/src/ostree/ot-builtin-summary.c b/src/ostree/ot-builtin-summary.c index d131f80b..f899d91c 100644 --- a/src/ostree/ot-builtin-summary.c +++ b/src/ostree/ot-builtin-summary.c @@ -93,10 +93,14 @@ build_additional_metadata (const char *const *args, GError **error) return g_variant_ref_sink (g_variant_builder_end (builder)); } +/* @out_summary_data is guaranteed to return non-NULL, but @out_summary_sig_data + * may return NULL (with no error) if the summary is unsigned. */ static gboolean -get_summary_data (OstreeRepo *repo, GBytes **out_summary_data, GError **error) +get_summary_data (OstreeRepo *repo, GBytes **out_summary_data, GBytes **out_summary_sig_data, + GError **error) { g_assert (out_summary_data != NULL); + g_assert (out_summary_sig_data != NULL); g_autoptr (GBytes) summary_data = NULL; glnx_autofd int fd = -1; @@ -106,7 +110,19 @@ get_summary_data (OstreeRepo *repo, GBytes **out_summary_data, GError **error) if (!summary_data) return FALSE; + g_autoptr (GBytes) summary_sig_data = NULL; + glnx_autofd int sig_fd = -1; + if (!ot_openat_ignore_enoent (repo->repo_dir_fd, "summary.sig", &sig_fd, error)) + return FALSE; + if (sig_fd >= 0) + { + summary_sig_data = ot_fd_readall_or_mmap (sig_fd, 0, error); + if (!summary_sig_data) + return FALSE; + } + *out_summary_data = g_steal_pointer (&summary_data); + *out_summary_sig_data = g_steal_pointer (&summary_sig_data); return TRUE; } @@ -119,6 +135,9 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati g_autoptr (OstreeRepo) repo = NULL; g_autoptr (OstreeSign) sign = NULL; OstreeDumpFlags flags = OSTREE_DUMP_NONE; + g_autoptr (GBytes) summary_data = NULL; + g_autoptr (GBytes) summary_sig_data = NULL; + gboolean show_signatures = FALSE; context = g_option_context_new (""); @@ -188,34 +207,32 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati } else if (opt_view || opt_raw) { - g_autoptr (GBytes) summary_data = NULL; - if (opt_raw) flags |= OSTREE_DUMP_RAW; - if (!get_summary_data (repo, &summary_data, error)) + if (!get_summary_data (repo, &summary_data, &summary_sig_data, error)) return FALSE; ot_dump_summary_bytes (summary_data, flags); + show_signatures = !opt_raw; } else if (opt_list_metadata_keys) { - g_autoptr (GBytes) summary_data = NULL; - - if (!get_summary_data (repo, &summary_data, error)) + if (!get_summary_data (repo, &summary_data, &summary_sig_data, error)) return FALSE; ot_dump_summary_metadata_keys (summary_data); + show_signatures = FALSE; } else if (opt_print_metadata_key) { - g_autoptr (GBytes) summary_data = NULL; - - if (!get_summary_data (repo, &summary_data, error)) + if (!get_summary_data (repo, &summary_data, &summary_sig_data, error)) return FALSE; if (!ot_dump_summary_metadata_key (summary_data, opt_print_metadata_key, error)) return FALSE; + + show_signatures = FALSE; } else { @@ -224,5 +241,49 @@ ostree_builtin_summary (int argc, char **argv, OstreeCommandInvocation *invocati return FALSE; } +#ifndef OSTREE_DISABLE_GPGME + if (show_signatures && summary_sig_data == NULL) + { + g_print ("Summary is unsigned\n"); + } + else if (show_signatures) + { + g_autoptr (OstreeGpgVerifyResult) result = NULL; + g_autoptr (GFile) gpg_homedir + = opt_gpg_homedir ? g_file_new_for_path (opt_gpg_homedir) : NULL; + g_autoptr (GError) local_error = NULL; + + g_assert (summary_data != NULL && summary_sig_data != NULL); + + result = ostree_repo_verify_local_summary (repo, summary_data, summary_sig_data, gpg_homedir, + NULL, cancellable, &local_error); + + if (g_error_matches (local_error, OSTREE_GPG_ERROR, OSTREE_GPG_ERROR_NO_SIGNATURE)) + { + /* Ignore */ + } + else if (local_error != NULL) + { + g_propagate_error (error, g_steal_pointer (&local_error)); + return FALSE; + } + else + { + unsigned int n_sigs = ostree_gpg_verify_result_count_all (result); + g_print ("\nFound %u signature%s on the summary:\n", n_sigs, n_sigs == 1 ? "" : "s"); + + g_autoptr (GString) buffer = g_string_sized_new (256); + for (unsigned int ii = 0; ii < n_sigs; ii++) + { + g_string_append_c (buffer, '\n'); + ostree_gpg_verify_result_describe (result, ii, buffer, " ", + OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT); + } + + g_print ("%s", buffer->str); + } + } +#endif /* OSTREE_DISABLE_GPGME */ + return TRUE; } diff --git a/tests/test-summary-view.sh b/tests/test-summary-view.sh index 473c6db5..c1d81bdd 100755 --- a/tests/test-summary-view.sh +++ b/tests/test-summary-view.sh @@ -27,8 +27,10 @@ set -euo pipefail echo "1..2" COMMIT_SIGN="" +SUMMARY_HOMEDIR="" if has_ostree_feature gpgme; then COMMIT_SIGN="--gpg-homedir=${TEST_GPG_KEYHOME} --gpg-sign=${TEST_GPG_KEYID_1}" + SUMMARY_HOMEDIR="--gpg-homedir=${TEST_GPG_KEYHOME}" fi setup_fake_remote_repo1 "archive" "${COMMIT_SIGN}" @@ -40,7 +42,7 @@ echo 'hello world some object' > hello-world ${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo commit ${COMMIT_SIGN} -b other -s "A commit" -m "Example commit body" # Generate the summary file. -${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u +${CMD_PREFIX} ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u ${COMMIT_SIGN} # Check out the repository. prev_dir=`pwd` @@ -51,17 +53,22 @@ ${CMD_PREFIX} ostree --repo=repo pull --mirror origin # Check the summary file exists in the checkout, and can be viewed. assert_has_file repo/summary -${OSTREE} summary --view > summary.txt +${OSTREE} summary ${SUMMARY_HOMEDIR} --view > summary.txt assert_file_has_content_literal summary.txt "* main" assert_file_has_content_literal summary.txt "* other" assert_file_has_content_literal summary.txt "ostree.summary.last-modified" assert_file_has_content_literal summary.txt "Timestamp (ostree.commit.timestamp): " assert_file_has_content_literal summary.txt "Version (ostree.commit.version): 3.2" +if has_ostree_feature gpgme; then + assert_file_has_content_literal summary.txt "Good signature from" +fi echo "ok view summary" -# Check the summary can be viewed raw too. -${OSTREE} summary --raw > raw-summary.txt +# Check the summary can be viewed raw too, but that it doesn’t include signature information. +${OSTREE} summary ${SUMMARY_HOMEDIR} --raw > raw-summary.txt assert_file_has_content_literal raw-summary.txt "('main', (" assert_file_has_content_literal raw-summary.txt "('other', (" assert_file_has_content_literal raw-summary.txt "'ostree.summary.last-modified':