From 788d6a5396dcba98b49c2269ea300d44e89dd7ac Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 29 Mar 2022 15:09:15 -0400 Subject: [PATCH] import libarchive-3.3.3-3.el8_5 --- SOURCES/libarchive-3.3.3-CVE-2021-23177.patch | 199 +++++ SOURCES/libarchive-3.3.3-CVE-2021-31566.patch | 811 ++++++++++++++++++ SPECS/libarchive.spec | 13 +- 3 files changed, 1022 insertions(+), 1 deletion(-) create mode 100644 SOURCES/libarchive-3.3.3-CVE-2021-23177.patch create mode 100644 SOURCES/libarchive-3.3.3-CVE-2021-31566.patch diff --git a/SOURCES/libarchive-3.3.3-CVE-2021-23177.patch b/SOURCES/libarchive-3.3.3-CVE-2021-23177.patch new file mode 100644 index 0000000..09babb5 --- /dev/null +++ b/SOURCES/libarchive-3.3.3-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 1/2] 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 aba41e5d..ed4e7a78 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 3928f3d6..31d27053 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 b0f5dfad..0ef3ad52 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.31.1 + diff --git a/SOURCES/libarchive-3.3.3-CVE-2021-31566.patch b/SOURCES/libarchive-3.3.3-CVE-2021-31566.patch new file mode 100644 index 0000000..48adb55 --- /dev/null +++ b/SOURCES/libarchive-3.3.3-CVE-2021-31566.patch @@ -0,0 +1,811 @@ +From b837c72c423b744a2e6c554742877173406dbfa0 Mon Sep 17 00:00:00 2001 +From: Martin Matuska +Date: Sat, 25 May 2019 23:46:59 +0200 +Subject: [PATCH] archive_write_disk_posix: open a fd when processing fixup + entries + +--- + libarchive/archive_write_disk_posix.c | 25 ++++++++++++++++++++----- + 1 file changed, 20 insertions(+), 5 deletions(-) + +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index 70b27b50..0583fbd1 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -182,6 +182,7 @@ struct fixup_entry { + void *mac_metadata; + int fixup; /* bitmask of what needs fixing */ + char *name; ++ int fd; + }; + + /* +@@ -2354,20 +2355,31 @@ _archive_write_disk_close(struct archive *_a) + + while (p != NULL) { + a->pst = NULL; /* Mark stat cache as out-of-date. */ ++ if (p->fd < 0 && p->fixup & ++ (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { ++ p->fd = open(p->name, ++ O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); ++ } + if (p->fixup & TODO_TIMES) { +- set_times(a, -1, p->mode, p->name, ++ set_times(a, p->fd, p->mode, p->name, + p->atime, p->atime_nanos, + p->birthtime, p->birthtime_nanos, + p->mtime, p->mtime_nanos, + p->ctime, p->ctime_nanos); + } +- if (p->fixup & TODO_MODE_BASE) ++ if (p->fixup & TODO_MODE_BASE) { ++#ifdef HAVE_FCHMOD ++ if (p->fd >= 0) ++ fchmod(p->fd, p->mode); ++ else ++#endif + chmod(p->name, p->mode); ++ } + if (p->fixup & TODO_ACLS) +- archive_write_disk_set_acls(&a->archive, -1, p->name, +- &p->acl, p->mode); ++ archive_write_disk_set_acls(&a->archive, p->fd, ++ p->name, &p->acl, p->mode); + if (p->fixup & TODO_FFLAGS) +- set_fflags_platform(a, -1, p->name, ++ set_fflags_platform(a, p->fd, p->name, + p->mode, p->fflags_set, 0); + if (p->fixup & TODO_MAC_METADATA) + set_mac_metadata(a, p->name, p->mac_metadata, +@@ -2376,6 +2388,8 @@ _archive_write_disk_close(struct archive *_a) + archive_acl_clear(&p->acl); + free(p->mac_metadata); + free(p->name); ++ if (p->fd >= 0) ++ close(p->fd); + free(p); + p = next; + } +@@ -2510,6 +2524,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname) + a->fixup_list = fe; + fe->fixup = 0; + fe->name = strdup(pathname); ++ fe->fd = -1; + return (fe); + } + +-- +2.31.1 + +From 6d5204058ed51e11588a438737e9033305cfd248 Mon Sep 17 00:00:00 2001 +From: Martin Matuska +Date: Thu, 6 Jun 2019 15:12:11 +0200 +Subject: [PATCH] archive_write_disk_posix changes - private file descriptor in + _archive_write_disk_close() - use la_opendirat() in edit_deep_directories() + +--- + libarchive/archive_write_disk_posix.c | 25 ++++++++++++------------- + 1 file changed, 12 insertions(+), 13 deletions(-) + +diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c +index 89941c64..b1a0bb38 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -186,7 +186,6 @@ struct fixup_entry { + void *mac_metadata; + int fixup; /* bitmask of what needs fixing */ + char *name; +- int fd; + }; + + /* +@@ -1947,7 +1946,7 @@ edit_deep_directories(struct archive_write_disk *a) + return; + + /* Try to record our starting dir. */ +- a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC); ++ a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC | O_DIRECTORY); + __archive_ensure_cloexec_flag(a->restore_pwd); + if (a->restore_pwd < 0) + return; +@@ -2380,7 +2379,7 @@ _archive_write_disk_close(struct archive *_a) + { + struct archive_write_disk *a = (struct archive_write_disk *)_a; + struct fixup_entry *next, *p; +- int ret; ++ int fd, ret; + + archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, + ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA, +@@ -2391,14 +2390,15 @@ _archive_write_disk_close(struct archive *_a) + p = sort_dir_list(a->fixup_list); + + while (p != NULL) { ++ fd = -1; + a->pst = NULL; /* Mark stat cache as out-of-date. */ +- if (p->fd < 0 && p->fixup & ++ if (p->fixup & + (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) { +- p->fd = open(p->name, ++ fd = open(p->name, + O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC); + } + if (p->fixup & TODO_TIMES) { +- set_times(a, p->fd, p->mode, p->name, ++ set_times(a, fd, p->mode, p->name, + p->atime, p->atime_nanos, + p->birthtime, p->birthtime_nanos, + p->mtime, p->mtime_nanos, +@@ -2406,17 +2406,17 @@ _archive_write_disk_close(struct archive *_a) + } + if (p->fixup & TODO_MODE_BASE) { + #ifdef HAVE_FCHMOD +- if (p->fd >= 0) +- fchmod(p->fd, p->mode); ++ if (fd >= 0) ++ fchmod(fd, p->mode); + else + #endif + chmod(p->name, p->mode); + } + if (p->fixup & TODO_ACLS) +- archive_write_disk_set_acls(&a->archive, p->fd, ++ archive_write_disk_set_acls(&a->archive, fd, + p->name, &p->acl, p->mode); + if (p->fixup & TODO_FFLAGS) +- set_fflags_platform(a, p->fd, p->name, ++ set_fflags_platform(a, fd, p->name, + p->mode, p->fflags_set, 0); + if (p->fixup & TODO_MAC_METADATA) + set_mac_metadata(a, p->name, p->mac_metadata, +@@ -2425,8 +2425,8 @@ _archive_write_disk_close(struct archive *_a) + archive_acl_clear(&p->acl); + free(p->mac_metadata); + free(p->name); +- if (p->fd >= 0) +- close(p->fd); ++ if (fd >= 0) ++ close(fd); + free(p); + p = next; + } +@@ -2561,7 +2561,6 @@ new_fixup(struct archive_write_disk *a, const char *pathname) + a->fixup_list = fe; + fe->fixup = 0; + fe->name = strdup(pathname); +- fe->fd = -1; + return (fe); + } + +-- +2.31.1 + +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 ba4e65df..8474617e 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -3927,7 +3927,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.31.1 + +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 58edb74e..c93a82e9 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -560,6 +560,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 8474617e..fcd733af 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -2461,6 +2461,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, +@@ -2478,6 +2479,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, +@@ -2492,7 +2507,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, +@@ -2503,6 +2523,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); +@@ -2643,6 +2664,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 b26f679c..53cc3e22 100644 +--- a/libarchive/test/CMakeLists.txt ++++ b/libarchive/test/CMakeLists.txt +@@ -209,6 +209,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 00000000..153cc3a9 +--- /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"); ++ assertFileMode("victim", 0600); ++} +-- +2.31.1 + +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 fcd733af..aadc5871 100644 +--- a/libarchive/archive_write_disk_posix.c ++++ b/libarchive/archive_write_disk_posix.c +@@ -2462,6 +2462,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, +@@ -2475,24 +2476,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, +@@ -2504,14 +2530,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) +@@ -2664,7 +2689,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 c399c984..b83b7307 100644 +--- a/libarchive/test/test_write_disk_fixup.c ++++ b/libarchive/test/test_write_disk_fixup.c +@@ -47,26 +47,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)); +@@ -75,6 +99,8 @@ DEFINE_TEST(test_write_disk_fixup) + assertEqualInt(ARCHIVE_OK, archive_write_free(ad)); + + /* Test the entries on disk. */ +- assertIsSymlink("dir", "victim"); +- assertFileMode("victim", 0600); ++ assertIsSymlink("dir1", "dir"); ++ assertIsSymlink("dir2", "file"); ++ assertFileMode("dir", 0700); ++ assertFileMode("file", 0600); + } +-- +2.31.1 + +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 aadc5871..7e57aac2 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 { + + #define HFS_BLOCKS(s) ((s) >> 12) + ++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) { + static ssize_t _archive_write_disk_data_block(struct archive *, const void *, + size_t, int64_t); + ++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) + { +@@ -822,6 +857,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; + } +@@ -832,6 +868,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)) { +@@ -865,6 +902,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)); + } +@@ -877,6 +915,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, +@@ -891,6 +930,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. */ + } +@@ -2463,7 +2503,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, +@@ -2490,32 +2530,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 +@@ -2689,6 +2750,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); + } +@@ -3811,6 +3873,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.31.1 + diff --git a/SPECS/libarchive.spec b/SPECS/libarchive.spec index bb5334a..0ce2924 100644 --- a/SPECS/libarchive.spec +++ b/SPECS/libarchive.spec @@ -2,7 +2,7 @@ Name: libarchive Version: 3.3.3 -Release: 1%{?dist} +Release: 3%{?dist} Summary: A library for handling streaming archive formats License: BSD @@ -20,6 +20,9 @@ Patch8: libarchive-3.3.2-CVE-2019-19221.patch # upstream reference # https://github.com/libarchive/libarchive/commit/aaacc8762fd8ced8823350edd8ce2e46b565582b#diff-bc144884a8e634e16f247e0588a266ee Patch9: libarchive-3.3.3-fixed-zstd_test.patch +Patch10: libarchive-3.3.3-CVE-2021-23177.patch +Patch11: libarchive-3.3.3-CVE-2021-31566.patch + BuildRequires: gcc @@ -37,6 +40,7 @@ BuildRequires: libxml2-devel BuildRequires: lz4-devel BuildRequires: automake BuildRequires: libzstd-devel +BuildRequires: libtool %description @@ -85,6 +89,7 @@ standard output. %prep %autosetup -p1 +bash build/autogen.sh %build @@ -225,6 +230,12 @@ run_testsuite %changelog +* Tue Dec 21 2021 Matej Mužila - 3.3.3-3 +- Do not follow symlinks when processing the fixup list (CVE-2021-31566) + +* Mon Dec 20 2021 Matej Mužila - 3.3.3-2 +- Fix handling of symbolic link ACLs (CVE-2021-23177) + * Thu Apr 30 2020 Ondrej Dubaj - 3.3.3-1 - Rebase to version 3.3.3