Skip to content

Commit

Permalink
Merge pull request #2401 from dbnicholson/gpg-key-info
Browse files Browse the repository at this point in the history
Remote GPG key info
  • Loading branch information
cgwalters authored Aug 20, 2021
2 parents 3209acb + 81df5c8 commit 98f3fe3
Show file tree
Hide file tree
Showing 22 changed files with 1,052 additions and 73 deletions.
6 changes: 3 additions & 3 deletions Makefile-libostree.am
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,9 @@ endif # USE_GPGME
symbol_files = $(top_srcdir)/src/libostree/libostree-released.sym

# Uncomment this include when adding new development symbols.
#if BUILDOPT_IS_DEVEL_BUILD
#symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
#endif
if BUILDOPT_IS_DEVEL_BUILD
symbol_files += $(top_srcdir)/src/libostree/libostree-devel.sym
endif

# http://blog.jgc.org/2007/06/escaping-comma-and-space-in-gnu-make.html
wl_versionscript_arg = -Wl,--version-script=
Expand Down
1 change: 1 addition & 0 deletions Makefile-ostree.am
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ ostree_SOURCES += \
if USE_GPGME
ostree_SOURCES += \
src/ostree/ot-remote-builtin-gpg-import.c \
src/ostree/ot-remote-builtin-list-gpg-keys.c \
$(NULL)
endif

Expand Down
2 changes: 2 additions & 0 deletions Makefile-otutil.am
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ if USE_GPGME
libotutil_la_SOURCES += \
src/libotutil/ot-gpg-utils.c \
src/libotutil/ot-gpg-utils.h \
src/libotutil/zbase32.c \
src/libotutil/zbase32.h \
$(NULL)
endif

Expand Down
1 change: 1 addition & 0 deletions Makefile-tests.am
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ _installed_or_uninstalled_test_scripts = \
if USE_GPGME
_installed_or_uninstalled_test_scripts += \
tests/test-remote-gpg-import.sh \
tests/test-remote-list-gpg-keys.sh \
tests/test-gpg-signed-commit.sh \
tests/test-admin-gpg.sh \
$(NULL)
Expand Down
3 changes: 3 additions & 0 deletions apidoc/ostree-sections.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,7 @@ ostree_repo_remote_list_collection_refs
ostree_repo_remote_get_url
ostree_repo_remote_get_gpg_verify
ostree_repo_remote_get_gpg_verify_summary
ostree_repo_remote_get_gpg_keys
ostree_repo_remote_gpg_import
ostree_repo_remote_fetch_summary
ostree_repo_remote_fetch_summary_with_options
Expand Down Expand Up @@ -482,6 +483,8 @@ ostree_repo_regenerate_summary
OSTREE_REPO
OSTREE_IS_REPO
OSTREE_TYPE_REPO
OSTREE_GPG_KEY_GVARIANT_STRING
OSTREE_GPG_KEY_GVARIANT_FORMAT
ostree_repo_get_type
ostree_repo_commit_modifier_get_type
ostree_repo_transaction_stats_get_type
Expand Down
35 changes: 35 additions & 0 deletions bash/ostree
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,40 @@ _ostree_remote_list_cookies() {
return 0
}

_ostree_remote_list_gpg_keys() {
local boolean_options="
$main_boolean_options
"

local options_with_args="
--repo
"

local options_with_args_glob=$( __ostree_to_extglob "$options_with_args" )

case "$prev" in
--repo)
__ostree_compreply_dirs_only
return 0
;;
esac

case "$cur" in
-*)
local all_options="$boolean_options $options_with_args"
__ostree_compreply_all_options
;;
*)
local argpos=$( __ostree_pos_first_nonflag $( __ostree_to_alternatives "$options_with_args" ) )

if [ $cword -eq $argpos ]; then
__ostree_compreply_remotes
fi
esac

return 0
}

