78e4f5b
From dd9b91de2149ee81d47f708e7b0bbf57da10ad42 Mon Sep 17 00:00:00 2001
78e4f5b
From: Nick Clifton <nickc@redhat.com>
78e4f5b
Date: Thu, 6 Nov 2014 14:49:10 +0000
78e4f5b
Subject: [PATCH] Prevent archive memebers with illegal pathnames from being extracted from an archive.
78e4f5b
78e4f5b
	PR binutils/17552, binutils/17533
78e4f5b
	* bucomm.c (is_valid_archive_path): New function.  Returns false
78e4f5b
	for absolute pathnames and pathnames that include /../.
78e4f5b
	* bucomm.h (is_valid_archive_path): Add prototype.
78e4f5b
	* ar.c (extract_file): Use new function to check for valid
78e4f5b
	pathnames when extracting files from an archive.
78e4f5b
	* objcopy.c (copy_archive): Likewise.
78e4f5b
	* doc/binutils.texi: Update documentation to mention the
78e4f5b
	limitation on pathname of archive members.
78e4f5b
---
78e4f5b
 binutils/ar.c              |    9 +++++++++
78e4f5b
 binutils/bucomm.c          |   26 ++++++++++++++++++++++++++
78e4f5b
 binutils/bucomm.h          |   12 ++++++++----
78e4f5b
 binutils/doc/binutils.texi |    3 ++-
78e4f5b
 binutils/objcopy.c         |    6 ++++++
78e4f5b
 6 files changed, 65 insertions(+), 7 deletions(-)
78e4f5b
78e4f5b
diff --git a/binutils/ar.c b/binutils/ar.c
78e4f5b
index ebd9528..117826d 100644
78e4f5b
--- a/binutils/ar.c
78e4f5b
+++ b/binutils/ar.c
78e4f5b
@@ -1034,6 +1034,15 @@ extract_file (bfd *abfd)
78e4f5b
   bfd_size_type size;
78e4f5b
   struct stat buf;
78e4f5b
 
78e4f5b
+  /* PR binutils/17533: Do not allow directory traversal
78e4f5b
+     outside of the current directory tree.  */
78e4f5b
+  if (! is_valid_archive_path (bfd_get_filename (abfd)))
78e4f5b
+    {
78e4f5b
+      non_fatal (_("illegal pathname found in archive member: %s"),
78e4f5b
+		 bfd_get_filename (abfd));
78e4f5b
+      return;
78e4f5b
+    }
78e4f5b
+
78e4f5b
   if (bfd_stat_arch_elt (abfd, &buf) != 0)
78e4f5b
     /* xgettext:c-format */
78e4f5b
     fatal (_("internal stat error on %s"), bfd_get_filename (abfd));
78e4f5b
diff --git a/binutils/bucomm.c b/binutils/bucomm.c
78e4f5b
index fd73070..b8deff5 100644
78e4f5b
--- a/binutils/bucomm.c
78e4f5b
+++ b/binutils/bucomm.c
78e4f5b
@@ -624,3 +624,29 @@ bfd_get_archive_filename (const bfd *abfd)
78e4f5b
 	   bfd_get_filename (abfd));
78e4f5b
   return buf;
78e4f5b
 }
78e4f5b
+
78e4f5b
+/* Returns TRUE iff PATHNAME, a filename of an archive member,
78e4f5b
+   is valid for writing.  For security reasons absolute paths
78e4f5b
+   and paths containing /../ are not allowed.  See PR 17533.  */
78e4f5b
+
78e4f5b
+bfd_boolean
78e4f5b
+is_valid_archive_path (char const * pathname)
78e4f5b
+{
78e4f5b
+  const char * n = pathname;
78e4f5b
+
78e4f5b
+  if (IS_ABSOLUTE_PATH (n))
78e4f5b
+    return FALSE;
78e4f5b
+
78e4f5b
+  while (*n)
78e4f5b
+    {
78e4f5b
+      if (*n == '.' && *++n == '.' && ( ! *++n || IS_DIR_SEPARATOR (*n)))
78e4f5b
+	return FALSE;
78e4f5b
+
78e4f5b
+      while (*n && ! IS_DIR_SEPARATOR (*n))
78e4f5b
+	n++;
78e4f5b
+      while (IS_DIR_SEPARATOR (*n))
78e4f5b
+	n++;
78e4f5b
+    }
78e4f5b
+
78e4f5b
+  return TRUE;
78e4f5b
+}
78e4f5b
diff --git a/binutils/bucomm.h b/binutils/bucomm.h
78e4f5b
index a93c378..a71a8fb 100644
78e4f5b
--- a/binutils/bucomm.h
78e4f5b
+++ b/binutils/bucomm.h
78e4f5b
@@ -21,6 +21,8 @@
78e4f5b
 #ifndef _BUCOMM_H
