libotutil: Add ot_gpgme_ctx_tmp_home_dir()

Currently used for signature verification, will also be used for
importing GPG keys.
This commit is contained in:
Matthew Barnes 2015-04-26 21:25:35 -04:00
parent ceacc57206
commit 97379ec38c
4 changed files with 142 additions and 135 deletions

View File

@ -116,7 +116,7 @@ check_PROGRAMS = $(TESTS)
TESTS_ENVIRONMENT = \ TESTS_ENVIRONMENT = \
G_TEST_SRCDIR=$(abs_srcdir)/tests \ G_TEST_SRCDIR=$(abs_srcdir)/tests \
G_TEST_BUILDDIR=$(abs_builddir)/tests G_TEST_BUILDDIR=$(abs_builddir)/tests
TESTS_CFLAGS = $(ostree_bin_shared_cflags) $(OT_INTERNAL_GIO_UNIX_CFLAGS) TESTS_CFLAGS = $(ostree_bin_shared_cflags) $(OT_INTERNAL_GIO_UNIX_CFLAGS) -I$(srcdir)/libglnx
TESTS_LDADD = $(ostree_bin_shared_ldadd) $(OT_INTERNAL_GIO_UNIX_LIBS) TESTS_LDADD = $(ostree_bin_shared_ldadd) $(OT_INTERNAL_GIO_UNIX_LIBS)
tests_test_rollsum_SOURCES = src/libostree/ostree-rollsum.c tests/test-rollsum.c tests_test_rollsum_SOURCES = src/libostree/ostree-rollsum.c tests/test-rollsum.c

View File

