gnu: libarchive: Update to 3.2.2.
* gnu/packages/backup.scm (libarchive): Update to 3.2.2. [source]: Remove obsolete patches. * gnu/packages/patches/libarchive-7zip-heap-overflow.patch, gnu/packages/patches/libarchive-fix-filesystem-attacks.patch, gnu/packages/patches/libarchive-fix-symlink-check.patch, gnu/packages/patches/libarchive-safe_fprintf-buffer-overflow.patch: Delete files. * gnu/local.mk (dist_patch_DATA): Remove them.master
parent
e673a1df29
commit
f265a3cee5
|
@ -647,10 +647,6 @@ dist_patch_DATA = \
|
||||||
%D%/packages/patches/liba52-link-with-libm.patch \
|
%D%/packages/patches/liba52-link-with-libm.patch \
|
||||||
%D%/packages/patches/liba52-set-soname.patch \
|
%D%/packages/patches/liba52-set-soname.patch \
|
||||||
%D%/packages/patches/liba52-use-mtune-not-mcpu.patch \
|
%D%/packages/patches/liba52-use-mtune-not-mcpu.patch \
|
||||||
%D%/packages/patches/libarchive-7zip-heap-overflow.patch \
|
|
||||||
%D%/packages/patches/libarchive-fix-symlink-check.patch \
|
|
||||||
%D%/packages/patches/libarchive-fix-filesystem-attacks.patch \
|
|
||||||
%D%/packages/patches/libarchive-safe_fprintf-buffer-overflow.patch \
|
|
||||||
%D%/packages/patches/libbonobo-activation-test-race.patch \
|
%D%/packages/patches/libbonobo-activation-test-race.patch \
|
||||||
%D%/packages/patches/libcanberra-sound-theme-freedesktop.patch \
|
%D%/packages/patches/libcanberra-sound-theme-freedesktop.patch \
|
||||||
%D%/packages/patches/libcmis-fix-test-onedrive.patch \
|
%D%/packages/patches/libcmis-fix-test-onedrive.patch \
|
||||||
|
|
|
@ -172,20 +172,15 @@ backups (called chunks) to allow easy burning to CD/DVD.")
|
||||||
(define-public libarchive
|
(define-public libarchive
|
||||||
(package
|
(package
|
||||||
(name "libarchive")
|
(name "libarchive")
|
||||||
(version "3.2.1")
|
(version "3.2.2")
|
||||||
(source
|
(source
|
||||||
(origin
|
(origin
|
||||||
(method url-fetch)
|
(method url-fetch)
|
||||||
(uri (string-append "http://libarchive.org/downloads/libarchive-"
|
(uri (string-append "http://libarchive.org/downloads/libarchive-"
|
||||||
version ".tar.gz"))
|
version ".tar.gz"))
|
||||||
(patches (search-patches
|
|
||||||
"libarchive-7zip-heap-overflow.patch"
|
|
||||||
"libarchive-fix-symlink-check.patch"
|
|
||||||
"libarchive-fix-filesystem-attacks.patch"
|
|
||||||
"libarchive-safe_fprintf-buffer-overflow.patch"))
|
|
||||||
(sha256
|
(sha256
|
||||||
(base32
|
(base32
|
||||||
"1lngng84k1kkljl74q0cdqc3s82vn2kimfm02dgm4d6m7x71mvkj"))))
|
"03q6y428rg723c9fj1vidzjw46w1vf8z0h95lkvz1l9jw571j739"))))
|
||||||
(build-system gnu-build-system)
|
(build-system gnu-build-system)
|
||||||
;; TODO: Add -L/path/to/nettle in libarchive.pc.
|
;; TODO: Add -L/path/to/nettle in libarchive.pc.
|
||||||
(inputs
|
(inputs
|
||||||
|
|
|
@ -1,77 +0,0 @@
|
||||||
Fix buffer overflow reading 7Zip files:
|
|
||||||
|
|
||||||
https://github.com/libarchive/libarchive/issues/761
|
|
||||||
|
|
||||||
Patch copied from upstream repository:
|
|
||||||
|
|
||||||
https://github.com/libarchive/libarchive/commit/7f17c791dcfd8c0416e2cd2485b19410e47ef126
|
|
||||||
|
|
||||||
From 7f17c791dcfd8c0416e2cd2485b19410e47ef126 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Tim Kientzle <kientzle@acm.org>
|
|
||||||
Date: Sun, 18 Sep 2016 18:14:58 -0700
|
|
||||||
Subject: [PATCH] Issue 761: Heap overflow reading corrupted 7Zip files
|
|
||||||
|
|
||||||
The sample file that demonstrated this had multiple 'EmptyStream'
|
|
||||||
attributes. The first one ended up being used to calculate
|
|
||||||
certain statistics, then was overwritten by the second which
|
|
||||||
was incompatible with those statistics.
|
|
||||||
|
|
||||||
The fix here is to reject any header with multiple EmptyStream
|
|
||||||
attributes. While here, also reject headers with multiple
|
|
||||||
EmptyFile, AntiFile, Name, or Attributes markers.
|
|
||||||
---
|
|
||||||
libarchive/archive_read_support_format_7zip.c | 10 ++++++++++
|
|
||||||
1 file changed, 10 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c
|
|
||||||
index 1dfe52b..c0a536c 100644
|
|
||||||
--- a/libarchive/archive_read_support_format_7zip.c
|
|
||||||
+++ b/libarchive/archive_read_support_format_7zip.c
|
|
||||||
@@ -2431,6 +2431,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case kEmptyStream:
|
|
||||||
+ if (h->emptyStreamBools != NULL)
|
|
||||||
+ return (-1);
|
|
||||||
h->emptyStreamBools = calloc((size_t)zip->numFiles,
|
|
||||||
sizeof(*h->emptyStreamBools));
|
|
||||||
if (h->emptyStreamBools == NULL)
|
|
||||||
@@ -2451,6 +2453,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
|
|
||||||
return (-1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
+ if (h->emptyFileBools != NULL)
|
|
||||||
+ return (-1);
|
|
||||||
h->emptyFileBools = calloc(empty_streams,
|
|
||||||
sizeof(*h->emptyFileBools));
|
|
||||||
if (h->emptyFileBools == NULL)
|
|
||||||
@@ -2465,6 +2469,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
|
|
||||||
return (-1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
+ if (h->antiBools != NULL)
|
|
||||||
+ return (-1);
|
|
||||||
h->antiBools = calloc(empty_streams,
|
|
||||||
sizeof(*h->antiBools));
|
|
||||||
if (h->antiBools == NULL)
|
|
||||||
@@ -2491,6 +2497,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
|
|
||||||
if ((ll & 1) || ll < zip->numFiles * 4)
|
|
||||||
return (-1);
|
|
||||||
|
|
||||||
+ if (zip->entry_names != NULL)
|
|
||||||
+ return (-1);
|
|
||||||
zip->entry_names = malloc(ll);
|
|
||||||
if (zip->entry_names == NULL)
|
|
||||||
return (-1);
|
|
||||||
@@ -2543,6 +2551,8 @@ read_Header(struct archive_read *a, struct _7z_header_info *h,
|
|
||||||
if ((p = header_bytes(a, 2)) == NULL)
|
|
||||||
return (-1);
|
|
||||||
allAreDefined = *p;
|
|
||||||
+ if (h->attrBools != NULL)
|
|
||||||
+ return (-1);
|
|
||||||
h->attrBools = calloc((size_t)zip->numFiles,
|
|
||||||
sizeof(*h->attrBools));
|
|
||||||
if (h->attrBools == NULL)
|
|
||||||
--
|
|
||||||
2.10.0
|
|
||||||
|
|
|
@ -1,445 +0,0 @@
|
||||||
This patch fixes two bugs that allow attackers to overwrite or change
|
|
||||||
the permissions of arbitrary files:
|
|
||||||
|
|
||||||
https://github.com/libarchive/libarchive/issues/745
|
|
||||||
https://github.com/libarchive/libarchive/issues/746
|
|
||||||
|
|
||||||
Patch copied from upstream repository:
|
|
||||||
|
|
||||||
https://github.com/libarchive/libarchive/commit/dfd6b54ce33960e420fb206d8872fb759b577ad9
|
|
||||||
|
|
||||||
From dfd6b54ce33960e420fb206d8872fb759b577ad9 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Tim Kientzle <kientzle@acm.org>
|
|
||||||
Date: Sun, 11 Sep 2016 13:21:57 -0700
|
|
||||||
Subject: [PATCH] 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 8f0421e..abe1a86 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 *);
|
|
||||||
@@ -2014,6 +2016,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. */
|
|
||||||
@@ -2022,6 +2028,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
|
|
||||||
@@ -2362,115 +2389,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 '/' .
|
|
||||||
@@ -2544,15 +2684,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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -2561,9 +2703,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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -2590,10 +2734,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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2624,7 +2769,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 '/././././/./'.
|
|
||||||
@@ -2639,6 +2784,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.
|
|
|
@ -1,60 +0,0 @@
|
||||||
Make sure to check for symlinks even if the pathname is very long:
|
|
||||||
|
|
||||||
https://github.com/libarchive/libarchive/issues/744
|
|
||||||
|
|
||||||
Patch copied from upstream repository:
|
|
||||||
|
|
||||||
https://github.com/libarchive/libarchive/commit/1fa9c7bf90f0862036a99896b0501c381584451a
|
|
||||||
|
|
||||||
From 1fa9c7bf90f0862036a99896b0501c381584451a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Tim Kientzle <kientzle@acm.org>
|
|
||||||
Date: Sun, 21 Aug 2016 17:11:45 -0700
|
|
||||||
Subject: [PATCH] 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 39ee3b6..8f0421e 100644
|
|
||||||
--- a/libarchive/archive_write_disk_posix.c
|
|
||||||
+++ b/libarchive/archive_write_disk_posix.c
|
|
||||||
@@ -2401,8 +2401,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') {
|
|
||||||
/*
|
|
|
@ -1,44 +0,0 @@
|
||||||
Fixes this buffer overflow:
|
|
||||||
https://github.com/libarchive/libarchive/issues/767
|
|
||||||
|
|
||||||
Patch copied from upstream source repository:
|
|
||||||
https://github.com/libarchive/libarchive/commit/e37b620fe8f14535d737e89a4dcabaed4517bf1a
|
|
||||||
|
|
||||||
From e37b620fe8f14535d737e89a4dcabaed4517bf1a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Tim Kientzle <kientzle@acm.org>
|
|
||||||
Date: Sun, 21 Aug 2016 10:51:43 -0700
|
|
||||||
Subject: [PATCH] Issue #767: Buffer overflow printing a filename
|
|
||||||
|
|
||||||
The safe_fprintf function attempts to ensure clean output for an
|
|
||||||
arbitrary sequence of bytes by doing a trial conversion of the
|
|
||||||
multibyte characters to wide characters -- if the resulting wide
|
|
||||||
character is printable then we pass through the corresponding bytes
|
|
||||||
unaltered, otherwise, we convert them to C-style ASCII escapes.
|
|
||||||
|
|
||||||
The stack trace in Issue #767 suggest that the 20-byte buffer
|
|
||||||
was getting overflowed trying to format a non-printable multibyte
|
|
||||||
character. This should only happen if there is a valid multibyte
|
|
||||||
character of more than 5 bytes that was unprintable. (Each byte
|
|
||||||
would get expanded to a four-charcter octal-style escape of the form
|
|
||||||
"\123" resulting in >20 characters for the >5 byte multibyte character.)
|
|
||||||
|
|
||||||
I've not been able to reproduce this, but have expanded the conversion
|
|
||||||
buffer to 128 bytes on the belief that no multibyte character set
|
|
||||||
has a single character of more than 32 bytes.
|
|
||||||
---
|
|
||||||
tar/util.c | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/tar/util.c b/tar/util.c
|
|
||||||
index 9ff22f2..2b4aebe 100644
|
|
||||||
--- a/tar/util.c
|
|
||||||
+++ b/tar/util.c
|
|
||||||
@@ -182,7 +182,7 @@ safe_fprintf(FILE *f, const char *fmt, ...)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If our output buffer is full, dump it and keep going. */
|
|
||||||
- if (i > (sizeof(outbuff) - 20)) {
|
|
||||||
+ if (i > (sizeof(outbuff) - 128)) {
|
|
||||||
outbuff[i] = '\0';
|
|
||||||
fprintf(f, "%s", outbuff);
|
|
||||||
i = 0;
|
|
Reference in New Issue