~ruther/guix-local

665d6a59161769e10b52ffcbcd5cd2db22f32681 — Efraim Flashner 8 years ago 13f7f2f
gnu: glibc: Add mitigations for CVE-2017-1000366.

* gnu/packages/base.scm (glibc/linux)[replacement]: New field.
(glibc-2.25-patched): New variable.
(glibc-2.24, glibc-2.23, glibc-2.22, glibc-2.21)[source]: Add patches.
[replacement]: New field.
(glibc-locales)[replacement]: New field.
* gnu/packages/patches/glibc-CVE-2017-1000366-pt1.patch,
gnu/packages/patches/glibc-CVE-2017-1000366-pt2.patch,
gnu/packages/patches/glibc-CVE-2017-1000366-pt3.patch: New files.
* gnu/local.mk (dist_patch_DATA): Add them.

Modified-By: Mark H Weaver <mhw@netris.org>
M gnu/local.mk => gnu/local.mk +3 -0
@@ 631,6 631,9 @@ dist_patch_DATA =						\
  %D%/packages/patches/ghostscript-runpath.patch		\
  %D%/packages/patches/glib-networking-ssl-cert-file.patch	\
  %D%/packages/patches/glib-tests-timer.patch			\
  %D%/packages/patches/glibc-CVE-2017-1000366-pt1.patch	\
  %D%/packages/patches/glibc-CVE-2017-1000366-pt2.patch	\
  %D%/packages/patches/glibc-CVE-2017-1000366-pt3.patch	\
  %D%/packages/patches/glibc-bootstrap-system.patch		\
  %D%/packages/patches/glibc-ldd-x86_64.patch			\
  %D%/packages/patches/glibc-locales.patch			\

M gnu/packages/base.scm => gnu/packages/base.scm +40 -7
@@ 5,7 5,7 @@
;;; Copyright © 2014, 2015, 2016 Mark H Weaver <mhw@netris.org>
;;; Copyright © 2014 Alex Kost <alezost@gmail.com>
;;; Copyright © 2014, 2015 Manolis Fragkiskos Ragkousis <manolis837@gmail.com>
;;; Copyright © 2016 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2016, 2017 Efraim Flashner <efraim@flashner.co.il>
;;; Copyright © 2016 Jan Nieuwenhuizen <janneke@gnu.org>
;;; Copyright © 2017 Marius Bakke <mbakke@fastmail.com>
;;;


@@ 558,6 558,7 @@ store.")
  (package
   (name "glibc")
   (version "2.25")
   (replacement glibc-2.25-patched)
   (source (origin
            (method url-fetch)
            (uri (string-append "mirror://gnu/glibc/glibc-"


@@ 904,34 905,62 @@ GLIBC/HURD for a Hurd host"
;; Below are old libc versions, which we use mostly to build locale data in
;; the old format (which the new libc cannot cope with.)

(define glibc-2.25-patched
  (package
    (inherit glibc)
    (replacement #f)
    (source (origin
              (inherit (package-source glibc))
              (patches (search-patches "glibc-ldd-x86_64.patch"
                                       "glibc-versioned-locpath.patch"
                                       "glibc-o-largefile.patch"
                                       "glibc-CVE-2017-1000366-pt1.patch"
                                       "glibc-CVE-2017-1000366-pt2.patch"
                                       "glibc-CVE-2017-1000366-pt3.patch"))))))

(define-public glibc-2.24
  (package
    (inherit glibc)
    (version "2.24")
    (replacement #f)
    (source (origin
              (inherit (package-source glibc))
              (uri (string-append "mirror://gnu/glibc/glibc-"
                                  version ".tar.xz"))
              (sha256
               (base32
                "1lxmprg9gm73gvafxd503x70z32phwjzcy74i0adfi6ixzla7m4r"))))))
                "1lxmprg9gm73gvafxd503x70z32phwjzcy74i0adfi6ixzla7m4r"))
              (patches (search-patches "glibc-ldd-x86_64.patch"
                                       "glibc-versioned-locpath.patch"
                                       "glibc-o-largefile.patch"
                                       "glibc-CVE-2017-1000366-pt1.patch"
                                       "glibc-CVE-2017-1000366-pt2.patch"
                                       "glibc-CVE-2017-1000366-pt3.patch"))))))

(define-public glibc-2.23
  (package
    (inherit glibc)
    (version "2.23")
    (replacement #f)
    (source (origin
              (inherit (package-source glibc))
              (uri (string-append "mirror://gnu/glibc/glibc-"
                                  version ".tar.xz"))
              (sha256
               (base32
                "1s8krs3y2n6pzav7ic59dz41alqalphv7vww4138ag30wh0fpvwl"))))))
                "1s8krs3y2n6pzav7ic59dz41alqalphv7vww4138ag30wh0fpvwl"))
              (patches (search-patches "glibc-ldd-x86_64.patch"
                                       "glibc-versioned-locpath.patch"
                                       "glibc-o-largefile.patch"
                                       "glibc-CVE-2017-1000366-pt1.patch"
                                       "glibc-CVE-2017-1000366-pt2.patch"
                                       "glibc-CVE-2017-1000366-pt3.patch"))))))

