aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2016-10-18 12:57:33 +0000
committerNatanael Copa <ncopa@alpinelinux.org>2016-10-18 12:58:20 +0000
commit87822f4ac4adaaafbbee3ffe58ab6eebdc12907e (patch)
tree92f8c83fd6068ca9cfe26fcbf671d30f1b6b10c9
parentc72d2f6e4c8e60010afb92635a3fe5c20e932f4d (diff)
downloadaports-87822f4ac4adaaafbbee3ffe58ab6eebdc12907e.tar.bz2
aports-87822f4ac4adaaafbbee3ffe58ab6eebdc12907e.tar.xz
main/libarchive: security fix for CVE-2016-5418, CVE-2016-7166
fixes #6247
-rw-r--r--main/libarchive/APKBUILD18
-rw-r--r--main/libarchive/CVE-2016-5418.patch590
-rw-r--r--main/libarchive/CVE-2016-7166.patch56
3 files changed, 660 insertions, 4 deletions
diff --git a/main/libarchive/APKBUILD b/main/libarchive/APKBUILD
index fe8ff7925a..fd43dbc0aa 100644
--- a/main/libarchive/APKBUILD
+++ b/main/libarchive/APKBUILD
@@ -18,6 +18,8 @@ source="http://www.libarchive.org/downloads/libarchive-$pkgver.tar.gz
CVE-2016-4809.patch
CVE-2016-5844.patch
CVE-2016-6250.patch
+ CVE-2016-5418.patch
+ CVE-2016-7166.patch
"
_builddir="$srcdir"/$pkgname-$pkgver
@@ -28,7 +30,9 @@ _builddir="$srcdir"/$pkgname-$pkgver
# - CVE-2016-4809
# - CVE-2016-5844
# - CVE-2016-6250
-
+# 3.1.2-r5:
+# - CVE-2016-5418
+# - CVE-2016-7166
prepare() {
cd "$_builddir"
@@ -69,7 +73,9 @@ b27c60d9288780261410366994103278 CVE-2015-2304.patch
671e37e5012868487c883d1d3d1a98e8 CVE-2016-4302.patch
441be3deb395c923f775e1a2d0f0d35e CVE-2016-4809.patch
fffa1304e451984b8fa43047da1c9178 CVE-2016-5844.patch
-d5e6f412445c5b463d3761995c23f84e CVE-2016-6250.patch"
+d5e6f412445c5b463d3761995c23f84e CVE-2016-6250.patch
+a608f39134680bac449c0f4659a86de4 CVE-2016-5418.patch
+827e8fab29485d7d251b19f06127d098 CVE-2016-7166.patch"
sha256sums="eb87eacd8fe49e8d90c8fdc189813023ccc319c5e752b01fb6ad0cc7b2c53d5e libarchive-3.1.2.tar.gz
75f30c3867d3924461bb764ea2ca3c1b1e43240aeb5b0dd93a103fd7a7ca7fe9 CVE-2013-0211.patch
5a862586b4684d819add1df9d747bc47f9a4f2fecd069175bf00f6927c9633bf CVE-2015-2304.patch
@@ -77,7 +83,9 @@ cfe651e5b9a626ea51b92e762474e8bc9ef28d95a42123f69bdbed3c14547b69 CVE-2016-1541.
f5e66529b373d23e9084c38df2c65d2406986cbb7039cf380ff884b3feb78312 CVE-2016-4302.patch
c108796584bdd539eaa892b7ea83257ccf9174c6a23afe4fa7d32f90ac140220 CVE-2016-4809.patch
dbdd82e4e5693fdfb3e510d6238e411f00d68d71c09d6ec84f4b6c7ca44b00d0 CVE-2016-5844.patch
-e46a9999388cae275c31ee758b44be99fc04b58257b0c3e068a3e58d266a0fdd CVE-2016-6250.patch"
+e46a9999388cae275c31ee758b44be99fc04b58257b0c3e068a3e58d266a0fdd CVE-2016-6250.patch
+0c2015fb4c686f48c6ae6b8fe6e9a44359f58bd21f14c7b0b2f3abf630dd0c75 CVE-2016-5418.patch
+14aa20637a4ca7dd1ce1d88ee4813572f3e4148088d65b9a035240de74edf3cb CVE-2016-7166.patch"
sha512sums="1f3c2a675031f93c7d42ae2ed06742b0b1e2236ff57d9117791d62fb8ae77d6cafffbcb5d45b5bd98daa908bd18c576cf82e01a9b1eba699705e23eff3688114 libarchive-3.1.2.tar.gz
c10470ab67dd94944489f72e4d6f39d98163f5d7a92bcd550aa323e9a1b96148588bd04ac7d8c6ff232dc388559fb3e67552bb5c83ac7626ad714517f5022fce CVE-2013-0211.patch
ae3161b36605c81622d4d4c44f33c31e596506dc60ffb43a91b0f7b831d15d48abdd64725cd770bca6795230f1505d301a74db63903c91507195ccdea0737b63 CVE-2015-2304.patch
@@ -85,4 +93,6 @@ ecbd54a125948c0bf172ad8d877f074e802a4f719a967a69f7c56ea7fda77ec68183bc47642f4437
94db9186246971fbad51d5d1b50719b2ae1d6baeb063fd344546fd4e1d8cec89438ea8baa299af75eb8e1157888b68e8fd53120aaccba1b802b3169baaf13c98 CVE-2016-4302.patch
464692946ad59f7f404a1ac1b123e06b407cabaece95bd062b5c0fca7c62355b4a9c2aa940055aee5b9c40fcc3077fbe2a3b5a3d416b5b2c453fc7518cbc858d CVE-2016-4809.patch
213fbf0b6ac1b6f7662a6d15119696db5c05e071ffa86cb6832677c9676040ed8df199bb22e72dc47264e8873e246737bad327d88f439d8b164c0520095210b2 CVE-2016-5844.patch
-1b93ce72c4769aa7467bb68ad7953551bed3b944eeb686ebbacc7ccd450833dc3250b0e3132cf63ae35d873b021ffbcbeb0f08a60f16037ffabc45536292af35 CVE-2016-6250.patch"
+1b93ce72c4769aa7467bb68ad7953551bed3b944eeb686ebbacc7ccd450833dc3250b0e3132cf63ae35d873b021ffbcbeb0f08a60f16037ffabc45536292af35 CVE-2016-6250.patch
+053e418589969d748192e9c8d232b979fbea2b8a8e323c5ce737b9ec211c41c5f697440dc3bf0e7cb0cd237289d9980d921001df077001b605d25f13ab1997af CVE-2016-5418.patch
+2f554e698615e4d62df29003c7ef24a3fbab207fb1ca2f5dce9a3a665f9c4155c398c28c107daabe63877d3648912fd31286bc785d3ae2a1c7ef4c1077f3fa68 CVE-2016-7166.patch"
diff --git a/main/libarchive/CVE-2016-5418.patch b/main/libarchive/CVE-2016-5418.patch
new file mode 100644
index 0000000000..3a119c27db
--- /dev/null
+++ b/main/libarchive/CVE-2016-5418.patch
@@ -0,0 +1,590 @@
+From d21917b195cf2d625d9f124378a558bec1d7fbba Mon Sep 17 00:00:00 2001
+From: Andrew Gregory <andrew.gregory.8@gmail.com>
+Date: Fri, 15 Jan 2016 02:40:00 -0500
+Subject: [PATCH 1/5] Restore modified path after lstat
+
+check_symlinks is intended to check each component of a path, but failed
+to restore the stripped trailing components after each iteration,
+leaving a NUL byte in the middle of the path.
+---
+ libarchive/archive_write_disk_posix.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
+index 74c03b9..78b3755 100644
+--- a/libarchive/archive_write_disk_posix.c
++++ b/libarchive/archive_write_disk_posix.c
+@@ -2428,6 +2428,9 @@ check_symlinks(struct archive_write_disk *a)
+ return (ARCHIVE_FAILED);
+ }
+ }
++ pn[0] = c;
++ if (pn[0] != '\0')
++ pn++; /* Advance to the next segment. */
+ }
+ pn[0] = c;
+ /* We've checked and/or cleaned the whole path, so remember it. */
+--
+2.2.1
+
+
+From 9180b88b7cdb012010a76a657f2ba3ece51fc3fa Mon Sep 17 00:00:00 2001
+From: Tim Kientzle <kientzle@gmail.com>
+Date: Tue, 9 Aug 2016 21:35:38 -0400
+Subject: [PATCH 2/5] Correct the usage of PATH_MAX as reported in Issue #744.
+
+---
+ libarchive/archive_write_disk_posix.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
+index 78b3755..024b1bd 100644
+--- a/libarchive/archive_write_disk_posix.c
++++ b/libarchive/archive_write_disk_posix.c
+@@ -1791,7 +1791,7 @@ edit_deep_directories(struct archive_write_disk *a)
+ char *tail = a->name;
+
+ /* If path is short, avoid the open() below. */
+- if (strlen(tail) <= PATH_MAX)
++ if (strlen(tail) < PATH_MAX)
+ return;
+
+ /* Try to record our starting dir. */
+@@ -1801,7 +1801,7 @@ edit_deep_directories(struct archive_write_disk *a)
+ return;
+
+ /* As long as the path is too long... */
+- while (strlen(tail) > PATH_MAX) {
++ while (strlen(tail) >= PATH_MAX) {
+ /* Locate a dir prefix shorter than PATH_MAX. */
+ tail += PATH_MAX - 8;
+ while (tail > a->name && *tail != '/')
+--
+2.2.1
+
+
+From f4a1e30c2c0947af8f492ed0f899098df137844e Mon Sep 17 00:00:00 2001
+From: Tim Kientzle <kientzle@acm.org>
+Date: Sun, 21 Aug 2016 17:11:45 -0700
+Subject: [PATCH 3/5] Issue #744 (part of Issue #743): Enforce sandbox with
+ very long pathnames
+
+Because check_symlinks is handled separately from the deep-directory
+support, very long pathnames cause problems. Previously, the code
+ignored most failures to lstat() a path component. In particular,
+this led to check_symlinks always passing for very long paths, which
+in turn provides a way to evade the symlink checks in the sandboxing
+code.
+
+We now fail on unrecognized lstat() failures, which plugs this
+hole at the cost of disabling deep directory support when the
+user requests sandboxing.
+
+TODO: This probably cannot be completely fixed without
+entirely reimplementing the deep directory support to
+integrate the symlink checks. I want to reimplement the
+deep directory hanlding someday anyway; openat() and
+related system calls now provide a much cleaner way to
+handle deep directories than the chdir approach used by this
+code.
+---
+ libarchive/archive_write_disk_posix.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
+index 024b1bd..040d031 100644
+--- a/libarchive/archive_write_disk_posix.c
++++ b/libarchive/archive_write_disk_posix.c
+@@ -2379,8 +2379,18 @@ check_symlinks(struct archive_write_disk *a)
+ r = lstat(a->name, &st);
+ if (r != 0) {
+ /* We've hit a dir that doesn't exist; stop now. */
+- if (errno == ENOENT)
++ if (errno == ENOENT) {
+ break;
++ } else {
++ /* Note: This effectively disables deep directory
++ * support when security checks are enabled.
++ * Otherwise, very long pathnames that trigger
++ * an error here could evade the sandbox.
++ * TODO: We could do better, but it would probably
++ * require merging the symlink checks with the
++ * deep-directory editing. */
++ return (ARCHIVE_FAILED);
++ }
+ } else if (S_ISLNK(st.st_mode)) {
+ if (c == '\0') {
+ /*
+--
+2.2.1
+
+
+From e5ab5d36fb6723cdc8e614bd477e4dff1a0aec59 Mon Sep 17 00:00:00 2001
+From: Andrew Gregory <andrew.gregory.8@gmail.com>
+Date: Fri, 15 Jan 2016 02:39:41 -0500
+Subject: [PATCH 4/5] Skip root directory symlink check
+
+The first time check_symlinks is called on an absolute path it will use
+the entry pathname directly, blanking the leading slash. This leads to
+calling lstat on an empty string, which returns ENOENT, terminating the
+loop early and falsely marking the path as safe.
+---
+ libarchive/archive_write_disk_posix.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
+index 040d031..47db454 100644
+--- a/libarchive/archive_write_disk_posix.c
++++ b/libarchive/archive_write_disk_posix.c
+@@ -2367,6 +2367,9 @@ check_symlinks(struct archive_write_disk *a)
+ while ((*pn != '\0') && (*p == *pn))
+ ++p, ++pn;
+ }
++ /* Skip the root directory if the path is absolute. */
++ if(pn == a->name && pn[0] == '/')
++ ++pn;
+ c = pn[0];
+ /* Keep going until we've checked the entire name. */
+ while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) {
+--
+2.2.1
+
+
+From cb56d88b7dd77f45db3779df65a8432a1238aa04 Mon Sep 17 00:00:00 2001
+From: Tim Kientzle <kientzle@acm.org>
+Date: Sun, 11 Sep 2016 13:21:57 -0700
+Subject: [PATCH 5/5] Fixes for Issue #745 and Issue #746 from Doran Moppert.
+
+---
+ libarchive/archive_write_disk_posix.c | 294 ++++++++++++++++++++++++++--------
+ 1 file changed, 227 insertions(+), 67 deletions(-)
+
+diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
+index 47db454..bfb12df 100644
+--- a/libarchive/archive_write_disk_posix.c
++++ b/libarchive/archive_write_disk_posix.c
+@@ -326,12 +326,14 @@ struct archive_write_disk {
+
+ #define HFS_BLOCKS(s) ((s) >> 12)
+
++static int check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags);
+ static int check_symlinks(struct archive_write_disk *);
+ static int create_filesystem_object(struct archive_write_disk *);
+ static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname);
+ #if defined(HAVE_FCHDIR) && defined(PATH_MAX)
+ static void edit_deep_directories(struct archive_write_disk *ad);
+ #endif
++static int cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags);
+ static int cleanup_pathname(struct archive_write_disk *);
+ static int create_dir(struct archive_write_disk *, char *);
+ static int create_parent_dir(struct archive_write_disk *, char *);
+@@ -1996,6 +1998,10 @@ create_filesystem_object(struct archive_write_disk *a)
+ const char *linkname;
+ mode_t final_mode, mode;
+ int r;
++ /* these for check_symlinks_fsobj */
++ char *linkname_copy; /* non-const copy of linkname */
++ struct archive_string error_string;
++ int error_number;
+
+ /* We identify hard/symlinks according to the link names. */
+ /* Since link(2) and symlink(2) don't handle modes, we're done here. */
+@@ -2004,6 +2010,27 @@ create_filesystem_object(struct archive_write_disk *a)
+ #if !HAVE_LINK
+ return (EPERM);
+ #else
++ archive_string_init(&error_string);
++ linkname_copy = strdup(linkname);
++ if (linkname_copy == NULL) {
++ return (EPERM);
++ }
++ /* TODO: consider using the cleaned-up path as the link target? */
++ r = cleanup_pathname_fsobj(linkname_copy, &error_number, &error_string, a->flags);
++ if (r != ARCHIVE_OK) {
++ archive_set_error(&a->archive, error_number, "%s", error_string.s);
++ free(linkname_copy);
++ /* EPERM is more appropriate than error_number for our callers */
++ return (EPERM);
++ }
++ r = check_symlinks_fsobj(linkname_copy, &error_number, &error_string, a->flags);
++ if (r != ARCHIVE_OK) {
++ archive_set_error(&a->archive, error_number, "%s", error_string.s);
++ free(linkname_copy);
++ /* EPERM is more appropriate than error_number for our callers */
++ return (EPERM);
++ }
++ free(linkname_copy);
+ r = link(linkname, a->name) ? errno : 0;
+ /*
+ * New cpio and pax formats allow hardlink entries
+@@ -2343,115 +2370,228 @@ current_fixup(struct archive_write_disk *a, const char *pathname)
+ * recent paths.
+ */
+ /* TODO: Extend this to support symlinks on Windows Vista and later. */
++
++/*
++ * Checks the given path to see if any elements along it are symlinks. Returns
++ * ARCHIVE_OK if there are none, otherwise puts an error in errmsg.
++ */
+ static int
+-check_symlinks(struct archive_write_disk *a)
++check_symlinks_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags)
+ {
+ #if !defined(HAVE_LSTAT)
+ /* Platform doesn't have lstat, so we can't look for symlinks. */
+ (void)a; /* UNUSED */
++ (void)path; /* UNUSED */
++ (void)error_number; /* UNUSED */
++ (void)error_string; /* UNUSED */
++ (void)flags; /* UNUSED */
+ return (ARCHIVE_OK);
+ #else
+- char *pn;
++ int res = ARCHIVE_OK;
++ char *tail;
++ char *head;
++ int last;
+ char c;
+ int r;
+ struct stat st;
++ int restore_pwd;
++
++ /* Nothing to do here if name is empty */
++ if(path[0] == '\0')
++ return (ARCHIVE_OK);
+
+ /*
+ * Guard against symlink tricks. Reject any archive entry whose
+ * destination would be altered by a symlink.
++ *
++ * Walk the filename in chunks separated by '/'. For each segment:
++ * - if it doesn't exist, continue
++ * - if it's symlink, abort or remove it
++ * - if it's a directory and it's not the last chunk, cd into it
++ * As we go:
++ * head points to the current (relative) path
++ * tail points to the temporary \0 terminating the segment we're currently examining
++ * c holds what used to be in *tail
++ * last is 1 if this is the last tail
+ */
+- /* Whatever we checked last time doesn't need to be re-checked. */
+- pn = a->name;
+- if (archive_strlen(&(a->path_safe)) > 0) {
+- char *p = a->path_safe.s;
+- while ((*pn != '\0') && (*p == *pn))
+- ++p, ++pn;
+- }
++ restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
++ __archive_ensure_cloexec_flag(restore_pwd);
++ if (restore_pwd < 0)
++ return (ARCHIVE_FATAL);
++ head = path;
++ tail = path;
++ last = 0;
++ /* TODO: reintroduce a safe cache here? */
+ /* Skip the root directory if the path is absolute. */
+- if(pn == a->name && pn[0] == '/')
+- ++pn;
+- c = pn[0];
+- /* Keep going until we've checked the entire name. */
+- while (pn[0] != '\0' && (pn[0] != '/' || pn[1] != '\0')) {
++ if(tail == path && tail[0] == '/')
++ ++tail;
++ /* Keep going until we've checked the entire name.
++ * head, tail, path all alias the same string, which is
++ * temporarily zeroed at tail, so be careful restoring the
++ * stashed (c=tail[0]) for error messages.
++ * Exiting the loop with break is okay; continue is not.
++ */
++ while (!last) {
++ /* Skip the separator we just consumed, plus any adjacent ones */
++ while (*tail == '/')
++ ++tail;
+ /* Skip the next path element. */
+- while (*pn != '\0' && *pn != '/')
+- ++pn;
+- c = pn[0];
+- pn[0] = '\0';
++ while (*tail != '\0' && *tail != '/')
++ ++tail;
++ /* is this the last path component? */
++ last = (tail[0] == '\0') || (tail[0] == '/' && tail[1] == '\0');
++ /* temporarily truncate the string here */
++ c = tail[0];
++ tail[0] = '\0';
+ /* Check that we haven't hit a symlink. */
+- r = lstat(a->name, &st);
++ r = lstat(head, &st);
+ if (r != 0) {
++ tail[0] = c;
+ /* We've hit a dir that doesn't exist; stop now. */
+ if (errno == ENOENT) {
+ break;
+ } else {
+- /* Note: This effectively disables deep directory
++ /* Treat any other error as fatal - best to be paranoid here
++ * Note: This effectively disables deep directory
+ * support when security checks are enabled.
+ * Otherwise, very long pathnames that trigger
+ * an error here could evade the sandbox.
+ * TODO: We could do better, but it would probably
+ * require merging the symlink checks with the
+ * deep-directory editing. */
+- return (ARCHIVE_FAILED);
++ if (error_number) *error_number = errno;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Could not stat %s",
++ path);
++ res = ARCHIVE_FAILED;
++ break;
++ }
++ } else if (S_ISDIR(st.st_mode)) {
++ if (!last) {
++ if (chdir(head) != 0) {
++ tail[0] = c;
++ if (error_number) *error_number = errno;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Could not chdir %s",
++ path);
++ res = (ARCHIVE_FATAL);
++ break;
++ }
++ /* Our view is now from inside this dir: */
++ head = tail + 1;
+ }
+ } else if (S_ISLNK(st.st_mode)) {
+- if (c == '\0') {
++ if (last) {
+ /*
+ * Last element is symlink; remove it
+ * so we can overwrite it with the
+ * item being extracted.
+ */
+- if (unlink(a->name)) {
+- archive_set_error(&a->archive, errno,
+- "Could not remove symlink %s",
+- a->name);
+- pn[0] = c;
+- return (ARCHIVE_FAILED);
++ if (unlink(head)) {
++ tail[0] = c;
++ if (error_number) *error_number = errno;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Could not remove symlink %s",
++ path);
++ res = ARCHIVE_FAILED;
++ break;
+ }
+- a->pst = NULL;
+ /*
+ * Even if we did remove it, a warning
+ * is in order. The warning is silly,
+ * though, if we're just replacing one
+ * symlink with another symlink.
+ */
+- if (!S_ISLNK(a->mode)) {
+- archive_set_error(&a->archive, 0,
+- "Removing symlink %s",
+- a->name);
++ tail[0] = c;
++ /* FIXME: not sure how important this is to restore
++ if (!S_ISLNK(path)) {
++ if (error_number) *error_number = 0;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Removing symlink %s",
++ path);
+ }
++ */
+ /* Symlink gone. No more problem! */
+- pn[0] = c;
+- return (0);
+- } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
++ res = ARCHIVE_OK;
++ break;
++ } else if (flags & ARCHIVE_EXTRACT_UNLINK) {
+ /* User asked us to remove problems. */
+- if (unlink(a->name) != 0) {
+- archive_set_error(&a->archive, 0,
+- "Cannot remove intervening symlink %s",
+- a->name);
+- pn[0] = c;
+- return (ARCHIVE_FAILED);
++ if (unlink(head) != 0) {
++ tail[0] = c;
++ if (error_number) *error_number = 0;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Cannot remove intervening symlink %s",
++ path);
++ res = ARCHIVE_FAILED;
++ break;
+ }
+- a->pst = NULL;
++ tail[0] = c;
+ } else {
+- archive_set_error(&a->archive, 0,
+- "Cannot extract through symlink %s",
+- a->name);
+- pn[0] = c;
+- return (ARCHIVE_FAILED);
++ tail[0] = c;
++ if (error_number) *error_number = 0;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Cannot extract through symlink %s",
++ path);
++ res = ARCHIVE_FAILED;
++ break;
+ }
+ }
+- pn[0] = c;
+- if (pn[0] != '\0')
+- pn++; /* Advance to the next segment. */
++ /* be sure to always maintain this */
++ tail[0] = c;
++ if (tail[0] != '\0')
++ tail++; /* Advance to the next segment. */
+ }
+- pn[0] = c;
+- /* We've checked and/or cleaned the whole path, so remember it. */
+- archive_strcpy(&a->path_safe, a->name);
+- return (ARCHIVE_OK);
++ /* Catches loop exits via break */
++ tail[0] = c;
++#ifdef HAVE_FCHDIR
++ /* If we changed directory above, restore it here. */
++ if (restore_pwd >= 0) {
++ r = fchdir(restore_pwd);
++ if (r != 0) {
++ if(error_number) *error_number = errno;
++ if(error_string)
++ archive_string_sprintf(error_string,
++ "chdir() failure");
++ }
++ close(restore_pwd);
++ restore_pwd = -1;
++ if (r != 0) {
++ res = (ARCHIVE_FATAL);
++ }
++ }
++#endif
++ /* TODO: reintroduce a safe cache here? */
++ return res;
+ #endif
+ }
+
++/*
++ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwise
++ * calls archive_set_error and returns ARCHIVE_{FATAL,FAILED}
++ */
++static int
++check_symlinks(struct archive_write_disk *a)
++{
++ struct archive_string error_string;
++ int error_number;
++ int rc;
++ archive_string_init(&error_string);
++ rc = check_symlinks_fsobj(a->name, &error_number, &error_string, a->flags);
++ if (rc != ARCHIVE_OK) {
++ archive_set_error(&a->archive, error_number, "%s", error_string.s);
++ }
++ archive_string_free(&error_string);
++ a->pst = NULL; /* to be safe */
++ return rc;
++}
++
++
+ #if defined(__CYGWIN__)
+ /*
+ * 1. Convert a path separator from '\' to '/' .
+@@ -2525,15 +2665,17 @@ cleanup_pathname_win(struct archive_write_disk *a)
+ * is set) if the path is absolute.
+ */
+ static int
+-cleanup_pathname(struct archive_write_disk *a)
++cleanup_pathname_fsobj(char *path, int *error_number, struct archive_string *error_string, int flags)
+ {
+ char *dest, *src;
+ char separator = '\0';
+
+- dest = src = a->name;
++ dest = src = path;
+ if (*src == '\0') {
+- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+- "Invalid empty pathname");
++ if (error_number) *error_number = ARCHIVE_ERRNO_MISC;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Invalid empty pathname");
+ return (ARCHIVE_FAILED);
+ }
+
+@@ -2542,9 +2684,11 @@ cleanup_pathname(struct archive_write_disk *a)
+ #endif
+ /* Skip leading '/'. */
+ if (*src == '/') {
+- if (a->flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
+- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+- "Path is absolute");
++ if (flags & ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS) {
++ if (error_number) *error_number = ARCHIVE_ERRNO_MISC;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Path is absolute");
+ return (ARCHIVE_FAILED);
+ }
+
+@@ -2571,10 +2715,11 @@ cleanup_pathname(struct archive_write_disk *a)
+ } else if (src[1] == '.') {
+ if (src[2] == '/' || src[2] == '\0') {
+ /* Conditionally warn about '..' */
+- if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
+- archive_set_error(&a->archive,
+- ARCHIVE_ERRNO_MISC,
+- "Path contains '..'");
++ if (flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) {
++ if (error_number) *error_number = ARCHIVE_ERRNO_MISC;
++ if (error_string)
++ archive_string_sprintf(error_string,
++ "Path contains '..'");
+ return (ARCHIVE_FAILED);
+ }
+ }
+@@ -2605,7 +2750,7 @@ cleanup_pathname(struct archive_write_disk *a)
+ * We've just copied zero or more path elements, not including the
+ * final '/'.
+ */
+- if (dest == a->name) {
++ if (dest == path) {
+ /*
+ * Nothing got copied. The path must have been something
+ * like '.' or '/' or './' or '/././././/./'.
+@@ -2620,6 +2765,21 @@ cleanup_pathname(struct archive_write_disk *a)
+ return (ARCHIVE_OK);
+ }
+
++static int
++cleanup_pathname(struct archive_write_disk *a)
++{
++ struct archive_string error_string;
++ int error_number;
++ int rc;
++ archive_string_init(&error_string);
++ rc = cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->flags);
++ if (rc != ARCHIVE_OK) {
++ archive_set_error(&a->archive, error_number, "%s", error_string.s);
++ }
++ archive_string_free(&error_string);
++ return rc;
++}
++
+ /*
+ * Create the parent directory of the specified path, assuming path
+ * is already in mutable storage.
+--
+2.2.1
+
diff --git a/main/libarchive/CVE-2016-7166.patch b/main/libarchive/CVE-2016-7166.patch
new file mode 100644
index 0000000000..27ed28612e
--- /dev/null
+++ b/main/libarchive/CVE-2016-7166.patch
@@ -0,0 +1,56 @@
+From 6e06b1c89dd0d16f74894eac4cfc1327a06ee4a0 Mon Sep 17 00:00:00 2001
+From: Tim Kientzle <kientzle@acm.org>
+Date: Sat, 10 Jan 2015 12:24:58 -0800
+Subject: [PATCH] Fix a potential crash issue discovered by Alexander
+ Cherepanov:
+
+It seems bsdtar automatically handles stacked compression. This is a
+nice feature but it could be problematic when it's completely
+unlimited. Most clearly it's illustrated with quines:
+
+$ curl -sRO http://www.maximumcompression.com/selfgz.gz
+$ (ulimit -v 10000000 && bsdtar -tvf selfgz.gz)
+bsdtar: Error opening archive: Can't allocate data for gzip decompression
+
+Without ulimit, bsdtar will eat all available memory. This could also
+be a problem for other applications using libarchive.
+---
+ Makefile.am | 2 ++
+ libarchive/archive_read.c | 7 ++--
+ libarchive/test/CMakeLists.txt | 1 +
+ libarchive/test/test_read_too_many_filters.c | 45 ++++++++++++++++++++++++
+ libarchive/test/test_read_too_many_filters.gz.uu | 15 ++++++++
+ 5 files changed, 68 insertions(+), 2 deletions(-)
+ create mode 100644 libarchive/test/test_read_too_many_filters.c
+ create mode 100644 libarchive/test/test_read_too_many_filters.gz.uu
+
+diff --git a/libarchive/archive_read.c b/libarchive/archive_read.c
+index 02bf8d3..8f71a8b 100644
+--- a/libarchive/archive_read.c
++++ b/libarchive/archive_read.c
+@@ -548,13 +548,13 @@ archive_read_open1(struct archive *_a)
+ static int
+ choose_filters(struct archive_read *a)
+ {
+- int number_bidders, i, bid, best_bid;
++ int number_bidders, i, bid, best_bid, n;
+ struct archive_read_filter_bidder *bidder, *best_bidder;
+ struct archive_read_filter *filter;
+ ssize_t avail;
+ int r;
+
+- for (;;) {
++ for (n = 0; n < 25; ++n) {
+ number_bidders = sizeof(a->bidders) / sizeof(a->bidders[0]);
+
+ best_bid = 0;
+@@ -600,6 +600,9 @@ choose_filters(struct archive_read *a)
+ return (ARCHIVE_FATAL);
+ }
+ }
++ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
++ "Input requires too many filters for decoding");
++ return (ARCHIVE_FATAL);
+ }
+
+ /*