_ostree_remote_refs() {
local boolean_options="
$main_boolean_options
Expand Down Expand Up @@ -1349,6 +1383,7 @@ _ostree_remote() {
gpg-import
list
list-cookies
list-gpg-keys
refs
show-url
summary
Expand Down
9 changes: 8 additions & 1 deletion man/ostree-remote.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ Boston, MA 02111-1307, USA.
<cmdsynopsis>
<command>ostree remote gpg-import</command> <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="req">NAME</arg> <arg choice="opt" rep="repeat">KEY-ID</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>ostree remote list-gpg-keys</command> <arg choice="req">NAME</arg>
</cmdsynopsis>
<cmdsynopsis>
<command>ostree remote refs</command> <arg choice="req">NAME</arg>
</cmdsynopsis>
Expand Down Expand Up @@ -106,7 +109,11 @@ Boston, MA 02111-1307, USA.
for more information.
</para>
<para>
The <command>gpg-import</command> subcommand can associate GPG keys to a specific remote repository for use when pulling signed commits from that repository (if GPG verification is enabled).
The <command>gpg-import</command> subcommand can associate GPG
keys to a specific remote repository for use when pulling signed
commits from that repository (if GPG verification is enabled). The
<command>list-gpg-keys</command> subcommand can be used to see the
GPG keys currently associated with a remote repository.
</para>
<para>
The GPG keys to import may be in binary OpenPGP format or ASCII armored. The optional <arg>KEY-ID</arg> list can restrict which keys are imported from a keyring file or input stream. All keys are imported if this list is omitted. If neither <option>--keyring</option> nor <option>--stdin</option> options are given, then keys are imported from the user's personal GPG keyring.
Expand Down
5 changes: 5 additions & 0 deletions src/libostree/libostree-devel.sym
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@
- uncomment the include in Makefile-libostree.am
*/

LIBOSTREE_2021.4 {
global:
ostree_repo_remote_get_gpg_keys;
} LIBOSTREE_2021.3;

/* Stub section for the stable release *after* this development one; don't
* edit this other than to update the year. This is just a copy/paste
* source. Replace $LASTSTABLE with the last stable version, and $NEWVERSION
Expand Down
194 changes: 149 additions & 45 deletions src/libostree/ostree-gpg-verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,43 +91,16 @@ verify_result_finalized_cb (gpointer data,
(void) glnx_shutil_rm_rf_at (AT_FDCWD, tmp_dir, NULL, NULL);
}

OstreeGpgVerifyResult *
_ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
GBytes *signed_data,
GBytes *signatures,
GCancellable *cancellable,
GError **error)
static gboolean
_ostree_gpg_verifier_import_keys (OstreeGpgVerifier *self,
gpgme_ctx_t gpgme_ctx,
GOutputStream *pubring_stream,
GCancellable *cancellable,
GError **error)
{
GLNX_AUTO_PREFIX_ERROR("GPG", error);
gpgme_error_t gpg_error = 0;
g_auto(gpgme_data_t) data_buffer = NULL;
g_auto(gpgme_data_t) signature_buffer = NULL;
g_autofree char *tmp_dir = NULL;
g_autoptr(GOutputStream) target_stream = NULL;
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
* temporary directory, then tell GPGME to use that directory as the
* home directory. */

if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;

result = g_initable_new (OSTREE_TYPE_GPG_VERIFY_RESULT,
cancellable, error, NULL);
if (result == NULL)
goto out;

if (!ot_gpgme_ctx_tmp_home_dir (result->context,
&tmp_dir, &target_stream,
cancellable, error))
goto out;

for (link = self->keyrings; link != NULL; link = link->next)
for (GList *link = self->keyrings; link != NULL; link = link->next)
{
g_autoptr(GFileInputStream) source_stream = NULL;
GFile *keyring_file = link->data;
Expand All @@ -145,15 +118,15 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
else if (local_error != NULL)
{
g_propagate_error (error, local_error);
goto out;
return FALSE;
}

bytes_written = g_output_stream_splice (target_stream,
bytes_written = g_output_stream_splice (pubring_stream,
G_INPUT_STREAM (source_stream),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
cancellable, error);
if (bytes_written < 0)
goto out;
return FALSE;
}

for (guint i = 0; i < self->keyring_data->len; i++)
Expand All @@ -162,23 +135,25 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
gsize len;
gsize bytes_written;
const guint8 *buf = g_bytes_get_data (keyringd, &len);
if (!g_output_stream_write_all (target_stream, buf, len, &bytes_written,
if (!g_output_stream_write_all (pubring_stream, buf, len, &bytes_written,
cancellable, error))
goto out;
return FALSE;
}

if (!g_output_stream_close (target_stream, cancellable, error))
goto out;
if (!g_output_stream_close (pubring_stream, cancellable, error))
return FALSE;

/* 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);
gboolean ret = FALSE;
int armor = gpgme_get_armor (gpgme_ctx);
gpgme_set_armor (gpgme_ctx, 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++)
{
gpgme_error_t gpg_error;
const char *path = self->key_ascii_files->pdata[i];
glnx_autofd int fd = -1;
g_auto(gpgme_data_t) kdata = NULL;
Expand All @@ -193,7 +168,7 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
goto out;
}

gpg_error = gpgme_op_import (result->context, kdata);
gpg_error = gpgme_op_import (gpgme_ctx, kdata);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_throw (gpg_error, error, "Failed to import key");
Expand All @@ -202,7 +177,136 @@ _ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
}
}

gpgme_set_armor (result->context, armor);
ret = TRUE;

out:
gpgme_set_armor (gpgme_ctx, armor);

return ret;
}

gboolean
_ostree_gpg_verifier_list_keys (OstreeGpgVerifier *self,
const char * const *key_ids,
GPtrArray **out_keys,
GCancellable *cancellable,
GError **error)
{
GLNX_AUTO_PREFIX_ERROR("GPG", error);
g_auto(gpgme_ctx_t) context = NULL;
g_autoptr(GOutputStream) pubring_stream = NULL;
g_autofree char *tmp_dir = NULL;
g_autoptr(GPtrArray) keys = NULL;
gpgme_error_t gpg_error = 0;
gboolean ret = FALSE;

if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;

context = ot_gpgme_new_ctx (NULL, error);
if (context == NULL)
goto out;

if (!ot_gpgme_ctx_tmp_home_dir (context, &tmp_dir, &pubring_stream,
cancellable, error))
goto out;

if (!_ostree_gpg_verifier_import_keys (self, context, pubring_stream,
cancellable, error))
goto out;

keys = g_ptr_array_new_with_free_func ((GDestroyNotify) gpgme_key_unref);
if (key_ids != NULL)
{
for (guint i = 0; key_ids[i] != NULL; i++)
{
gpgme_key_t key = NULL;

gpg_error = gpgme_get_key (context, key_ids[i], &key, 0);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_throw (gpg_error, error, "Unable to find key \"%s\"",
key_ids[i]);
goto out;
}

/* Transfer ownership. */
g_ptr_array_add (keys, key);
}
}
else
{
gpg_error = gpgme_op_keylist_start (context, NULL, 0);
while (gpg_error == GPG_ERR_NO_ERROR)
{
gpgme_key_t key = NULL;

gpg_error = gpgme_op_keylist_next (context, &key);
if (gpg_error != GPG_ERR_NO_ERROR)
break;

/* Transfer ownership. */
g_ptr_array_add (keys, key);
}

if (gpgme_err_code (gpg_error) != GPG_ERR_EOF)
{
ot_gpgme_throw (gpg_error, error, "Unable to list keys");
goto out;
}
}

if (out_keys != NULL)
*out_keys = g_steal_pointer (&keys);

ret = TRUE;

out:
if (tmp_dir != NULL) {
ot_gpgme_kill_agent (tmp_dir);
(void) glnx_shutil_rm_rf_at (AT_FDCWD, tmp_dir, NULL, NULL);
}

return ret;
}

OstreeGpgVerifyResult *
_ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
GBytes *signed_data,
GBytes *signatures,
GCancellable *cancellable,
GError **error)
{
GLNX_AUTO_PREFIX_ERROR("GPG", error);
gpgme_error_t gpg_error = 0;
g_auto(gpgme_data_t) data_buffer = NULL;
g_auto(gpgme_data_t) signature_buffer = NULL;
g_autofree char *tmp_dir = NULL;
g_autoptr(GOutputStream) target_stream = NULL;
OstreeGpgVerifyResult *result = NULL;
gboolean success = FALSE;

/* GPGME has no API for using multiple keyrings (aka, gpg --keyring),
* so we concatenate all the keyring files into one pubring.gpg in a
* temporary directory, then tell GPGME to use that directory as the
* home directory. */

if (g_cancellable_set_error_if_cancelled (cancellable, error))
goto out;

result = g_initable_new (OSTREE_TYPE_GPG_VERIFY_RESULT,
cancellable, error, NULL);
if (result == NULL)
goto out;

if (!ot_gpgme_ctx_tmp_home_dir (result->context,
&tmp_dir, &target_stream,
cancellable, error))
goto out;

if (!_ostree_gpg_verifier_import_keys (self, result->context, target_stream,
cancellable, error))
goto out;

/* Both the signed data and signature GBytes instances will outlive the
* gpgme_data_t structs, so we can safely reuse the GBytes memory buffer
Expand Down
Loading

0 comments on commit 98f3fe3

Please sign in to comment.