Skip to content

Commit

Permalink
Fix metadata file contents after null terminators being ignored
Browse files Browse the repository at this point in the history
In particular, if a null terminator is placed inside the metadata file,
Flatpak will only compare the text *before* it to the value of
xa.metadata, but the full file will be parsed when permissions are set
at runtime. This means that any app can include a null terminator in its
permissions metadata, and Flatpak will only show the user the
permissions *preceding* the terminator during install, but the
permissions *after* the terminator are applied at runtime.

Fixes GHSA-qpjc-vq3c-572j / CVE-2021-43860

Signed-off-by: Ryan Gonzalez <ryan.gonzalez@collabora.com>
  • Loading branch information
refi64 authored and alexlarsson committed Jan 12, 2022
1 parent 2380309 commit ba818f5
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 17 deletions.
36 changes: 27 additions & 9 deletions common/flatpak-dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1794,19 +1794,29 @@ static gboolean
validate_commit_metadata (GVariant *commit_data,
const char *ref,
const char *required_metadata,
gsize required_metadata_size,
gboolean require_xa_metadata,
GError **error)
{
g_autoptr(GVariant) commit_metadata = NULL;
g_autoptr(GVariant) xa_metadata_v = NULL;
const char *xa_metadata = NULL;
gsize xa_metadata_size = 0;

commit_metadata = g_variant_get_child_value (commit_data, 0);

if (commit_metadata != NULL)
g_variant_lookup (commit_metadata, "xa.metadata", "&s", &xa_metadata);
{
xa_metadata_v = g_variant_lookup_value (commit_metadata,
"xa.metadata",
G_VARIANT_TYPE_STRING);
if (xa_metadata_v)
xa_metadata = g_variant_get_string (xa_metadata_v, &xa_metadata_size);
}

if ((xa_metadata == NULL && require_xa_metadata) ||
(xa_metadata != NULL && g_strcmp0 (required_metadata, xa_metadata) != 0))
(xa_metadata != NULL && (xa_metadata_size != required_metadata_size ||
memcmp (xa_metadata, required_metadata, xa_metadata_size) != 0)))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
_("Commit metadata for %s not matching expected metadata"), ref);
Expand Down Expand Up @@ -3515,6 +3525,7 @@ upgrade_deploy_data (GBytes *deploy_data,
g_autoptr(GKeyFile) keyfile = NULL;
g_autoptr(GFile) metadata_file = NULL;
g_autofree char *metadata_contents = NULL;
gsize metadata_size = 0;
g_autofree char *id = flatpak_decomposed_dup_id (ref);

/* Add fields from commit metadata to deploy */
Expand All @@ -3528,9 +3539,9 @@ upgrade_deploy_data (GBytes *deploy_data,
keyfile = g_key_file_new ();
metadata_file = g_file_resolve_relative_path (deploy_dir, "metadata");
if (!g_file_load_contents (metadata_file, cancellable,
&metadata_contents, NULL, NULL, error))
&metadata_contents, &metadata_size, NULL, error))
return NULL;
if (!g_key_file_load_from_data (keyfile, metadata_contents, -1, 0, error))
if (!g_key_file_load_from_data (keyfile, metadata_contents, metadata_size, 0, error))
return NULL;
add_metadata_to_deploy_data (&metadata_dict, keyfile);

Expand Down Expand Up @@ -5833,8 +5844,13 @@ flatpak_dir_pull (FlatpakDir *self,
{
g_autoptr(GVariant) commit_data = NULL;
if (!ostree_repo_load_commit (repo, rev, &commit_data, NULL, error) ||
!validate_commit_metadata (commit_data, ref, (const char *)g_bytes_get_data (require_metadata, NULL), TRUE, error))
return FALSE;
!validate_commit_metadata (commit_data,
ref,
(const char *)g_bytes_get_data (require_metadata, NULL),
g_bytes_get_size (require_metadata),
TRUE,
error))
goto out;
}

