diff -Nru cpio-2.13+dfsg/debian/changelog cpio-2.13+dfsg/debian/changelog --- cpio-2.13+dfsg/debian/changelog 2021-08-25 10:52:28.000000000 +0000 +++ cpio-2.13+dfsg/debian/changelog 2024-04-28 12:31:25.000000000 +0000 @@ -1,3 +1,14 @@ +cpio (2.13+dfsg-2ubuntu0.4) focal-security; urgency=medium + + * SECURITY UPDATE: Path traversal vulnerability + - debian/patches/CVE-2023-7207.patch: Create symlink placeholder + if --no-absolute-filenames was given and replace placeholders + after extraction. + - debian/patches/revert-CVE-2015-1197-handling.patch: Removed. + - CVE-2023-7207 + + -- Fabian Toepfer Sun, 28 Apr 2024 14:31:25 +0200 + cpio (2.13+dfsg-2ubuntu0.3) focal-security; urgency=medium * SECURITY UPDATE: arbitrary code execution via crafted pattern file diff -Nru cpio-2.13+dfsg/debian/patches/CVE-2023-7207.patch cpio-2.13+dfsg/debian/patches/CVE-2023-7207.patch --- cpio-2.13+dfsg/debian/patches/CVE-2023-7207.patch 1970-01-01 00:00:00.000000000 +0000 +++ cpio-2.13+dfsg/debian/patches/CVE-2023-7207.patch 2024-01-10 14:28:57.000000000 +0000 @@ -0,0 +1,298 @@ +From 376d663340a9dc91c91a5849e5713f07571c1628 Mon Sep 17 00:00:00 2001 +From: Sergey Poznyakoff +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. +--- + src/copyin.c | 173 ++++++++++++++++++++++++++++++++++++++++++------- + tests/CVE-2015-1197.at | 7 +- + 2 files changed, 153 insertions(+), 27 deletions(-) + +--- cpio-2.13+dfsg.orig/src/copyin.c ++++ cpio-2.13+dfsg/src/copyin.c +@@ -36,6 +36,7 @@ + #ifndef HAVE_LCHOWN + # define lchown(f,u,g) 0 + #endif ++#include + + static void copyin_regular_file(struct cpio_file_stat* file_hdr, + int in_file_des); +@@ -622,6 +623,136 @@ copyin_device (struct cpio_file_stat* fi + 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) + { +@@ -647,29 +778,26 @@ copyin_link (struct cpio_file_stat *file + 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); + } +@@ -1417,6 +1545,7 @@ process_copy_in (void) + if (dot_flag) + fputc ('\n', stderr); + ++ replace_symlink_placeholders (); + apply_delayed_set_stat (); + + cpio_file_stat_free (&file_hdr); +--- cpio-2.13+dfsg.orig/tests/CVE-2015-1197.at ++++ cpio-2.13+dfsg/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 + ]) +--- cpio-2.13+dfsg.orig/tests/testsuite ++++ cpio-2.13+dfsg/tests/testsuite +@@ -2789,11 +2789,11 @@ at_fn_group_banner 14 'CVE-2015-1197.at: + "CVE-2015-1197 (--no-absolute-filenames for symlinks)" "" + at_xfail=no + ( +- $as_echo "14. $at_setup_line: testing $at_desc ..." ++ printf "%s\n" "14. $at_setup_line: testing $at_desc ..." + $at_traceon + + { set +x +-$as_echo "$at_srcdir/CVE-2015-1197.at:18: ++printf "%s\n" "$at_srcdir/CVE-2015-1197.at:18: + tempdir=\$(pwd)/tmp + mkdir \$tempdir + touch \$tempdir/file +@@ -2803,10 +2803,8 @@ dir + dir/file + _ATEOF + +-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 + " + at_fn_check_prepare_notrace 'a $(...) command substitution' "CVE-2015-1197.at:18" +@@ -2820,19 +2818,16 @@ dir + dir/file + _ATEOF + +-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 + + ) >>"$at_stdout" 2>>"$at_stderr" 5>&- + at_status=$? at_failed=false + $at_check_filter +-echo >>"$at_stderr"; $as_echo "1 block +-cpio: Removing leading \`/' from hard link targets ++echo >>"$at_stderr"; printf "%s\n" "1 block + dir +-cpio: dir/file: Cannot open: No such file or directory ++cpio: dir/file: Cannot open: Not a directory + dir/file + 1 block + " | \ diff -Nru cpio-2.13+dfsg/debian/patches/revert-CVE-2015-1197-handling.patch cpio-2.13+dfsg/debian/patches/revert-CVE-2015-1197-handling.patch --- cpio-2.13+dfsg/debian/patches/revert-CVE-2015-1197-handling.patch 2020-02-01 13:11:00.000000000 +0000 +++ cpio-2.13+dfsg/debian/patches/revert-CVE-2015-1197-handling.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -From: Chris Lamb -Date: Sat, 1 Feb 2020 13:36:37 +0100 -Subject: Fix a regression in handling of CVE-2015-1197 & - --no-absolute-filenames. - -See: - - * https://bugs.debian.org/946267 - * https://bugs.debian.org/946469 - -This reverts (most of): https://git.savannah.gnu.org/cgit/cpio.git/diff/?id=45b0ee2b407913c533f7ded8d6f8cbeec16ff6ca&id2=3177d660a4c62a6acb538b0f7c54ba423698889a ---- - src/copyin.c | 2 -- - tests/testsuite | 2 +- - 2 files changed, 1 insertion(+), 3 deletions(-) - -diff --git a/src/copyin.c b/src/copyin.c -index e1350af..e0a092a 100644 ---- a/src/copyin.c -+++ b/src/copyin.c -@@ -646,8 +646,6 @@ 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) -diff --git a/tests/testsuite b/tests/testsuite -index b45c731..4438c33 100755 ---- a/tests/testsuite -+++ b/tests/testsuite -@@ -2787,7 +2787,7 @@ read at_status <"$at_status_file" - #AT_START_14 - at_fn_group_banner 14 'CVE-2015-1197.at:17' \ - "CVE-2015-1197 (--no-absolute-filenames for symlinks)" "" --at_xfail=no -+at_xfail=yes - ( - $as_echo "14. $at_setup_line: testing $at_desc ..." - $at_traceon diff -Nru cpio-2.13+dfsg/debian/patches/series cpio-2.13+dfsg/debian/patches/series --- cpio-2.13+dfsg/debian/patches/series 2021-08-25 10:52:24.000000000 +0000 +++ cpio-2.13+dfsg/debian/patches/series 2024-01-10 14:28:57.000000000 +0000 @@ -10,7 +10,7 @@ 695717-no-cpio.info.patch fix.win32.undef.stat.patch fix.mt-erase.manpage.patch -revert-CVE-2015-1197-handling.patch CVE-2021-38185.patch CVE-2021-38185.2.patch CVE-2021-38185.3.patch +CVE-2023-7207.patch