From f244c702772c69378099685316033d4a6f7b862c Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Wed, 16 Nov 2016 09:13:54 -0500 Subject: [PATCH] Add "gpgkeypath" option to remotes For Project Atomic, we already have RPM signatures which use files in `/etc/pki/rpm-gpg`. It's convenient to simply bind the OSTree remote configuration to those file paths, rather than having duplicate key data. This does mean that we need to parse the files for verification, so we end up importing them into the verifier's temporary keyring, which is a bit ugly, but it's what other projects do. Closes: https://github.com/ostreedev/ostree/issues/573 Closes: #575 Approved by: giuseppe --- man/ostree.repo-config.xml | 3 +- man/ostree.xml | 8 +++-- src/libostree/ostree-gpg-verifier.c | 51 +++++++++++++++++++++++++++++ src/libostree/ostree-gpg-verifier.h | 3 ++ src/libostree/ostree-repo.c | 8 +++++ tests/test-remote-gpg-import.sh | 31 ++++++++++++++++-- 6 files changed, 99 insertions(+), 5 deletions(-) diff --git a/man/ostree.repo-config.xml b/man/ostree.repo-config.xml index 1182aa93..876c9dfc 100644 --- a/man/ostree.repo-config.xml +++ b/man/ostree.repo-config.xml @@ -199,7 +199,8 @@ Boston, MA 02111-1307, USA. Per-remote GPG keyrings and verification - OSTree supports a per-remote GPG keyring. For more information see + OSTree supports a per-remote GPG keyring, as well as a + gpgkeypath option. For more information see ostree1. in the section GPG verification. diff --git a/man/ostree.xml b/man/ostree.xml index 80b0b0c1..e31d58b2 100644 --- a/man/ostree.xml +++ b/man/ostree.xml @@ -433,8 +433,12 @@ Boston, MA 02111-1307, USA. in this directory. - In addition to the system repository, OSTree supports a - per-remote + In addition to the system repository, OSTree supports two + other paths. First, there is a + gpgkeypath option for remotes, which must + point to the filename of an ASCII-armored key. + + Second, there is support for a per-remote remotename.trustedkeys.gpg file stored in the toplevel of the repository (alongside objects/ and such). This is diff --git a/src/libostree/ostree-gpg-verifier.c b/src/libostree/ostree-gpg-verifier.c index 9eae7ceb..eda05e9a 100644 --- a/src/libostree/ostree-gpg-verifier.c +++ b/src/libostree/ostree-gpg-verifier.c @@ -40,6 +40,7 @@ struct OstreeGpgVerifier { GObject parent; GList *keyrings; + GPtrArray *key_ascii_files; }; G_DEFINE_TYPE (OstreeGpgVerifier, _ostree_gpg_verifier, G_TYPE_OBJECT) @@ -50,6 +51,8 @@ ostree_gpg_verifier_finalize (GObject *object) OstreeGpgVerifier *self = OSTREE_GPG_VERIFIER (object); g_list_free_full (self->keyrings, g_object_unref); + if (self->key_ascii_files) + g_ptr_array_unref (self->key_ascii_files); G_OBJECT_CLASS (_ostree_gpg_verifier_parent_class)->finalize (object); } @@ -98,6 +101,7 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self, OstreeGpgVerifyResult *result = NULL; gboolean success = FALSE; GList *link; + int armor; /* GPGME has no API for using multiple keyrings (aka, gpg --keyring), * so we concatenate all the keyring files into one pubring.gpg in a @@ -149,6 +153,44 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self, if (!g_output_stream_close (target_stream, cancellable, error)) goto out; + /* Save the previous armor value - we need it on for importing ASCII keys */ + armor = gpgme_get_armor (result->context); + gpgme_set_armor (result->context, 1); + + /* Now, use the API to import ASCII-armored keys */ + if (self->key_ascii_files) + { + for (guint i = 0; i < self->key_ascii_files->len; i++) + { + const char *path = self->key_ascii_files->pdata[i]; + glnx_fd_close int fd = -1; + ot_auto_gpgme_data gpgme_data_t kdata = NULL; + + fd = openat (AT_FDCWD, path, O_RDONLY | O_CLOEXEC) ; + if (fd < 0) + { + glnx_set_prefix_error_from_errno (error, "Opening %s", path); + goto out; + } + + gpg_error = gpgme_data_new_from_fd (&kdata, fd); + if (gpg_error != GPG_ERR_NO_ERROR) + { + ot_gpgme_error_to_gio_error (gpg_error, error); + goto out; + } + + gpg_error = gpgme_op_import (result->context, kdata); + if (gpg_error != GPG_ERR_NO_ERROR) + { + ot_gpgme_error_to_gio_error (gpg_error, error); + goto out; + } + } + } + + gpgme_set_armor (result->context, armor); + /* Both the signed data and signature GBytes instances will outlive the * gpgme_data_t structs, so we can safely reuse the GBytes memory buffer * directly and avoid a copy. */ @@ -225,6 +267,15 @@ _ostree_gpg_verifier_add_keyring (OstreeGpgVerifier *self, self->keyrings = g_list_append (self->keyrings, g_object_ref (path)); } +void +_ostree_gpg_verifier_add_key_ascii_file (OstreeGpgVerifier *self, + const char *path) +{ + if (!self->key_ascii_files) + self->key_ascii_files = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (self->key_ascii_files, g_strdup (path)); +} + gboolean _ostree_gpg_verifier_add_keyring_dir (OstreeGpgVerifier *self, GFile *path, diff --git a/src/libostree/ostree-gpg-verifier.h b/src/libostree/ostree-gpg-verifier.h index 2db39f3b..4156d1bd 100644 --- a/src/libostree/ostree-gpg-verifier.h +++ b/src/libostree/ostree-gpg-verifier.h @@ -62,4 +62,7 @@ gboolean _ostree_gpg_verifier_add_global_keyring_dir (OstreeGpgVerifier *s void _ostree_gpg_verifier_add_keyring (OstreeGpgVerifier *self, GFile *path); +void _ostree_gpg_verifier_add_key_ascii_file (OstreeGpgVerifier *self, + const char *path); + G_END_DECLS diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c index 584e570c..c0cbede6 100644 --- a/src/libostree/ostree-repo.c +++ b/src/libostree/ostree-repo.c @@ -4282,6 +4282,7 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo *self, } else if (remote_name != NULL) { + g_autofree char *gpgkeypath = NULL; /* Add the remote's keyring file if it exists. */ OstreeRemote *remote; @@ -4299,6 +4300,13 @@ _ostree_repo_gpg_verify_data_internal (OstreeRepo *self, add_global_keyring_dir = FALSE; } + if (!ot_keyfile_get_value_with_default (remote->options, remote->group, "gpgkeypath", NULL, + &gpgkeypath, error)) + return NULL; + + if (gpgkeypath) + _ostree_gpg_verifier_add_key_ascii_file (verifier, gpgkeypath); + ost_remote_unref (remote); } diff --git a/tests/test-remote-gpg-import.sh b/tests/test-remote-gpg-import.sh index 8d155f79..4dbe7fd9 100755 --- a/tests/test-remote-gpg-import.sh +++ b/tests/test-remote-gpg-import.sh @@ -26,7 +26,7 @@ unset OSTREE_GPG_HOME setup_fake_remote_repo1 "archive-z2" -echo "1..1" +echo "1..2" cd ${test_tmpdir} mkdir repo @@ -143,5 +143,32 @@ if ${OSTREE} pull R2:main >/dev/null 2>&1; then fi ${OSTREE} pull R3:main >/dev/null -libtest_cleanup_gpg echo "ok" + +rm repo/refs/remotes/* -rf +${OSTREE} prune --refs-only + +# Test the successful gpgkeypath option +${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key3.asc R4 $(cat httpd-address)/ostree/gnomerepo +${OSTREE} pull R4:main >/dev/null + +rm repo/refs/remotes/* -rf +${OSTREE} prune --refs-only + +${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/INVALIDKEYPATH.asc R5 $(cat httpd-address)/ostree/gnomerepo +if ${OSTREE} pull R5:main 2>err.txt; then + assert_not_reached "Unexpectedly succeeded at pulling with nonexistent key" +fi +assert_file_has_content err.txt "INVALIDKEYPATH.*No such file or directory" + +rm repo/refs/remotes/* -rf +${OSTREE} prune --refs-only + +${OSTREE} remote add --set=gpgkeypath=${test_tmpdir}/gpghome/key2.asc R6 $(cat httpd-address)/ostree/gnomerepo +if ${OSTREE} pull R6:main 2>err.txt; then + assert_not_reached "Unexpectedly succeeded at pulling with different key" +fi +assert_file_has_content err.txt "GPG signatures found, but none are in trusted keyring" + +echo "ok" +libtest_cleanup_gpg