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