Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote GPG key info #2401

Merged
merged 12 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To align with the command above, gpg-list-keys or gpg-list maybe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I like gpg-list-keys.

</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);
Comment on lines +265 to +267
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably make these autocleanups too in the future.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so. The gpgme code could use some sprucing up.

}

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