diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2015-03-12 19:01:44 -0400 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2015-03-18 11:53:13 -0400 |
commit | d886c9ccb5920543750a74c3d59e5a3826478c5d (patch) | |
tree | 76fb7a44e7164a2b72d6a0944f6db4d472a59283 /src/ostree/ot-builtin-gpg-sign.c | |
parent | 9033cf5da1588c0539ed0edf6f484144a6599af4 (diff) | |
download | ostree-d886c9ccb5920543750a74c3d59e5a3826478c5d.tar.gz |
gpg-sign: Add a --delete option to delete signatures
Diffstat (limited to 'src/ostree/ot-builtin-gpg-sign.c')
-rw-r--r-- | src/ostree/ot-builtin-gpg-sign.c | 169 |
1 files changed, 168 insertions, 1 deletions
diff --git a/src/ostree/ot-builtin-gpg-sign.c b/src/ostree/ot-builtin-gpg-sign.c index cc298434..6c446ba3 100644 --- a/src/ostree/ot-builtin-gpg-sign.c +++ b/src/ostree/ot-builtin-gpg-sign.c @@ -27,10 +27,12 @@ #include "ostree.h" #include "otutil.h" +static gboolean opt_delete; static char *opt_gpg_homedir; static GOptionEntry options[] = { - { "gpg-homedir", 0, 0, G_OPTION_ARG_STRING, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR"}, + { "delete", 'd', 0, G_OPTION_ARG_NONE, &opt_delete, "Delete signatures having any of the GPG KEY-IDs" }, + { "gpg-homedir", 0, 0, G_OPTION_ARG_STRING, &opt_gpg_homedir, "GPG Homedir to use when looking for keyrings", "HOMEDIR" }, }; static void @@ -41,6 +43,156 @@ usage_error (GOptionContext *context, const char *message, GError **error) g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, message); } +static gboolean +delete_signatures (OstreeRepo *repo, + const char *commit_checksum, + const char * const *key_ids, + guint n_key_ids, + guint *out_n_deleted, + GCancellable *cancellable, + GError **error) +{ + GVariantDict metadata_dict; + gs_unref_object OstreeGpgVerifyResult *result = NULL; + gs_unref_variant GVariant *old_metadata = NULL; + gs_unref_variant GVariant *new_metadata = NULL; + gs_unref_variant GVariant *signature_data = NULL; + GVariantIter iter; + GVariant *child; + GQueue signatures = G_QUEUE_INIT; + GQueue trash = G_QUEUE_INIT; + guint n_deleted = 0; + guint ii; + gboolean ret = FALSE; + GError *local_error = NULL; + + /* XXX Should this code be a new OstreeRepo function in libostree? + * Feels slightly too low-level here, and I have to know about + * the metadata key name and format which are both declared in + * ostree-core-private.h. + * + * OTOH, would this really be a useful addition to libostree? + */ + + if (!ostree_repo_read_commit_detached_metadata (repo, + commit_checksum, + &old_metadata, + cancellable, + error)) + goto out; + + g_variant_dict_init (&metadata_dict, old_metadata); + + signature_data = g_variant_dict_lookup_value (&metadata_dict, + "ostree.gpgsigs", + G_VARIANT_TYPE ("aay")); + + /* Taking the approach of deleting whatever matches we find for the + * provided key IDs, even if we don't find a match for EVERY key ID. + * So no signatures means no matches, which is okay... I guess. */ + if (signature_data == NULL) + { + g_variant_dict_clear (&metadata_dict); + goto shortcut; + } + + /* Parse the signatures on this commit by running a verify operation + * on it. Use the result to match key IDs to signatures for deletion. + * + * XXX Reading detached metadata from disk twice here. Another reason + * to move this into libostree? + */ + result = ostree_repo_verify_commit_ext (repo, commit_checksum, + NULL, NULL, + cancellable, &local_error); + if (result == NULL) + { + g_variant_dict_clear (&metadata_dict); + goto out; + } + + /* Convert the GVariant array to a GQueue. */ + g_variant_iter_init (&iter, signature_data); + while ((child = g_variant_iter_next_value (&iter)) != NULL) + { + /* Takes ownership of the child. */ + g_queue_push_tail (&signatures, child); + } + + /* Signature count and ordering of signatures in the GQueue and + * OstreeGpgVerifyResult must agree. We use this below to mark + * items in the GQueue for deletion based on the index returned + * by ostree_gpg_verify_result_lookup(). */ + g_assert_cmpuint (ostree_gpg_verify_result_count_all (result), ==, signatures.length); + + /* Build a trash queue which points at nodes in the signature queue. */ + for (ii = 0; ii < n_key_ids; ii++) + { + guint index; + + if (ostree_gpg_verify_result_lookup (result, key_ids[ii], &index)) + { + GList *link = g_queue_peek_nth_link (&signatures, index); + + /* Avoid duplicates in the trash queue. */ + if (g_queue_find (&trash, link) == NULL) + g_queue_push_tail (&trash, link); + } + } + + n_deleted = trash.length; + + /* Reduce the signature queue by emptying the trash. */ + while (!g_queue_is_empty (&trash)) + { + GList *link = g_queue_pop_head (&trash); + g_variant_unref (link->data); + g_queue_delete_link (&signatures, link); + } + + /* Update the metadata dictionary. */ + if (g_queue_is_empty (&signatures)) + { + g_variant_dict_remove (&metadata_dict, "ostree.gpgsigs"); + } + else + { + GVariantBuilder signature_builder; + + g_variant_builder_init (&signature_builder, G_VARIANT_TYPE ("aay")); + + while (!g_queue_is_empty (&signatures)) + { + GVariant *child = g_queue_pop_head (&signatures); + g_variant_builder_add_value (&signature_builder, child); + g_variant_unref (child); + } + + g_variant_dict_insert_value (&metadata_dict, + "ostree.gpgsigs", + g_variant_builder_end (&signature_builder)); + } + + /* Commit the new metadata. */ + new_metadata = g_variant_dict_end (&metadata_dict); + if (!ostree_repo_write_commit_detached_metadata (repo, + commit_checksum, + new_metadata, + cancellable, + error)) + goto out; + +shortcut: + + if (out_n_deleted != NULL) + *out_n_deleted = n_deleted; + + ret = TRUE; + +out: + return ret; +} + gboolean ostree_builtin_gpg_sign (int argc, char **argv, GCancellable *cancellable, GError **error) { @@ -76,6 +228,21 @@ ostree_builtin_gpg_sign (int argc, char **argv, GCancellable *cancellable, GErro if (!ostree_repo_resolve_rev (repo, commit, FALSE, &resolved_commit, error)) goto out; + if (opt_delete) + { + guint n_deleted = 0; + + if (delete_signatures (repo, resolved_commit, + (const char * const *) key_ids, n_key_ids, + &n_deleted, cancellable, error)) + { + g_print ("Signatures deleted: %u\n", n_deleted); + ret = TRUE; + } + + goto out; + } + for (ii = 0; ii < n_key_ids; ii++) { if (!ostree_repo_sign_commit (repo, resolved_commit, key_ids[ii], |