78e4f5b
 #define _BUCOMM_H
78e4f5b
 
78e4f5b
+/* In bucomm.c.  */
78e4f5b
+
78e4f5b
 /* Return the filename in a static buffer.  */
78e4f5b
 const char *bfd_get_archive_filename (const bfd *);
78e4f5b
 
78e4f5b
@@ -56,20 +58,22 @@ bfd_vma parse_vma (const char *, const char *);
78e4f5b
 
78e4f5b
 off_t get_file_size (const char *);
78e4f5b
 
78e4f5b
+bfd_boolean is_valid_archive_path (char const *);
78e4f5b
+
78e4f5b
 extern char *program_name;
78e4f5b
 
78e4f5b
-/* filemode.c */
78e4f5b
+/* In filemode.c.  */
78e4f5b
 void mode_string (unsigned long, char *);
78e4f5b
 
78e4f5b
-/* version.c */
78e4f5b
+/* In version.c.  */
78e4f5b
 extern void print_version (const char *);
78e4f5b
 
78e4f5b
-/* rename.c */
78e4f5b
+/* In rename.c.  */
78e4f5b
 extern void set_times (const char *, const struct stat *);
78e4f5b
 
78e4f5b
 extern int smart_rename (const char *, const char *, int);
78e4f5b
 
78e4f5b
-/* libiberty.  */
78e4f5b
+/* In libiberty.  */
78e4f5b
 void *xmalloc (size_t);
78e4f5b
 
78e4f5b
 void *xrealloc (void *, size_t);
78e4f5b
diff --git a/binutils/doc/binutils.texi b/binutils/doc/binutils.texi
78e4f5b
index eee77b1..39eb1d2 100644
78e4f5b
--- a/binutils/doc/binutils.texi
78e4f5b
+++ b/binutils/doc/binutils.texi
78e4f5b
@@ -234,7 +234,8 @@ a normal archive.  Instead the elements of the first archive are added
78e4f5b
 individually to the second archive.
78e4f5b
 
78e4f5b
 The paths to the elements of the archive are stored relative to the
78e4f5b
-archive itself.
78e4f5b
+archive itself.  For security reasons absolute paths and paths with a
78e4f5b
+@code{/../} component are not allowed.
78e4f5b
 
78e4f5b
 @cindex compatibility, @command{ar}
78e4f5b
 @cindex @command{ar} compatibility
78e4f5b
diff --git a/binutils/objcopy.c b/binutils/objcopy.c
78e4f5b
index 3b353ad..8454bc6 100644
78e4f5b
--- a/binutils/objcopy.c
78e4f5b
+++ b/binutils/objcopy.c
78e4f5b
@@ -2295,6 +2295,12 @@ copy_archive (bfd *ibfd, bfd *obfd, const char *output_target,
78e4f5b
       bfd_boolean del = TRUE;
78e4f5b
       bfd_boolean ok_object;
78e4f5b
 
78e4f5b
+      /* PR binutils/17533: Do not allow directory traversal
78e4f5b
+	 outside of the current directory tree by archive members.  */
78e4f5b
+      if (! is_valid_archive_path (bfd_get_filename (this_element)))
78e4f5b
+	fatal (_("illegal pathname found in archive member: %s"),
78e4f5b
+	       bfd_get_filename (this_element));
78e4f5b
+
78e4f5b
       /* Create an output file for this member.  */
78e4f5b
       output_name = concat (dir, "/",
78e4f5b
 			    bfd_get_filename (this_element), (char *) 0);
78e4f5b
-- 
78e4f5b
1.7.1
78e4f5b