@ -104,32 +104,62 @@ _ostree_gpg_verifier_initable_iface_init (GInitableIface *iface)
iface->init = ostree_gpg_verifier_initable_init; iface->init = ostree_gpg_verifier_initable_init;
} }
static gboolean static void
concatenate_keyrings (OstreeGpgVerifier *self, verify_result_finalized_cb (gpointer data,
GFile *destination, GObject *finalized_verify_result)
GCancellable *cancellable,
GError **error)
{ {
gs_unref_object GOutputStream *target_stream = NULL; g_autofree gchar *tmp_dir = data; /* assume ownership */
GList *link;
gboolean ret = FALSE;
target_stream = (GOutputStream *) g_file_replace (destination, /* XXX OstreeGpgVerifyResult could do this cleanup in its own
NULL, /* no etag */ * finalize() method, but I didn't want this keyring hack
FALSE, /* no backup */ * bleeding into multiple classes. */
G_FILE_CREATE_NONE,
cancellable, error); (void) glnx_shutil_rm_rf_at (AT_FDCWD, tmp_dir, NULL, NULL);
if (target_stream == NULL) }
OstreeGpgVerifyResult *
_ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
GBytes *signed_data,
GBytes *signatures,
GCancellable *cancellable,
GError **error)
{
gpgme_ctx_t gpg_ctx = NULL;
gpgme_error_t gpg_error = NULL;
gpgme_data_t data_buffer = NULL;
gpgme_data_t signature_buffer = NULL;
g_autofree char *tmp_dir = NULL;
glnx_unref_object GOutputStream *target_stream = NULL;
OstreeGpgVerifyResult *result = NULL;
gboolean success = FALSE;
GList *link;
/* 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, NULL,
&tmp_dir, &target_stream,
cancellable, error))
goto out; goto out;
for (link = self->keyrings; link != NULL; link = link->next) for (link = self->keyrings; link != NULL; link = link->next)
{ {
gs_unref_object GInputStream *source_stream = NULL; glnx_unref_object GFileInputStream *source_stream = NULL;
GFile *keyring_file = link->data; GFile *keyring_file = link->data;
gssize bytes_written; gssize bytes_written;
GError *local_error = NULL; GError *local_error = NULL;
source_stream = (GInputStream *) g_file_read (keyring_file, cancellable, &local_error); source_stream = g_file_read (keyring_file, cancellable, &local_error);
/* Disregard non-existent keyrings. */ /* Disregard non-existent keyrings. */
if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
@ -144,129 +174,16 @@ concatenate_keyrings (OstreeGpgVerifier *self,
} }
bytes_written = g_output_stream_splice (target_stream, bytes_written = g_output_stream_splice (target_stream,
source_stream, G_INPUT_STREAM (source_stream),
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE,
cancellable, error); cancellable, error);
if (bytes_written == -1) if (bytes_written < 0)
goto out; goto out;
} }
if (!g_output_stream_close (target_stream, cancellable, error)) if (!g_output_stream_close (target_stream, cancellable, error))
goto out; goto out;
ret = TRUE;
out:
return ret;
}
static gboolean
override_gpgme_home_dir (gpgme_ctx_t gpg_ctx,
const char *home_dir,
GError **error)
{
gpgme_engine_info_t gpg_engine_info;
gboolean ret = FALSE;
/* Override the OpenPGP engine's configuration directory without
* affecting other parameters. This requires finding the current
* parameters since the engine API takes all parameters at once. */
for (gpg_engine_info = gpgme_ctx_get_engine_info (gpg_ctx);
gpg_engine_info != NULL;
gpg_engine_info = gpg_engine_info->next)
{
if (gpg_engine_info->protocol == GPGME_PROTOCOL_OpenPGP)
{
gpgme_error_t gpg_error;
gpg_error = gpgme_ctx_set_engine_info (gpg_ctx,
gpg_engine_info->protocol,
gpg_engine_info->file_name,
home_dir);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_error_to_gio_error (gpg_error, error);
goto out;
}
break;
}
}
if (gpg_engine_info == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"GPGME: No OpenPGP engine available");
goto out;
}
ret = TRUE;
out:
return ret;
}
static void
verify_result_finalized_cb (gpointer data,
GObject *finalized_verify_result)
{
g_autofree gchar *temp_dir = data; /* assume ownership */
/* XXX OstreeGpgVerifyResult could do this cleanup in its own
* finalize() method, but I didn't want this keyring hack
* bleeding into multiple classes. */
(void) glnx_shutil_rm_rf_at (AT_FDCWD, temp_dir, NULL, NULL);
}
OstreeGpgVerifyResult *
_ostree_gpg_verifier_check_signature (OstreeGpgVerifier *self,
GBytes *signed_data,
GBytes *signatures,
GCancellable *cancellable,
GError **error)
{
gpgme_ctx_t gpg_ctx = NULL;
gpgme_error_t gpg_error = NULL;
gpgme_data_t data_buffer = NULL;
gpgme_data_t signature_buffer = NULL;
gs_unref_object GFile *pubring_file = NULL;
gs_free char *pubring_path = NULL;
gs_free char *temp_dir = 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;
temp_dir = g_build_filename (g_get_tmp_dir (), "ostree-gpg-XXXXXX", NULL);
if (mkdtemp (temp_dir) == NULL)
{
gs_set_error_from_errno (error, errno);
goto out;
}
pubring_path = g_build_filename (temp_dir, "pubring.gpg", NULL);
pubring_file = g_file_new_for_path (pubring_path);
if (!concatenate_keyrings (self, pubring_file, cancellable, error))
goto out;
result = g_initable_new (OSTREE_TYPE_GPG_VERIFY_RESULT,
cancellable, error, NULL);
if (result == NULL)
goto out;
if (!override_gpgme_home_dir (result->context, temp_dir, error))
goto out;
/* Both the signed data and signature GBytes instances will outlive the /* Both the signed data and signature GBytes instances will outlive the
* gpgme_data_t structs, so we can safely reuse the GBytes memory buffer * gpgme_data_t structs, so we can safely reuse the GBytes memory buffer
* directly and avoid a copy. */ * directly and avoid a copy. */
@ -325,7 +242,7 @@ out:
* the fabricated pubring.gpg keyring. */ * the fabricated pubring.gpg keyring. */
g_object_weak_ref (G_OBJECT (result), g_object_weak_ref (G_OBJECT (result),
verify_result_finalized_cb, verify_result_finalized_cb,
g_strdup (temp_dir)); g_strdup (tmp_dir));
} }
else else
{ {
@ -333,8 +250,8 @@ out:
g_clear_object (&result); g_clear_object (&result);
/* Try to clean up the temporary directory. */ /* Try to clean up the temporary directory. */
if (temp_dir != NULL) if (tmp_dir != NULL)
(void) glnx_shutil_rm_rf_at (AT_FDCWD, temp_dir, NULL, NULL); (void) glnx_shutil_rm_rf_at (AT_FDCWD, tmp_dir, NULL, NULL);
} }
g_prefix_error (error, "GPG: "); g_prefix_error (error, "GPG: ");

