common: Support unsigned summary files and separate repo metadata

In order to eliminate some race conditions around updating the
summary{,.sig} file on the server, and to decouple signing the summary
from signing commits, and to support peer to peer mirrors of content
from multiple upstream collections: add support for unsigned summary
files.

This relaxes the requirement for gpg-verify-summary=true iff
collection-id is set in a remote’s local configuration. It depends on
some pending libostree changes to verify the ref for each commit using
the commit’s signed metadata. See
https://github.com/ostreedev/ostree/issues/983.

Metadata storage has moved from the summary file to a new
ostree-metadata well-known branch on each repository, since this can be
signed for each update and for each collection separately. If the
collection-id is set in a remote’s local configuration, flatpak will
retrieve all repository metadata from this branch rather than from the
summary file. If collection-id is unset, it will ignore this branch and
continue to use the summary file, which will continue to be updated (and
externally signed as summary.sig) for backwards compatibility.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
tingping/wmclass
Philip Withnall 2017-06-30 15:48:06 +01:00 committed by Alexander Larsson
parent ae7d960372
commit 024d835460
6 changed files with 505 additions and 97 deletions

View File

@ -118,6 +118,7 @@ get_config_from_opts (FlatpakDir *dir, const char *remote_name, gboolean *change
if (opt_collection_id)
{
g_key_file_set_string (config, group, "collection-id", opt_collection_id);
g_key_file_set_boolean (config, group, "gpg-verify-summary", FALSE);
*changed = TRUE;
}

View File

@ -2430,7 +2430,9 @@ oci_pull_progress_cb (guint64 total_size, guint64 pulled_size,
NULL);
}
/* Look up a piece of per-repository metadata. */
/* Look up a piece of per-repository metadata. Previously, this was stored in
* the summary file; now its stored the commit metadata of a special branch.
* Differentiate based on whether the collection ID is set for the remote. */
gboolean
flatpak_dir_lookup_repo_metadata (FlatpakDir *self,
const char *remote_name,
@ -2443,11 +2445,18 @@ flatpak_dir_lookup_repo_metadata (FlatpakDir *self,
va_list args;
g_autoptr(GVariant) metadata = NULL;
g_autoptr(GVariant) value = NULL;
g_autofree char *collection_id = NULL;
if (!flatpak_dir_ensure_repo (self, cancellable, error))
return FALSE;
if (TRUE)
#ifdef FLATPAK_ENABLE_P2P
if (!ostree_repo_get_remote_option (self->repo, remote_name, "collection-id",
NULL, &collection_id, error))
return FALSE;
#endif /* FLATPAK_ENABLE_P2P */
if (collection_id == NULL)
{
g_autoptr(GVariant) summary_v = NULL;
@ -2457,6 +2466,32 @@ flatpak_dir_lookup_repo_metadata (FlatpakDir *self,
metadata = g_variant_get_child_value (summary_v, 1);
}
else
{
#ifdef FLATPAK_ENABLE_P2P
g_autofree char *latest_rev = NULL;
g_autoptr(GVariant) commit_v = NULL;
/* Make sure the branch is up to date. */
if (!flatpak_dir_fetch_remote_repo_metadata (self, remote_name, cancellable, error))
return FALSE;
/* Look up the commit containing the latest repository metadata. */
latest_rev = flatpak_dir_lookup_ref_from_summary (self, remote_name,
OSTREE_REPO_METADATA_REF,
NULL,
cancellable, error);
if (latest_rev == NULL)
return FALSE;
if (!ostree_repo_load_commit (self->repo, latest_rev, &commit_v, NULL, error))
return FALSE;
metadata = g_variant_get_child_value (commit_v, 0);
#else /* if !FLATPAK_ENABLE_P2P */
g_assert_not_reached ();
#endif /* !FLATPAK_ENABLE_P2P */
}
/* Extract the metadata from it, if set. */
value = g_variant_lookup_value (metadata, key, NULL);
@ -2944,6 +2979,7 @@ flatpak_dir_pull_untrusted_local (FlatpakDir *self,
g_autofree char *current_checksum = NULL;
gboolean gpg_verify_summary;
gboolean gpg_verify;
g_autofree char *collection_id = NULL;
char *summary_data = NULL;
char *summary_sig_data = NULL;
g_autofree char *remote_and_branch = NULL;
@ -2966,12 +3002,18 @@ flatpak_dir_pull_untrusted_local (FlatpakDir *self,
&gpg_verify_summary, error))
return FALSE;
#ifdef FLATPAK_ENABLE_P2P
if (!ostree_repo_get_remote_option (self->repo, remote_name, "collection-id",
NULL, &collection_id, error))
return FALSE;
#endif /* FLATPAK_ENABLE_P2P */
if (!ostree_repo_remote_get_gpg_verify (self->repo, remote_name,
&gpg_verify, error))
return FALSE;
/* This was verified in the client, but lets do it here too */
if (!gpg_verify_summary || !gpg_verify)
if ((!gpg_verify_summary && collection_id == NULL) || !gpg_verify)
return flatpak_fail (error, "Can't pull from untrusted non-gpg verified remote");
/* We verify the summary manually before anything else to make sure
@ -2979,27 +3021,30 @@ flatpak_dir_pull_untrusted_local (FlatpakDir *self,
so we can check for a downgrade before pulling and updating the
ref */
if (!g_file_load_contents (summary_sig_file, cancellable,
&summary_sig_data, &summary_sig_data_size, NULL, NULL))
return flatpak_fail (error, "GPG verification enabled, but no summary signatures found");
summary_sig_bytes = g_bytes_new_take (summary_sig_data, summary_sig_data_size);
if (!g_file_load_contents (summary_file, cancellable,
&summary_data, &summary_data_size, NULL, NULL))
return flatpak_fail (error, "No summary found");
summary_bytes = g_bytes_new_take (summary_data, summary_data_size);
gpg_result = ostree_repo_verify_summary (self->repo,
remote_name,
summary_bytes,
summary_sig_bytes,
cancellable, error);
if (gpg_result == NULL)
return FALSE;
if (gpg_verify_summary)
{
if (!g_file_load_contents (summary_sig_file, cancellable,
&summary_sig_data, &summary_sig_data_size, NULL, NULL))
return flatpak_fail (error, "GPG verification enabled, but no summary signatures found");
if (ostree_gpg_verify_result_count_valid (gpg_result) == 0)
return flatpak_fail (error, "GPG signatures found, but none are in trusted keyring");
summary_sig_bytes = g_bytes_new_take (summary_sig_data, summary_sig_data_size);
gpg_result = ostree_repo_verify_summary (self->repo,
remote_name,
summary_bytes,
summary_sig_bytes,
cancellable, error);
if (gpg_result == NULL)
return FALSE;
if (ostree_gpg_verify_result_count_valid (gpg_result) == 0)
return flatpak_fail (error, "GPG signatures found, but none are in trusted keyring");
}
summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT, summary_bytes, FALSE));
if (!flatpak_summary_lookup_ref (summary,
@ -5328,6 +5373,7 @@ flatpak_dir_install (FlatpakDir *self,
g_autofree char *url = NULL;
gboolean gpg_verify_summary;
gboolean gpg_verify;
g_autofree char *collection_id = NULL;
gboolean is_oci;
system_helper = flatpak_dir_get_system_helper (self);
@ -5351,6 +5397,12 @@ flatpak_dir_install (FlatpakDir *self,
&gpg_verify_summary, error))
return FALSE;
#ifdef FLATPAK_ENABLE_P2P
if (!ostree_repo_get_remote_option (self->repo, remote_name, "collection-id",
NULL, &collection_id, error))
return FALSE;
#endif /* FLATPAK_ENABLE_P2P */
if (!ostree_repo_remote_get_gpg_verify (self->repo, remote_name,
&gpg_verify, error))
return FALSE;
@ -5361,14 +5413,17 @@ flatpak_dir_install (FlatpakDir *self,
{
/* Do nothing */
}
else if (!gpg_verify_summary || !gpg_verify)
else if ((!gpg_verify_summary && collection_id == NULL) || !gpg_verify)
{
/* The remote is not gpg verified, so we don't want to allow installation via
a download in the home directory, as there is no way to verify you're not
injecting anything into the remote. However, in the case of a remote
configured to a local filesystem we can just let the system helper do
the installation, as it can then avoid network i/o and be certain the
data comes from the right place. */
data comes from the right place.
If a collection ID is available, we can verify the refs in commit
metadata. */
if (g_str_has_prefix (url, "file:"))
helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL;
else
@ -5420,6 +5475,16 @@ flatpak_dir_install (FlatpakDir *self,
progress, cancellable, error))
return FALSE;
#ifdef FLATPAK_ENABLE_P2P
if (collection_id != NULL &&
!flatpak_dir_pull (self, remote_name, OSTREE_REPO_METADATA_REF, NULL, NULL, NULL,
child_repo,
flatpak_flags,
OSTREE_REPO_PULL_FLAGS_MIRROR,
progress, cancellable, error))
return FALSE;
#endif /* FLATPAK_ENABLE_P2P */
summary_file = g_file_get_child (ostree_repo_get_path (child_repo), "summary");
if (!g_file_replace_contents (summary_file,
g_bytes_get_data (summary_copy, NULL),
@ -5427,12 +5492,15 @@ flatpak_dir_install (FlatpakDir *self,
NULL, FALSE, 0, NULL, cancellable, NULL))
return FALSE;
summary_sig_file = g_file_get_child (ostree_repo_get_path (child_repo), "summary.sig");
if (!g_file_replace_contents (summary_sig_file,
g_bytes_get_data (summary_sig_copy, NULL),
g_bytes_get_size (summary_sig_copy),
NULL, FALSE, 0, NULL, cancellable, NULL))
return FALSE;
if (collection_id == NULL)
{
summary_sig_file = g_file_get_child (ostree_repo_get_path (child_repo), "summary.sig");
if (!g_file_replace_contents (summary_sig_file,
g_bytes_get_data (summary_sig_copy, NULL),
g_bytes_get_size (summary_sig_copy),
NULL, FALSE, 0, NULL, cancellable, NULL))
return FALSE;
}
child_repo_path = g_file_get_path (ostree_repo_get_path (child_repo));
}
@ -5922,6 +5990,7 @@ flatpak_dir_update (FlatpakDir *self,
g_autofree char *url = NULL;
gboolean gpg_verify_summary;
gboolean gpg_verify;
g_autofree char *collection_id = NULL;
system_helper = flatpak_dir_get_system_helper (self);
g_assert (system_helper != NULL);
@ -5941,6 +6010,12 @@ flatpak_dir_update (FlatpakDir *self,
&gpg_verify_summary, error))
return FALSE;
#ifdef FLATPAK_ENABLE_P2P
if (!ostree_repo_get_remote_option (self->repo, remote_name, "collection-id", NULL,
&collection_id, NULL))
collection_id = NULL;
#endif /* FLATPAK_ENABLE_P2P */
if (!ostree_repo_remote_get_gpg_verify (self->repo, remote_name,
&gpg_verify, error))
return FALSE;
@ -5948,14 +6023,17 @@ flatpak_dir_update (FlatpakDir *self,
if (no_pull)
{
}
else if (!gpg_verify_summary || !gpg_verify)
else if ((!gpg_verify_summary && collection_id == NULL) || !gpg_verify)
{
/* The remote is not gpg verified, so we don't want to allow installation via
a download in the home directory, as there is no way to verify you're not
injecting anything into the remote. However, in the case of a remote
configured to a local filesystem we can just let the system helper do
the installation, as it can then avoid network i/o and be certain the
data comes from the right place. */
data comes from the right place.
If @collection_id is non-%NULL, we can verify the refs in commit
metadata, so dont need to verify the summary. */
if (g_str_has_prefix (url, "file:"))
helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL;
else
@ -6002,6 +6080,14 @@ flatpak_dir_update (FlatpakDir *self,
flatpak_flags, OSTREE_REPO_PULL_FLAGS_MIRROR,
progress, cancellable, error))
return FALSE;
#ifdef FLATPAK_ENABLE_P2P
if (collection_id != NULL &&
!flatpak_dir_pull (self, remote_name, OSTREE_REPO_METADATA_REF, NULL, NULL, NULL,
child_repo,
flatpak_flags, OSTREE_REPO_PULL_FLAGS_MIRROR,
progress, cancellable, error))
return FALSE;
#endif /* FLATPAK_ENABLE_P2P */
summary_file = g_file_get_child (ostree_repo_get_path (child_repo), "summary");
if (!g_file_replace_contents (summary_file,
@ -6010,12 +6096,15 @@ flatpak_dir_update (FlatpakDir *self,
NULL, FALSE, 0, NULL, cancellable, NULL))
return FALSE;
summary_sig_file = g_file_get_child (ostree_repo_get_path (child_repo), "summary.sig");
if (!g_file_replace_contents (summary_sig_file,
g_bytes_get_data (summary_sig_copy, NULL),
g_bytes_get_size (summary_sig_copy),
NULL, FALSE, 0, NULL, cancellable, NULL))
return FALSE;
if (collection_id == NULL)
{
summary_sig_file = g_file_get_child (ostree_repo_get_path (child_repo), "summary.sig");
if (!g_file_replace_contents (summary_sig_file,
g_bytes_get_data (summary_sig_copy, NULL),
g_bytes_get_size (summary_sig_copy),
NULL, FALSE, 0, NULL, cancellable, NULL))
return FALSE;
}
child_repo_path = g_file_get_path (ostree_repo_get_path (child_repo));
}
@ -7767,16 +7856,10 @@ create_origin_remote_config (OstreeRepo *repo,
g_key_file_set_string (new_config, group, "xa.title", title);
g_key_file_set_string (new_config, group, "xa.noenumerate", "true");
g_key_file_set_string (new_config, group, "xa.prio", "0");
if (gpg_verify)
{
g_key_file_set_string (new_config, group, "gpg-verify", "true");
g_key_file_set_string (new_config, group, "gpg-verify-summary", "true");
}
else
{
g_key_file_set_string (new_config, group, "gpg-verify", "false");
g_key_file_set_string (new_config, group, "gpg-verify-summary", "false");
}
/* Dont enable summary verification if a collection ID is set, as collection
* IDs enable the verification of refs from commit metadata instead. */
g_key_file_set_string (new_config, group, "gpg-verify-summary", (gpg_verify && collection_id == NULL) ? "true" : "false");
g_key_file_set_string (new_config, group, "gpg-verify", gpg_verify ? "true" : "false");
if (main_ref)
g_key_file_set_string (new_config, group, "xa.main-ref", main_ref);
@ -7888,7 +7971,6 @@ flatpak_dir_parse_repofile (FlatpakDir *self,
gpg_data = g_bytes_new_take (decoded, decoded_len);
g_key_file_set_boolean (config, group, "gpg-verify", TRUE);
g_key_file_set_boolean (config, group, "gpg-verify-summary", TRUE);
}
#ifdef FLATPAK_ENABLE_P2P
@ -7908,6 +7990,11 @@ flatpak_dir_parse_repofile (FlatpakDir *self,
g_key_file_set_string (config, group, "collection-id", collection_id);
}
/* If a collection ID is set, refs are verified from commit metadata rather
* than the summary file. */
g_key_file_set_boolean (config, group, "gpg-verify-summary",
(gpg_key != NULL && collection_id == NULL));
*gpg_data_out = g_steal_pointer (&gpg_data);
return g_steal_pointer (&config);
@ -8536,6 +8623,139 @@ flatpak_dir_fetch_remote_summary (FlatpakDir *self,
return fetch_remote_summary_file (self, remote, NULL, cancellable, error);
}
gboolean
flatpak_dir_fetch_remote_repo_metadata (FlatpakDir *self,
const char *remote_name,
GCancellable *cancellable,
GError **error)
{
#ifdef FLATPAK_ENABLE_P2P
FlatpakPullFlags flatpak_flags;
flatpak_flags = FLATPAK_PULL_FLAGS_DOWNLOAD_EXTRA_DATA;
flatpak_flags |= FLATPAK_PULL_FLAGS_NO_STATIC_DELTAS;
if (flatpak_dir_use_system_helper (self, NULL))
{
g_autoptr(OstreeRepo) child_repo = NULL;
g_auto(GLnxLockFile) child_repo_lock = GLNX_LOCK_FILE_INIT;
const char *installation = flatpak_dir_get_id (self);
const char *subpaths[] = {NULL};
g_autofree char *child_repo_path = NULL;
FlatpakSystemHelper *system_helper;
FlatpakHelperDeployFlags helper_flags = 0;
g_autofree char *url = NULL;
gboolean gpg_verify_summary;
gboolean gpg_verify;
g_autofree char *collection_id = NULL;
gboolean is_oci;
system_helper = flatpak_dir_get_system_helper (self);
g_assert (system_helper != NULL);
if (!flatpak_dir_ensure_repo (self, cancellable, error))
return FALSE;
if (!ostree_repo_remote_get_url (self->repo,
remote_name,
&url,
error))
return FALSE;
if (!ostree_repo_remote_get_gpg_verify_summary (self->repo, remote_name,
&gpg_verify_summary, error))
return FALSE;
if (!ostree_repo_get_remote_option (self->repo, remote_name, "collection-id",
NULL, &collection_id, error))
return FALSE;
if (!ostree_repo_remote_get_gpg_verify (self->repo, remote_name,
&gpg_verify, error))
return FALSE;
is_oci = flatpak_dir_get_remote_oci (self, remote_name);
if ((!gpg_verify_summary && collection_id == NULL) || !gpg_verify)
{
/* The remote is not gpg verified, so we don't want to allow installation via
a download in the home directory, as there is no way to verify you're not
injecting anything into the remote. However, in the case of a remote
configured to a local filesystem we can just let the system helper do
the installation, as it can then avoid network i/o and be certain the
data comes from the right place.
If a collection ID is available, we can verify the refs in commit
metadata. */
if (g_str_has_prefix (url, "file:"))
helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_LOCAL_PULL;
else
return flatpak_fail (error, "Can't pull from untrusted non-gpg verified remote");
}
else if (is_oci)
{
g_autoptr(FlatpakOciRegistry) registry = NULL;
g_autoptr(GFile) registry_file = NULL;
registry = flatpak_dir_create_system_child_oci_registry (self, &child_repo_lock, error);
if (registry == NULL)
return FALSE;
registry_file = g_file_new_for_uri (flatpak_oci_registry_get_uri (registry));
child_repo_path = g_file_get_path (registry_file);
if (!flatpak_dir_mirror_oci (self, registry, remote_name, OSTREE_REPO_METADATA_REF, NULL, NULL, cancellable, error))
return FALSE;
}
else
{
/* We're pulling from a remote source, we do the network mirroring pull as a
user and hand back the resulting data to the system-helper, that trusts us
due to the GPG signatures in the repo */
child_repo = flatpak_dir_create_system_child_repo (self, &child_repo_lock, NULL, error);
if (child_repo == NULL)
return FALSE;
if (!flatpak_dir_pull (self, remote_name, OSTREE_REPO_METADATA_REF, NULL, NULL, NULL,
child_repo,
flatpak_flags,
OSTREE_REPO_PULL_FLAGS_MIRROR,
NULL, cancellable, error))
return FALSE;
child_repo_path = g_file_get_path (ostree_repo_get_path (child_repo));
}
helper_flags |= FLATPAK_HELPER_DEPLOY_FLAGS_NO_DEPLOY;
g_debug ("Calling system helper: Deploy");
if (!flatpak_system_helper_call_deploy_sync (system_helper,
child_repo_path ? child_repo_path : "",
helper_flags, OSTREE_REPO_METADATA_REF, remote_name,
(const char * const *) subpaths,
installation ? installation : "",
cancellable,
error))
return FALSE;
if (child_repo_path)
(void) glnx_shutil_rm_rf_at (AT_FDCWD, child_repo_path, NULL, NULL);
return TRUE;
}
if (!flatpak_dir_pull (self, remote_name, OSTREE_REPO_METADATA_REF, NULL, NULL, NULL, NULL,
flatpak_flags, OSTREE_REPO_PULL_FLAGS_NONE,
NULL, cancellable, error))
return FALSE;
return TRUE;
#else /* if !FLATPAK_ENABLE_P2P */
g_assert_not_reached ();
#endif /* FLATPAK_ENABLE_P2P */
}
static gboolean
flatpak_dir_update_remote_configuration_for_dict (FlatpakDir *self,
const char *remote,
@ -8674,6 +8894,36 @@ flatpak_dir_update_remote_configuration_for_summary (FlatpakDir *self,
cancellable, error);
}
gboolean
flatpak_dir_update_remote_configuration_for_repo_metadata (FlatpakDir *self,
const char *remote,
GVariant *summary,
gboolean dry_run,
gboolean *has_changed_out,
GCancellable *cancellable,
GError **error)
{
#ifdef FLATPAK_ENABLE_P2P
g_autofree char *latest_rev = NULL;
g_autoptr(GVariant) commit_v = NULL;
g_autoptr(GVariant) metadata = NULL;
if (!flatpak_summary_lookup_ref (summary, OSTREE_REPO_METADATA_REF, &latest_rev, NULL))
return flatpak_fail (error, "No such ref '%s' in remote %s", OSTREE_REPO_METADATA_REF, remote);
if (!ostree_repo_load_commit (self->repo, latest_rev, &commit_v, NULL, error))
return FALSE;
metadata = g_variant_get_child_value (commit_v, 0);
return flatpak_dir_update_remote_configuration_for_dict (self, remote, metadata,
dry_run, has_changed_out,
cancellable, error);
#else /* if !FLATPAK_ENABLE_P2P */
g_assert_not_reached ();
#endif /* FLATPAK_ENABLE_P2P */
}
gboolean
flatpak_dir_update_remote_configuration (FlatpakDir *self,
const char *remote,
@ -8683,6 +8933,7 @@ flatpak_dir_update_remote_configuration (FlatpakDir *self,
g_autoptr(GVariant) summary = NULL;
g_autoptr(GBytes) summary_sig_bytes = NULL;
gboolean is_oci;
g_autofree char *collection_id = NULL;
if (flatpak_dir_get_remote_disabled (self, remote))
return TRUE;
@ -8691,10 +8942,20 @@ flatpak_dir_update_remote_configuration (FlatpakDir *self,
if (is_oci)
return TRUE;
#ifdef FLATPAK_ENABLE_P2P
if (!ostree_repo_get_remote_option (self->repo, remote, "collection-id",
NULL, &collection_id, error))
return FALSE;
#endif /* FLATPAK_ENABLE_P2P */
summary = fetch_remote_summary_file (self, remote, &summary_sig_bytes, cancellable, error);
if (summary == NULL)
return FALSE;
if (collection_id != NULL &&
!flatpak_dir_fetch_remote_repo_metadata (self, remote, cancellable, error))
return FALSE;
if (flatpak_dir_use_system_helper (self, NULL))
{
gboolean has_changed = FALSE;
@ -8707,17 +8968,19 @@ flatpak_dir_update_remote_configuration (FlatpakDir *self,
if (!ostree_repo_remote_get_gpg_verify (self->repo, remote, &gpg_verify, error))
return FALSE;
if (!gpg_verify_summary || !gpg_verify)
if ((!gpg_verify_summary && collection_id == NULL) || !gpg_verify)
{
g_debug ("Ignoring automatic updates for system-helper remotes without gpg signatures");
return TRUE;
}
if (!flatpak_dir_update_remote_configuration_for_summary (self, remote, summary, TRUE, &has_changed, cancellable, error))
if ((collection_id == NULL &&
!flatpak_dir_update_remote_configuration_for_summary (self, remote, summary, TRUE, &has_changed, cancellable, error)) ||
(collection_id != NULL &&
!flatpak_dir_update_remote_configuration_for_repo_metadata (self, remote, summary, TRUE, &has_changed, cancellable, error)))
return FALSE;
if (summary_sig_bytes == NULL)
if (collection_id == NULL && summary_sig_bytes == NULL)
{
g_debug ("Can't update remote configuration as user, no GPG signature)");
return TRUE;
@ -8742,11 +9005,14 @@ flatpak_dir_update_remote_configuration (FlatpakDir *self,
if (glnx_loop_write (summary_fd, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes)) < 0)
return glnx_throw_errno (error);
summary_sig_fd = g_file_open_tmp ("remote-summary-sig.XXXXXX", &summary_sig_path, error);
if (summary_sig_fd == -1)
return FALSE;
if (glnx_loop_write (summary_sig_fd, g_bytes_get_data (summary_sig_bytes, NULL), g_bytes_get_size (summary_sig_bytes)) < 0)
return glnx_throw_errno (error);
if (summary_sig_bytes != NULL)
{
summary_sig_fd = g_file_open_tmp ("remote-summary-sig.XXXXXX", &summary_sig_path, error);
if (summary_sig_fd == -1)
return FALSE;
if (glnx_loop_write (summary_sig_fd, g_bytes_get_data (summary_sig_bytes, NULL), g_bytes_get_size (summary_sig_bytes)) < 0)
return glnx_throw_errno (error);
}
installation = flatpak_dir_get_id (self);
@ -8764,12 +9030,15 @@ flatpak_dir_update_remote_configuration (FlatpakDir *self,
return TRUE;
}
return flatpak_dir_update_remote_configuration_for_summary (self, remote, summary, FALSE, NULL, cancellable, error);
if (collection_id == NULL)
return flatpak_dir_update_remote_configuration_for_summary (self, remote, summary, FALSE, NULL, cancellable, error);
else
return flatpak_dir_update_remote_configuration_for_repo_metadata (self, remote, summary, FALSE, NULL, cancellable, error);
}
static gboolean
flatpak_dir_parse_summary_for_ref (FlatpakDir *self,
GVariant *summary,
const char *remote_name,
const char *ref,
guint64 *download_size,
guint64 *installed_size,
@ -8777,20 +9046,20 @@ flatpak_dir_parse_summary_for_ref (FlatpakDir *self,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GVariant) extensions = NULL;
g_autoptr(GVariant) cache_v = NULL;
g_autoptr(GVariant) cache = NULL;
g_autoptr(GVariant) res = NULL;
g_autoptr(GVariant) refdata = NULL;
int pos;
g_autoptr(GError) local_error = NULL;
extensions = g_variant_get_child_value (summary, 1);
cache_v = g_variant_lookup_value (extensions, "xa.cache", NULL);
if (cache_v == NULL)
if (!flatpak_dir_lookup_repo_metadata (self, remote_name, cancellable, &local_error,
"xa.cache", "@*", &cache_v))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
_("No flatpak cache in remote summary"));
if (local_error == NULL)
g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
_("No flatpak cache in remote summary"));
g_propagate_error (error, g_steal_pointer (&local_error));
return FALSE;
}
@ -8836,13 +9105,7 @@ flatpak_dir_fetch_ref_cache (FlatpakDir *self,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GVariant) summary = NULL;
summary = fetch_remote_summary_file (self, remote_name, NULL, cancellable, error);
if (summary == NULL)
return FALSE;
return flatpak_dir_parse_summary_for_ref (self, summary, ref,
return flatpak_dir_parse_summary_for_ref (self, remote_name, ref,
download_size, installed_size,
metadata,
cancellable, error);
@ -8980,7 +9243,7 @@ flatpak_dir_find_remote_related (FlatpakDir *self,
if (summary == NULL)
return NULL;
if (flatpak_dir_parse_summary_for_ref (self, summary, ref,
if (flatpak_dir_parse_summary_for_ref (self, remote_name, ref,
NULL, NULL, &metadata,
NULL, NULL) &&
g_key_file_load_from_data (metakey, metadata, -1, 0, NULL))

View File

@ -555,6 +555,10 @@ GVariant *flatpak_dir_fetch_remote_summary (FlatpakDir *self,
const char *remote,
GCancellable *cancellable,
GError **error);
gboolean flatpak_dir_fetch_remote_repo_metadata (FlatpakDir *self,
const char *remote_name,
GCancellable *cancellable,
GError **error);
char * flatpak_dir_fetch_remote_title (FlatpakDir *self,
const char *remote,
GCancellable *cancellable,
@ -574,6 +578,13 @@ gboolean flatpak_dir_update_remote_configuration_for_summary (FlatpakDir *self
gboolean *has_changed_out,
GCancellable *cancellable,
GError **error);
gboolean flatpak_dir_update_remote_configuration_for_repo_metadata (FlatpakDir *self,
const char *remote,
GVariant *summary,
gboolean dry_run,
gboolean *has_changed_out,
GCancellable *cancellable,
GError **error);
gboolean flatpak_dir_fetch_ref_cache (FlatpakDir *self,
const char *remote_name,
const char *ref,

View File

@ -3031,6 +3031,11 @@ populate_commit_data_cache (GVariant *metadata,
}
}
/* Update the metadata in the summary file for @repo, and then re-sign the file.
* If the repo has a collection ID set, additionally store the metadata on a
* contentless commit in a well-known branch, which is the preferred way of
* broadcasting per-repo metadata (putting it in the summary file is deprecated,
* but kept for backwards compatibility). */
gboolean
flatpak_repo_update (OstreeRepo *repo,
const char **gpg_key_ids,
@ -3053,6 +3058,9 @@ flatpak_repo_update (OstreeRepo *repo,
g_autoptr(GList) ordered_keys = NULL;
GList *l = NULL;
g_autoptr(GHashTable) commit_data_cache = NULL;
const char *collection_id;
g_autofree char *old_ostree_metadata_checksum = NULL;
g_autoptr(GVariant) old_ostree_metadata_v = NULL;
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
@ -3066,6 +3074,12 @@ flatpak_repo_update (OstreeRepo *repo,
redirect_url = g_key_file_get_string (config, "flatpak", "redirect-url", NULL);
}
#ifdef FLATPAK_ENABLE_P2P
collection_id = ostree_repo_get_collection_id (repo);
#else /* if !FLATPAK_ENABLE_P2P */
collection_id = NULL;
#endif /* FLATPAK_ENABLE_P2P */
if (title)
g_variant_builder_add (&builder, "{sv}", "xa.title",
g_variant_new_string (title));
@ -3120,7 +3134,22 @@ flatpak_repo_update (OstreeRepo *repo,
g_free, commit_data_free);
old_summary = flatpak_repo_load_summary (repo, NULL);
if (old_summary != NULL)
#ifdef FLATPAK_ENABLE_P2P
if (!ostree_repo_resolve_rev (repo, OSTREE_REPO_METADATA_REF,
TRUE, &old_ostree_metadata_checksum, error))
return FALSE;
#endif /* FLATPAK_ENABLE_P2P */
if (old_summary != NULL &&
old_ostree_metadata_checksum != NULL &&
ostree_repo_load_commit (repo, old_ostree_metadata_checksum, &old_ostree_metadata_v, NULL, NULL))
{
g_autoptr(GVariant) metadata = g_variant_get_child_value (old_ostree_metadata_v, 0);
populate_commit_data_cache (metadata, old_summary, commit_data_cache);
}
else if (old_summary != NULL)
{
g_autoptr(GVariant) extensions = g_variant_get_child_value (old_summary, 1);
@ -3201,6 +3230,71 @@ flatpak_repo_update (OstreeRepo *repo,
g_variant_new_variant (g_variant_builder_end (&ref_data_builder)));
new_summary = g_variant_ref_sink (g_variant_builder_end (&builder));
/* Write out a new metadata commit for the repository. */
if (collection_id != NULL)
{
#ifdef FLATPAK_ENABLE_P2P
OstreeCollectionRef collection_ref = { (gchar *) collection_id, (gchar *) OSTREE_REPO_METADATA_REF };
g_autofree gchar *new_ostree_metadata_checksum = NULL;
g_autoptr(OstreeMutableTree) mtree = NULL;
g_autoptr(OstreeRepoFile) repo_file = NULL;
g_autoptr(GVariantDict) new_summary_commit_dict = NULL;
g_autoptr(GVariant) new_summary_commit = NULL;
/* Add bindings to the metadata. */
new_summary_commit_dict = g_variant_dict_new (new_summary);
g_variant_dict_insert (new_summary_commit_dict, "ostree.collection-binding",
"s", collection_ref.collection_id);
g_variant_dict_insert_value (new_summary_commit_dict, "ostree.ref-binding",
g_variant_new_strv ((const gchar * const *) &collection_ref.ref_name, 1));
new_summary_commit = g_variant_dict_end (new_summary_commit_dict);
if (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error))
goto out;
mtree = ostree_mutable_tree_new ();
if (!ostree_repo_write_dfd_to_mtree (repo, AT_FDCWD, ".", mtree, NULL, NULL, error))
goto out;
if (!ostree_repo_write_mtree (repo, mtree, (GFile **) &repo_file, NULL, error))
goto out;
if (!ostree_repo_write_commit (repo, old_ostree_metadata_checksum,
NULL /* subject */, NULL /* body */,
new_summary_commit, repo_file, &new_ostree_metadata_checksum,
NULL, error))
goto out;
if (gpg_key_ids != NULL)
{
const char * const *iter;
for (iter = gpg_key_ids; iter != NULL && *iter != NULL; iter++)
{
const char *key_id = *iter;
if (!ostree_repo_sign_commit (repo,
new_ostree_metadata_checksum,
key_id,
gpg_homedir,
cancellable,
error))
goto out;
}
}
ostree_repo_transaction_set_collection_ref (repo, &collection_ref,
new_ostree_metadata_checksum);
if (!ostree_repo_commit_transaction (repo, NULL, cancellable, error))
goto out;
#else /* if !FLATPAK_ENABLE_P2P */
g_assert_not_reached ();
goto out;
#endif /* FLATPAK_ENABLE_P2P */
}
/* Regenerate and re-sign the summary file. */
if (!ostree_repo_regenerate_summary (repo, new_summary, cancellable, error))
return FALSE;
@ -3215,6 +3309,11 @@ flatpak_repo_update (OstreeRepo *repo,
}
return TRUE;
out:
if (repo != NULL)
ostree_repo_abort_transaction (repo, cancellable, NULL);
return FALSE;
}
gboolean

View File

@ -78,7 +78,13 @@
</varlistentry>
<varlistentry>
<term><option>gpg-verify-summary</option> (boolean)</term>
<listitem><para>Whether to use GPG verification for the summary of this remote.</para></listitem>
<listitem>
<para>Whether to use GPG verification for the summary of this remote.</para>
<!--
FIXME: Uncomment this when P2P support is made unconditional on enable-p2p.
<para>This is ignored if <option>collection-id</option> is set, as refs are verified in commit metadata in that case. Enabling <option>gpg-verify-summary</option> would break peer to peer distribution of refs.</para>
-->
</listitem>
</varlistentry>
<!-- FIXME: Uncomment this when enable-p2p is enabled unconditionally.
<varlistentry>
@ -142,6 +148,11 @@
[remote "gnome-nightly-apps"]
gpg-verify=true
gpg-verify-summary=true
<!--
FIXME: Uncomment this when P2P support is made unconditional on enable-p2p.
gpg-verify-summary=false
collection-id=org.gnome.Apps.Nightly
-->
url=https://sdk.gnome.org/nightly/repo-apps/
xa.title=GNOME Applications
</programlisting>

View File

@ -696,6 +696,7 @@ handle_update_remote (FlatpakSystemHelper *object,
gsize summary_sig_size;
g_autoptr(GBytes) summary_sig_bytes = NULL;
g_autoptr(OstreeGpgVerifyResult) gpg_result = NULL;
g_autofree char *collection_id = NULL;
g_debug ("UpdateRemote %u %s %s %s %s", arg_flags, arg_remote, arg_installation, arg_summary_path, arg_summary_sig_path);
@ -726,6 +727,17 @@ handle_update_remote (FlatpakSystemHelper *object,
return TRUE;
}
#ifdef FLATPAK_ENABLE_P2P
if (!ostree_repo_get_remote_option (flatpak_dir_get_repo (system), arg_remote, "collection-id",
NULL, &collection_id, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
else if (collection_id != NULL && *collection_id == '\0')
g_clear_pointer (&collection_id, g_free);
#endif /* FLATPAK_ENABLE_P2P */
if (!g_file_get_contents (arg_summary_path, &summary_data, &summary_size, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
@ -733,33 +745,44 @@ handle_update_remote (FlatpakSystemHelper *object,
}
summary_bytes = g_bytes_new_take (summary_data, summary_size);
if (!g_file_get_contents (arg_summary_sig_path, &summary_sig_data, &summary_sig_size, &error))
if (collection_id == NULL)
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
if (!g_file_get_contents (arg_summary_sig_path, &summary_sig_data, &summary_sig_size, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
summary_sig_bytes = g_bytes_new_take (summary_sig_data, summary_sig_size);
gpg_result = ostree_repo_verify_summary (flatpak_dir_get_repo (system),
arg_remote,
summary_bytes,
summary_sig_bytes,
NULL, &error);
if (gpg_result == NULL ||
!ostree_gpg_verify_result_require_valid_signature (gpg_result, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT,
summary_bytes, FALSE));
if (!flatpak_dir_update_remote_configuration_for_summary (system, arg_remote, summary,
FALSE, NULL, NULL, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
}
summary_sig_bytes = g_bytes_new_take (summary_sig_data, summary_sig_size);
gpg_result = ostree_repo_verify_summary (flatpak_dir_get_repo (system),
arg_remote,
summary_bytes,
summary_sig_bytes,
NULL, &error);
if (gpg_result == NULL ||
!ostree_gpg_verify_result_require_valid_signature (gpg_result, &error))
else
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
summary = g_variant_ref_sink (g_variant_new_from_bytes (OSTREE_SUMMARY_GVARIANT_FORMAT,
summary_bytes, FALSE));
if (!flatpak_dir_update_remote_configuration_for_summary (system, arg_remote, summary,
FALSE, NULL, NULL, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
if (!flatpak_dir_update_remote_configuration_for_repo_metadata (system, arg_remote, summary,
FALSE, NULL, NULL, &error))
{
g_dbus_method_invocation_return_gerror (invocation, error);
return TRUE;
}
}
flatpak_system_helper_complete_update_remote (object, invocation);