if (!flatpak_dir_pull_extra_data (self, repo,
Expand Down Expand Up @@ -8156,6 +8172,7 @@ flatpak_dir_deploy (FlatpakDir *self,
g_auto(GLnxLockFile) lock = { 0, };
g_autoptr(GFile) metadata_file = NULL;
g_autofree char *metadata_contents = NULL;
gsize metadata_size = 0;
gboolean is_oci;
const char *flatpak;

Expand Down Expand Up @@ -8366,11 +8383,12 @@ flatpak_dir_deploy (FlatpakDir *self,
keyfile = g_key_file_new ();
metadata_file = g_file_resolve_relative_path (checkoutdir, "metadata");
if (g_file_load_contents (metadata_file, NULL,
&metadata_contents, NULL, NULL, NULL))
&metadata_contents,
&metadata_size, NULL, NULL))
{
if (!g_key_file_load_from_data (keyfile,
metadata_contents,
-1,
metadata_size,
0, error))
return FALSE;

Expand All @@ -8386,7 +8404,7 @@ flatpak_dir_deploy (FlatpakDir *self,
*/
is_oci = flatpak_dir_get_remote_oci (self, origin);
if (!validate_commit_metadata (commit_data, flatpak_decomposed_get_ref (ref),
metadata_contents, !is_oci, error))
metadata_contents, metadata_size, !is_oci, error))
return FALSE;

dotref = g_file_resolve_relative_path (checkoutdir, "files/.ref");
Expand Down
8 changes: 4 additions & 4 deletions common/flatpak-transaction.c
Original file line number Diff line number Diff line change
Expand Up @@ -2539,7 +2539,7 @@ flatpak_transaction_add_ref (FlatpakTransaction *self,
return FALSE;

if (external_metadata)
op->external_metadata = g_bytes_new (external_metadata, strlen (external_metadata) + 1);
op->external_metadata = g_bytes_new (external_metadata, strlen (external_metadata));

return TRUE;
}
Expand Down Expand Up @@ -2950,7 +2950,7 @@ load_deployed_metadata (FlatpakTransaction *self, FlatpakDecomposed *ref, char *
return NULL;
}

return g_bytes_new_take (g_steal_pointer (&metadata_contents), metadata_contents_length + 1);
return g_bytes_new_take (g_steal_pointer (&metadata_contents), metadata_contents_length);
}

static void
Expand Down Expand Up @@ -3051,7 +3051,7 @@ resolve_op_from_commit (FlatpakTransaction *self,
if (xa_metadata == NULL)
g_message ("Warning: No xa.metadata in local commit %s ref %s", checksum, flatpak_decomposed_get_ref (op->ref));
else
metadata_bytes = g_bytes_new (xa_metadata, strlen (xa_metadata) + 1);
metadata_bytes = g_bytes_new (xa_metadata, strlen (xa_metadata));

if (g_variant_lookup (commit_metadata, "xa.download-size", "t", &download_size))
op->download_size = GUINT64_FROM_BE (download_size);
Expand Down Expand Up @@ -3091,7 +3091,7 @@ try_resolve_op_from_metadata (FlatpakTransaction *self,
&download_size, &installed_size, &metadata, NULL))
return FALSE;

metadata_bytes = g_bytes_new (metadata, strlen (metadata) + 1);
metadata_bytes = g_bytes_new (metadata, strlen (metadata));

if (flatpak_remote_state_lookup_ref (state, flatpak_decomposed_get_ref (op->ref),
NULL, NULL, &info, NULL, NULL))
Expand Down
9 changes: 5 additions & 4 deletions common/flatpak-utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -6667,6 +6667,7 @@ flatpak_pull_from_bundle (OstreeRepo *repo,
GCancellable *cancellable,
GError **error)
{
gsize metadata_size = 0;
g_autofree char *metadata_contents = NULL;
g_autofree char *to_checksum = NULL;
g_autoptr(GFile) root = NULL;
Expand All @@ -6683,6 +6684,8 @@ flatpak_pull_from_bundle (OstreeRepo *repo,
if (metadata == NULL)
return FALSE;

metadata_size = strlen (metadata_contents);

if (!ostree_repo_get_remote_option (repo, remote, "collection-id", NULL,
&remote_collection_id, NULL))
remote_collection_id = NULL;
Expand Down Expand Up @@ -6752,12 +6755,10 @@ flatpak_pull_from_bundle (OstreeRepo *repo,
cancellable, error) < 0)
return FALSE;

/* Null terminate */
g_output_stream_write (G_OUTPUT_STREAM (data_stream), "\0", 1, NULL, NULL);

metadata_valid =
metadata_contents != NULL &&
strcmp (metadata_contents, g_memory_output_stream_get_data (data_stream)) == 0;
metadata_size == g_memory_output_stream_get_data_size (data_stream) &&
memcmp (metadata_contents, g_memory_output_stream_get_data (data_stream), metadata_size) == 0;
}
else
{
Expand Down

0 comments on commit ba818f5

Please sign in to comment.