From mboxrd@z Thu Jan 1 00:00:00 1970 From: Leo Famulari Subject: libarchive security fixes (was Re: Core-updates timeline) Date: Sun, 2 Oct 2016 16:14:04 -0400 Message-ID: <20161002201404.GA9126@jasmine> References: <20160920045607.18936-1-donttrustben@gmail.com> <20160920045607.18936-3-donttrustben@gmail.com> <87fuopriox.fsf@gnu.org> <61f55931-6fd2-2fd1-9f61-e52b7302d3b8@uq.edu.au> <8760pci4pv.fsf@gnu.org> <20161001164049.GD1499@jasmine> <87mvimg9al.fsf@gnu.org> <20161002185034.GA32485@jasmine> Mime-Version: 1.0 Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="XOIedfhf+7KOe/yw" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:33257) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bqn9U-0002j5-Ct for guix-devel@gnu.org; Sun, 02 Oct 2016 16:14:27 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bqn9P-0002GB-O4 for guix-devel@gnu.org; Sun, 02 Oct 2016 16:14:23 -0400 Content-Disposition: inline In-Reply-To: <20161002185034.GA32485@jasmine> List-Id: "Development of GNU Guix and the GNU System distribution." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guix-devel-bounces+gcggd-guix-devel=m.gmane.org@gnu.org Sender: "Guix-devel" To: Ludovic =?iso-8859-1?Q?Court=E8s?= Cc: guix-devel@gnu.org --XOIedfhf+7KOe/yw Content-Type: multipart/mixed; boundary="huq684BweRXVnRxX" Content-Disposition: inline --huq684BweRXVnRxX Content-Type: text/plain; charset=utf-8 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On Sun, Oct 02, 2016 at 02:50:34PM -0400, Leo Famulari wrote: > On Sun, Oct 02, 2016 at 03:38:58PM +0200, Ludovic Court=C3=A8s wrote: > > We could wait an additional day for libarchive if it=E2=80=99s more con= venient, > > but maybe not longer than that. > >=20 > > What do you think would be the most convenient approach? >=20 > I will send a patch that cherry-picks what I think are the most > important bug fixes. I can't guess when libarchive 3.2.2 will be > released. I've attached a patch. It cherry-picks some fixes for some filesystem attacks and two overflows that can be triggered with "crafted" input. The details are in the patch files. I understand if this approach of cherry-picking a handful of commits is not acceptable. It's hard to judge the full impact of taking only these changes, some of which a quite significant, without being familiar with the libarchive code. That's the reason why I've been waiting for a new upstream release. But I figured I should at least try to get these bug fixes into the next release of Guix :) --huq684BweRXVnRxX Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="0001-gnu-libarchive-Fix-several-security-issues.patch" Content-Transfer-Encoding: quoted-printable =46rom 042d5a7df4962c3b81fbfefa0027b6f1cf356b5f Mon Sep 17 00:00:00 2001 =46rom: Leo Famulari Date: Sun, 2 Oct 2016 15:58:06 -0400 Subject: [PATCH] gnu: libarchive: Fix several security issues. * gnu/packages/backup.scm (libarchive)[replacement]: New field. (libarchive/fixed): New variable. * gnu/packages/patches/libarchive-7zip-heap-overflow.patch, gnu/packages/patches/libarchive-fix-symlink-check.patch, gnu/packages/patches/libarchive-fix-filesystem-attacks.patch, gnu/packages/patches/libarchive-safe_fprintf-buffer-overflow.patch: New fil= es. * gnu/local.mk (dist_patch_DATA): Add them. --- gnu/local.mk | 4 + gnu/packages/backup.scm | 12 + .../patches/libarchive-7zip-heap-overflow.patch | 77 ++++ .../libarchive-fix-filesystem-attacks.patch | 445 +++++++++++++++++= ++++ .../libarchive-safe_fprintf-buffer-overflow.patch | 44 ++ 5 files changed, 582 insertions(+) create mode 100644 gnu/packages/patches/libarchive-7zip-heap-overflow.patch create mode 100644 gnu/packages/patches/libarchive-fix-filesystem-attacks.= patch create mode 100644 gnu/packages/patches/libarchive-safe_fprintf-buffer-ove= rflow.patch diff --git a/gnu/local.mk b/gnu/local.mk index 4260a92..02cd680 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -622,6 +622,10 @@ dist_patch_DATA =3D \ %D%/packages/patches/liba52-link-with-libm.patch \ %D%/packages/patches/liba52-set-soname.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/libcanberra-sound-theme-freedesktop.patch \ %D%/packages/patches/libcmis-fix-test-onedrive.patch \ diff --git a/gnu/packages/backup.scm b/gnu/packages/backup.scm index c6f1321..797c06e 100644 --- a/gnu/packages/backup.scm +++ b/gnu/packages/backup.scm @@ -172,6 +172,7 @@ backups (called chunks) to allow easy burning to CD/DVD= =2E") (define-public libarchive (package (name "libarchive") + (replacement libarchive/fixed) (version "3.2.1") (source (origin @@ -227,6 +228,17 @@ archive. In particular, note that there is currently = no built-in support for random access nor for in-place modification.") (license license:bsd-2))) =20 +(define libarchive/fixed + (package + (inherit libarchive) + (source (origin + (inherit (package-source libarchive)) + (patches (search-patches + "libarchive-7zip-heap-overflow.patch" + "libarchive-fix-symlink-check.patch" + "libarchive-fix-filesystem-attacks.patch" + "libarchive-safe_fprintf-buffer-overflow.patch"))= )))) + (define-public rdup (package (name "rdup") diff --git a/gnu/packages/patches/libarchive-7zip-heap-overflow.patch b/gnu= /packages/patches/libarchive-7zip-heap-overflow.patch new file mode 100644 index 0000000..bef628f --- /dev/null +++ b/gnu/packages/patches/libarchive-7zip-heap-overflow.patch @@ -0,0 +1,77 @@ +Fix buffer overflow reading 7Zip files: + +https://github.com/libarchive/libarchive/issues/761 + +Patch copied from upstream repository: + +https://github.com/libarchive/libarchive/commit/7f17c791dcfd8c0416e2cd2485= b19410e47ef126 + +From 7f17c791dcfd8c0416e2cd2485b19410e47ef126 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +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/ar= chive_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_heade= r_info *h, +=20 + switch (type) { + case kEmptyStream: ++ if (h->emptyStreamBools !=3D NULL) ++ return (-1); + h->emptyStreamBools =3D calloc((size_t)zip->numFiles, + sizeof(*h->emptyStreamBools)); + if (h->emptyStreamBools =3D=3D NULL) +@@ -2451,6 +2453,8 @@ read_Header(struct archive_read *a, struct _7z_heade= r_info *h, + return (-1); + break; + } ++ if (h->emptyFileBools !=3D NULL) ++ return (-1); + h->emptyFileBools =3D calloc(empty_streams, + sizeof(*h->emptyFileBools)); + if (h->emptyFileBools =3D=3D NULL) +@@ -2465,6 +2469,8 @@ read_Header(struct archive_read *a, struct _7z_heade= r_info *h, + return (-1); + break; + } ++ if (h->antiBools !=3D NULL) ++ return (-1); + h->antiBools =3D calloc(empty_streams, + sizeof(*h->antiBools)); + if (h->antiBools =3D=3D NULL) +@@ -2491,6 +2497,8 @@ read_Header(struct archive_read *a, struct _7z_heade= r_info *h, + if ((ll & 1) || ll < zip->numFiles * 4) + return (-1); +=20 ++ if (zip->entry_names !=3D NULL) ++ return (-1); + zip->entry_names =3D malloc(ll); + if (zip->entry_names =3D=3D NULL) + return (-1); +@@ -2543,6 +2551,8 @@ read_Header(struct archive_read *a, struct _7z_heade= r_info *h, + if ((p =3D header_bytes(a, 2)) =3D=3D NULL) + return (-1); + allAreDefined =3D *p; ++ if (h->attrBools !=3D NULL) ++ return (-1); + h->attrBools =3D calloc((size_t)zip->numFiles, + sizeof(*h->attrBools)); + if (h->attrBools =3D=3D NULL) +--=20 +2.10.0 + diff --git a/gnu/packages/patches/libarchive-fix-filesystem-attacks.patch b= /gnu/packages/patches/libarchive-fix-filesystem-attacks.patch new file mode 100644 index 0000000..bce63d5 --- /dev/null +++ b/gnu/packages/patches/libarchive-fix-filesystem-attacks.patch @@ -0,0 +1,445 @@ +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/dfd6b54ce33960e420fb206d88= 72fb759b577ad9 + +From dfd6b54ce33960e420fb206d8872fb759b577ad9 Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +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_wr= ite_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 { +=20 + #define HFS_BLOCKS(s) ((s) >> 12) +=20 ++static int check_symlinks_fsobj(char *path, int *error_number, struct arc= hive_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 *, con= st 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 a= rchive_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; +=20 + /* 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 =3D strdup(linkname); ++ if (linkname_copy =3D=3D NULL) { ++ return (EPERM); ++ } ++ /* TODO: consider using the cleaned-up path as the link target? */ ++ r =3D cleanup_pathname_fsobj(linkname_copy, &error_number, &error_strin= g, a->flags); ++ if (r !=3D 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 =3D check_symlinks_fsobj(linkname_copy, &error_number, &error_string,= a->flags); ++ if (r !=3D 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 =3D 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, cons= t 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. R= eturns ++ * 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 =3D 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] =3D=3D '\0') ++ return (ARCHIVE_OK); +=20 + /* + * 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 curren= tly 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 =3D a->name; +- if (archive_strlen(&(a->path_safe)) > 0) { +- char *p =3D a->path_safe.s; +- while ((*pn !=3D '\0') && (*p =3D=3D *pn)) +- ++p, ++pn; +- } ++ restore_pwd =3D open(".", O_RDONLY | O_BINARY | O_CLOEXEC); ++ __archive_ensure_cloexec_flag(restore_pwd); ++ if (restore_pwd < 0) ++ return (ARCHIVE_FATAL); ++ head =3D path; ++ tail =3D path; ++ last =3D 0; ++ /* TODO: reintroduce a safe cache here? */ + /* Skip the root directory if the path is absolute. */ +- if(pn =3D=3D a->name && pn[0] =3D=3D '/') +- ++pn; +- c =3D pn[0]; +- /* Keep going until we've checked the entire name. */ +- while (pn[0] !=3D '\0' && (pn[0] !=3D '/' || pn[1] !=3D '\0')) { ++ if(tail =3D=3D path && tail[0] =3D=3D '/') ++ ++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=3Dtail[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 =3D=3D '/') ++ ++tail; + /* Skip the next path element. */ +- while (*pn !=3D '\0' && *pn !=3D '/') +- ++pn; +- c =3D pn[0]; +- pn[0] =3D '\0'; ++ while (*tail !=3D '\0' && *tail !=3D '/') ++ ++tail; ++ /* is this the last path component? */ ++ last =3D (tail[0] =3D=3D '\0') || (tail[0] =3D=3D '/' && tail[1] =3D=3D= '\0'); ++ /* temporarily truncate the string here */ ++ c =3D tail[0]; ++ tail[0] =3D '\0'; + /* Check that we haven't hit a symlink. */ +- r =3D lstat(a->name, &st); ++ r =3D lstat(head, &st); + if (r !=3D 0) { ++ tail[0] =3D c; + /* We've hit a dir that doesn't exist; stop now. */ + if (errno =3D=3D 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 =3D errno; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Could not stat %s", ++ path); ++ res =3D ARCHIVE_FAILED; ++ break; ++ } ++ } else if (S_ISDIR(st.st_mode)) { ++ if (!last) { ++ if (chdir(head) !=3D 0) { ++ tail[0] =3D c; ++ if (error_number) *error_number =3D errno; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Could not chdir %s", ++ path); ++ res =3D (ARCHIVE_FATAL); ++ break; ++ } ++ /* Our view is now from inside this dir: */ ++ head =3D tail + 1; + } + } else if (S_ISLNK(st.st_mode)) { +- if (c =3D=3D '\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] =3D c; +- return (ARCHIVE_FAILED); ++ if (unlink(head)) { ++ tail[0] =3D c; ++ if (error_number) *error_number =3D errno; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Could not remove symlink %s", ++ path); ++ res =3D ARCHIVE_FAILED; ++ break; + } +- a->pst =3D 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] =3D c; ++ /* FIXME: not sure how important this is to restore ++ if (!S_ISLNK(path)) { ++ if (error_number) *error_number =3D 0; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Removing symlink %s", ++ path); + } ++ */ + /* Symlink gone. No more problem! */ +- pn[0] =3D c; +- return (0); +- } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { ++ res =3D ARCHIVE_OK; ++ break; ++ } else if (flags & ARCHIVE_EXTRACT_UNLINK) { + /* User asked us to remove problems. */ +- if (unlink(a->name) !=3D 0) { +- archive_set_error(&a->archive, 0, +- "Cannot remove intervening symlink %s", +- a->name); +- pn[0] =3D c; +- return (ARCHIVE_FAILED); ++ if (unlink(head) !=3D 0) { ++ tail[0] =3D c; ++ if (error_number) *error_number =3D 0; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Cannot remove intervening symlink %s", ++ path); ++ res =3D ARCHIVE_FAILED; ++ break; + } +- a->pst =3D NULL; ++ tail[0] =3D c; + } else { +- archive_set_error(&a->archive, 0, +- "Cannot extract through symlink %s", +- a->name); +- pn[0] =3D c; +- return (ARCHIVE_FAILED); ++ tail[0] =3D c; ++ if (error_number) *error_number =3D 0; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Cannot extract through symlink %s", ++ path); ++ res =3D ARCHIVE_FAILED; ++ break; + } + } +- pn[0] =3D c; +- if (pn[0] !=3D '\0') +- pn++; /* Advance to the next segment. */ ++ /* be sure to always maintain this */ ++ tail[0] =3D c; ++ if (tail[0] !=3D '\0') ++ tail++; /* Advance to the next segment. */ + } +- pn[0] =3D 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] =3D c; ++#ifdef HAVE_FCHDIR ++ /* If we changed directory above, restore it here. */ ++ if (restore_pwd >=3D 0) { ++ r =3D fchdir(restore_pwd); ++ if (r !=3D 0) { ++ if(error_number) *error_number =3D errno; ++ if(error_string) ++ archive_string_sprintf(error_string, ++ "chdir() failure"); ++ } ++ close(restore_pwd); ++ restore_pwd =3D -1; ++ if (r !=3D 0) { ++ res =3D (ARCHIVE_FATAL); ++ } ++ } ++#endif ++ /* TODO: reintroduce a safe cache here? */ ++ return res; + #endif + } +=20 ++/* ++ * Check a->name for symlinks, returning ARCHIVE_OK if its clean, otherwi= se ++ * 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 =3D check_symlinks_fsobj(a->name, &error_number, &error_string, a->fl= ags); ++ if (rc !=3D ARCHIVE_OK) { ++ archive_set_error(&a->archive, error_number, "%s", error_string.s); ++ } ++ archive_string_free(&error_string); ++ a->pst =3D 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_stri= ng *error_string, int flags) + { + char *dest, *src; + char separator =3D '\0'; +=20 +- dest =3D src =3D a->name; ++ dest =3D src =3D path; + if (*src =3D=3D '\0') { +- archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, +- "Invalid empty pathname"); ++ if (error_number) *error_number =3D ARCHIVE_ERRNO_MISC; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Invalid empty pathname"); + return (ARCHIVE_FAILED); + } +=20 +@@ -2561,9 +2703,11 @@ cleanup_pathname(struct archive_write_disk *a) + #endif + /* Skip leading '/'. */ + if (*src =3D=3D '/') { +- 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 =3D ARCHIVE_ERRNO_MISC; ++ if (error_string) ++ archive_string_sprintf(error_string, ++ "Path is absolute"); + return (ARCHIVE_FAILED); + } +=20 +@@ -2590,10 +2734,11 @@ cleanup_pathname(struct archive_write_disk *a) + } else if (src[1] =3D=3D '.') { + if (src[2] =3D=3D '/' || src[2] =3D=3D '\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 =3D 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 =3D=3D a->name) { ++ if (dest =3D=3D 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); + } +=20 ++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 =3D cleanup_pathname_fsobj(a->name, &error_number, &error_string, a->= flags); ++ if (rc !=3D 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. diff --git a/gnu/packages/patches/libarchive-safe_fprintf-buffer-overflow.p= atch b/gnu/packages/patches/libarchive-safe_fprintf-buffer-overflow.patch new file mode 100644 index 0000000..0e70ac9 --- /dev/null +++ b/gnu/packages/patches/libarchive-safe_fprintf-buffer-overflow.patch @@ -0,0 +1,44 @@ +Fixes this buffer overflow: +https://github.com/libarchive/libarchive/commit/e37b620fe8f14535d737e89a4d= cabaed4517bf1a + +Patch copied from upstream source repository: +https://github.com/libarchive/libarchive/commit/e37b620fe8f14535d737e89a4d= cabaed4517bf1a + +From e37b620fe8f14535d737e89a4dcabaed4517bf1a Mon Sep 17 00:00:00 2001 +From: Tim Kientzle +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, ...) + } +=20 + /* If our output buffer is full, dump it and keep going. */ +- if (i > (sizeof(outbuff) - 20)) { ++ if (i > (sizeof(outbuff) - 128)) { + outbuff[i] =3D '\0'; + fprintf(f, "%s", outbuff); + i =3D 0; --=20 2.10.0 --huq684BweRXVnRxX-- --XOIedfhf+7KOe/yw Content-Type: application/pgp-signature; name="signature.asc" -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAEBCAAGBQJX8WqIAAoJECZG+jC6yn8IA4EP/RZMe/mkPiiQA8qMUE4k3c48 pSYear/dgiuWVyR4FIo/gbeEIw3/+J9Pzp/Adpxpdr/qZFIZsETVxXo3AKhS6fQr BJ6/k0IoAIa/kXTZHT9Gqiz4gdNWkf67piAGkvi2FxbCzYDoLb7NWBK+UvgSmkli 7NdMS4e1siP2ftHatoDMUX07DoUij7JqyF7xN97TRd0K7OQ6LHsAtN4efk0jm9Jl t0fGY69bbw18THmtwScri+WWTEaLwg8kSunssNhzTsINB3Qux72pOOLFDuXR4rvK 2OpqZzy8xM5w/84QD8lQEbQ7ZNFi4x2NlZgtox7Og3ppvjrW6q5FkFcUGxZ5Zkll YUH/EynWjepPu8S/uv1Awg6oTitNxsDPteIqoMYQUPzlWZl8L2xPKiIKmkN1qNGS Ha52XzKLRKH+4mrMzaXSkngDPmzt1OpQe2V2qXkyHxK73eKzVHCugRYYGJlEOX+d Yryczr1yNKUDLmJPX7vHeHtFX+wbfToUpOIXeJl24zntfRWhWcroestqfMyJNJqv necnJ3BH+A6INP7yrG1ZoOfuAuan1kzZ3fwDWQOGDfpXdEeF/oohzeGqvmB6Ft9i q4taBR2sXG+yqEzD4kKcAxBJR3grJkHsHTZBdk6IsG88Y0OV3Rgu9z3sB296cxMF xkoJEm5PaADoIK2aSxz8 =mjg8 -----END PGP SIGNATURE----- --XOIedfhf+7KOe/yw--