diff --git a/backport-0001-CVE-2021-31566.patch b/backport-0001-CVE-2021-31566.patch new file mode 100644 index 0000000000000000000000000000000000000000..45498f96a2ad931ca0e71c1b5c212c0c5e52290b --- /dev/null +++ b/backport-0001-CVE-2021-31566.patch @@ -0,0 +1,190 @@ +From b41daecb5ccb4c8e3b2c53fd6147109fc12c3043 Mon Sep 17 00:00:00 2001 +From: Martin Matuska +Date: Fri, 20 Aug 2021 01:50:27 +0200 +Subject: [PATCH] Do not follow symlinks when processing the fixup list + +Use lchmod() instead of chmod() and tell the remaining functions that the +real file to be modified is a symbolic link. + +Fixes #1566 +--- + Makefile.am | 1 + + libarchive/archive_write_disk_posix.c | 24 +++++++- + libarchive/test/CMakeLists.txt | 1 + + libarchive/test/test_write_disk_fixup.c | 77 +++++++++++++++++++++++++ + 4 files changed, 102 insertions(+), 1 deletion(-) + create mode 100644 libarchive/test/test_write_disk_fixup.c + +diff --git a/Makefile.am b/Makefile.am +index d526daa..d227f81 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -556,6 +556,7 @@ libarchive_test_SOURCES= \ + libarchive/test/test_write_disk.c \ + libarchive/test/test_write_disk_appledouble.c \ + libarchive/test/test_write_disk_failures.c \ ++ libarchive/test/test_write_disk_fixup.c \ + libarchive/test/test_write_disk_hardlink.c \ + libarchive/test/test_write_disk_hfs_compression.c \ + libarchive/test/test_write_disk_lookup.c \ +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index 0168d0d..35b494c 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -2444,6 +2444,7 @@ _archive_write_disk_close(struct archive *_a) + { + struct archive_write_disk *a = (struct archive_write_disk *)_a; + struct fixup_entry *next, *p; ++ struct stat st; + int fd, ret; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, +@@ -2461,6 +2462,20 @@ _archive_write_disk_close(struct archive *_a) + (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { + fd = open(p->name, + O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); ++ if (fd == -1) { ++ /* If we cannot lstat, skip entry */ ++ if (lstat(p->name, &st) != 0) ++ goto skip_fixup_entry; ++ /* ++ * If we deal with a symbolic link, mark ++ * it in the fixup mode to ensure no ++ * modifications are made to its target. ++ */ ++ if (S_ISLNK(st.st_mode)) { ++ p->mode &= ~S_IFMT; ++ p->mode |= S_IFLNK; ++ } ++ } + } + if (p->fixup & TODO_TIMES) { + set_times(a, fd, p->mode, p->name, +@@ -2475,7 +2490,12 @@ _archive_write_disk_close(struct archive *_a) + fchmod(fd, p->mode); + else + #endif +- chmod(p->name, p->mode); ++#ifdef HAVE_LCHMOD ++ lchmod(p->name, p->mode); ++#else ++ if (!S_ISLNK(p->mode)) ++ chmod(p->name, p->mode); ++#endif + } + if (p->fixup & TODO_ACLS) + archive_write_disk_set_acls(&a->archive, fd, +@@ -2486,6 +2506,7 @@ _archive_write_disk_close(struct archive *_a) + if (p->fixup & TODO_MAC_METADATA) + set_mac_metadata(a, p->name, p->mac_metadata, + p->mac_metadata_size); ++skip_fixup_entry: + next = p->next; + archive_acl_clear(&p->acl); + free(p->mac_metadata); +@@ -2626,6 +2647,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname) + fe->next = a->fixup_list; + a->fixup_list = fe; + fe->fixup = 0; ++ fe->mode = 0; + fe->name = strdup(pathname); + return (fe); + } +diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt +index 4be5bf4..5d6d8ce 100644 +--- a/libarchive/test/CMakeLists.txt ++++ b/libarchive/test/CMakeLists.txt +@@ -208,6 +208,7 @@ IF(ENABLE_TEST) + test_write_disk.c + test_write_disk_appledouble.c + test_write_disk_failures.c ++ test_write_disk_fixup.c + test_write_disk_hardlink.c + test_write_disk_hfs_compression.c + test_write_disk_lookup.c +diff --git a/libarchive/test/test_write_disk_fixup.c b/libarchive/test/test_write_disk_fixup.c +new file mode 100644 +index 0000000..153cc3a +--- /dev/null ++++ b/libarchive/test/test_write_disk_fixup.c +@@ -0,0 +1,77 @@ ++/*- ++ * Copyright (c) 2021 Martin Matuska ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include "test.h" ++ ++/* ++ * Test fixup entries don't follow symlinks ++ */ ++DEFINE_TEST(test_write_disk_fixup) ++{ ++ struct archive *ad; ++ struct archive_entry *ae; ++ int r; ++ ++ if (!canSymlink()) { ++ skipping("Symlinks not supported"); ++ return; ++ } ++ ++ /* Write entries to disk. */ ++ assert((ad = archive_write_disk_new()) != NULL); ++ ++ /* ++ * Create a file ++ */ ++ assertMakeFile("victim", 0600, "a"); ++ ++ /* ++ * Create a directory and a symlink with the same name ++ */ ++ ++ /* Directory: dir */ ++ assert((ae = archive_entry_new()) != NULL); ++ archive_entry_copy_pathname(ae, "dir"); ++ archive_entry_set_mode(ae, AE_IFDIR | 0606); ++ assertEqualIntA(ad, 0, archive_write_header(ad, ae)); ++ assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); ++ archive_entry_free(ae); ++ ++ /* Symbolic Link: dir -> foo */ ++ assert((ae = archive_entry_new()) != NULL); ++ archive_entry_copy_pathname(ae, "dir"); ++ archive_entry_set_mode(ae, AE_IFLNK | 0777); ++ archive_entry_set_size(ae, 0); ++ archive_entry_copy_symlink(ae, "victim"); ++ assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); ++ if (r >= ARCHIVE_WARN) ++ assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); ++ archive_entry_free(ae); ++ ++ assertEqualInt(ARCHIVE_OK, archive_write_free(ad)); ++ ++ /* Test the entries on disk. */ ++ assertIsSymlink("dir", "victim", 0); ++ assertFileMode("victim", 0600); ++} +-- +2.23.0 + diff --git a/backport-0002-CVE-2021-31566.patch b/backport-0002-CVE-2021-31566.patch new file mode 100644 index 0000000000000000000000000000000000000000..5cad265049b08b00819c411b7d20313971d7a261 --- /dev/null +++ b/backport-0002-CVE-2021-31566.patch @@ -0,0 +1,30 @@ +From e2ad1a2c3064fa9eba6274b3641c4c1beed25c0b Mon Sep 17 00:00:00 2001 +From: Martin Matuska +Date: Sun, 22 Aug 2021 03:53:28 +0200 +Subject: [PATCH] Never follow symlinks when setting file flags on Linux + +When opening a file descriptor to set file flags on linux, ensure +no symbolic links are followed. This fixes the case when an archive +contains a directory entry followed by a symlink entry with the same +path. The fixup code would modify file flags of the symlink target. +--- + libarchive/archive_write_disk_posix.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index 35b494c..64522da 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -3909,7 +3909,8 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name, + + /* If we weren't given an fd, open it ourselves. */ + if (myfd < 0) { +- myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC); ++ myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | ++ O_CLOEXEC | O_NOFOLLOW); + __archive_ensure_cloexec_flag(myfd); + } + if (myfd < 0) +-- +2.23.0 + diff --git a/backport-0003-CVE-2021-31566.patch b/backport-0003-CVE-2021-31566.patch new file mode 100644 index 0000000000000000000000000000000000000000..0c91d8580db158c79adc64fea3a97524363be2e5 --- /dev/null +++ b/backport-0003-CVE-2021-31566.patch @@ -0,0 +1,198 @@ +From 8a1bd5c18e896f0411a991240ce0d772bb02c840 Mon Sep 17 00:00:00 2001 +From: Martin Matuska +Date: Fri, 27 Aug 2021 10:56:28 +0200 +Subject: [PATCH] Fix following symlinks when processing the fixup list + +The previous fix in b41daecb5 was incomplete. Fixup entries are +given the original path without calling cleanup_pathname(). +To make sure we don't follow a symlink, we must strip trailing +slashes from the path. + +The fixup entries are always directories. Make sure we try to modify +only directories by providing O_DIRECTORY to open() (if supported) +and if it fails to check directory via lstat(). + +Fixes #1566 +--- + libarchive/archive_write_disk_posix.c | 62 +++++++++++++++++-------- + libarchive/test/test_write_disk_fixup.c | 44 ++++++++++++++---- + 2 files changed, 78 insertions(+), 28 deletions(-) + +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index 64522da..41ebdaa 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -2445,6 +2445,7 @@ _archive_write_disk_close(struct archive *_a) + struct archive_write_disk *a = (struct archive_write_disk *)_a; + struct fixup_entry *next, *p; + struct stat st; ++ char *c; + int fd, ret; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, +@@ -2458,24 +2459,49 @@ _archive_write_disk_close(struct archive *_a) + while (p != NULL) { + fd = -1; + a->pst = NULL; /* Mark stat cache as out-of-date. */ +- if (p->fixup & +- (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { +- fd = open(p->name, +- O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); ++ ++ /* We must strip trailing slashes from the path to avoid ++ dereferencing symbolic links to directories */ ++ c = p->name; ++ while (*c != '\0') ++ c++; ++ while (c != p->name && *(c - 1) == '/') { ++ c--; ++ *c = '\0'; ++ } ++ ++ if (p->fixup == 0) ++ goto skip_fixup_entry; ++ else { ++ fd = open(p->name, O_BINARY | O_NOFOLLOW | O_RDONLY ++#if defined(O_DIRECTORY) ++ | O_DIRECTORY ++#endif ++ | O_CLOEXEC); ++ /* ++ ` * If we don't support O_DIRECTORY, ++ * or open() has failed, we must stat() ++ * to verify that we are opening a directory ++ */ ++#if defined(O_DIRECTORY) + if (fd == -1) { +- /* If we cannot lstat, skip entry */ +- if (lstat(p->name, &st) != 0) ++ if (lstat(p->name, &st) != 0 || ++ !S_ISDIR(st.st_mode)) { + goto skip_fixup_entry; +- /* +- * If we deal with a symbolic link, mark +- * it in the fixup mode to ensure no +- * modifications are made to its target. +- */ +- if (S_ISLNK(st.st_mode)) { +- p->mode &= ~S_IFMT; +- p->mode |= S_IFLNK; + } + } ++#else ++#if HAVE_FSTAT ++ if (fd > 0 && ( ++ fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode))) { ++ goto skip_fixup_entry; ++ } else ++#endif ++ if (lstat(p->name, &st) != 0 || ++ !S_ISDIR(st.st_mode)) { ++ goto skip_fixup_entry; ++ } ++#endif + } + if (p->fixup & TODO_TIMES) { + set_times(a, fd, p->mode, p->name, +@@ -2487,14 +2513,13 @@ _archive_write_disk_close(struct archive *_a) + if (p->fixup & TODO_MODE_BASE) { + #ifdef HAVE_FCHMOD + if (fd >= 0) +- fchmod(fd, p->mode); ++ fchmod(fd, p->mode & 07777); + else + #endif + #ifdef HAVE_LCHMOD +- lchmod(p->name, p->mode); ++ lchmod(p->name, p->mode & 07777); + #else +- if (!S_ISLNK(p->mode)) +- chmod(p->name, p->mode); ++ chmod(p->name, p->mode & 07777); + #endif + } + if (p->fixup & TODO_ACLS) +@@ -2647,7 +2672,6 @@ new_fixup(struct archive_write_disk *a, const char *pathname) + fe->next = a->fixup_list; + a->fixup_list = fe; + fe->fixup = 0; +- fe->mode = 0; + fe->name = strdup(pathname); + return (fe); + } +diff --git a/libarchive/test/test_write_disk_fixup.c b/libarchive/test/test_write_disk_fixup.c +index 153cc3a..900fb81 100644 +--- a/libarchive/test/test_write_disk_fixup.c ++++ b/libarchive/test/test_write_disk_fixup.c +@@ -44,26 +44,50 @@ DEFINE_TEST(test_write_disk_fixup) + /* + * Create a file + */ +- assertMakeFile("victim", 0600, "a"); ++ assertMakeFile("file", 0600, "a"); ++ ++ /* ++ * Create a directory ++ */ ++ assertMakeDir("dir", 0700); + + /* + * Create a directory and a symlink with the same name + */ + +- /* Directory: dir */ ++ /* Directory: dir1 */ ++ assert((ae = archive_entry_new()) != NULL); ++ archive_entry_copy_pathname(ae, "dir1/"); ++ archive_entry_set_mode(ae, AE_IFDIR | 0555); ++ assertEqualIntA(ad, 0, archive_write_header(ad, ae)); ++ assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); ++ archive_entry_free(ae); ++ ++ /* Directory: dir2 */ + assert((ae = archive_entry_new()) != NULL); +- archive_entry_copy_pathname(ae, "dir"); +- archive_entry_set_mode(ae, AE_IFDIR | 0606); ++ archive_entry_copy_pathname(ae, "dir2/"); ++ archive_entry_set_mode(ae, AE_IFDIR | 0555); + assertEqualIntA(ad, 0, archive_write_header(ad, ae)); + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + +- /* Symbolic Link: dir -> foo */ ++ /* Symbolic Link: dir1 -> dir */ ++ assert((ae = archive_entry_new()) != NULL); ++ archive_entry_copy_pathname(ae, "dir1"); ++ archive_entry_set_mode(ae, AE_IFLNK | 0777); ++ archive_entry_set_size(ae, 0); ++ archive_entry_copy_symlink(ae, "dir"); ++ assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); ++ if (r >= ARCHIVE_WARN) ++ assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); ++ archive_entry_free(ae); ++ ++ /* Symbolic Link: dir2 -> file */ + assert((ae = archive_entry_new()) != NULL); +- archive_entry_copy_pathname(ae, "dir"); ++ archive_entry_copy_pathname(ae, "dir2"); + archive_entry_set_mode(ae, AE_IFLNK | 0777); + archive_entry_set_size(ae, 0); +- archive_entry_copy_symlink(ae, "victim"); ++ archive_entry_copy_symlink(ae, "file"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); +@@ -72,6 +96,8 @@ DEFINE_TEST(test_write_disk_fixup) + assertEqualInt(ARCHIVE_OK, archive_write_free(ad)); + + /* Test the entries on disk. */ +- assertIsSymlink("dir", "victim", 0); +- assertFileMode("victim", 0600); ++ assertIsSymlink("dir1", "dir", 0); ++ assertIsSymlink("dir2", "file", 0); ++ assertFileMode("dir", 0700); ++ assertFileMode("file", 0600); + } +-- +2.23.0 + diff --git a/backport-0004-CVE-2021-31566.patch b/backport-0004-CVE-2021-31566.patch new file mode 100644 index 0000000000000000000000000000000000000000..ead5448eaa619e7ea3c4c4190fd9f5258bbb0dd0 --- /dev/null +++ b/backport-0004-CVE-2021-31566.patch @@ -0,0 +1,211 @@ +From ede459d2ebb879f5eedb6f7abea203be0b334230 Mon Sep 17 00:00:00 2001 +From: Martin Matuska +Date: Wed, 17 Nov 2021 21:06:00 +0100 +Subject: [PATCH] archive_write_disk_posix: fix writing fflags broken in + 8a1bd5c + +The fixup list was erroneously assumed to be directories only. +Only in the case of critical file flags modification (e.g. SF_IMMUTABLE +on BSD systems), other file types (e.g. regular files or symbolic links) +may be added to the fixup list. We still need to verify that we are writing +to the correct file type, so compare the archive entry file type with +the file type of the file to be modified. + +Fixes #1617 +--- + libarchive/archive_write_disk_posix.c | 87 +++++++++++++++++++++++---- + 1 file changed, 75 insertions(+), 12 deletions(-) + +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index 41ebdaa..1a3bba3 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -173,6 +173,7 @@ struct fixup_entry { + struct fixup_entry *next; + struct archive_acl acl; + mode_t mode; ++ __LA_MODE_T filetype; + int64_t atime; + int64_t birthtime; + int64_t mtime; +@@ -357,6 +358,7 @@ struct archive_write_disk { + + static int la_opendirat(int, const char *); + static int la_mktemp(struct archive_write_disk *); ++static int la_verify_filetype(mode_t, __LA_MODE_T); + static void fsobj_error(int *, struct archive_string *, int, const char *, + const char *); + static int check_symlinks_fsobj(char *, int *, struct archive_string *, +@@ -464,6 +466,39 @@ la_opendirat(int fd, const char *path) { + #endif + } + ++static int ++la_verify_filetype(mode_t mode, __LA_MODE_T filetype) { ++ int ret = 0; ++ ++ switch (filetype) { ++ case AE_IFREG: ++ ret = (S_ISREG(mode)); ++ break; ++ case AE_IFDIR: ++ ret = (S_ISDIR(mode)); ++ break; ++ case AE_IFLNK: ++ ret = (S_ISLNK(mode)); ++ break; ++ case AE_IFSOCK: ++ ret = (S_ISSOCK(mode)); ++ break; ++ case AE_IFCHR: ++ ret = (S_ISCHR(mode)); ++ break; ++ case AE_IFBLK: ++ ret = (S_ISBLK(mode)); ++ break; ++ case AE_IFIFO: ++ ret = (S_ISFIFO(mode)); ++ break; ++ default: ++ break; ++ } ++ ++ return (ret); ++} ++ + static int + lazy_stat(struct archive_write_disk *a) + { +@@ -810,6 +845,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) + fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); ++ fe->filetype = archive_entry_filetype(entry); + fe->fixup |= TODO_MODE_BASE; + fe->mode = a->mode; + } +@@ -820,6 +856,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) + fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); ++ fe->filetype = archive_entry_filetype(entry); + fe->mode = a->mode; + fe->fixup |= TODO_TIMES; + if (archive_entry_atime_is_set(entry)) { +@@ -853,6 +890,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) + fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); ++ fe->filetype = archive_entry_filetype(entry); + fe->fixup |= TODO_ACLS; + archive_acl_copy(&fe->acl, archive_entry_acl(entry)); + } +@@ -865,6 +903,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) + fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); ++ fe->filetype = archive_entry_filetype(entry); + fe->mac_metadata = malloc(metadata_size); + if (fe->mac_metadata != NULL) { + memcpy(fe->mac_metadata, metadata, +@@ -879,6 +918,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) + fe = current_fixup(a, archive_entry_pathname(entry)); + if (fe == NULL) + return (ARCHIVE_FATAL); ++ fe->filetype = archive_entry_filetype(entry); + fe->fixup |= TODO_FFLAGS; + /* TODO: Complete this.. defer fflags from below. */ + } +@@ -2446,7 +2486,7 @@ _archive_write_disk_close(struct archive *_a) + struct fixup_entry *next, *p; + struct stat st; + char *c; +- int fd, ret; ++ int fd, ret, openflags; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, +@@ -2473,32 +2513,53 @@ _archive_write_disk_close(struct archive *_a) + if (p->fixup == 0) + goto skip_fixup_entry; + else { +- fd = open(p->name, O_BINARY | O_NOFOLLOW | O_RDONLY ++ /* ++ * We need to verify if the type of the file ++ * we are going to open matches the file type ++ * of the fixup entry. ++ */ ++ openflags = O_BINARY | O_NOFOLLOW | O_RDONLY ++ | O_CLOEXEC; + #if defined(O_DIRECTORY) +- | O_DIRECTORY ++ if (p->filetype == AE_IFDIR) ++ openflags |= O_DIRECTORY; + #endif +- | O_CLOEXEC); ++ fd = open(p->name, openflags); ++ ++#if defined(O_DIRECTORY) + /* +- ` * If we don't support O_DIRECTORY, +- * or open() has failed, we must stat() +- * to verify that we are opening a directory ++ * If we support O_DIRECTORY and open was ++ * successful we can skip the file type check ++ * for directories. For other file types ++ * we need to verify via fstat() or lstat() + */ +-#if defined(O_DIRECTORY) +- if (fd == -1) { ++ if (fd == -1 || p->filetype != AE_IFDIR) { ++#if HAVE_FSTAT ++ if (fd > 0 && ( ++ fstat(fd, &st) != 0 || ++ la_verify_filetype(st.st_mode, ++ p->filetype) == 0)) { ++ goto skip_fixup_entry; ++ } else ++#endif + if (lstat(p->name, &st) != 0 || +- !S_ISDIR(st.st_mode)) { ++ la_verify_filetype(st.st_mode, ++ p->filetype) == 0) { + goto skip_fixup_entry; + } + } + #else + #if HAVE_FSTAT + if (fd > 0 && ( +- fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode))) { ++ fstat(fd, &st) != 0 || ++ la_verify_filetype(st.st_mode, ++ p->filetype) == 0)) { + goto skip_fixup_entry; + } else + #endif + if (lstat(p->name, &st) != 0 || +- !S_ISDIR(st.st_mode)) { ++ la_verify_filetype(st.st_mode, ++ p->filetype) == 0) { + goto skip_fixup_entry; + } + #endif +@@ -2672,6 +2733,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname) + fe->next = a->fixup_list; + a->fixup_list = fe; + fe->fixup = 0; ++ fe->filetype = 0; + fe->name = strdup(pathname); + return (fe); + } +@@ -3771,6 +3833,7 @@ set_fflags(struct archive_write_disk *a) + le = current_fixup(a, a->name); + if (le == NULL) + return (ARCHIVE_FATAL); ++ le->filetype = archive_entry_filetype(a->entry); + le->fixup |= TODO_FFLAGS; + le->fflags_set = set; + /* Store the mode if it's not already there. */ +-- +2.23.0 + diff --git a/backport-CVE-2021-23177.patch b/backport-CVE-2021-23177.patch new file mode 100644 index 0000000000000000000000000000000000000000..f6ccce55b115de4bed2470948200c45abf19bbd3 --- /dev/null +++ b/backport-CVE-2021-23177.patch @@ -0,0 +1,199 @@ +From fba4f123cc456d2b2538f811bb831483bf336bad Mon Sep 17 00:00:00 2001 +From: Martin Matuska +Date: Sat, 21 Aug 2021 20:51:07 +0200 +Subject: [PATCH] Fix handling of symbolic link ACLs + +On Linux ACLs on symbolic links are not supported. +We must avoid calling acl_set_file() on symbolic links as their +targets are modified instead. + +While here, do not try to set default ACLs on non-directories. + +Fixes #1565 +--- + libarchive/archive_disk_acl_freebsd.c | 20 +++++++++++++++----- + libarchive/archive_disk_acl_linux.c | 23 ++++++++++++++++++++--- + libarchive/archive_disk_acl_sunos.c | 13 +++++++++---- + 3 files changed, 44 insertions(+), 12 deletions(-) + +diff --git a/libarchive/archive_disk_acl_freebsd.c b/libarchive/archive_disk_acl_freebsd.c +index aba41e5..ed4e7a7 100644 +--- a/libarchive/archive_disk_acl_freebsd.c ++++ b/libarchive/archive_disk_acl_freebsd.c +@@ -319,7 +319,7 @@ translate_acl(struct archive_read_disk *a, + + static int + set_acl(struct archive *a, int fd, const char *name, +- struct archive_acl *abstract_acl, ++ struct archive_acl *abstract_acl, __LA_MODE_T mode, + int ae_requested_type, const char *tname) + { + int acl_type = 0; +@@ -364,6 +364,13 @@ set_acl(struct archive *a, int fd, const char *name, + return (ARCHIVE_FAILED); + } + ++ if (acl_type == ACL_TYPE_DEFAULT && !S_ISDIR(mode)) { ++ errno = EINVAL; ++ archive_set_error(a, errno, ++ "Cannot set default ACL on non-directory"); ++ return (ARCHIVE_WARN); ++ } ++ + acl = acl_init(entries); + if (acl == (acl_t)NULL) { + archive_set_error(a, errno, +@@ -542,7 +549,10 @@ set_acl(struct archive *a, int fd, const char *name, + else if (acl_set_link_np(name, acl_type, acl) != 0) + #else + /* FreeBSD older than 8.0 */ +- else if (acl_set_file(name, acl_type, acl) != 0) ++ else if (S_ISLNK(mode)) { ++ /* acl_set_file() follows symbolic links, skip */ ++ ret = ARCHIVE_OK; ++ } else if (acl_set_file(name, acl_type, acl) != 0) + #endif + { + if (errno == EOPNOTSUPP) { +@@ -677,14 +687,14 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { +- ret = set_acl(a, fd, name, abstract_acl, ++ ret = set_acl(a, fd, name, abstract_acl, mode, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); + if (ret != ARCHIVE_OK) + return (ret); + } + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) +- ret = set_acl(a, fd, name, abstract_acl, ++ ret = set_acl(a, fd, name, abstract_acl, mode, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); + + /* Simultaneous POSIX.1e and NFSv4 is not supported */ +@@ -693,7 +703,7 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + #if ARCHIVE_ACL_FREEBSD_NFS4 + else if ((archive_acl_types(abstract_acl) & + ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { +- ret = set_acl(a, fd, name, abstract_acl, ++ ret = set_acl(a, fd, name, abstract_acl, mode, + ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); + } + #endif +diff --git a/libarchive/archive_disk_acl_linux.c b/libarchive/archive_disk_acl_linux.c +index 3928f3d..31d2705 100644 +--- a/libarchive/archive_disk_acl_linux.c ++++ b/libarchive/archive_disk_acl_linux.c +@@ -343,6 +343,11 @@ set_richacl(struct archive *a, int fd, const char *name, + return (ARCHIVE_FAILED); + } + ++ if (S_ISLNK(mode)) { ++ /* Linux does not support RichACLs on symbolic links */ ++ return (ARCHIVE_OK); ++ } ++ + richacl = richacl_alloc(entries); + if (richacl == NULL) { + archive_set_error(a, errno, +@@ -455,7 +460,7 @@ exit_free: + #if ARCHIVE_ACL_LIBACL + static int + set_acl(struct archive *a, int fd, const char *name, +- struct archive_acl *abstract_acl, ++ struct archive_acl *abstract_acl, __LA_MODE_T mode, + int ae_requested_type, const char *tname) + { + int acl_type = 0; +@@ -488,6 +493,18 @@ set_acl(struct archive *a, int fd, const char *name, + return (ARCHIVE_FAILED); + } + ++ if (S_ISLNK(mode)) { ++ /* Linux does not support ACLs on symbolic links */ ++ return (ARCHIVE_OK); ++ } ++ ++ if (acl_type == ACL_TYPE_DEFAULT && !S_ISDIR(mode)) { ++ errno = EINVAL; ++ archive_set_error(a, errno, ++ "Cannot set default ACL on non-directory"); ++ return (ARCHIVE_WARN); ++ } ++ + acl = acl_init(entries); + if (acl == (acl_t)NULL) { + archive_set_error(a, errno, +@@ -727,14 +744,14 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) { +- ret = set_acl(a, fd, name, abstract_acl, ++ ret = set_acl(a, fd, name, abstract_acl, mode, + ARCHIVE_ENTRY_ACL_TYPE_ACCESS, "access"); + if (ret != ARCHIVE_OK) + return (ret); + } + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) +- ret = set_acl(a, fd, name, abstract_acl, ++ ret = set_acl(a, fd, name, abstract_acl, mode, + ARCHIVE_ENTRY_ACL_TYPE_DEFAULT, "default"); + } + #endif /* ARCHIVE_ACL_LIBACL */ +diff --git a/libarchive/archive_disk_acl_sunos.c b/libarchive/archive_disk_acl_sunos.c +index b0f5dfa..a63b64a 100644 +--- a/libarchive/archive_disk_acl_sunos.c ++++ b/libarchive/archive_disk_acl_sunos.c +@@ -443,7 +443,7 @@ translate_acl(struct archive_read_disk *a, + + static int + set_acl(struct archive *a, int fd, const char *name, +- struct archive_acl *abstract_acl, ++ struct archive_acl *abstract_acl, __LA_MODE_T mode, + int ae_requested_type, const char *tname) + { + aclent_t *aclent; +@@ -467,7 +467,6 @@ set_acl(struct archive *a, int fd, const char *name, + if (entries == 0) + return (ARCHIVE_OK); + +- + switch (ae_requested_type) { + case ARCHIVE_ENTRY_ACL_TYPE_POSIX1E: + cmd = SETACL; +@@ -492,6 +491,12 @@ set_acl(struct archive *a, int fd, const char *name, + return (ARCHIVE_FAILED); + } + ++ if (S_ISLNK(mode)) { ++ /* Skip ACLs on symbolic links */ ++ ret = ARCHIVE_OK; ++ goto exit_free; ++ } ++ + e = 0; + + while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type, +@@ -801,7 +806,7 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + if ((archive_acl_types(abstract_acl) + & ARCHIVE_ENTRY_ACL_TYPE_POSIX1E) != 0) { + /* Solaris writes POSIX.1e access and default ACLs together */ +- ret = set_acl(a, fd, name, abstract_acl, ++ ret = set_acl(a, fd, name, abstract_acl, mode, + ARCHIVE_ENTRY_ACL_TYPE_POSIX1E, "posix1e"); + + /* Simultaneous POSIX.1e and NFSv4 is not supported */ +@@ -810,7 +815,7 @@ archive_write_disk_set_acls(struct archive *a, int fd, const char *name, + #if ARCHIVE_ACL_SUNOS_NFS4 + else if ((archive_acl_types(abstract_acl) & + ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) { +- ret = set_acl(a, fd, name, abstract_acl, ++ ret = set_acl(a, fd, name, abstract_acl, mode, + ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4"); + } + #endif +-- +2.23.0 + diff --git a/libarchive.spec b/libarchive.spec index 5df3197ddb55088bbed917c3ce2c5300b5bd6095..ee7a2250c3126ef1a52eaaf94c6414dd4991e811 100644 --- a/libarchive.spec +++ b/libarchive.spec @@ -2,7 +2,7 @@ Name: libarchive Version: 3.4.3 -Release: 3 +Release: 4 Summary: Multi-format archive and compression library License: BSD @@ -12,6 +12,7 @@ Source0: https://www.libarchive.org/downloads/%{name}-%{version}.tar.gz BuildRequires: gcc bison sharutils zlib-devel bzip2-devel xz-devel BuildRequires: lzo-devel e2fsprogs-devel libacl-devel libattr-devel BuildRequires: openssl-devel libxml2-devel lz4-devel automake libzstd-devel +BuildRequires: libtool Provides: bsdtar bsdcpio bsdcat Obsoletes: bsdtar bsdcpio bsdcat @@ -20,6 +21,11 @@ Patch6001: libarchive-uninitialized-value.patch Patch6002: libarchive-3.4.3-lchmod-support-check.patch Patch6003: libarchive-3.4.3-avoid-stack-overflow.patch Patch6004: backport-CVE-2021-36976.patch +Patch6005: backport-CVE-2021-23177.patch +Patch6006: backport-0001-CVE-2021-31566.patch +Patch6007: backport-0002-CVE-2021-31566.patch +Patch6008: backport-0003-CVE-2021-31566.patch +Patch6009: backport-0004-CVE-2021-31566.patch %description %{name} is an open-source BSD-licensed C programming library that @@ -42,6 +48,7 @@ applications that want to make use of %{name}. %autosetup -n %{name}-%{version} -p1 %build +autoreconf -ifv %configure --disable-rpath %disable_rpath @@ -150,6 +157,12 @@ run_testsuite %{_mandir}/man5/* %changelog +* Wed Jan 12 2022 yangcheng - 3.4.3-4 +- Type:CVE +- ID:CVE-2021-23177,CVE-2021-31566 +- SUG:NA +- DESC:fix CVE-2021-23177,CVE-2021-31566 + * Thu Oct 14 2021 yangcheng - 3.4.3-3 - Type:CVE - ID:CVE-2021-36976