cpio/backport-CVE-2015-1197-Fix-45b0ee2b407913c533f7ded8d6f8cbeec16ff6ca.patch
2023-05-27 17:47:54 +08:00

274 lines
7.3 KiB
Diff
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 376d663340a9dc91c91a5849e5713f07571c1628 Mon Sep 17 00:00:00 2001
From: Sergey Poznyakoff <gray@gnu.org>
Date: Thu, 27 Apr 2023 15:14:23 +0300
Subject: [PATCH] Fix 45b0ee2b407913c533f7ded8d6f8cbeec16ff6ca.
The commit in question brought in more problems than solutions. To
properly fix the issue, use symlink placeholders, modelled after
delayed symlinks in tar.
* src/copyin.c (symlink_placeholder)
(replace_symlink_placeholders): New functions.
(copyin_link): Create symlink placeholder if --no-absolute-filenames
was given.
(process_copy_in): Replace placeholders after extraction.
* tests/CVE-2015-1197.at: Update. Don't use /tmp.
---
src/copyin.c | 172 +++++++++++++++++++++++++++++++++++------
tests/CVE-2015-1197.at | 40 +++++++++++++++++++++++++++++++++++
2 files changed, 192 insertions(+), 20 deletions(-)
diff --git a/src/copyin.c b/src/copyin.c
index 60cef9d..5ed2db6 100644
--- a/src/copyin.c
+++ b/src/copyin.c
@@ -30,6 +30,7 @@
#ifndef FNM_PATHNAME
# include <fnmatch.h>
#endif
+#include <hash.h>
#ifndef HAVE_LCHOWN
# define lchown(f,u,g) 0
@@ -620,6 +621,136 @@ copyin_device (struct cpio_file_stat* file_hdr)
file_hdr->c_mtime);
}
+struct delayed_link
+ {
+ /* The device and inode number of the placeholder. */
+ dev_t dev;
+ ino_t ino;
+
+ /* The desired link metadata. */
+ mode_t mode;
+ uid_t uid;
+ gid_t gid;
+ time_t mtime;
+
+ /* Link source and target names. */
+ char *source;
+ char target[1];
+ };
+
+static Hash_table *delayed_link_table;
+
+static size_t
+dl_hash (void const *entry, size_t table_size)
+{
+ struct delayed_link const *dl = entry;
+ uintmax_t n = dl->dev;
+ int nshift = (sizeof (n) - sizeof (dl->dev)) * CHAR_BIT;
+ if (0 < nshift)
+ n <<= nshift;
+ n ^= dl->ino;
+ return n % table_size;
+}
+
+static bool
+dl_compare (void const *a, void const *b)
+{
+ struct delayed_link const *da = a, *db = b;
+ return (da->dev == db->dev) & (da->ino == db->ino);
+}
+
+static int
+symlink_placeholder (char *oldpath, char *newpath, struct cpio_file_stat *file_stat)
+{
+ int fd = open (newpath, O_WRONLY | O_CREAT | O_EXCL, 0);
+ struct stat st;
+ struct delayed_link *p;
+ size_t newlen = strlen (newpath);
+
+ if (fd < 0)
+ {
+ open_error (newpath);
+ return -1;
+ }
+
+ if (fstat (fd, &st) != 0)
+ {
+ stat_error (newpath);
+ close (fd);
+ return -1;
+ }
+
+ close (fd);
+
+ p = xmalloc (sizeof (*p) + strlen (oldpath) + newlen + 1);
+ p->dev = st.st_dev;
+ p->ino = st.st_ino;
+
+ p->mode = file_stat->c_mode;
+ p->uid = file_stat->c_uid;
+ p->gid = file_stat->c_gid;
+ p->mtime = file_stat->c_mtime;
+
+ strcpy (p->target, newpath);
+ p->source = p->target + newlen + 1;
+ strcpy (p->source, oldpath);
+
+ if (!((delayed_link_table
+ || (delayed_link_table = hash_initialize (0, 0, dl_hash,
+ dl_compare, free)))
+ && hash_insert (delayed_link_table, p)))
+ xalloc_die ();
+
+ return 0;
+}
+
+static void
+replace_symlink_placeholders (void)
+{
+ struct delayed_link *dl;
+
+ if (!delayed_link_table)
+ return;
+ for (dl = hash_get_first (delayed_link_table);
+ dl;
+ dl = hash_get_next (delayed_link_table, dl))
+ {
+ struct stat st;
+
+ /* Make sure the placeholder file is still there. If not,
+ don't create a link, as the placeholder was probably
+ removed by a later extraction. */
+ if (lstat (dl->target, &st) == 0
+ && st.st_dev == dl->dev
+ && st.st_ino == dl->ino)
+ {
+ if (unlink (dl->target))
+ unlink_error (dl->target);
+ else
+ {
+ int res = UMASKED_SYMLINK (dl->source, dl->target, dl->mode);
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (dl->target);
+ res = UMASKED_SYMLINK (dl->source, dl->target, dl->mode);
+ }
+ if (res < 0)
+ symlink_error (dl->source, dl->target);
+ else if (!no_chown_flag)
+ {
+ uid_t uid = set_owner_flag ? set_owner : dl->uid;
+ gid_t gid = set_group_flag ? set_group : dl->gid;
+ if (lchown (dl->target, uid, gid) < 0 && errno != EPERM)
+ chown_error_details (dl->target, uid, gid);
+ }
+ }
+ }
+ }
+
+ hash_free (delayed_link_table);
+ delayed_link_table = NULL;
+}
+
static void
copyin_link (struct cpio_file_stat *file_hdr, int in_file_des)
{
@@ -645,28 +776,26 @@ copyin_link (struct cpio_file_stat *file_hdr, int in_file_des)
link_name = xstrdup (file_hdr->c_tar_linkname);
}
- res = UMASKED_SYMLINK (link_name, file_hdr->c_name,
- file_hdr->c_mode);
- if (res < 0 && create_dir_flag)
+ if (no_abs_paths_flag)
+ symlink_placeholder (link_name, file_hdr->c_name, file_hdr);
+ else
{
- create_all_directories (file_hdr->c_name);
res = UMASKED_SYMLINK (link_name, file_hdr->c_name,
- file_hdr->c_mode);
- }
- if (res < 0)
- {
- error (0, errno, _("%s: Cannot symlink to %s"),
- quotearg_colon (link_name), quote_n (1, file_hdr->c_name));
- free (link_name);
- return;
- }
- if (!no_chown_flag)
- {
- uid_t uid = set_owner_flag ? set_owner : file_hdr->c_uid;
- gid_t gid = set_group_flag ? set_group : file_hdr->c_gid;
- if ((lchown (file_hdr->c_name, uid, gid) < 0)
- && errno != EPERM)
- chown_error_details (file_hdr->c_name, uid, gid);
+ file_hdr->c_mode);
+ if (res < 0 && create_dir_flag)
+ {
+ create_all_directories (file_hdr->c_name);
+ res = UMASKED_SYMLINK (link_name, file_hdr->c_name, file_hdr->c_mode);
+ }
+ if (res < 0)
+ symlink_error (link_name, file_hdr->c_name);
+ else if (!no_chown_flag)
+ {
+ uid_t uid = set_owner_flag ? set_owner : file_hdr->c_uid;
+ gid_t gid = set_group_flag ? set_group : file_hdr->c_gid;
+ if (lchown (file_hdr->c_name, uid, gid) < 0 && errno != EPERM)
+ chown_error_details (file_hdr->c_name, uid, gid);
+ }
}
free (link_name);
}
@@ -1425,6 +1553,7 @@ process_copy_in (void)
if (dot_flag)
fputc ('\n', stderr);
+ replace_symlink_placeholders ();
apply_delayed_set_stat ();
cpio_file_stat_free (&file_hdr);
diff --git a/tests/CVE-2015-1197.at b/tests/CVE-2015-1197.at
new file mode 100644
index 0000000..6079af7
--- /dev/null
+++ b/tests/CVE-2015-1197.at
@@ -0,0 +1,40 @@
+# Process this file with autom4te to create testsuite. -*- Autotest-*-
+# Copyright (C) 2009-2019 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP([CVE-2015-1197 (--no-absolute-filenames for symlinks)])
+AT_CHECK([
+tempdir=$(pwd)/tmp
+mkdir $tempdir
+touch $tempdir/file
+ln -s $tempdir dir
+AT_DATA([filelist],
+[dir
+dir/file
+])
+cpio -o < filelist > test.cpio
+rm -rf dir $tempdir
+cpio --no-absolute-filenames -iv < test.cpio
+],
+[2],
+[],
+[1 block
+dir
+cpio: dir/file: Cannot open: Not a directory
+dir/file
+1 block
+])
+AT_CLEANUP
+
--
2.27.0