diff options
author | Colin Walters <walters@verbum.org> | 2021-08-31 08:04:21 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-31 08:04:21 -0400 |
commit | 3691a23a419690ebd864fa961e01ed14f8cc151e (patch) | |
tree | ee3f57dfefdf06b357bd9bf352735a60c1740cd3 /src | |
parent | cfa2aec839cfe687a2f0fe8e717af0400c20ed8e (diff) | |
parent | 359435de843ce2a1e94941c24ec4ddd7d5a7bccb (diff) | |
download | ostree-3691a23a419690ebd864fa961e01ed14f8cc151e.tar.gz |
Merge pull request #2340 from cgwalters/sign-verify-api
Add an API to verify a commit signature explicitly
Diffstat (limited to 'src')
-rw-r--r-- | src/libostree/libostree-devel.sym | 1 | ||||
-rw-r--r-- | src/libostree/ostree-repo-pull-verify.c | 115 | ||||
-rw-r--r-- | src/libostree/ostree-repo.h | 23 | ||||
-rw-r--r-- | src/ostree/ot-admin-builtin-status.c | 36 |
4 files changed, 174 insertions, 1 deletions
diff --git a/src/libostree/libostree-devel.sym b/src/libostree/libostree-devel.sym index 75bc4647..7e6f7784 100644 --- a/src/libostree/libostree-devel.sym +++ b/src/libostree/libostree-devel.sym @@ -25,6 +25,7 @@ LIBOSTREE_2021.4 { global: ostree_repo_remote_get_gpg_keys; + ostree_repo_signature_verify_commit_data; } LIBOSTREE_2021.3; /* Stub section for the stable release *after* this development one; don't diff --git a/src/libostree/ostree-repo-pull-verify.c b/src/libostree/ostree-repo-pull-verify.c index fa170f94..e469dc0b 100644 --- a/src/libostree/ostree-repo-pull-verify.c +++ b/src/libostree/ostree-repo-pull-verify.c @@ -270,6 +270,7 @@ _sign_verify_for_remote (GPtrArray *verifiers, g_assert (out_success_message == NULL || *out_success_message == NULL); + g_assert (verifiers); g_assert_cmpuint (verifiers->len, >=, 1); for (guint i = 0; i < verifiers->len; i++) { @@ -346,6 +347,120 @@ _process_gpg_verify_result (OtPullData *pull_data, } #endif /* OSTREE_DISABLE_GPGME */ +static gboolean +validate_metadata_size (const char *prefix, GBytes *buf, GError **error) +{ + gsize len = g_bytes_get_size (buf); + if (len > OSTREE_MAX_METADATA_SIZE) + return glnx_throw (error, "%s is %" G_GUINT64_FORMAT " bytes, exceeding maximum %" G_GUINT64_FORMAT, prefix, (guint64)len, (guint64)OSTREE_MAX_METADATA_SIZE); + return TRUE; +} + +/** + * ostree_repo_signature_verify_commit_data: + * @self: Repo + * @remote_name: Name of remote + * @commit_data: Commit object data (GVariant) + * @commit_metadata: Commit metadata (GVariant `a{sv}`), must contain at least one valid signature + * @flags: Optionally disable GPG or signapi + * @out_results: (nullable) (out) (transfer full): Textual description of results + * @error: Error + * + * Validate the commit data using the commit metadata which must + * contain at least one valid signature. If GPG and signapi are + * both enabled, then both must find at least one valid signature. + */ +gboolean +ostree_repo_signature_verify_commit_data (OstreeRepo *self, + const char *remote_name, + GBytes *commit_data, + GBytes *commit_metadata, + OstreeRepoVerifyFlags flags, + char **out_results, + GError **error) +{ + g_assert (self); + g_assert (remote_name); + g_assert (commit_data); + + gboolean gpg = !(flags & OSTREE_REPO_VERIFY_FLAGS_NO_GPG); + gboolean signapi = !(flags & OSTREE_REPO_VERIFY_FLAGS_NO_SIGNAPI); + // Must ask for at least one type of verification + if (!(gpg || signapi)) + return glnx_throw (error, "No commit verification types enabled via API"); + + if (!validate_metadata_size ("Commit", commit_data, error)) + return FALSE; + /* Nothing to check if detached metadata is absent */ + if (commit_metadata == NULL) + return glnx_throw (error, "Can't verify commit without detached metadata"); + if (!validate_metadata_size ("Commit metadata", commit_metadata, error)) + return FALSE; + g_autoptr(GVariant) commit_metadata_v = g_variant_new_from_bytes (G_VARIANT_TYPE_VARDICT, commit_metadata, FALSE); + + g_autoptr(GString) results_buf = g_string_new (""); + gboolean verified = FALSE; + + if (gpg) + { + if (!ostree_repo_remote_get_gpg_verify (self, remote_name, + &gpg, error)) + return FALSE; + } + + /* TODO - we could cache this in the repo */ + g_autoptr(GPtrArray) signapi_verifiers = NULL; + if (signapi) + { + if (!_signapi_init_for_remote (self, remote_name, &signapi_verifiers, NULL, error)) + return FALSE; + } + + if (!(gpg || signapi_verifiers)) + return glnx_throw (error, "Cannot verify commit for remote %s; GPG verification disabled, and no signapi verifiers configured", remote_name); + +#ifndef OSTREE_DISABLE_GPGME + if (gpg) + { + g_autoptr(OstreeGpgVerifyResult) result = + _ostree_repo_gpg_verify_with_metadata (self, commit_data, + commit_metadata_v, + remote_name, + NULL, NULL, NULL, error); + if (!result) + return FALSE; + if (!ostree_gpg_verify_result_require_valid_signature (result, error)) + return FALSE; + + const guint n_signatures = ostree_gpg_verify_result_count_all (result); + g_assert_cmpuint (n_signatures, >, 0); + for (guint jj = 0; jj < n_signatures; jj++) + { + ostree_gpg_verify_result_describe (result, jj, results_buf, "GPG: ", + OSTREE_GPG_SIGNATURE_FORMAT_DEFAULT); + } + verified = TRUE; + } +#endif /* OSTREE_DISABLE_GPGME */ + + if (signapi_verifiers) + { + g_autofree char *success_message = NULL; + if (!_sign_verify_for_remote (signapi_verifiers, commit_data, commit_metadata_v, &success_message, error)) + return glnx_prefix_error (error, "Can't verify commit"); + if (verified) + g_string_append_c (results_buf, '\n'); + g_string_append (results_buf, success_message); + verified = TRUE; + } + + /* Must be true since we did g_assert (gpg || signapi) */ + g_assert (verified); + if (out_results) + *out_results = g_string_free (g_steal_pointer (&results_buf), FALSE); + return TRUE; +} + gboolean _verify_unwritten_commit (OtPullData *pull_data, const char *checksum, diff --git a/src/libostree/ostree-repo.h b/src/libostree/ostree-repo.h index 962fa8cc..522cb034 100644 --- a/src/libostree/ostree-repo.h +++ b/src/libostree/ostree-repo.h @@ -1538,6 +1538,29 @@ OstreeGpgVerifyResult * ostree_repo_verify_summary (OstreeRepo *self, GCancellable *cancellable, GError **error); +/** + * OstreeRepoVerifyFlags: + * @OSTREE_REPO_VERIFY_FLAGS_NONE: No flags + * @OSTREE_REPO_VERIFY_FLAGS_NO_GPG: Skip GPG verification + * @OSTREE_REPO_VERIFY_FLAGS_NO_SIGNAPI: Skip all other signature verification methods + * + * Since: 2021.4 + */ +typedef enum { + OSTREE_REPO_VERIFY_FLAGS_NONE = 0, + OSTREE_REPO_VERIFY_FLAGS_NO_GPG = (1 << 0), + OSTREE_REPO_VERIFY_FLAGS_NO_SIGNAPI = (1 << 1), +} OstreeRepoVerifyFlags; + +_OSTREE_PUBLIC +gboolean ostree_repo_signature_verify_commit_data (OstreeRepo *self, + const char *remote_name, + GBytes *commit_data, + GBytes *commit_metadata, + OstreeRepoVerifyFlags flags, + char **out_results, + GError **error); + _OSTREE_PUBLIC gboolean ostree_repo_regenerate_summary (OstreeRepo *self, GVariant *additional_metadata, diff --git a/src/ostree/ot-admin-builtin-status.c b/src/ostree/ot-admin-builtin-status.c index c6c52382..8b2325d5 100644 --- a/src/ostree/ot-admin-builtin-status.c +++ b/src/ostree/ot-admin-builtin-status.c @@ -31,7 +31,10 @@ #include <glib/gi18n.h> +static gboolean opt_verify; + static GOptionEntry options[] = { + { "verify", 'V', 0, G_OPTION_ARG_NONE, &opt_verify, "Print the commit verification status", NULL }, { NULL } }; @@ -86,6 +89,12 @@ deployment_print_status (OstreeSysroot *sysroot, g_autoptr(GVariant) commit_metadata = NULL; if (commit) commit_metadata = g_variant_get_child_value (commit, 0); + g_autoptr(GVariant) commit_detached_metadata = NULL; + if (commit) + { + if (!ostree_repo_read_commit_detached_metadata (repo, ref, &commit_detached_metadata, cancellable, error)) + return FALSE; + } const char *version = NULL; const char *source_title = NULL; @@ -139,7 +148,7 @@ deployment_print_status (OstreeSysroot *sysroot, } #ifndef OSTREE_DISABLE_GPGME - if (deployment_get_gpg_verify (deployment, repo)) + if (!opt_verify && deployment_get_gpg_verify (deployment, repo)) { g_autoptr(GString) output_buffer = g_string_sized_new (256); /* Print any digital signatures on this commit. */ @@ -172,6 +181,31 @@ deployment_print_status (OstreeSysroot *sysroot, g_print ("%s", output_buffer->str); } #endif /* OSTREE_DISABLE_GPGME */ + if (opt_verify) + { + if (!commit) + return glnx_throw (error, "Cannot verify, failed to load commit"); + + if (origin == NULL) + return glnx_throw (error, "Cannot verify deployment with no origin"); + + g_autofree char *refspec = g_key_file_get_string (origin, "origin", "refspec", NULL); + if (refspec == NULL) + return glnx_throw (error, "No origin/refspec, cannot verify"); + g_autofree char *remote = NULL; + if (!ostree_parse_refspec (refspec, &remote, NULL, NULL)) + return FALSE; + if (remote == NULL) + return glnx_throw (error, "Cannot verify deployment without remote"); + + g_autoptr(GBytes) commit_data = g_variant_get_data_as_bytes (commit); + g_autoptr(GBytes) commit_detached_metadata_bytes = + commit_detached_metadata ? g_variant_get_data_as_bytes (commit_detached_metadata) : NULL; + g_autofree char *verify_text = NULL; + if (!ostree_repo_signature_verify_commit_data (repo, remote, commit_data, commit_detached_metadata_bytes, 0, &verify_text, error)) + return FALSE; + g_print ("%s\n", verify_text); + } return TRUE; } |