Blame SOURCES/file-roller-3.28.1-CVE-2020-11736.patch

47e97d
From 72ac9340d9fe9554ea5c3f0ea6d08d0afa04cbf5 Mon Sep 17 00:00:00 2001
47e97d
From: Paolo Bacchilega <paobac@src.gnome.org>
47e97d
Date: Sun, 12 Apr 2020 11:38:35 +0200
47e97d
Subject: [PATCH 1/2] libarchive: do not follow external links when extracting
47e97d
 files
47e97d
47e97d
Do not extract a file if its parent is a symbolic link to a
47e97d
directory external to the destination.
47e97d
---
47e97d
 src/fr-archive-libarchive.c | 157 ++++++++++++++++++++++++++++++++++++
47e97d
 1 file changed, 157 insertions(+)
47e97d
47e97d
diff --git a/src/fr-archive-libarchive.c b/src/fr-archive-libarchive.c
47e97d
index 70c07e23..8d84b2ea 100644
47e97d
--- a/src/fr-archive-libarchive.c
47e97d
+++ b/src/fr-archive-libarchive.c
47e97d
@@ -601,6 +601,149 @@ _g_output_stream_add_padding (ExtractData    *extract_data,
47e97d
 }
47e97d
 
47e97d
 
47e97d
+static gboolean
47e97d
+_symlink_is_external_to_destination (GFile      *file,
47e97d
+				     const char *symlink,
47e97d
+				     GFile      *destination,
47e97d
+				     GHashTable *external_links);
47e97d
+
47e97d
+
47e97d
+static gboolean
47e97d
+_g_file_is_external_link (GFile      *file,
47e97d
+			  GFile      *destination,
47e97d
+			  GHashTable *external_links)
47e97d
+{
47e97d
+	GFileInfo *info;
47e97d
+	gboolean   external;
47e97d
+
47e97d
+	if (g_hash_table_lookup (external_links, file) != NULL)
47e97d
+		return TRUE;
47e97d
+
47e97d
+	info = g_file_query_info (file,
47e97d
+				  G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK "," G_FILE_ATTRIBUTE_STANDARD_SYMLINK_TARGET,
47e97d
+				  G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
47e97d
+				  NULL,
47e97d
+				  NULL);
47e97d
+
47e97d
+	if (info == NULL)
47e97d
+		return FALSE;
47e97d
+
47e97d
+	external = FALSE;
47e97d
+
47e97d
+	if (g_file_info_get_is_symlink (info)) {
47e97d
+		if (_symlink_is_external_to_destination (file,
47e97d
+							 g_file_info_get_symlink_target (info),
47e97d
+							 destination,
47e97d
+							 external_links))
47e97d
+		{
47e97d
+			g_hash_table_insert (external_links, g_object_ref (file), GINT_TO_POINTER (1));
47e97d
+			external = TRUE;
47e97d
+		}
47e97d
+	}
47e97d
+
47e97d
+	g_object_unref (info);
47e97d
+
47e97d
+	return external;
47e97d
+}
47e97d
+
47e97d
+
47e97d
+static gboolean
47e97d
+_symlink_is_external_to_destination (GFile      *file,
47e97d
+				     const char *symlink,
47e97d
+				     GFile      *destination,
47e97d
+				     GHashTable *external_links)
47e97d
+{
47e97d
+	gboolean  external = FALSE;
47e97d
+	GFile    *parent;
47e97d
+	char    **components;
47e97d
+	int       i;
47e97d
+
47e97d
+	if ((file == NULL) || (symlink == NULL))
47e97d
+		return FALSE;
47e97d
+
47e97d
+	if (symlink[0] == '/')
47e97d
+		return TRUE;
47e97d
+
47e97d
+	parent = g_file_get_parent (file);
47e97d
+	components = g_strsplit (symlink, "/", -1);
47e97d
+	for (i = 0; components[i] != NULL; i++) {
47e97d
+		char  *name = components[i];
47e97d
+		GFile *tmp;
47e97d
+
47e97d
+		if ((name[0] == 0) || ((name[0] == '.') && (name[1] == 0)))
47e97d
+			continue;
47e97d
+
47e97d
+		if ((name[0] == '.') && (name[1] == '.') && (name[2] == 0)) {
47e97d
+			if (g_file_equal (parent, destination)) {
47e97d
+				external = TRUE;
47e97d
+				break;
47e97d
+			}
47e97d
+			else {
47e97d
+				tmp = g_file_get_parent (parent);
47e97d
+				g_object_unref (parent);
47e97d
+				parent = tmp;
47e97d
+			}
47e97d
+		}
47e97d
+		else {
47e97d
+			tmp = g_file_get_child (parent, components[i]);
47e97d
+			g_object_unref (parent);
47e97d
+			parent = tmp;
47e97d
+		}
47e97d
+
47e97d
+		if (_g_file_is_external_link (parent, destination, external_links)) {
47e97d
+			external = TRUE;
47e97d
+			break;
47e97d
+		}
47e97d
+	}
47e97d
+
47e97d
+	g_strfreev (components);
47e97d
+	g_object_unref (parent);
47e97d
+
47e97d
+	return external;
47e97d
+}
47e97d
+
47e97d
+
47e97d
+static gboolean
47e97d
+_g_path_is_external_to_destination (const char *relative_path,
47e97d
+				    GFile      *destination,
47e97d
+				    GHashTable *external_links)
47e97d
+{
47e97d
+	gboolean  external = FALSE;
47e97d
+	GFile    *parent;
47e97d
+	char    **components;
47e97d
+	int       i;
47e97d
+
47e97d
+	if (relative_path == NULL)
47e97d
+		return FALSE;
47e97d
+
47e97d
+	if (destination == NULL)
47e97d
+		return TRUE;
47e97d
+
47e97d
+	parent = g_object_ref (destination);
47e97d
+	components = g_strsplit (relative_path, "/", -1);
47e97d
+	for (i = 0; (components[i] != NULL) && (components[i + 1] != NULL); i++) {
47e97d
+		GFile *tmp;
47e97d
+
47e97d
+		if (components[i][0] == 0)
47e97d
+			continue;
47e97d
+
47e97d
+		tmp = g_file_get_child (parent, components[i]);
47e97d
+		g_object_unref (parent);
47e97d
+		parent = tmp;
47e97d
+
47e97d
+		if (_g_file_is_external_link (parent, destination, external_links)) {
47e97d
+			external = TRUE;
47e97d
+			break;
47e97d
+		}
47e97d
+	}
47e97d
+
47e97d
+	g_strfreev (components);
47e97d
+	g_object_unref (parent);
47e97d
+
47e97d
+	return external;
47e97d
+}
47e97d
+
47e97d
+
47e97d
 static void
