gnu: glibc: Remove old versions.
* gnu/packages/patches/glibc-CVE-2015-5180.patch, gnu/packages/patches/glibc-CVE-2015-7547.patch, gnu/packages/patches/glibc-CVE-2016-3075.patch, gnu/packages/patches/glibc-CVE-2016-3706.patch, gnu/packages/patches/glibc-CVE-2016-4429.patch, 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, gnu/packages/patches/glibc-o-largefile.patch, gnu/packages/patches/glibc-vectorized-strcspn-guards.patch: Delete files. * gnu/local.mk (dist_patch_DATA): Adjust accordingly. * gnu/packages/base.scm (glibc-2.26, glibc-2.25, glibc-2.24, glibc-2.23, glibc-2.22): Remove variables.master
parent
ccc1d743a6
commit
ebd1ba713c
10
gnu/local.mk
10
gnu/local.mk
|
@ -1016,14 +1016,6 @@ dist_patch_DATA = \
|
|||
%D%/packages/patches/ghostscript-no-header-uuid.patch \
|
||||
%D%/packages/patches/ghostscript-no-header-creationdate.patch \
|
||||
%D%/packages/patches/glib-tests-timer.patch \
|
||||
%D%/packages/patches/glibc-CVE-2015-5180.patch \
|
||||
%D%/packages/patches/glibc-CVE-2015-7547.patch \
|
||||
%D%/packages/patches/glibc-CVE-2016-3075.patch \
|
||||
%D%/packages/patches/glibc-CVE-2016-3706.patch \
|
||||
%D%/packages/patches/glibc-CVE-2016-4429.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-CVE-2018-11236.patch \
|
||||
%D%/packages/patches/glibc-CVE-2018-11237.patch \
|
||||
%D%/packages/patches/glibc-CVE-2019-7309.patch \
|
||||
|
@ -1045,9 +1037,7 @@ dist_patch_DATA = \
|
|||
%D%/packages/patches/glibc-ldd-x86_64.patch \
|
||||
%D%/packages/patches/glibc-locales.patch \
|
||||
%D%/packages/patches/glibc-locales-2.28.patch \
|
||||
%D%/packages/patches/glibc-o-largefile.patch \
|
||||
%D%/packages/patches/glibc-reinstate-prlimit64-fallback.patch \
|
||||
%D%/packages/patches/glibc-vectorized-strcspn-guards.patch \
|
||||
%D%/packages/patches/glibc-versioned-locpath.patch \
|
||||
%D%/packages/patches/glibc-2.27-git-fixes.patch \
|
||||
%D%/packages/patches/glibc-2.28-git-fixes.patch \
|
||||
|
|
|
@ -1014,118 +1014,6 @@ with the Linux kernel.")
|
|||
"glibc-CVE-2018-11237.patch"))))
|
||||
(properties `((lint-hidden-cve . ("CVE-2017-18269")))))) ; glibc-2.27-git-fixes
|
||||
|
||||
(define-public glibc-2.26
|
||||
(package
|
||||
(inherit glibc)
|
||||
;; This version number corresponds to the output of `git describe` and the
|
||||
;; archive can be generated by checking out the commit ID and running:
|
||||
;; git archive --prefix=$(git describe)/ HEAD | xz > $(git describe).tar.xz
|
||||
;; See <https://bugs.gnu.org/29406> for why this was necessary.
|
||||
(version "2.26.105-g0890d5379c")
|
||||
(source (origin
|
||||
(inherit (package-source glibc))
|
||||
(uri (string-append "https://alpha.gnu.org/gnu/guix/mirror/"
|
||||
"glibc-" (version-major+minor version) "-"
|
||||
(caddr (string-split version #\.)) ".tar.xz"))
|
||||
(sha256
|
||||
(base32
|
||||
"1jck0c1i248sn02rvsfjykk77qncma34bjq89dyy2irwm50d7s3g"))
|
||||
(patches (search-patches "glibc-ldd-x86_64.patch"
|
||||
"glibc-versioned-locpath.patch"
|
||||
"glibc-allow-kernel-2.6.32.patch"))))))
|
||||
|
||||
(define-public glibc-2.25
|
||||
(package
|
||||
(inherit glibc)
|
||||
(version "2.25")
|
||||
(source (origin
|
||||
(inherit (package-source glibc))
|
||||
(uri (string-append "mirror://gnu/glibc/glibc-"
|
||||
version ".tar.xz"))
|
||||
(sha256
|
||||
(base32
|
||||
"1813dzkgw6v8q8q1m4v96yfis7vjqc9pslqib6j9mrwh6fxxjyq6"))
|
||||
(patches (search-patches "glibc-ldd-x86_64.patch"
|
||||
"glibc-versioned-locpath.patch"
|
||||
"glibc-vectorized-strcspn-guards.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")
|
||||
(source (origin
|
||||
(inherit (package-source glibc))
|
||||
(uri (string-append "mirror://gnu/glibc/glibc-"
|
||||
version ".tar.xz"))
|
||||
(sha256
|
||||
(base32
|
||||
"1lxmprg9gm73gvafxd503x70z32phwjzcy74i0adfi6ixzla7m4r"))
|
||||
(patches (search-patches "glibc-ldd-x86_64.patch"
|
||||
"glibc-versioned-locpath.patch"
|
||||
"glibc-vectorized-strcspn-guards.patch"
|
||||
"glibc-CVE-2015-5180.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")
|
||||
(source (origin
|
||||
(inherit (package-source glibc))
|
||||
(uri (string-append "mirror://gnu/glibc/glibc-"
|
||||
version ".tar.xz"))
|
||||
(sha256
|
||||
(base32
|
||||
"1s8krs3y2n6pzav7ic59dz41alqalphv7vww4138ag30wh0fpvwl"))
|
||||
(patches (search-patches "glibc-ldd-x86_64.patch"
|
||||
"glibc-versioned-locpath.patch"
|
||||
"glibc-vectorized-strcspn-guards.patch"
|
||||
"glibc-CVE-2015-5180.patch"
|
||||
"glibc-CVE-2016-3075.patch"
|
||||
"glibc-CVE-2016-3706.patch"
|
||||
"glibc-CVE-2016-4429.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")
|
||||
(source (origin
|
||||
(inherit (package-source glibc))
|
||||
(uri (string-append "mirror://gnu/glibc/glibc-"
|
||||
version ".tar.xz"))
|
||||
(sha256
|
||||
(base32
|
||||
"0j49682pm2nh4qbdw35bas82p1pgfnz4d2l7iwfyzvrvj0318wzb"))
|
||||
(patches (search-patches "glibc-ldd-x86_64.patch"
|
||||
"glibc-o-largefile.patch"
|
||||
"glibc-vectorized-strcspn-guards.patch"
|
||||
"glibc-CVE-2015-5180.patch"
|
||||
"glibc-CVE-2015-7547.patch"
|
||||
"glibc-CVE-2016-3075.patch"
|
||||
"glibc-CVE-2016-3706.patch"
|
||||
"glibc-CVE-2016-4429.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)
|
||||
`(modify-phases ,phases
|
||||
(add-before 'configure 'fix-pwd
|
||||
(lambda _
|
||||
;; Use `pwd' instead of `/bin/pwd' for glibc-2.22.
|
||||
(substitute* "configure"
|
||||
(("/bin/pwd") "pwd"))
|
||||
#t))))))))
|
||||
|
||||
(define-public (make-gcc-libc base-gcc libc)
|
||||
"Return a GCC that targets LIBC."
|
||||
(package (inherit base-gcc)
|
||||
|
|
|
@ -1,311 +0,0 @@
|
|||
From b3b37f1a5559a7620e31c8053ed1b44f798f2b6d Mon Sep 17 00:00:00 2001
|
||||
From: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Sat, 31 Dec 2016 20:22:09 +0100
|
||||
Subject: [PATCH] CVE-2015-5180: resolv: Fix crash with internal QTYPE [BZ
|
||||
#18784]
|
||||
|
||||
Also rename T_UNSPEC because an upcoming public header file
|
||||
update will use that name.
|
||||
|
||||
(cherry picked from commit fc82b0a2dfe7dbd35671c10510a8da1043d746a5)
|
||||
---
|
||||
ChangeLog | 14 ++++
|
||||
NEWS | 6 ++
|
||||
include/arpa/nameser_compat.h | 6 +-
|
||||
resolv/Makefile | 5 ++
|
||||
resolv/nss_dns/dns-host.c | 2 +-
|
||||
resolv/res_mkquery.c | 4 +
|
||||
resolv/res_query.c | 6 +-
|
||||
resolv/tst-resolv-qtypes.c | 185 ++++++++++++++++++++++++++++++++++++++++++
|
||||
8 files changed, 221 insertions(+), 7 deletions(-)
|
||||
create mode 100644 resolv/tst-resolv-qtypes.c
|
||||
|
||||
diff --git a/include/arpa/nameser_compat.h b/include/arpa/nameser_compat.h
|
||||
index 2e735ed..7c0deed 100644
|
||||
--- a/include/arpa/nameser_compat.h
|
||||
+++ b/include/arpa/nameser_compat.h
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef _ARPA_NAMESER_COMPAT_
|
||||
#include <resolv/arpa/nameser_compat.h>
|
||||
|
||||
-/* Picksome unused number to represent lookups of IPv4 and IPv6 (i.e.,
|
||||
- T_A and T_AAAA). */
|
||||
-#define T_UNSPEC 62321
|
||||
+/* The number is outside the 16-bit RR type range and is used
|
||||
+ internally by the implementation. */
|
||||
+#define T_QUERY_A_AND_AAAA 439963904
|
||||
|
||||
#endif
|
||||
diff --git a/resolv/Makefile b/resolv/Makefile
|
||||
index 8be41d3..a4c86b9 100644
|
||||
--- a/resolv/Makefile
|
||||
+++ b/resolv/Makefile
|
||||
@@ -40,6 +40,9 @@ ifeq ($(have-thread-library),yes)
|
||||
extra-libs += libanl
|
||||
routines += gai_sigqueue
|
||||
tests += tst-res_hconf_reorder
|
||||
+
|
||||
+# This test sends millions of packets and is rather slow.
|
||||
+xtests += tst-resolv-qtypes
|
||||
endif
|
||||
extra-libs-others = $(extra-libs)
|
||||
libresolv-routines := gethnamaddr res_comp res_debug \
|
||||
@@ -117,3 +120,5 @@ tst-leaks2-ENV = MALLOC_TRACE=$(objpfx)tst-leaks2.mtrace
|
||||
$(objpfx)mtrace-tst-leaks2.out: $(objpfx)tst-leaks2.out
|
||||
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks2.mtrace > $@; \
|
||||
$(evaluate-test)
|
||||
+
|
||||
+$(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
|
||||
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
|
||||
index 5f9e357..d16fa4b 100644
|
||||
--- a/resolv/nss_dns/dns-host.c
|
||||
+++ b/resolv/nss_dns/dns-host.c
|
||||
@@ -323,7 +323,7 @@ _nss_dns_gethostbyname4_r (const char *name, struct gaih_addrtuple **pat,
|
||||
|
||||
int olderr = errno;
|
||||
enum nss_status status;
|
||||
- int n = __libc_res_nsearch (&_res, name, C_IN, T_UNSPEC,
|
||||
+ int n = __libc_res_nsearch (&_res, name, C_IN, T_QUERY_A_AND_AAAA,
|
||||
host_buffer.buf->buf, 2048, &host_buffer.ptr,
|
||||
&ans2p, &nans2p, &resplen2, &ans2p_malloced);
|
||||
if (n >= 0)
|
||||
diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c
|
||||
index 12f9730..d80b531 100644
|
||||
--- a/resolv/res_mkquery.c
|
||||
+++ b/resolv/res_mkquery.c
|
||||
@@ -103,6 +103,10 @@ res_nmkquery(res_state statp,
|
||||
int n;
|
||||
u_char *dnptrs[20], **dpp, **lastdnptr;
|
||||
|
||||
+ if (class < 0 || class > 65535
|
||||
+ || type < 0 || type > 65535)
|
||||
+ return -1;
|
||||
+
|
||||
#ifdef DEBUG
|
||||
if (statp->options & RES_DEBUG)
|
||||
printf(";; res_nmkquery(%s, %s, %s, %s)\n",
|
||||
diff --git a/resolv/res_query.c b/resolv/res_query.c
|
||||
index 944d1a9..07dc6f6 100644
|
||||
--- a/resolv/res_query.c
|
||||
+++ b/resolv/res_query.c
|
||||
@@ -122,7 +122,7 @@ __libc_res_nquery(res_state statp,
|
||||
int n, use_malloc = 0;
|
||||
u_int oflags = statp->_flags;
|
||||
|
||||
- size_t bufsize = (type == T_UNSPEC ? 2 : 1) * QUERYSIZE;
|
||||
+ size_t bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * QUERYSIZE;
|
||||
u_char *buf = alloca (bufsize);
|
||||
u_char *query1 = buf;
|
||||
int nquery1 = -1;
|
||||
@@ -137,7 +137,7 @@ __libc_res_nquery(res_state statp,
|
||||
printf(";; res_query(%s, %d, %d)\n", name, class, type);
|
||||
#endif
|
||||
|
||||
- if (type == T_UNSPEC)
|
||||
+ if (type == T_QUERY_A_AND_AAAA)
|
||||
{
|
||||
n = res_nmkquery(statp, QUERY, name, class, T_A, NULL, 0, NULL,
|
||||
query1, bufsize);
|
||||
@@ -190,7 +190,7 @@ __libc_res_nquery(res_state statp,
|
||||
if (__builtin_expect (n <= 0, 0) && !use_malloc) {
|
||||
/* Retry just in case res_nmkquery failed because of too
|
||||
short buffer. Shouldn't happen. */
|
||||
- bufsize = (type == T_UNSPEC ? 2 : 1) * MAXPACKET;
|
||||
+ bufsize = (type == T_QUERY_A_AND_AAAA ? 2 : 1) * MAXPACKET;
|
||||
buf = malloc (bufsize);
|
||||
if (buf != NULL) {
|
||||
query1 = buf;
|
||||
diff --git a/resolv/tst-resolv-qtypes.c b/resolv/tst-resolv-qtypes.c
|
||||
new file mode 100644
|
||||
index 0000000..b3e60c6
|
||||
--- /dev/null
|
||||
+++ b/resolv/tst-resolv-qtypes.c
|
||||
@@ -0,0 +1,185 @@
|
||||
+/* Exercise low-level query functions with different QTYPEs.
|
||||
+ Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <http://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <resolv.h>
|
||||
+#include <string.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/check_nss.h>
|
||||
+#include <support/resolv_test.h>
|
||||
+#include <support/support.h>
|
||||
+#include <support/test-driver.h>
|
||||
+#include <support/xmemstream.h>
|
||||
+
|
||||
+/* If ture, the response function will send the actual response packet
|
||||
+ over TCP instead of UDP. */
|
||||
+static volatile bool force_tcp;
|
||||
+
|
||||
+/* Send back a fake resource record matching the QTYPE. */
|
||||
+static void
|
||||
+response (const struct resolv_response_context *ctx,
|
||||
+ struct resolv_response_builder *b,
|
||||
+ const char *qname, uint16_t qclass, uint16_t qtype)
|
||||
+{
|
||||
+ if (force_tcp && ctx->tcp)
|
||||
+ {
|
||||
+ resolv_response_init (b, (struct resolv_response_flags) { .tc = 1 });
|
||||
+ resolv_response_add_question (b, qname, qclass, qtype);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ resolv_response_init (b, (struct resolv_response_flags) { });
|
||||
+ resolv_response_add_question (b, qname, qclass, qtype);
|
||||
+ resolv_response_section (b, ns_s_an);
|
||||
+ resolv_response_open_record (b, qname, qclass, qtype, 0);
|
||||
+ resolv_response_add_data (b, &qtype, sizeof (qtype));
|
||||
+ resolv_response_close_record (b);
|
||||
+}
|
||||
+
|
||||
+static const const char *domain = "www.example.com";
|
||||
+
|
||||
+static int
|
||||
+wrap_res_query (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ return res_query (domain, C_IN, type, answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+wrap_res_search (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ return res_query (domain, C_IN, type, answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+wrap_res_querydomain (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ return res_querydomain ("www", "example.com", C_IN, type,
|
||||
+ answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+wrap_res_send (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ unsigned char buf[512];
|
||||
+ int ret = res_mkquery (QUERY, domain, C_IN, type,
|
||||
+ (const unsigned char *) "", 0, NULL,
|
||||
+ buf, sizeof (buf));
|
||||
+ if (type < 0 || type >= 65536)
|
||||
+ {
|
||||
+ /* res_mkquery fails for out-of-range record types. */
|
||||
+ TEST_VERIFY_EXIT (ret == -1);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ TEST_VERIFY_EXIT (ret > 12); /* DNS header length. */
|
||||
+ return res_send (buf, ret, answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+wrap_res_nquery (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ return res_nquery (&_res, domain, C_IN, type, answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+wrap_res_nsearch (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ return res_nquery (&_res, domain, C_IN, type, answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+wrap_res_nquerydomain (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ return res_nquerydomain (&_res, "www", "example.com", C_IN, type,
|
||||
+ answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+wrap_res_nsend (int type, unsigned char *answer, int answer_length)
|
||||
+{
|
||||
+ unsigned char buf[512];
|
||||
+ int ret = res_nmkquery (&_res, QUERY, domain, C_IN, type,
|
||||
+ (const unsigned char *) "", 0, NULL,
|
||||
+ buf, sizeof (buf));
|
||||
+ if (type < 0 || type >= 65536)
|
||||
+ {
|
||||
+ /* res_mkquery fails for out-of-range record types. */
|
||||
+ TEST_VERIFY_EXIT (ret == -1);
|
||||
+ return -1;
|
||||
+ }
|
||||
+ TEST_VERIFY_EXIT (ret > 12); /* DNS header length. */
|
||||
+ return res_nsend (&_res, buf, ret, answer, answer_length);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+test_function (const char *fname,
|
||||
+ int (*func) (int type,
|
||||
+ unsigned char *answer, int answer_length))
|
||||
+{
|
||||
+ unsigned char buf[512];
|
||||
+ for (int tcp = 0; tcp < 2; ++tcp)
|
||||
+ {
|
||||
+ force_tcp = tcp;
|
||||
+ for (unsigned int type = 1; type <= 65535; ++type)
|
||||
+ {
|
||||
+ if (test_verbose)
|
||||
+ printf ("info: sending QTYPE %d with %s (tcp=%d)\n",
|
||||
+ type, fname, tcp);
|
||||
+ int ret = func (type, buf, sizeof (buf));
|
||||
+ if (ret != 47)
|
||||
+ FAIL_EXIT1 ("%s tcp=%d qtype=%d return value %d",
|
||||
+ fname,tcp, type, ret);
|
||||
+ /* One question, one answer record. */
|
||||
+ TEST_VERIFY (memcmp (buf + 4, "\0\1\0\1\0\0\0\0", 8) == 0);
|
||||
+ /* Question section. */
|
||||
+ static const char qname[] = "\3www\7example\3com";
|
||||
+ size_t qname_length = sizeof (qname);
|
||||
+ TEST_VERIFY (memcmp (buf + 12, qname, qname_length) == 0);
|
||||
+ /* RDATA part of answer. */
|
||||
+ uint16_t type16 = type;
|
||||
+ TEST_VERIFY (memcmp (buf + ret - 2, &type16, sizeof (type16)) == 0);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ TEST_VERIFY (func (-1, buf, sizeof (buf) == -1));
|
||||
+ TEST_VERIFY (func (65536, buf, sizeof (buf) == -1));
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct resolv_redirect_config config =
|
||||
+ {
|
||||
+ .response_callback = response,
|
||||
+ };
|
||||
+ struct resolv_test *obj = resolv_test_start (config);
|
||||
+
|
||||
+ test_function ("res_query", &wrap_res_query);
|
||||
+ test_function ("res_search", &wrap_res_search);
|
||||
+ test_function ("res_querydomain", &wrap_res_querydomain);
|
||||
+ test_function ("res_send", &wrap_res_send);
|
||||
+
|
||||
+ test_function ("res_nquery", &wrap_res_nquery);
|
||||
+ test_function ("res_nsearch", &wrap_res_nsearch);
|
||||
+ test_function ("res_nquerydomain", &wrap_res_nquerydomain);
|
||||
+ test_function ("res_nsend", &wrap_res_nsend);
|
||||
+
|
||||
+ resolv_test_end (obj);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#define TIMEOUT 300
|
||||
+#include <support/test-driver.c>
|
||||
--
|
||||
2.9.3
|
||||
|
|
@ -1,590 +0,0 @@
|
|||
From b995d95a5943785be3ab862b2d3276f3b4a22481 Mon Sep 17 00:00:00 2001
|
||||
From: Carlos O'Donell <carlos@systemhalted.org>
|
||||
Date: Tue, 16 Feb 2016 21:26:37 -0500
|
||||
Subject: [PATCH] CVE-2015-7547: getaddrinfo() stack-based buffer overflow (Bug
|
||||
18665).
|
||||
|
||||
* A stack-based buffer overflow was found in libresolv when invoked from
|
||||
libnss_dns, allowing specially crafted DNS responses to seize control
|
||||
of execution flow in the DNS client. The buffer overflow occurs in
|
||||
the functions send_dg (send datagram) and send_vc (send TCP) for the
|
||||
NSS module libnss_dns.so.2 when calling getaddrinfo with AF_UNSPEC
|
||||
family. The use of AF_UNSPEC triggers the low-level resolver code to
|
||||
send out two parallel queries for A and AAAA. A mismanagement of the
|
||||
buffers used for those queries could result in the response of a query
|
||||
writing beyond the alloca allocated buffer created by
|
||||
_nss_dns_gethostbyname4_r. Buffer management is simplified to remove
|
||||
the overflow. Thanks to the Google Security Team and Red Hat for
|
||||
reporting the security impact of this issue, and Robert Holiday of
|
||||
Ciena for reporting the related bug 18665. (CVE-2015-7547)
|
||||
|
||||
See also:
|
||||
https://sourceware.org/ml/libc-alpha/2016-02/msg00416.html
|
||||
https://sourceware.org/ml/libc-alpha/2016-02/msg00418.html
|
||||
|
||||
(cherry picked from commit e9db92d3acfe1822d56d11abcea5bfc4c41cf6ca)
|
||||
---
|
||||
ChangeLog | 15 +++
|
||||
NEWS | 14 +++
|
||||
resolv/nss_dns/dns-host.c | 111 ++++++++++++++++++-
|
||||
resolv/res_query.c | 3 +
|
||||
resolv/res_send.c | 264 ++++++++++++++++++++++++++++++++++------------
|
||||
5 files changed, 338 insertions(+), 69 deletions(-)
|
||||
|
||||
diff --git a/resolv/nss_dns/dns-host.c b/resolv/nss_dns/dns-host.c
|
||||
index 357ac04..a0fe9a8 100644
|
||||
--- a/resolv/nss_dns/dns-host.c
|
||||
+++ b/resolv/nss_dns/dns-host.c
|
||||
@@ -1031,7 +1031,10 @@ gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
|
||||
int h_namelen = 0;
|
||||
|
||||
if (ancount == 0)
|
||||
- return NSS_STATUS_NOTFOUND;
|
||||
+ {
|
||||
+ *h_errnop = HOST_NOT_FOUND;
|
||||
+ return NSS_STATUS_NOTFOUND;
|
||||
+ }
|
||||
|
||||
while (ancount-- > 0 && cp < end_of_message && had_error == 0)
|
||||
{
|
||||
@@ -1208,7 +1211,14 @@ gaih_getanswer_slice (const querybuf *answer, int anslen, const char *qname,
|
||||
/* Special case here: if the resolver sent a result but it only
|
||||
contains a CNAME while we are looking for a T_A or T_AAAA record,
|
||||
we fail with NOTFOUND instead of TRYAGAIN. */
|
||||
- return canon == NULL ? NSS_STATUS_TRYAGAIN : NSS_STATUS_NOTFOUND;
|
||||
+ if (canon != NULL)
|
||||
+ {
|
||||
+ *h_errnop = HOST_NOT_FOUND;
|
||||
+ return NSS_STATUS_NOTFOUND;
|
||||
+ }
|
||||
+
|
||||
+ *h_errnop = NETDB_INTERNAL;
|
||||
+ return NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
|
||||
|
||||
@@ -1222,11 +1232,101 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
|
||||
|
||||
enum nss_status status = NSS_STATUS_NOTFOUND;
|
||||
|
||||
+ /* Combining the NSS status of two distinct queries requires some
|
||||
+ compromise and attention to symmetry (A or AAAA queries can be
|
||||
+ returned in any order). What follows is a breakdown of how this
|
||||
+ code is expected to work and why. We discuss only SUCCESS,
|
||||
+ TRYAGAIN, NOTFOUND and UNAVAIL, since they are the only returns
|
||||
+ that apply (though RETURN and MERGE exist). We make a distinction
|
||||
+ between TRYAGAIN (recoverable) and TRYAGAIN' (not-recoverable).
|
||||
+ A recoverable TRYAGAIN is almost always due to buffer size issues
|
||||
+ and returns ERANGE in errno and the caller is expected to retry
|
||||
+ with a larger buffer.
|
||||
+
|
||||
+ Lastly, you may be tempted to make significant changes to the
|
||||
+ conditions in this code to bring about symmetry between responses.
|
||||
+ Please don't change anything without due consideration for
|
||||
+ expected application behaviour. Some of the synthesized responses
|
||||
+ aren't very well thought out and sometimes appear to imply that
|
||||
+ IPv4 responses are always answer 1, and IPv6 responses are always
|
||||
+ answer 2, but that's not true (see the implementation of send_dg
|
||||
+ and send_vc to see response can arrive in any order, particularly
|
||||
+ for UDP). However, we expect it holds roughly enough of the time
|
||||
+ that this code works, but certainly needs to be fixed to make this
|
||||
+ a more robust implementation.
|
||||
+
|
||||
+ ----------------------------------------------
|
||||
+ | Answer 1 Status / | Synthesized | Reason |
|
||||
+ | Answer 2 Status | Status | |
|
||||
+ |--------------------------------------------|
|
||||
+ | SUCCESS/SUCCESS | SUCCESS | [1] |
|
||||
+ | SUCCESS/TRYAGAIN | TRYAGAIN | [5] |
|
||||
+ | SUCCESS/TRYAGAIN' | SUCCESS | [1] |
|
||||
+ | SUCCESS/NOTFOUND | SUCCESS | [1] |
|
||||
+ | SUCCESS/UNAVAIL | SUCCESS | [1] |
|
||||
+ | TRYAGAIN/SUCCESS | TRYAGAIN | [2] |
|
||||
+ | TRYAGAIN/TRYAGAIN | TRYAGAIN | [2] |
|
||||
+ | TRYAGAIN/TRYAGAIN' | TRYAGAIN | [2] |
|
||||
+ | TRYAGAIN/NOTFOUND | TRYAGAIN | [2] |
|
||||
+ | TRYAGAIN/UNAVAIL | TRYAGAIN | [2] |
|
||||
+ | TRYAGAIN'/SUCCESS | SUCCESS | [3] |
|
||||
+ | TRYAGAIN'/TRYAGAIN | TRYAGAIN | [3] |
|
||||
+ | TRYAGAIN'/TRYAGAIN' | TRYAGAIN' | [3] |
|
||||
+ | TRYAGAIN'/NOTFOUND | TRYAGAIN' | [3] |
|
||||
+ | TRYAGAIN'/UNAVAIL | UNAVAIL | [3] |
|
||||
+ | NOTFOUND/SUCCESS | SUCCESS | [3] |
|
||||
+ | NOTFOUND/TRYAGAIN | TRYAGAIN | [3] |
|
||||
+ | NOTFOUND/TRYAGAIN' | TRYAGAIN' | [3] |
|
||||
+ | NOTFOUND/NOTFOUND | NOTFOUND | [3] |
|
||||
+ | NOTFOUND/UNAVAIL | UNAVAIL | [3] |
|
||||
+ | UNAVAIL/SUCCESS | UNAVAIL | [4] |
|
||||
+ | UNAVAIL/TRYAGAIN | UNAVAIL | [4] |
|
||||
+ | UNAVAIL/TRYAGAIN' | UNAVAIL | [4] |
|
||||
+ | UNAVAIL/NOTFOUND | UNAVAIL | [4] |
|
||||
+ | UNAVAIL/UNAVAIL | UNAVAIL | [4] |
|
||||
+ ----------------------------------------------
|
||||
+
|
||||
+ [1] If the first response is a success we return success.
|
||||
+ This ignores the state of the second answer and in fact
|
||||
+ incorrectly sets errno and h_errno to that of the second
|
||||
+ answer. However because the response is a success we ignore
|
||||
+ *errnop and *h_errnop (though that means you touched errno on
|
||||
+ success). We are being conservative here and returning the
|
||||
+ likely IPv4 response in the first answer as a success.
|
||||
+
|
||||
+ [2] If the first response is a recoverable TRYAGAIN we return
|
||||
+ that instead of looking at the second response. The
|
||||
+ expectation here is that we have failed to get an IPv4 response
|
||||
+ and should retry both queries.
|
||||
+
|
||||
+ [3] If the first response was not a SUCCESS and the second
|
||||
+ response is not NOTFOUND (had a SUCCESS, need to TRYAGAIN,
|
||||
+ or failed entirely e.g. TRYAGAIN' and UNAVAIL) then use the
|
||||
+ result from the second response, otherwise the first responses
|
||||
+ status is used. Again we have some odd side-effects when the
|
||||
+ second response is NOTFOUND because we overwrite *errnop and
|
||||
+ *h_errnop that means that a first answer of NOTFOUND might see
|
||||
+ its *errnop and *h_errnop values altered. Whether it matters
|
||||
+ in practice that a first response NOTFOUND has the wrong
|
||||
+ *errnop and *h_errnop is undecided.
|
||||
+
|
||||
+ [4] If the first response is UNAVAIL we return that instead of
|
||||
+ looking at the second response. The expectation here is that
|
||||
+ it will have failed similarly e.g. configuration failure.
|
||||
+
|
||||
+ [5] Testing this code is complicated by the fact that truncated
|
||||
+ second response buffers might be returned as SUCCESS if the
|
||||
+ first answer is a SUCCESS. To fix this we add symmetry to
|
||||
+ TRYAGAIN with the second response. If the second response
|
||||
+ is a recoverable error we now return TRYAGIN even if the first
|
||||
+ response was SUCCESS. */
|
||||
+
|
||||
if (anslen1 > 0)
|
||||
status = gaih_getanswer_slice(answer1, anslen1, qname,
|
||||
&pat, &buffer, &buflen,
|
||||
errnop, h_errnop, ttlp,
|
||||
&first);
|
||||
+
|
||||
if ((status == NSS_STATUS_SUCCESS || status == NSS_STATUS_NOTFOUND
|
||||
|| (status == NSS_STATUS_TRYAGAIN
|
||||
/* We want to look at the second answer in case of an
|
||||
@@ -1242,8 +1342,15 @@ gaih_getanswer (const querybuf *answer1, int anslen1, const querybuf *answer2,
|
||||
&pat, &buffer, &buflen,
|
||||
errnop, h_errnop, ttlp,
|
||||
&first);
|
||||
+ /* Use the second response status in some cases. */
|
||||
if (status != NSS_STATUS_SUCCESS && status2 != NSS_STATUS_NOTFOUND)
|
||||
status = status2;
|
||||
+ /* Do not return a truncated second response (unless it was
|
||||
+ unavoidable e.g. unrecoverable TRYAGAIN). */
|
||||
+ if (status == NSS_STATUS_SUCCESS
|
||||
+ && (status2 == NSS_STATUS_TRYAGAIN
|
||||
+ && *errnop == ERANGE && *h_errnop != NO_RECOVERY))
|
||||
+ status = NSS_STATUS_TRYAGAIN;
|
||||
}
|
||||
|
||||
return status;
|
||||
diff --git a/resolv/res_query.c b/resolv/res_query.c
|
||||
index 4a9b3b3..95470a9 100644
|
||||
--- a/resolv/res_query.c
|
||||
+++ b/resolv/res_query.c
|
||||
@@ -396,6 +396,7 @@ __libc_res_nsearch(res_state statp,
|
||||
{
|
||||
free (*answerp2);
|
||||
*answerp2 = NULL;
|
||||
+ *nanswerp2 = 0;
|
||||
*answerp2_malloced = 0;
|
||||
}
|
||||
}
|
||||
@@ -447,6 +448,7 @@ __libc_res_nsearch(res_state statp,
|
||||
{
|
||||
free (*answerp2);
|
||||
*answerp2 = NULL;
|
||||
+ *nanswerp2 = 0;
|
||||
*answerp2_malloced = 0;
|
||||
}
|
||||
|
||||
@@ -521,6 +523,7 @@ __libc_res_nsearch(res_state statp,
|
||||
{
|
||||
free (*answerp2);
|
||||
*answerp2 = NULL;
|
||||
+ *nanswerp2 = 0;
|
||||
*answerp2_malloced = 0;
|
||||
}
|
||||
if (saved_herrno != -1)
|
||||
diff --git a/resolv/res_send.c b/resolv/res_send.c
|
||||
index 5e53cc2..6511bb1 100644
|
||||
--- a/resolv/res_send.c
|
||||
+++ b/resolv/res_send.c
|
||||
@@ -1,3 +1,20 @@
|
||||
+/* Copyright (C) 2016 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <http://www.gnu.org/licenses/>. */
|
||||
+
|
||||
/*
|
||||
* Copyright (c) 1985, 1989, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
@@ -363,6 +380,8 @@ __libc_res_nsend(res_state statp, const u_char *buf, int buflen,
|
||||
#ifdef USE_HOOKS
|
||||
if (__glibc_unlikely (statp->qhook || statp->rhook)) {
|
||||
if (anssiz < MAXPACKET && ansp) {
|
||||
+ /* Always allocate MAXPACKET, callers expect
|
||||
+ this specific size. */
|
||||
u_char *buf = malloc (MAXPACKET);
|
||||
if (buf == NULL)
|
||||
return (-1);
|
||||
@@ -638,6 +657,77 @@ get_nsaddr (res_state statp, int n)
|
||||
return (struct sockaddr *) (void *) &statp->nsaddr_list[n];
|
||||
}
|
||||
|
||||
+/* The send_vc function is responsible for sending a DNS query over TCP
|
||||
+ to the nameserver numbered NS from the res_state STATP i.e.
|
||||
+ EXT(statp).nssocks[ns]. The function supports sending both IPv4 and
|
||||
+ IPv6 queries at the same serially on the same socket.
|
||||
+
|
||||
+ Please note that for TCP there is no way to disable sending both
|
||||
+ queries, unlike UDP, which honours RES_SNGLKUP and RES_SNGLKUPREOP
|
||||
+ and sends the queries serially and waits for the result after each
|
||||
+ sent query. This implemetnation should be corrected to honour these
|
||||
+ options.
|
||||
+
|
||||
+ Please also note that for TCP we send both queries over the same
|
||||
+ socket one after another. This technically violates best practice
|
||||
+ since the server is allowed to read the first query, respond, and
|
||||
+ then close the socket (to service another client). If the server
|
||||
+ does this, then the remaining second query in the socket data buffer
|
||||
+ will cause the server to send the client an RST which will arrive
|
||||
+ asynchronously and the client's OS will likely tear down the socket
|
||||
+ receive buffer resulting in a potentially short read and lost
|
||||
+ response data. This will force the client to retry the query again,
|
||||
+ and this process may repeat until all servers and connection resets
|
||||
+ are exhausted and then the query will fail. It's not known if this
|
||||
+ happens with any frequency in real DNS server implementations. This
|
||||
+ implementation should be corrected to use two sockets by default for
|
||||
+ parallel queries.
|
||||
+
|
||||
+ The query stored in BUF of BUFLEN length is sent first followed by
|
||||
+ the query stored in BUF2 of BUFLEN2 length. Queries are sent
|
||||
+ serially on the same socket.
|
||||
+
|
||||
+ Answers to the query are stored firstly in *ANSP up to a max of
|
||||
+ *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP
|
||||
+ is non-NULL (to indicate that modifying the answer buffer is allowed)
|
||||
+ then malloc is used to allocate a new response buffer and ANSCP and
|
||||
+ ANSP will both point to the new buffer. If more than *ANSSIZP bytes
|
||||
+ are needed but ANSCP is NULL, then as much of the response as
|
||||
+ possible is read into the buffer, but the results will be truncated.
|
||||
+ When truncation happens because of a small answer buffer the DNS
|
||||
+ packets header field TC will bet set to 1, indicating a truncated
|
||||
+ message and the rest of the socket data will be read and discarded.
|
||||
+
|
||||
+ Answers to the query are stored secondly in *ANSP2 up to a max of
|
||||
+ *ANSSIZP2 bytes, with the actual response length stored in
|
||||
+ *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2
|
||||
+ is non-NULL (required for a second query) then malloc is used to
|
||||
+ allocate a new response buffer, *ANSSIZP2 is set to the new buffer
|
||||
+ size and *ANSP2_MALLOCED is set to 1.
|
||||
+
|
||||
+ The ANSP2_MALLOCED argument will eventually be removed as the
|
||||
+ change in buffer pointer can be used to detect the buffer has
|
||||
+ changed and that the caller should use free on the new buffer.
|
||||
+
|
||||
+ Note that the answers may arrive in any order from the server and
|
||||
+ therefore the first and second answer buffers may not correspond to
|
||||
+ the first and second queries.
|
||||
+
|
||||
+ It is not supported to call this function with a non-NULL ANSP2
|
||||
+ but a NULL ANSCP. Put another way, you can call send_vc with a
|
||||
+ single unmodifiable buffer or two modifiable buffers, but no other
|
||||
+ combination is supported.
|
||||
+
|
||||
+ It is the caller's responsibility to free the malloc allocated
|
||||
+ buffers by detecting that the pointers have changed from their
|
||||
+ original values i.e. *ANSCP or *ANSP2 has changed.
|
||||
+
|
||||
+ If errors are encountered then *TERRNO is set to an appropriate
|
||||
+ errno value and a zero result is returned for a recoverable error,
|
||||
+ and a less-than zero result is returned for a non-recoverable error.
|
||||
+
|
||||
+ If no errors are encountered then *TERRNO is left unmodified and
|
||||
+ a the length of the first response in bytes is returned. */
|
||||
static int
|
||||
send_vc(res_state statp,
|
||||
const u_char *buf, int buflen, const u_char *buf2, int buflen2,
|
||||
@@ -647,11 +737,7 @@ send_vc(res_state statp,
|
||||
{
|
||||
const HEADER *hp = (HEADER *) buf;
|
||||
const HEADER *hp2 = (HEADER *) buf2;
|
||||
- u_char *ans = *ansp;
|
||||
- int orig_anssizp = *anssizp;
|
||||
- // XXX REMOVE
|
||||
- // int anssiz = *anssizp;
|
||||
- HEADER *anhp = (HEADER *) ans;
|
||||
+ HEADER *anhp = (HEADER *) *ansp;
|
||||
struct sockaddr *nsap = get_nsaddr (statp, ns);
|
||||
int truncating, connreset, n;
|
||||
/* On some architectures compiler might emit a warning indicating
|
||||
@@ -743,6 +829,8 @@ send_vc(res_state statp,
|
||||
* Receive length & response
|
||||
*/
|
||||
int recvresp1 = 0;
|
||||
+ /* Skip the second response if there is no second query.
|
||||
+ To do that we mark the second response as received. */
|
||||
int recvresp2 = buf2 == NULL;
|
||||
uint16_t rlen16;
|
||||
read_len:
|
||||
@@ -779,40 +867,14 @@ send_vc(res_state statp,
|
||||
u_char **thisansp;
|
||||
int *thisresplenp;
|
||||
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
|
||||
+ /* We have not received any responses
|
||||
+ yet or we only have one response to
|
||||
+ receive. */
|
||||
thisanssizp = anssizp;
|
||||
thisansp = anscp ?: ansp;
|
||||
assert (anscp != NULL || ansp2 == NULL);
|
||||
thisresplenp = &resplen;
|
||||
} else {
|
||||
- if (*anssizp != MAXPACKET) {
|
||||
- /* No buffer allocated for the first
|
||||
- reply. We can try to use the rest
|
||||
- of the user-provided buffer. */
|
||||
-#if __GNUC_PREREQ (4, 7)
|
||||
- DIAG_PUSH_NEEDS_COMMENT;
|
||||
- DIAG_IGNORE_NEEDS_COMMENT (5, "-Wmaybe-uninitialized");
|
||||
-#endif
|
||||
-#if _STRING_ARCH_unaligned
|
||||
- *anssizp2 = orig_anssizp - resplen;
|
||||
- *ansp2 = *ansp + resplen;
|
||||
-#else
|
||||
- int aligned_resplen
|
||||
- = ((resplen + __alignof__ (HEADER) - 1)
|
||||
- & ~(__alignof__ (HEADER) - 1));
|
||||
- *anssizp2 = orig_anssizp - aligned_resplen;
|
||||
- *ansp2 = *ansp + aligned_resplen;
|
||||
-#endif
|
||||
-#if __GNUC_PREREQ (4, 7)
|
||||
- DIAG_POP_NEEDS_COMMENT;
|
||||
-#endif
|
||||
- } else {
|
||||
- /* The first reply did not fit into the
|
||||
- user-provided buffer. Maybe the second
|
||||
- answer will. */
|
||||
- *anssizp2 = orig_anssizp;
|
||||
- *ansp2 = *ansp;
|
||||
- }
|
||||
-
|
||||
thisanssizp = anssizp2;
|
||||
thisansp = ansp2;
|
||||
thisresplenp = resplen2;
|
||||
@@ -820,10 +882,14 @@ send_vc(res_state statp,
|
||||
anhp = (HEADER *) *thisansp;
|
||||
|
||||
*thisresplenp = rlen;
|
||||
- if (rlen > *thisanssizp) {
|
||||
- /* Yes, we test ANSCP here. If we have two buffers
|
||||
- both will be allocatable. */
|
||||
- if (__glibc_likely (anscp != NULL)) {
|
||||
+ /* Is the answer buffer too small? */
|
||||
+ if (*thisanssizp < rlen) {
|
||||
+ /* If the current buffer is not the the static
|
||||
+ user-supplied buffer then we can reallocate
|
||||
+ it. */
|
||||
+ if (thisansp != NULL && thisansp != ansp) {
|
||||
+ /* Always allocate MAXPACKET, callers expect
|
||||
+ this specific size. */
|
||||
u_char *newp = malloc (MAXPACKET);
|
||||
if (newp == NULL) {
|
||||
*terrno = ENOMEM;
|
||||
@@ -835,6 +901,9 @@ send_vc(res_state statp,
|
||||
if (thisansp == ansp2)
|
||||
*ansp2_malloced = 1;
|
||||
anhp = (HEADER *) newp;
|
||||
+ /* A uint16_t can't be larger than MAXPACKET
|
||||
+ thus it's safe to allocate MAXPACKET but
|
||||
+ read RLEN bytes instead. */
|
||||
len = rlen;
|
||||
} else {
|
||||
Dprint(statp->options & RES_DEBUG,
|
||||
@@ -997,6 +1066,66 @@ reopen (res_state statp, int *terrno, int ns)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+/* The send_dg function is responsible for sending a DNS query over UDP
|
||||
+ to the nameserver numbered NS from the res_state STATP i.e.
|
||||
+ EXT(statp).nssocks[ns]. The function supports IPv4 and IPv6 queries
|
||||
+ along with the ability to send the query in parallel for both stacks
|
||||
+ (default) or serially (RES_SINGLKUP). It also supports serial lookup
|
||||
+ with a close and reopen of the socket used to talk to the server
|
||||
+ (RES_SNGLKUPREOP) to work around broken name servers.
|
||||
+
|
||||
+ The query stored in BUF of BUFLEN length is sent first followed by
|
||||
+ the query stored in BUF2 of BUFLEN2 length. Queries are sent
|
||||
+ in parallel (default) or serially (RES_SINGLKUP or RES_SNGLKUPREOP).
|
||||
+
|
||||
+ Answers to the query are stored firstly in *ANSP up to a max of
|
||||
+ *ANSSIZP bytes. If more than *ANSSIZP bytes are needed and ANSCP
|
||||
+ is non-NULL (to indicate that modifying the answer buffer is allowed)
|
||||
+ then malloc is used to allocate a new response buffer and ANSCP and
|
||||
+ ANSP will both point to the new buffer. If more than *ANSSIZP bytes
|
||||
+ are needed but ANSCP is NULL, then as much of the response as
|
||||
+ possible is read into the buffer, but the results will be truncated.
|
||||
+ When truncation happens because of a small answer buffer the DNS
|
||||
+ packets header field TC will bet set to 1, indicating a truncated
|
||||
+ message, while the rest of the UDP packet is discarded.
|
||||
+
|
||||
+ Answers to the query are stored secondly in *ANSP2 up to a max of
|
||||
+ *ANSSIZP2 bytes, with the actual response length stored in
|
||||
+ *RESPLEN2. If more than *ANSSIZP bytes are needed and ANSP2
|
||||
+ is non-NULL (required for a second query) then malloc is used to
|
||||
+ allocate a new response buffer, *ANSSIZP2 is set to the new buffer
|
||||
+ size and *ANSP2_MALLOCED is set to 1.
|
||||
+
|
||||
+ The ANSP2_MALLOCED argument will eventually be removed as the
|
||||
+ change in buffer pointer can be used to detect the buffer has
|
||||
+ changed and that the caller should use free on the new buffer.
|
||||
+
|
||||
+ Note that the answers may arrive in any order from the server and
|
||||
+ therefore the first and second answer buffers may not correspond to
|
||||
+ the first and second queries.
|
||||
+
|
||||
+ It is not supported to call this function with a non-NULL ANSP2
|
||||
+ but a NULL ANSCP. Put another way, you can call send_vc with a
|
||||
+ single unmodifiable buffer or two modifiable buffers, but no other
|
||||
+ combination is supported.
|
||||
+
|
||||
+ It is the caller's responsibility to free the malloc allocated
|
||||
+ buffers by detecting that the pointers have changed from their
|
||||
+ original values i.e. *ANSCP or *ANSP2 has changed.
|
||||
+
|
||||
+ If an answer is truncated because of UDP datagram DNS limits then
|
||||
+ *V_CIRCUIT is set to 1 and the return value non-zero to indicate to
|
||||
+ the caller to retry with TCP. The value *GOTSOMEWHERE is set to 1
|
||||
+ if any progress was made reading a response from the nameserver and
|
||||
+ is used by the caller to distinguish between ECONNREFUSED and
|
||||
+ ETIMEDOUT (the latter if *GOTSOMEWHERE is 1).
|
||||
+
|
||||
+ If errors are encountered then *TERRNO is set to an appropriate
|
||||
+ errno value and a zero result is returned for a recoverable error,
|
||||
+ and a less-than zero result is returned for a non-recoverable error.
|
||||
+
|
||||
+ If no errors are encountered then *TERRNO is left unmodified and
|
||||
+ a the length of the first response in bytes is returned. */
|
||||
static int
|
||||
send_dg(res_state statp,
|
||||
const u_char *buf, int buflen, const u_char *buf2, int buflen2,
|
||||
@@ -1006,8 +1135,6 @@ send_dg(res_state statp,
|
||||
{
|
||||
const HEADER *hp = (HEADER *) buf;
|
||||
const HEADER *hp2 = (HEADER *) buf2;
|
||||
- u_char *ans = *ansp;
|
||||
- int orig_anssizp = *anssizp;
|
||||
struct timespec now, timeout, finish;
|
||||
struct pollfd pfd[1];
|
||||
int ptimeout;
|
||||
@@ -1040,6 +1167,8 @@ send_dg(res_state statp,
|
||||
int need_recompute = 0;
|
||||
int nwritten = 0;
|
||||
int recvresp1 = 0;
|
||||
+ /* Skip the second response if there is no second query.
|
||||
+ To do that we mark the second response as received. */
|
||||
int recvresp2 = buf2 == NULL;
|
||||
pfd[0].fd = EXT(statp).nssocks[ns];
|
||||
pfd[0].events = POLLOUT;
|
||||
@@ -1203,55 +1332,56 @@ send_dg(res_state statp,
|
||||
int *thisresplenp;
|
||||
|
||||
if ((recvresp1 | recvresp2) == 0 || buf2 == NULL) {
|
||||
+ /* We have not received any responses
|
||||
+ yet or we only have one response to
|
||||
+ receive. */
|
||||
thisanssizp = anssizp;
|
||||
thisansp = anscp ?: ansp;
|
||||
assert (anscp != NULL || ansp2 == NULL);
|
||||
thisresplenp = &resplen;
|
||||
} else {
|
||||
- if (*anssizp != MAXPACKET) {
|
||||
- /* No buffer allocated for the first
|
||||
- reply. We can try to use the rest
|
||||
- of the user-provided buffer. */
|
||||
-#if _STRING_ARCH_unaligned
|
||||
- *anssizp2 = orig_anssizp - resplen;
|
||||
- *ansp2 = *ansp + resplen;
|
||||
-#else
|
||||
- int aligned_resplen
|
||||
- = ((resplen + __alignof__ (HEADER) - 1)
|
||||
- & ~(__alignof__ (HEADER) - 1));
|
||||
- *anssizp2 = orig_anssizp - aligned_resplen;
|
||||
- *ansp2 = *ansp + aligned_resplen;
|
||||
-#endif
|
||||
- } else {
|
||||
- /* The first reply did not fit into the
|
||||
- user-provided buffer. Maybe the second
|
||||
- answer will. */
|
||||
- *anssizp2 = orig_anssizp;
|
||||
- *ansp2 = *ansp;
|
||||
- }
|
||||
-
|
||||
thisanssizp = anssizp2;
|
||||
thisansp = ansp2;
|
||||
thisresplenp = resplen2;
|
||||
}
|
||||
|
||||
if (*thisanssizp < MAXPACKET
|
||||
- /* Yes, we test ANSCP here. If we have two buffers
|
||||
- both will be allocatable. */
|
||||
- && anscp
|
||||
+ /* If the current buffer is not the the static
|
||||
+ user-supplied buffer then we can reallocate
|
||||
+ it. */
|
||||
+ && (thisansp != NULL && thisansp != ansp)
|
||||
#ifdef FIONREAD
|
||||
+ /* Is the size too small? */
|
||||
&& (ioctl (pfd[0].fd, FIONREAD, thisresplenp) < 0
|
||||
|| *thisanssizp < *thisresplenp)
|
||||
#endif
|
||||
) {
|
||||
+ /* Always allocate MAXPACKET, callers expect
|
||||
+ this specific size. */
|
||||
u_char *newp = malloc (MAXPACKET);
|
||||
if (newp != NULL) {
|
||||
- *anssizp = MAXPACKET;
|
||||
- *thisansp = ans = newp;
|
||||
+ *thisanssizp = MAXPACKET;
|
||||
+ *thisansp = newp;
|
||||
if (thisansp == ansp2)
|
||||
*ansp2_malloced = 1;
|
||||
}
|
||||
}
|
||||
+ /* We could end up with truncation if anscp was NULL
|
||||
+ (not allowed to change caller's buffer) and the
|
||||
+ response buffer size is too small. This isn't a
|
||||
+ reliable way to detect truncation because the ioctl
|
||||
+ may be an inaccurate report of the UDP message size.
|
||||
+ Therefore we use this only to issue debug output.
|
||||
+ To do truncation accurately with UDP we need
|
||||
+ MSG_TRUNC which is only available on Linux. We
|
||||
+ can abstract out the Linux-specific feature in the
|
||||
+ future to detect truncation. */
|
||||
+ if (__glibc_unlikely (*thisanssizp < *thisresplenp)) {
|
||||
+ Dprint(statp->options & RES_DEBUG,
|
||||
+ (stdout, ";; response may be truncated (UDP)\n")
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
HEADER *anhp = (HEADER *) *thisansp;
|
||||
socklen_t fromlen = sizeof(struct sockaddr_in6);
|
||||
assert (sizeof(from) <= fromlen);
|
||||
--
|
||||
2.9.3
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
From 146b58d11fddbef15b888906e3be4f33900c416f Mon Sep 17 00:00:00 2001
|
||||
From: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue, 29 Mar 2016 12:57:56 +0200
|
||||
Subject: [PATCH] CVE-2016-3075: Stack overflow in _nss_dns_getnetbyname_r [BZ
|
||||
#19879]
|
||||
|
||||
The defensive copy is not needed because the name may not alias the
|
||||
output buffer.
|
||||
|
||||
(cherry picked from commit 317b199b4aff8cfa27f2302ab404d2bb5032b9a4)
|
||||
(cherry picked from commit 883dceebc8f11921a9890211a4e202e5be17562f)
|
||||
---
|
||||
ChangeLog | 7 +++++++
|
||||
NEWS | 10 ++++++++--
|
||||
resolv/nss_dns/dns-network.c | 5 +----
|
||||
3 files changed, 16 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/resolv/nss_dns/dns-network.c b/resolv/nss_dns/dns-network.c
|
||||
index 2eb2f67..8f301a7 100644
|
||||
--- a/resolv/nss_dns/dns-network.c
|
||||
+++ b/resolv/nss_dns/dns-network.c
|
||||
@@ -118,17 +118,14 @@ _nss_dns_getnetbyname_r (const char *name, struct netent *result,
|
||||
} net_buffer;
|
||||
querybuf *orig_net_buffer;
|
||||
int anslen;
|
||||
- char *qbuf;
|
||||
enum nss_status status;
|
||||
|
||||
if (__res_maybe_init (&_res, 0) == -1)
|
||||
return NSS_STATUS_UNAVAIL;
|
||||
|
||||
- qbuf = strdupa (name);
|
||||
-
|
||||
net_buffer.buf = orig_net_buffer = (querybuf *) alloca (1024);
|
||||
|
||||
- anslen = __libc_res_nsearch (&_res, qbuf, C_IN, T_PTR, net_buffer.buf->buf,
|
||||
+ anslen = __libc_res_nsearch (&_res, name, C_IN, T_PTR, net_buffer.buf->buf,
|
||||
1024, &net_buffer.ptr, NULL, NULL, NULL, NULL);
|
||||
if (anslen < 0)
|
||||
{
|
||||
--
|
||||
2.9.3
|
||||
|
|
@ -1,188 +0,0 @@
|
|||
From 1a8a7c12950a0026a3c406a7cb1608f96aa1460e Mon Sep 17 00:00:00 2001
|
||||
From: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri, 29 Apr 2016 10:35:34 +0200
|
||||
Subject: [PATCH] CVE-2016-3706: getaddrinfo: stack overflow in hostent
|
||||
conversion [BZ #20010]
|
||||
|
||||
When converting a struct hostent response to struct gaih_addrtuple, the
|
||||
gethosts macro (which is called from gaih_inet) used alloca, without
|
||||
malloc fallback for large responses. This commit changes this code to
|
||||
use calloc unconditionally.
|
||||
|
||||
This commit also consolidated a second hostent-to-gaih_addrtuple
|
||||
conversion loop (in gaih_inet) to use the new conversion function.
|
||||
|
||||
(cherry picked from commit 4ab2ab03d4351914ee53248dc5aef4a8c88ff8b9)
|
||||
---
|
||||
ChangeLog | 10 ++++
|
||||
sysdeps/posix/getaddrinfo.c | 130 +++++++++++++++++++++++---------------------
|
||||
2 files changed, 79 insertions(+), 61 deletions(-)
|
||||
|
||||
diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
|
||||
index 1ef3f20..fed2d3b 100644
|
||||
--- a/sysdeps/posix/getaddrinfo.c
|
||||
+++ b/sysdeps/posix/getaddrinfo.c
|
||||
@@ -168,9 +168,58 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/* Convert struct hostent to a list of struct gaih_addrtuple objects.
|
||||
+ h_name is not copied, and the struct hostent object must not be
|
||||
+ deallocated prematurely. *RESULT must be NULL or a pointer to an
|
||||
+ object allocated using malloc, which is freed. */
|
||||
+static bool
|
||||
+convert_hostent_to_gaih_addrtuple (const struct addrinfo *req,
|
||||
+ int family,
|
||||
+ struct hostent *h,
|
||||
+ struct gaih_addrtuple **result)
|
||||
+{
|
||||
+ free (*result);
|
||||
+ *result = NULL;
|
||||
+
|
||||
+ /* Count the number of addresses in h->h_addr_list. */
|
||||
+ size_t count = 0;
|
||||
+ for (char **p = h->h_addr_list; *p != NULL; ++p)
|
||||
+ ++count;
|
||||
+
|
||||
+ /* Report no data if no addresses are available, or if the incoming
|
||||
+ address size is larger than what we can store. */
|
||||
+ if (count == 0 || h->h_length > sizeof (((struct gaih_addrtuple) {}).addr))
|
||||
+ return true;
|
||||
+
|
||||
+ struct gaih_addrtuple *array = calloc (count, sizeof (*array));
|
||||
+ if (array == NULL)
|
||||
+ return false;
|
||||
+
|
||||
+ for (size_t i = 0; i < count; ++i)
|
||||
+ {
|
||||
+ if (family == AF_INET && req->ai_family == AF_INET6)
|
||||
+ {
|
||||
+ /* Perform address mapping. */
|
||||
+ array[i].family = AF_INET6;
|
||||
+ memcpy(array[i].addr + 3, h->h_addr_list[i], sizeof (uint32_t));
|
||||
+ array[i].addr[2] = htonl (0xffff);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ array[i].family = family;
|
||||
+ memcpy (array[i].addr, h->h_addr_list[i], h->h_length);
|
||||
+ }
|
||||
+ array[i].next = array + i + 1;
|
||||
+ }
|
||||
+ array[0].name = h->h_name;
|
||||
+ array[count - 1].next = NULL;
|
||||
+
|
||||
+ *result = array;
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
#define gethosts(_family, _type) \
|
||||
{ \
|
||||
- int i; \
|
||||
int herrno; \
|
||||
struct hostent th; \
|
||||
struct hostent *h; \
|
||||
@@ -219,36 +268,23 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
|
||||
} \
|
||||
else if (h != NULL) \
|
||||
{ \
|
||||
- for (i = 0; h->h_addr_list[i]; i++) \
|
||||
+ /* Make sure that addrmem can be freed. */ \
|
||||
+ if (!malloc_addrmem) \
|
||||
+ addrmem = NULL; \
|
||||
+ if (!convert_hostent_to_gaih_addrtuple (req, _family,h, &addrmem)) \
|
||||
{ \
|
||||
- if (*pat == NULL) \
|
||||
- { \
|
||||
- *pat = __alloca (sizeof (struct gaih_addrtuple)); \
|
||||
- (*pat)->scopeid = 0; \
|
||||
- } \
|
||||
- uint32_t *addr = (*pat)->addr; \
|
||||
- (*pat)->next = NULL; \
|
||||
- (*pat)->name = i == 0 ? strdupa (h->h_name) : NULL; \
|
||||
- if (_family == AF_INET && req->ai_family == AF_INET6) \
|
||||
- { \
|
||||
- (*pat)->family = AF_INET6; \
|
||||
- addr[3] = *(uint32_t *) h->h_addr_list[i]; \
|
||||
- addr[2] = htonl (0xffff); \
|
||||
- addr[1] = 0; \
|
||||
- addr[0] = 0; \
|
||||
- } \
|
||||
- else \
|
||||
- { \
|
||||
- (*pat)->family = _family; \
|
||||
- memcpy (addr, h->h_addr_list[i], sizeof(_type)); \
|
||||
- } \
|
||||
- pat = &((*pat)->next); \
|
||||
+ _res.options |= old_res_options & RES_USE_INET6; \
|
||||
+ result = -EAI_SYSTEM; \
|
||||
+ goto free_and_return; \
|
||||
} \
|
||||
+ *pat = addrmem; \
|
||||
+ /* The conversion uses malloc unconditionally. */ \
|
||||
+ malloc_addrmem = true; \
|
||||
\
|
||||
if (localcanon != NULL && canon == NULL) \
|
||||
canon = strdupa (localcanon); \
|
||||
\
|
||||
- if (_family == AF_INET6 && i > 0) \
|
||||
+ if (_family == AF_INET6 && *pat != NULL) \
|
||||
got_ipv6 = true; \
|
||||
} \
|
||||
}
|
||||
@@ -612,44 +648,16 @@ gaih_inet (const char *name, const struct gaih_service *service,
|
||||
{
|
||||
if (h != NULL)
|
||||
{
|
||||
- int i;
|
||||
- /* We found data, count the number of addresses. */
|
||||
- for (i = 0; h->h_addr_list[i]; ++i)
|
||||
- ;
|
||||
- if (i > 0 && *pat != NULL)
|
||||
- --i;
|
||||
-
|
||||
- if (__libc_use_alloca (alloca_used
|
||||
- + i * sizeof (struct gaih_addrtuple)))
|
||||
- addrmem = alloca_account (i * sizeof (struct gaih_addrtuple),
|
||||
- alloca_used);
|
||||
- else
|
||||
- {
|
||||
- addrmem = malloc (i
|
||||
- * sizeof (struct gaih_addrtuple));
|
||||
- if (addrmem == NULL)
|
||||
- {
|
||||
- result = -EAI_MEMORY;
|
||||
- goto free_and_return;
|
||||
- }
|
||||
- malloc_addrmem = true;
|
||||
- }
|
||||
-
|
||||
- /* Now convert it into the list. */
|
||||
- struct gaih_addrtuple *addrfree = addrmem;
|
||||
- for (i = 0; h->h_addr_list[i]; ++i)
|
||||
+ /* We found data, convert it. */
|
||||
+ if (!convert_hostent_to_gaih_addrtuple
|
||||
+ (req, AF_INET, h, &addrmem))
|
||||
{
|
||||
- if (*pat == NULL)
|
||||
- {
|
||||
- *pat = addrfree++;
|
||||
- (*pat)->scopeid = 0;
|
||||
- }
|
||||
- (*pat)->next = NULL;
|
||||
- (*pat)->family = AF_INET;
|
||||
- memcpy ((*pat)->addr, h->h_addr_list[i],
|
||||
- h->h_length);
|
||||
- pat = &((*pat)->next);
|
||||
+ result = -EAI_MEMORY;
|
||||
+ goto free_and_return;
|
||||
}
|
||||
+ *pat = addrmem;
|
||||
+ /* The conversion uses malloc unconditionally. */
|
||||
+ malloc_addrmem = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
--
|
||||
2.9.3
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
From bdce95930e1d9a7d013d1ba78740243491262879 Mon Sep 17 00:00:00 2001
|
||||
From: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon, 23 May 2016 20:18:34 +0200
|
||||
Subject: [PATCH] CVE-2016-4429: sunrpc: Do not use alloca in clntudp_call [BZ
|
||||
#20112]
|
||||
|
||||
The call is technically in a loop, and under certain circumstances
|
||||
(which are quite difficult to reproduce in a test case), alloca
|
||||
can be invoked repeatedly during a single call to clntudp_call.
|
||||
As a result, the available stack space can be exhausted (even
|
||||
though individual alloca sizes are bounded implicitly by what
|
||||
can fit into a UDP packet, as a side effect of the earlier
|
||||
successful send operation).
|
||||
|
||||
(cherry picked from commit bc779a1a5b3035133024b21e2f339fe4219fb11c)
|
||||
---
|
||||
ChangeLog | 7 +++++++
|
||||
NEWS | 4 ++++
|
||||
sunrpc/clnt_udp.c | 10 +++++++++-
|
||||
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/sunrpc/clnt_udp.c b/sunrpc/clnt_udp.c
|
||||
index a6cf5f1..4d9acb1 100644
|
||||
--- a/sunrpc/clnt_udp.c
|
||||
+++ b/sunrpc/clnt_udp.c
|
||||
@@ -388,9 +388,15 @@ send_again:
|
||||
struct sock_extended_err *e;
|
||||
struct sockaddr_in err_addr;
|
||||
struct iovec iov;
|
||||
- char *cbuf = (char *) alloca (outlen + 256);
|
||||
+ char *cbuf = malloc (outlen + 256);
|
||||
int ret;
|
||||
|
||||
+ if (cbuf == NULL)
|
||||
+ {
|
||||
+ cu->cu_error.re_errno = errno;
|
||||
+ return (cu->cu_error.re_status = RPC_CANTRECV);
|
||||
+ }
|
||||
+
|
||||
iov.iov_base = cbuf + 256;
|
||||
iov.iov_len = outlen;
|
||||
msg.msg_name = (void *) &err_addr;
|
||||
@@ -415,10 +421,12 @@ send_again:
|
||||
cmsg = CMSG_NXTHDR (&msg, cmsg))
|
||||
if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_RECVERR)
|
||||
{
|
||||
+ free (cbuf);
|
||||
e = (struct sock_extended_err *) CMSG_DATA(cmsg);
|
||||
cu->cu_error.re_errno = e->ee_errno;
|
||||
return (cu->cu_error.re_status = RPC_CANTRECV);
|
||||
}
|
||||
+ free (cbuf);
|
||||
}
|
||||
#endif
|
||||
do
|
||||
--
|
||||
2.9.3
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,206 +0,0 @@
|
|||
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
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
This fixes <https://sourceware.org/bugzilla/show_bug.cgi?id=18781>
|
||||
whereby, on 32-bit platforms, libc 2.22 would fail to pass O_LARGEFILE
|
||||
to 'openat'. This was caught by 'tests/sparse03.at' in the tar
|
||||
test suite.
|
||||
|
||||
commit eb32b0d40308166c4d8f6330cc2958cb1e545075
|
||||
Author: Andreas Schwab <schwab@suse.de>
|
||||
Date: Mon Aug 10 14:12:47 2015 +0200
|
||||
|
||||
Readd O_LARGEFILE flag for openat64 (bug 18781)
|
||||
|
||||
--- a/sysdeps/unix/sysv/linux/openat.c
|
||||
+++ b/sysdeps/unix/sysv/linux/openat.c
|
||||
@@ -68,6 +68,11 @@ __OPENAT (int fd, const char *file, int oflag, ...)
|
||||
va_end (arg);
|
||||
}
|
||||
|
||||
+ /* We have to add the O_LARGEFILE flag for openat64. */
|
||||
+#ifdef MORE_OFLAGS
|
||||
+ oflag |= MORE_OFLAGS;
|
||||
+#endif
|
||||
+
|
||||
return SYSCALL_CANCEL (openat, fd, file, oflag, mode);
|
||||
}
|
||||
libc_hidden_def (__OPENAT)
|
|
@ -1,23 +0,0 @@
|
|||
Copied from Debian.
|
||||
|
||||
2017-06-14 Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
* sysdeps/i386/i686/multiarch/strcspn-c.c: Add IS_IN (libc) guard.
|
||||
* sysdeps/i386/i686/multiarch/varshift.c: Likewise.
|
||||
|
||||
--- a/sysdeps/i386/i686/multiarch/strcspn-c.c
|
||||
+++ b/sysdeps/i386/i686/multiarch/strcspn-c.c
|
||||
@@ -1,2 +1,4 @@
|
||||
-#define __strcspn_sse2 __strcspn_ia32
|
||||
-#include <sysdeps/x86_64/multiarch/strcspn-c.c>
|
||||
+#if IS_IN (libc)
|
||||
+# define __strcspn_sse2 __strcspn_ia32
|
||||
+# include <sysdeps/x86_64/multiarch/strcspn-c.c>
|
||||
+#endif
|
||||
--- a/sysdeps/i386/i686/multiarch/varshift.c
|
||||
+++ b/sysdeps/i386/i686/multiarch/varshift.c
|
||||
@@ -1 +1,3 @@
|
||||
-#include <sysdeps/x86_64/multiarch/varshift.c>
|
||||
+#if IS_IN (libc)
|
||||
+# include <sysdeps/x86_64/multiarch/varshift.c>
|
||||
+#endif
|
Reference in New Issue