View File

@ -22,6 +22,10 @@
#include "ot-gpg-utils.h" #include "ot-gpg-utils.h"
#include <stdlib.h>
#include "libglnx.h"
void void
ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error, ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error,
GError **error) GError **error)
@ -55,3 +59,82 @@ ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error,
gpgme_strsource (gpg_error), gpgme_strsource (gpg_error),
gpgme_strerror (gpg_error)); gpgme_strerror (gpg_error));
} }
gboolean
ot_gpgme_ctx_tmp_home_dir (gpgme_ctx_t gpgme_ctx,
const char *tmp_dir,
char **out_tmp_home_dir,
GOutputStream **out_pubring_stream,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GFile) pubring_file = NULL;
g_autoptr(GOutputStream) target_stream = NULL;
g_autofree char *pubring_path = NULL;
g_autofree char *tmp_home_dir = NULL;
gpgme_error_t gpg_error;
gboolean ret = FALSE;
g_return_val_if_fail (gpgme_ctx != NULL, FALSE);
/* GPGME has no API for using multiple keyrings (aka, gpg --keyring),
* so we create a temporary directory and tell GPGME to use it as the
* home directory. Then (optionally) create a pubring.gpg file there
* and hand the caller an open output stream to concatenate necessary
* keyring files. */
if (tmp_dir == NULL)
tmp_dir = g_get_tmp_dir ();
tmp_home_dir = g_build_filename (tmp_dir, "ostree-gpg-XXXXXX", NULL);
if (mkdtemp (tmp_home_dir) == NULL)
{
glnx_set_error_from_errno (error);
goto out;
}
/* Not documented, but gpgme_ctx_set_engine_info() accepts NULL for
* the executable file name, which leaves the old setting unchanged. */
gpg_error = gpgme_ctx_set_engine_info (gpgme_ctx,
GPGME_PROTOCOL_OpenPGP,
NULL, tmp_home_dir);
if (gpg_error != GPG_ERR_NO_ERROR)
{
ot_gpgme_error_to_gio_error (gpg_error, error);
goto out;
}
if (out_pubring_stream != NULL)
{
GFileOutputStream *pubring_stream;
glnx_unref_object GFile *pubring_file = NULL;
g_autofree char *pubring_path = NULL;
pubring_path = g_build_filename (tmp_home_dir, "pubring.gpg", NULL);
pubring_file = g_file_new_for_path (pubring_path);
pubring_stream = g_file_create (pubring_file,
G_FILE_CREATE_NONE,
cancellable, error);
if (pubring_stream == NULL)
goto out;
/* Sneaky cast from GFileOutputStream to GOutputStream. */
*out_pubring_stream = g_steal_pointer (&pubring_stream);
}
if (out_tmp_home_dir != NULL)
*out_tmp_home_dir = g_steal_pointer (&tmp_home_dir);
ret = TRUE;
out:
if (!ret)
{
/* Clean up our mess on error. */
(void) glnx_shutil_rm_rf_at (AT_FDCWD, tmp_home_dir, NULL, NULL);
}
return ret;
}

View File

@ -27,4 +27,11 @@ G_BEGIN_DECLS
void ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error, GError **error); void ot_gpgme_error_to_gio_error (gpgme_error_t gpg_error, GError **error);
gboolean ot_gpgme_ctx_tmp_home_dir (gpgme_ctx_t gpgme_ctx,
const char *tmp_dir,
char **out_tmp_home_dir,
GOutputStream **out_pubring_stream,
GCancellable *cancellable,
GError **error);
G_END_DECLS G_END_DECLS