cpio/backport-CVE-2015-1197.patch
ziyangc 7370c39eae backport CVE-2015-1197 and following 2 bugfix patches. Delete revert-CVE-2015-1197.patch
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)
2023-09-11 17:04:33 +08:00

260 lines
6.7 KiB
Diff
Raw Permalink 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: 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