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