because new CVE patch will fix the problem of old CVE patch, so we don't need to revert old CVE patch. (cherry picked from commit bdaa060cf319507a37e75c5b96c647d2fd2bc9bd)
260 lines
6.7 KiB
Diff
260 lines
6.7 KiB
Diff
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: 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.
|
||
|
||
Reference:http://git.savannah.gnu.org/cgit/cpio.git/commit/?id=376d663340a9dc91c91a5849e5713f07571c1628
|
||
Conflict:NA
|
||
|
||
---
|
||
src/copyin.c | 173 ++++++++++++++++++++++++++++++++++++++++++-------
|
||
tests/CVE-2015-1197.at | 7 +-
|
||
2 files changed, 153 insertions(+), 27 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,29 +776,26 @@ copyin_link (struct cpio_file_stat *file_hdr, int in_file_des)
|
||
link_name = xstrdup (file_hdr->c_tar_linkname);
|
||
}
|
||
|
||
- cpio_safer_name_suffix (link_name, true, !no_abs_paths_flag, false);
|
||
-
|
||
- res = UMASKED_SYMLINK (link_name, file_hdr->c_name,
|
||
- 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)
|
||
- {
|
||
- 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)
|
||
+ if (no_abs_paths_flag)
|
||
+ symlink_placeholder (link_name, file_hdr->c_name, file_hdr);
|
||
+ else
|
||
{
|
||
- 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);
|
||
+ res = UMASKED_SYMLINK (link_name, file_hdr->c_name,
|
||
+ 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
|
||
index 57ebe43..74591b1 100644
|
||
--- a/tests/CVE-2015-1197.at
|
||
+++ b/tests/CVE-2015-1197.at
|
||
@@ -24,18 +24,15 @@ AT_DATA([filelist],
|
||
[dir
|
||
dir/file
|
||
])
|
||
-ln -s /tmp dir
|
||
-touch /tmp/file
|
||
cpio -o < filelist > test.cpio
|
||
-rm dir /tmp/file
|
||
+rm -rf dir $tempdir
|
||
cpio --no-absolute-filenames -iv < test.cpio
|
||
],
|
||
[2],
|
||
[],
|
||
[1 block
|
||
-cpio: Removing leading `/' from hard link targets
|
||
dir
|
||
-cpio: dir/file: Cannot open: No such file or directory
|
||
+cpio: dir/file: Cannot open: Not a directory
|
||
dir/file
|
||
1 block
|
||
])
|
||
--
|
||
cgit v1.1
|
||
|
||
|