(define-public glibc-2.22
  (package
    (inherit glibc)
    (version "2.22")
    (replacement #f)
    (source (origin
              (inherit (package-source glibc))
              (uri (string-append "mirror://gnu/glibc/glibc-"


@@ 939,7 968,10 @@ GLIBC/HURD for a Hurd host"
              (sha256
               (base32
                "0j49682pm2nh4qbdw35bas82p1pgfnz4d2l7iwfyzvrvj0318wzb"))
              (patches (search-patches "glibc-ldd-x86_64.patch"))))
              (patches (search-patches "glibc-ldd-x86_64.patch"
                                       "glibc-CVE-2017-1000366-pt1.patch"
                                       "glibc-CVE-2017-1000366-pt2.patch"
                                       "glibc-CVE-2017-1000366-pt3.patch"))))
    (arguments
      (substitute-keyword-arguments (package-arguments glibc)
        ((#:phases phases)


@@ 955,19 987,20 @@ GLIBC/HURD for a Hurd host"
  (package
    (inherit glibc-2.22)
    (version "2.21")
    (replacement #f)
    (source (origin
              (inherit (package-source glibc))
              (inherit (package-source glibc-2.22))
              (uri (string-append "mirror://gnu/glibc/glibc-"
                                  version ".tar.xz"))
              (sha256
               (base32
                "1f135546j34s9bfkydmx2nhh9vwxlx60jldi80zmsnln6wj3dsxf"))
              (patches (search-patches "glibc-ldd-x86_64.patch"))))))
                "1f135546j34s9bfkydmx2nhh9vwxlx60jldi80zmsnln6wj3dsxf"))))))

(define-public glibc-locales
  (package
    (inherit glibc)
    (name "glibc-locales")
    (replacement #f)
    (source (origin (inherit (package-source glibc))
                    (patches (cons (search-patch "glibc-locales.patch")
                                   (origin-patches (package-source glibc))))))

A gnu/packages/patches/glibc-CVE-2017-1000366-pt1.patch => gnu/packages/patches/glibc-CVE-2017-1000366-pt1.patch +36 -0
@@ 0,0 1,36 @@
From f6110a8fee2ca36f8e2d2abecf3cba9fa7b8ea7d Mon Sep 17 00:00:00 2001
From: Florian Weimer <fweimer@redhat.com>
Date: Mon, 19 Jun 2017 17:09:55 +0200
Subject: [PATCH] CVE-2017-1000366: Ignore LD_LIBRARY_PATH for AT_SECURE=1
 programs [BZ #21624]

LD_LIBRARY_PATH can only be used to reorder system search paths, which
is not useful functionality.

This makes an exploitable unbounded alloca in _dl_init_paths unreachable
for AT_SECURE=1 programs.

patch from:
https://sourceware.org/git/?p=glibc.git;a=commit;h=f6110a8fee2ca36f8e2d2abecf3cba9fa7b8ea7d
---
 ChangeLog  | 7 +++++++
 elf/rtld.c | 3 ++-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/elf/rtld.c b/elf/rtld.c
index 2446a87..2269dbe 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2422,7 +2422,8 @@ process_envvars (enum mode *modep)
 
 	case 12:
 	  /* The library search path.  */
-	  if (memcmp (envline, "LIBRARY_PATH", 12) == 0)
+	  if (!__libc_enable_secure
+	      && memcmp (envline, "LIBRARY_PATH", 12) == 0)
 	    {
 	      library_path = &envline[13];
 	      break;
-- 
2.9.3


A gnu/packages/patches/glibc-CVE-2017-1000366-pt2.patch => gnu/packages/patches/glibc-CVE-2017-1000366-pt2.patch +206 -0
@@ 0,0 1,206 @@
From 81b82fb966ffbd94353f793ad17116c6088dedd9 Mon Sep 17 00:00:00 2001
From: Florian Weimer <fweimer@redhat.com>
Date: Mon, 19 Jun 2017 22:32:12 +0200
Subject: [PATCH] ld.so: Reject overly long LD_AUDIT path elements

Also only process the last LD_AUDIT entry.

patch from:
https://sourceware.org/git/?p=glibc.git;a=commit;h=81b82fb966ffbd94353f793ad17116c6088dedd9

---
 ChangeLog  |  11 +++++++
 elf/rtld.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 106 insertions(+), 15 deletions(-)

diff --git a/elf/rtld.c b/elf/rtld.c
index 86ae20c..65647fb 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -129,13 +129,91 @@ dso_name_valid_for_suid (const char *p)
   return *p != '\0';
 }
 
-/* List of auditing DSOs.  */
+/* LD_AUDIT variable contents.  Must be processed before the
+   audit_list below.  */
+const char *audit_list_string;
+
+/* Cyclic list of auditing DSOs.  audit_list->next is the first
+   element.  */
 static struct audit_list
 {
   const char *name;
   struct audit_list *next;
 } *audit_list;
 
+/* Iterator for audit_list_string followed by audit_list.  */
+struct audit_list_iter
+{
+  /* Tail of audit_list_string still needing processing, or NULL.  */
+  const char *audit_list_tail;
+
+  /* The list element returned in the previous iteration.  NULL before
+     the first element.  */
+  struct audit_list *previous;
+
+  /* Scratch buffer for returning a name which is part of
+     audit_list_string.  */
+  char fname[SECURE_NAME_LIMIT];
+};
+
+/* Initialize an audit list iterator.  */
+static void
+audit_list_iter_init (struct audit_list_iter *iter)
+{
+  iter->audit_list_tail = audit_list_string;
+  iter->previous = NULL;
+}
+
+/* Iterate through both audit_list_string and audit_list.  */
+static const char *
+audit_list_iter_next (struct audit_list_iter *iter)
+{
+  if (iter->audit_list_tail != NULL)
+    {
+      /* First iterate over audit_list_string.  */
+      while (*iter->audit_list_tail != '\0')
+	{
+	  /* Split audit list at colon.  */
+	  size_t len = strcspn (iter->audit_list_tail, ":");
+	  if (len > 0 && len < sizeof (iter->fname))
+	    {
+	      memcpy (iter->fname, iter->audit_list_tail, len);
+	      iter->fname[len] = '\0';
+	    }
+	  else
+	    /* Do not return this name to the caller.  */
+	    iter->fname[0] = '\0';
+
+	  /* Skip over the substring and the following delimiter.  */
+	  iter->audit_list_tail += len;
+	  if (*iter->audit_list_tail == ':')
+	    ++iter->audit_list_tail;
+
+	  /* If the name is valid, return it.  */
+	  if (dso_name_valid_for_suid (iter->fname))
+	    return iter->fname;
+	  /* Otherwise, wrap around and try the next name.  */
+	}
+      /* Fall through to the procesing of audit_list.  */
+    }
+
+  if (iter->previous == NULL)
+    {
+      if (audit_list == NULL)
+	/* No pre-parsed audit list.  */
+	return NULL;
+      /* Start of audit list.  The first list element is at
+	 audit_list->next (cyclic list).  */
+      iter->previous = audit_list->next;
+      return iter->previous->name;
+    }
+  if (iter->previous == audit_list)
+    /* Cyclic list wrap-around.  */
+    return NULL;
+  iter->previous = iter->previous->next;
+  return iter->previous->name;
+}
+
 #ifndef HAVE_INLINED_SYSCALLS
 /* Set nonzero during loading and initialization of executable and
    libraries, cleared before the executable's entry point runs.  This
@@ -1305,11 +1383,13 @@ of this helper program; chances are you did not intend to run this program.\n\
     GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid ();
 
   /* If we have auditing DSOs to load, do it now.  */
-  if (__glibc_unlikely (audit_list != NULL))
+  bool need_security_init = true;
+  if (__glibc_unlikely (audit_list != NULL)
+      || __glibc_unlikely (audit_list_string != NULL))
     {
-      /* Iterate over all entries in the list.  The order is important.  */
       struct audit_ifaces *last_audit = NULL;
-      struct audit_list *al = audit_list->next;
+      struct audit_list_iter al_iter;
+      audit_list_iter_init (&al_iter);
 
       /* Since we start using the auditing DSOs right away we need to
 	 initialize the data structures now.  */
@@ -1320,9 +1400,14 @@ of this helper program; chances are you did not intend to run this program.\n\
 	 use different values (especially the pointer guard) and will
 	 fail later on.  */
       security_init ();
+      need_security_init = false;
 
-      do
+      while (true)
 	{
+	  const char *name = audit_list_iter_next (&al_iter);
+	  if (name == NULL)
+	    break;
+
 	  int tls_idx = GL(dl_tls_max_dtv_idx);
 
 	  /* Now it is time to determine the layout of the static TLS
@@ -1331,7 +1416,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 	     no DF_STATIC_TLS bit is set.  The reason is that we know
 	     glibc will use the static model.  */
 	  struct dlmopen_args dlmargs;
-	  dlmargs.fname = al->name;
+	  dlmargs.fname = name;
 	  dlmargs.map = NULL;
 
 	  const char *objname;
@@ -1344,7 +1429,7 @@ of this helper program; chances are you did not intend to run this program.\n\
 	    not_loaded:
 	      _dl_error_printf ("\
 ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
-				al->name, err_str);
+				name, err_str);
 	      if (malloced)
 		free ((char *) err_str);
 	    }
@@ -1448,10 +1533,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 		  goto not_loaded;
 		}
 	    }
-
-	  al = al->next;
 	}
-      while (al != audit_list->next);
 
       /* If we have any auditing modules, announce that we already
 	 have two objects loaded.  */
@@ -1715,7 +1797,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
   if (tcbp == NULL)
     tcbp = init_tls ();
 
-  if (__glibc_likely (audit_list == NULL))
+  if (__glibc_likely (need_security_init))
     /* Initialize security features.  But only if we have not done it
        earlier.  */
     security_init ();
@@ -2346,9 +2428,7 @@ process_dl_audit (char *str)
   char *p;
 
   while ((p = (strsep) (&str, ":")) != NULL)
-    if (p[0] != '\0'
-	&& (__builtin_expect (! __libc_enable_secure, 1)
-	    || strchr (p, '/') == NULL))
+    if (dso_name_valid_for_suid (p))
       {
 	/* This is using the local malloc, not the system malloc.  The
 	   memory can never be freed.  */
@@ -2412,7 +2492,7 @@ process_envvars (enum mode *modep)
 	      break;
 	    }
 	  if (memcmp (envline, "AUDIT", 5) == 0)
-	    process_dl_audit (&envline[6]);
+	    audit_list_string = &envline[6];
 	  break;
 
 	case 7:
-- 
2.9.3


A gnu/packages/patches/glibc-CVE-2017-1000366-pt3.patch => gnu/packages/patches/glibc-CVE-2017-1000366-pt3.patch +124 -0
@@ 0,0 1,124 @@
From 6d0ba622891bed9d8394eef1935add53003b12e8 Mon Sep 17 00:00:00 2001
From: Florian Weimer <fweimer@redhat.com>
Date: Mon, 19 Jun 2017 22:31:04 +0200
Subject: [PATCH] ld.so: Reject overly long LD_PRELOAD path elements

patch from:
https://sourceware.org/git/?p=glibc.git;a=patch;h=6d0ba622891bed9d8394eef1935add53003b12e8

---
 ChangeLog  |  7 ++++++
 elf/rtld.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++------------
 2 files changed, 73 insertions(+), 16 deletions(-)

diff --git a/elf/rtld.c b/elf/rtld.c
index 2269dbe..86ae20c 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -99,6 +99,35 @@ uintptr_t __pointer_chk_guard_local
 strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
 #endif
 
+/* Length limits for names and paths, to protect the dynamic linker,
+   particularly when __libc_enable_secure is active.  */
+#ifdef NAME_MAX
+# define SECURE_NAME_LIMIT NAME_MAX
+#else
+# define SECURE_NAME_LIMIT 255
+#endif
+#ifdef PATH_MAX
+# define SECURE_PATH_LIMIT PATH_MAX
+#else
+# define SECURE_PATH_LIMIT 1024
+#endif
+
+/* Check that AT_SECURE=0, or that the passed name does not contain
+   directories and is not overly long.  Reject empty names
+   unconditionally.  */
+static bool
+dso_name_valid_for_suid (const char *p)
+{
+  if (__glibc_unlikely (__libc_enable_secure))
+    {
+      /* Ignore pathnames with directories for AT_SECURE=1
+	 programs, and also skip overlong names.  */
+      size_t len = strlen (p);
+      if (len >= SECURE_NAME_LIMIT || memchr (p, '/', len) != NULL)
+	return false;
+    }
+  return *p != '\0';
+}
 
 /* List of auditing DSOs.  */
 static struct audit_list
@@ -718,6 +747,42 @@ static const char *preloadlist attribute_relro;
 /* Nonzero if information about versions has to be printed.  */
 static int version_info attribute_relro;
 
+/* The LD_PRELOAD environment variable gives list of libraries
+   separated by white space or colons that are loaded before the
+   executable's dependencies and prepended to the global scope list.
+   (If the binary is running setuid all elements containing a '/' are
+   ignored since it is insecure.)  Return the number of preloads
+   performed.  */
+unsigned int
+handle_ld_preload (const char *preloadlist, struct link_map *main_map)
+{
+  unsigned int npreloads = 0;
+  const char *p = preloadlist;
+  char fname[SECURE_PATH_LIMIT];
+
+  while (*p != '\0')
+    {
+      /* Split preload list at space/colon.  */
+      size_t len = strcspn (p, " :");
+      if (len > 0 && len < sizeof (fname))
+	{
+	  memcpy (fname, p, len);
+	  fname[len] = '\0';
+	}
+      else
+	fname[0] = '\0';
+
+      /* Skip over the substring and the following delimiter.  */
+      p += len;
+      if (*p != '\0')
+	++p;
+
+      if (dso_name_valid_for_suid (fname))
+	npreloads += do_preload (fname, main_map, "LD_PRELOAD");
+    }
+  return npreloads;
+}
+
 static void
 dl_main (const ElfW(Phdr) *phdr,
 	 ElfW(Word) phnum,
@@ -1464,23 +1529,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 
   if (__glibc_unlikely (preloadlist != NULL))
     {
-      /* The LD_PRELOAD environment variable gives list of libraries
-	 separated by white space or colons that are loaded before the
-	 executable's dependencies and prepended to the global scope
-	 list.  If the binary is running setuid all elements
-	 containing a '/' are ignored since it is insecure.  */
-      char *list = strdupa (preloadlist);
-      char *p;
-
       HP_TIMING_NOW (start);
-
-      /* Prevent optimizing strsep.  Speed is not important here.  */
-      while ((p = (strsep) (&list, " :")) != NULL)
-	if (p[0] != '\0'
-	    && (__builtin_expect (! __libc_enable_secure, 1)
-		|| strchr (p, '/') == NULL))
-	  npreloads += do_preload (p, main_map, "LD_PRELOAD");
-
+      npreloads += handle_ld_preload (preloadlist, main_map);
       HP_TIMING_NOW (stop);
       HP_TIMING_DIFF (diff, start, stop);
       HP_TIMING_ACCUM_NT (load_time, diff);
-- 
2.9.3