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
This commit is contained in:
Colin Walters
2016-11-16 09:13:54 -05:00
committed by Atomic Bot
parent 3cd5e6b41a
commit f244c70277
6 changed files with 99 additions and 5 deletions

View File

@ -199,7 +199,8 @@ Boston, MA 02111-1307, USA.
<refsect1>
<title>Per-remote GPG keyrings and verification</title>
<para>
OSTree supports a per-remote GPG keyring. For more information see
OSTree supports a per-remote GPG keyring, as well as a
<literal>gpgkeypath</literal> option. For more information see
<citerefentry><refentrytitle>ostree</refentrytitle><manvolnum>1</manvolnum></citerefentry>.
in the section <literal>GPG verification</literal>.
</para>

View File

@ -433,8 +433,12 @@ Boston, MA 02111-1307, USA.
in this directory.
</para>
<para>
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
<literal>gpgkeypath</literal> option for remotes, which must
point to the filename of an ASCII-armored key.
</para>
<para>Second, there is support for a per-remote
<filename><replaceable>remotename</replaceable>.trustedkeys.gpg</filename>
file stored in the toplevel of the repository (alongside
<filename>objects/</filename> and such). This is

View File

@ -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,

View File

@ -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

View File

@ -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);
}

View File

@ -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