47e97d
 extract_archive_thread (GSimpleAsyncResult *result,
47e97d
 			GObject            *object,
47e97d
@@ -611,6 +754,7 @@ extract_archive_thread (GSimpleAsyncResult *result,
47e97d
 	GHashTable           *checked_folders;
47e97d
 	GHashTable           *created_files;
47e97d
 	GHashTable           *folders_created_during_extraction;
47e97d
+	GHashTable           *external_links;
47e97d
 	struct archive       *a;
47e97d
 	struct archive_entry *entry;
47e97d
 	int                   r;
47e97d
@@ -621,6 +765,7 @@ extract_archive_thread (GSimpleAsyncResult *result,
47e97d
 	checked_folders = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
47e97d
 	created_files = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, g_object_unref);
47e97d
 	folders_created_during_extraction = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
47e97d
+	external_links = g_hash_table_new_full (g_file_hash, (GEqualFunc) g_file_equal, g_object_unref, NULL);
47e97d
 	fr_archive_progress_set_total_files (load_data->archive, extract_data->n_files_to_extract);
47e97d
 
47e97d
 	a = archive_read_new ();
47e97d
@@ -652,6 +797,15 @@ extract_archive_thread (GSimpleAsyncResult *result,
47e97d
 		fullpath = (*pathname == '/') ? g_strdup (pathname) : g_strconcat ("/", pathname, NULL);
47e97d
 		relative_path = _g_path_get_relative_basename_safe (fullpath, extract_data->base_dir, extract_data->junk_paths);
47e97d
 		if (relative_path == NULL) {
47e97d
+			fr_archive_progress_inc_completed_files (load_data->archive, 1);
47e97d
+			fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
47e97d
+			archive_read_data_skip (a);
47e97d
+			continue;
47e97d
+		}
47e97d
+
47e97d
+		if (_g_path_is_external_to_destination (relative_path, extract_data->destination, external_links)) {
47e97d
+			fr_archive_progress_inc_completed_files (load_data->archive, 1);
47e97d
+			fr_archive_progress_inc_completed_bytes (load_data->archive, archive_entry_size_is_set (entry) ? archive_entry_size (entry) : 0);
47e97d
 			archive_read_data_skip (a);
47e97d
 			continue;
47e97d
 		}
47e97d
@@ -860,6 +1014,8 @@ extract_archive_thread (GSimpleAsyncResult *result,
47e97d
 						load_data->error = g_error_copy (local_error);
47e97d
 					g_clear_error (&local_error);
47e97d
 				}
47e97d
+				else if (_symlink_is_external_to_destination (file, archive_entry_symlink (entry), extract_data->destination, external_links))
47e97d
+					g_hash_table_insert (external_links, g_object_ref (file), GINT_TO_POINTER (1));
47e97d
 				archive_read_data_skip (a);
47e97d
 				break;
47e97d
 
47e97d
@@ -894,6 +1050,7 @@ extract_archive_thread (GSimpleAsyncResult *result,
47e97d
 	g_hash_table_unref (folders_created_during_extraction);
47e97d
 	g_hash_table_unref (created_files);
47e97d
 	g_hash_table_unref (checked_folders);
47e97d
+	g_hash_table_unref (external_links);
47e97d
 	archive_read_free (a);
47e97d
 	extract_data_free (extract_data);
47e97d
 }
47e97d
-- 
47e97d
2.26.2
47e97d
47e97d
47e97d
From 25f0759274f3be9480aaba01ed801d1308a00026 Mon Sep 17 00:00:00 2001
47e97d
From: Paolo Bacchilega <paobac@src.gnome.org>
47e97d
Date: Sun, 12 Apr 2020 12:19:18 +0200
47e97d
Subject: [PATCH 2/2] libarchive: overwrite the symbolic link as well
47e97d
47e97d
---
47e97d
 src/fr-archive-libarchive.c | 14 ++++++++++++--
47e97d
 1 file changed, 12 insertions(+), 2 deletions(-)
47e97d
47e97d
diff --git a/src/fr-archive-libarchive.c b/src/fr-archive-libarchive.c
47e97d
index 8d84b2ea..07babbc4 100644
47e97d
--- a/src/fr-archive-libarchive.c
47e97d
+++ b/src/fr-archive-libarchive.c
47e97d
@@ -1010,11 +1010,21 @@ extract_archive_thread (GSimpleAsyncResult *result,
47e97d
 
47e97d
 			case AE_IFLNK:
47e97d
 				if (! g_file_make_symbolic_link (file, archive_entry_symlink (entry), cancellable, &local_error)) {
47e97d
-					if (! g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS))
47e97d
+					if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
47e97d
+						g_clear_error (&local_error);
47e97d
+						if (g_file_delete (file, cancellable, &local_error)) {
47e97d
+							g_clear_error (&local_error);
47e97d
+							if (! g_file_make_symbolic_link (file, archive_entry_symlink (entry), cancellable, &local_error))
47e97d
+								load_data->error = g_error_copy (local_error);
47e97d
+						}
47e97d
+						else
47e97d
+							load_data->error = g_error_copy (local_error);
47e97d
+					}
47e97d
+					else
47e97d
 						load_data->error = g_error_copy (local_error);
47e97d
 					g_clear_error (&local_error);
47e97d
 				}
47e97d
-				else if (_symlink_is_external_to_destination (file, archive_entry_symlink (entry), extract_data->destination, external_links))
47e97d
+				if ((load_data->error == NULL) && _symlink_is_external_to_destination (file, archive_entry_symlink (entry), extract_data->destination, external_links))
47e97d
 					g_hash_table_insert (external_links, g_object_ref (file), GINT_TO_POINTER (1));
47e97d
 				archive_read_data_skip (a);
47e97d
 				break;
47e97d
-- 
47e97d
2.26.2
47e97d