File CVE-2021-43860.patch of Package flatpak
diff -urpN flatpak-1.2.3.orig/common/flatpak-dir.c flatpak-1.2.3/common/flatpak-dir.c
--- flatpak-1.2.3.orig/common/flatpak-dir.c 2022-08-31 15:19:04.784529834 -0500
+++ flatpak-1.2.3/common/flatpak-dir.c 2022-09-01 10:08:57.818529522 -0500
@@ -1003,19 +1003,28 @@ static gboolean
validate_commit_metadata (GVariant *commit_data,
const char *ref,
const char *required_metadata,
- gboolean require_xa_metadata,
+ gsize required_metadata_size,
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))
+ if (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);
@@ -4806,8 +4815,12 @@ flatpak_dir_pull (FlatpakDir
{
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),
+ error))
+ goto out;
}
if (!flatpak_dir_pull_extra_data (self, repo,
@@ -6949,9 +6962,9 @@ flatpak_dir_deploy (FlatpakDir
g_auto(GLnxLockFile) lock = { 0, };
g_autoptr(GFile) metadata_file = NULL;
g_autofree char *metadata_contents = NULL;
+ gsize metadata_size = 0;
g_auto(GStrv) ref_parts = NULL;
gboolean is_app;
- gboolean is_oci;
if (!flatpak_dir_ensure_repo (self, cancellable, error))
return FALSE;
@@ -7194,11 +7207,12 @@ flatpak_dir_deploy (FlatpakDir
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;
@@ -7209,11 +7223,8 @@ flatpak_dir_deploy (FlatpakDir
/* Check the metadata in the commit to make sure it matches the actual
* deployed metadata, in case we relied on the one in the commit for
* a decision
- * Note: For historical reason we don't enforce commits to contain xa.metadata
- * since this was lacking in fedora builds.
*/
- is_oci = flatpak_dir_get_remote_oci (self, origin);
- if (!validate_commit_metadata (commit_data, ref, metadata_contents, !is_oci, error))
+ if (!validate_commit_metadata (commit_data, ref, metadata_contents, metadata_size, error))
return FALSE;
dotref = g_file_resolve_relative_path (checkoutdir, "files/.ref");
@@ -7941,6 +7952,13 @@ flatpak_dir_ensure_bundle_remote (Flatpa
if (metadata == NULL)
return NULL;
+ /* If we rely on metadata (to e.g. print permissions), check it exists before creating the remote */
+ if (out_metadata && fp_metadata == NULL)
+ {
+ flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA, "No metadata in bundler header");
+ return NULL;
+ }
+
gpg_data = extra_gpg_data ? extra_gpg_data : included_gpg_data;
parts = flatpak_decompose_ref (ref, error);
diff -urpN flatpak-1.2.3.orig/common/flatpak-transaction.c flatpak-1.2.3/common/flatpak-transaction.c
--- flatpak-1.2.3.orig/common/flatpak-transaction.c 2022-08-31 15:19:04.784529834 -0500
+++ flatpak-1.2.3/common/flatpak-transaction.c 2022-09-01 10:10:36.199543664 -0500
@@ -1746,7 +1746,7 @@ flatpak_transaction_add_ref (FlatpakTran
op = flatpak_transaction_add_op (self, remote, ref, subpaths, commit, bundle, kind);
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;
}
@@ -1988,14 +1988,15 @@ load_deployed_metadata (FlatpakTransacti
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
+static gboolean
mark_op_resolved (FlatpakTransactionOperation *op,
const char *commit,
GBytes *metadata,
- GBytes *old_metadata)
+ GBytes *old_metadata,
+ GError **error)
{
g_debug ("marking op %s:%s resolved to %s", kind_to_str (op->kind), op->ref, commit ? commit : "-");
@@ -2009,13 +2010,12 @@ mark_op_resolved (FlatpakTransactionOper
if (metadata)
{
g_autoptr(GKeyFile) metakey = g_key_file_new ();
- if (g_key_file_load_from_bytes (metakey, metadata, G_KEY_FILE_NONE, NULL))
- {
- op->resolved_metadata = g_bytes_ref (metadata);
- op->resolved_metakey = g_steal_pointer (&metakey);
- }
- else
- g_message ("Warning: Failed to parse metadata for %s\n", op->ref);
+ if (!g_key_file_load_from_bytes (metakey, metadata, G_KEY_FILE_NONE, NULL))
+ return flatpak_fail_error (error, FLATPAK_ERROR_INVALID_DATA,
+ "Metadata for %s is invalid", op->ref);
+
+ op->resolved_metadata = g_bytes_ref (metadata);
+ op->resolved_metakey = g_steal_pointer (&metakey);
}
if (old_metadata)
{
@@ -2026,8 +2026,13 @@ mark_op_resolved (FlatpakTransactionOper
op->resolved_old_metakey = g_steal_pointer (&metakey);
}
else
- g_message ("Warning: Failed to parse old metadata for %s\n", op->ref);
+ {
+ /* This shouldn't happen, but a NULL old metadata is safe (all permisssions are considered new) */
+ g_message ("Warning: Failed to parse old metadata for %s\n", op->ref);
+ }
}
+
+ return TRUE;
}
static gboolean
@@ -2074,7 +2079,8 @@ resolve_p2p_ops (FlatpakTransaction *sel
op->installed_size = resolve->installed_size;
old_metadata_bytes = load_deployed_metadata (self, op->ref);
- mark_op_resolved (op, resolve->resolved_commit, resolve->resolved_metadata, old_metadata_bytes);
+ if (!mark_op_resolved (op, resolve->resolved_commit, resolve->resolved_metadata, old_metadata_bytes, error))
+ return FALSE;
}
return TRUE;
@@ -2114,14 +2120,16 @@ resolve_ops (FlatpakTransaction *self,
/* We resolve to the deployed metadata, becasue we need it to uninstall related ops */
metadata_bytes = load_deployed_metadata (self, op->ref);
- mark_op_resolved (op, NULL, metadata_bytes, NULL);
+ if (!mark_op_resolved (op, NULL, metadata_bytes, NULL, error))
+ return FALSE;
continue;
}
if (op->kind == FLATPAK_TRANSACTION_OPERATION_INSTALL_BUNDLE)
{
g_assert (op->commit != NULL);
- mark_op_resolved (op, op->commit, op->external_metadata, NULL);
+ if (!mark_op_resolved (op, op->commit, op->external_metadata, NULL, error))
+ return FALSE;
continue;
}
@@ -2157,7 +2165,7 @@ resolve_ops (FlatpakTransaction *self,
if (xa_metadata == NULL)
g_message ("Warning: No xa.metadata in local commit %s ref %s", checksum, 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);
@@ -2165,7 +2173,8 @@ resolve_ops (FlatpakTransaction *self,
op->installed_size = GUINT64_FROM_BE (installed_size);
old_metadata_bytes = load_deployed_metadata (self, op->ref);
- mark_op_resolved (op, checksum, metadata_bytes, old_metadata_bytes);
+ if (!mark_op_resolved (op, checksum, metadata_bytes, old_metadata_bytes, error))
+ return FALSE;
}
else if (state->collection_id == NULL) /* In the non-p2p case we have all the info available in the summary, so use it */
{
@@ -2200,13 +2209,14 @@ resolve_ops (FlatpakTransaction *self,
g_clear_error (&local_error);
}
else
- metadata_bytes = g_bytes_new (metadata, strlen (metadata) + 1);
+ metadata_bytes = g_bytes_new (metadata, strlen (metadata));
op->installed_size = installed_size;
op->download_size = download_size;
old_metadata_bytes = load_deployed_metadata (self, op->ref);
- mark_op_resolved (op, checksum, metadata_bytes, old_metadata_bytes);
+ if (!mark_op_resolved (op, checksum, metadata_bytes, old_metadata_bytes, error))
+ return FALSE;
}
else
{
diff -urpN flatpak-1.2.3.orig/common/flatpak-utils.c flatpak-1.2.3/common/flatpak-utils.c
--- flatpak-1.2.3.orig/common/flatpak-utils.c 2022-08-30 16:26:04.237655063 -0500
+++ flatpak-1.2.3/common/flatpak-utils.c 2022-08-31 15:29:09.423721916 -0500
@@ -4799,6 +4799,7 @@ flatpak_pull_from_bundle (OstreeRepo *
GCancellable *cancellable,
GError **error)
{
+ gsize metadata_size = 0;
g_autofree char *metadata_contents = NULL;
g_autofree char *to_checksum = NULL;
@@ -4816,6 +4817,8 @@ flatpak_pull_from_bundle (OstreeRepo *
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;
@@ -4885,12 +4888,10 @@ flatpak_pull_from_bundle (OstreeRepo *
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
{
diff -urpN flatpak-1.2.3.orig/tests/Makefile.am.inc flatpak-1.2.3/tests/Makefile.am.inc
--- flatpak-1.2.3.orig/tests/Makefile.am.inc 2019-02-11 07:44:28.000000000 -0600
+++ flatpak-1.2.3/tests/Makefile.am.inc 2022-09-01 09:47:56.657202015 -0500
@@ -127,6 +127,7 @@ TEST_MATRIX_SOURCE = \
tests/test-run.sh{{user+system},{nodeltas+deltas}} \
tests/test-info.sh{user+system} \
tests/test-repo.sh{user+system+collections+collections-server-only} \
+ tests/test-metadata-validation.sh \
tests/test-extensions.sh \
tests/test-bundle.sh{user+system} \
tests/test-oci.sh \
diff -urpN flatpak-1.2.3.orig/tests/Makefile-test-matrix.am.inc flatpak-1.2.3/tests/Makefile-test-matrix.am.inc
--- flatpak-1.2.3.orig/tests/Makefile-test-matrix.am.inc 2019-02-11 07:44:28.000000000 -0600
+++ flatpak-1.2.3/tests/Makefile-test-matrix.am.inc 2022-09-01 09:47:04.841037395 -0500
@@ -21,6 +21,7 @@ TEST_MATRIX_DIST= \
tests/test-config.sh \
tests/test-build-update-repo.sh \
tests/test-http-utils.sh \
+ tests/test-metadata-validation.sh \
tests/test-extensions.sh \
tests/test-oci.sh \
tests/test-unsigned-summaries.sh \
diff -urpN flatpak-1.2.3.orig/tests/test-metadata-validation.sh flatpak-1.2.3/tests/test-metadata-validation.sh
--- flatpak-1.2.3.orig/tests/test-metadata-validation.sh 1969-12-31 18:00:00.000000000 -0600
+++ flatpak-1.2.3/tests/test-metadata-validation.sh 2022-09-01 09:46:07.408854952 -0500
@@ -0,0 +1,158 @@
+#!/bin/bash
+#
+# Copyright (C) 2021 Matthew Leeds <mwleeds@protonmail.com>
+#
+# SPDX-License-Identifier: LGPL-2.0-or-later
+
+set -euo pipefail
+
+. $(dirname $0)/libtest.sh
+
+echo "1..7"
+
+setup_repo
+
+COUNTER=1
+
+create_app () {
+ local OPTIONS="$1"
+ local DIR=`mktemp -d`
+
+ mkdir ${DIR}/files
+ echo $COUNTER > ${DIR}/files/counter
+ let COUNTER=COUNTER+1
+
+ local INVALID=""
+ if [[ $OPTIONS =~ "invalid" ]]; then
+ INVALID=invalidkeyfileline
+ fi
+ cat > ${DIR}/metadata <<EOF
+[Application]
+name=org.test.Malicious
+runtime=org.test.Platform/${ARCH}/master
+$INVALID
+
+[Context]
+EOF
+ if [[ $OPTIONS =~ "mismatch" ]]; then
+ echo -e "filesystems=host;" >> ${DIR}/metadata
+ fi
+ if [[ $OPTIONS =~ "hidden" ]]; then
+ echo -ne "\0" >> ${DIR}/metadata
+ echo -e "\nfilesystems=home;" >> ${DIR}/metadata
+ fi
+ local XA_METADATA=--add-metadata-string=xa.metadata="$(head -n6 ${DIR}/metadata)"$'\n'
+ if [[ $OPTIONS =~ "no-xametadata" ]]; then
+ XA_METADATA="--add-metadata-string=xa.nometadata=1"
+ fi
+ ostree commit --repo=repos/test --branch=app/org.test.Malicious/${ARCH}/master ${FL_GPGARGS} "$XA_METADATA" ${DIR}/
+ if [[ $OPTIONS =~ "no-cache-in-summary" ]]; then
+ ostree --repo=repos/test ${FL_GPGARGS} summary -u
+ # force use of legacy summary format
+ rm -rf repos/test/summary.idx repos/test/summaries
+ else
+ update_repo
+ fi
+ rm -rf ${DIR}
+}
+
+cleanup_repo () {
+ ostree refs --repo=repos/test --delete app/org.test.Malicious/${ARCH}/master
+ update_repo
+}
+
+create_app "hidden"
+
+if ${FLATPAK} ${U} install -y test-repo org.test.Malicious 2>install-error-log; then
+ assert_not_reached "Should not be able to install app with hidden permissions"
+fi
+
+assert_file_has_content install-error-log "not matching expected metadata"
+
+assert_not_has_dir $FL_DIR/app/org.test.Malicious/current/active
+
+cleanup_repo
+
+ok "app with hidden permissions can't be installed (CVE-2021-43860)"
+
+create_app no-xametadata
+
+# The install will fail because the metadata in the summary doesn't match the metadata on the commit
+# The missing xa.metadata in the commit got turned into "" in the xa.cache
+if ${FLATPAK} ${U} install -y test-repo org.test.Malicious 2>install-error-log; then
+ assert_not_reached "Should not be able to install app with missing xa.metadata"
+fi
+
+assert_file_has_content install-error-log "not matching expected metadata"
+
+assert_not_has_dir $FL_DIR/app/org.test.Malicious/current/active
+
+cleanup_repo
+
+ok "app with no xa.metadata can't be installed"
+
+create_app "no-xametadata no-cache-in-summary"
+
+# The install will fail because there's no metadata in the summary or on the commit
+if ${FLATPAK} ${U} install -y test-repo org.test.Malicious 2>install-error-log; then
+ assert_not_reached "Should not be able to install app with missing metadata"
+fi
+assert_file_has_content install-error-log "No xa.metadata in local commit"
+
+assert_not_has_dir $FL_DIR/app/org.test.Malicious/current/active
+
+cleanup_repo
+
+ok "app with no xa.metadata and no metadata in summary can't be installed"
+
+create_app "invalid"
+
+if ${FLATPAK} ${U} install -y test-repo org.test.Malicious 2>install-error-log; then
+ assert_not_reached "Should not be able to install app with invalid metadata"
+fi
+assert_file_has_content install-error-log "Metadata for .* is invalid"
+
+assert_not_has_dir $FL_DIR/app/org.test.Malicious/current/active
+
+cleanup_repo
+
+ok "app with invalid metadata (in summary) can't be installed"
+
+create_app "invalid no-cache-in-summary"
+
+if ${FLATPAK} ${U} install -y test-repo org.test.Malicious 2>install-error-log; then
+ assert_not_reached "Should not be able to install app with invalid metadata"
+fi
+assert_file_has_content install-error-log "Metadata for .* is invalid"
+
+assert_not_has_dir $FL_DIR/app/org.test.Malicious/current/active
+
+cleanup_repo
+
+ok "app with invalid metadata (in commit) can't be installed"
+
+create_app "mismatch no-cache-in-summary"
+
+if ${FLATPAK} ${U} install -y test-repo org.test.Malicious 2>install-error-log; then
+ assert_not_reached "Should not be able to install app with non-matching metadata"
+fi
+assert_file_has_content install-error-log "Commit metadata for .* not matching expected metadata"
+
+assert_not_has_dir $FL_DIR/app/org.test.Malicious/current/active
+
+cleanup_repo
+
+ok "app with mismatched metadata (in commit) can't be installed"
+
+create_app "mismatch"
+
+if ${FLATPAK} ${U} install -y test-repo org.test.Malicious 2>install-error-log; then
+ assert_not_reached "Should not be able to install app with non-matching metadata"
+fi
+assert_file_has_content install-error-log "Commit metadata for .* not matching expected metadata"
+
+assert_not_has_dir $FL_DIR/app/org.test.Malicious/current/active
+
+cleanup_repo
+
+ok "app with mismatched metadata (in summary) can't be installed"