summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSona Sarmadi <sona.sarmadi@enea.com>2017-09-11 13:15:37 +0200
committerMartin Borg <martin.borg@enea.com>2017-09-11 15:56:34 +0200
commitc5b7ab645351fd3767d096a3c500355a9a896878 (patch)
tree1e3155d6aa55c045af8c494cb69607f9c1816a04
parentdd30c9d0cab364eecd2ad387817568f699231733 (diff)
downloadmeta-el-common-c5b7ab645351fd3767d096a3c500355a9a896878.tar.gz
glibc:CVE-2017-12132
The DNS stub resolver in the glibc or libc6 before version 2.26, when EDNS support is enabled, will solicit large UDP responses from name servers, potentially simplifying off-path DNS spoofing attacks due to IP fragmentation. Reference: https://security-tracker.debian.org/tracker/CVE-2017-12132 Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com> Signed-off-by: Martin Borg <martin.borg@enea.com>
-rw-r--r--recipes-core/glibc/glibc/CVE-2017-12132.patch866
-rw-r--r--recipes-core/glibc/glibc_%.bbappend1
2 files changed, 867 insertions, 0 deletions
diff --git a/recipes-core/glibc/glibc/CVE-2017-12132.patch b/recipes-core/glibc/glibc/CVE-2017-12132.patch
new file mode 100644
index 0000000..dc184db
--- /dev/null
+++ b/recipes-core/glibc/glibc/CVE-2017-12132.patch
@@ -0,0 +1,866 @@
1From 44cf81c6c008316876cfcc8208ae982621949e0e Mon Sep 17 00:00:00 2001
2From: Sona Sarmadi <sona.sarmadi@enea.com>
3Date: Mon, 11 Sep 2017 10:55:45 +0200
4Subject: [PATCH] glibc: CVE-2017-12132
5
6From e14a27723cc3a154d67f3f26e719d08c0ba9ad25 Mon Sep 17 00:00:00 2001
7From: Florian Weimer <fweimer@redhat.com>
8Date: Thu, 13 Apr 2017 13:09:38 +0200
9Subject: [PATCH] resolv: Reduce EDNS payload size to 1200 bytes [BZ #21361]
10
11This hardens the stub resolver against fragmentation-based attacks.
12
13CVE: CVE-2017-12132
14Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=patch;h=e14a27723cc3a154d67f3f26e719d08c0ba9ad25]
15
16Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com>
17---
18 ChangeLog | 21 ++
19 include/resolv.h | 3 -
20 resolv/Makefile | 2 +
21 resolv/res_mkquery.c | 28 ++-
22 resolv/res_query.c | 23 ++-
23 resolv/resolv-internal.h | 18 ++
24 resolv/tst-resolv-edns.c | 501 +++++++++++++++++++++++++++++++++++++++++++++++
25 support/resolv_test.c | 56 +++++-
26 support/resolv_test.h | 11 ++
27 9 files changed, 650 insertions(+), 13 deletions(-)
28 create mode 100644 resolv/tst-resolv-edns.c
29
30diff --git a/ChangeLog b/ChangeLog
31index 7bfdf45..a0c2f51 100644
32--- a/ChangeLog
33+++ b/ChangeLog
34@@ -1,3 +1,24 @@
35+2017-04-13 Florian Weimer <fweimer@redhat.com>
36+
37+ [BZ #21361]
38+ Limit EDNS buffer size to 1200 bytes.
39+ * include/resolv.h (__res_nopt): Remove declaration.
40+ * resolv/Makefile (tests): tst-resolv-edns.
41+ (tst-resolv-edns): Link with -lresolv, -lpthread.
42+ * resolv/res_mkquery.c (__res_ntop): Limit EDNS buffer size to the
43+ interval [512, 1200].
44+ * resolv/res_query.c (__libc_res_nquery): Use 1200 buffer size if
45+ we can resize the buffer.
46+ * resolv/resolv-internal.h (RESOLV_EDNS_BUFFER_SIZE): Define.
47+ (__res_nopt): Declare.
48+ * resolv/tst-resolv-edns.c: New file.
49+ * resolv/resolv_test.h (struct resolv_edns_info): Define.
50+ (struct resolv_response_context): Add edns member.
51+ * resolv/resolv_test.c (struct query_info): Add edns member.
52+ (parse_query): Extract EDNS information from the query.
53+ (server_thread_udp_process_one): Propagate EDNS data.
54+ (server_thread_tcp_client): Likewise.
55+
56 2017-06-19 Florian Weimer <fweimer@redhat.com>
57
58 [BZ #21624]
59diff --git a/include/resolv.h b/include/resolv.h
60index 95dcd3c..e8f477c 100644
61--- a/include/resolv.h
62+++ b/include/resolv.h
63@@ -37,8 +37,6 @@ extern void res_pquery (const res_state __statp, const unsigned char *__msg,
64 extern int res_ourserver_p (const res_state __statp,
65 const struct sockaddr_in6 *__inp);
66 extern void __res_iclose (res_state statp, bool free_addr);
67-extern int __res_nopt(res_state statp, int n0, unsigned char *buf, int buflen,
68- int anslen);
69 libc_hidden_proto (__res_ninit)
70 libc_hidden_proto (__res_maybe_init)
71 libc_hidden_proto (__res_nclose)
72@@ -91,7 +89,6 @@ libresolv_hidden_proto (__res_nameinquery)
73 libresolv_hidden_proto (__res_queriesmatch)
74 libresolv_hidden_proto (__res_nsend)
75 libresolv_hidden_proto (__b64_ntop)
76-libresolv_hidden_proto (__res_nopt)
77 libresolv_hidden_proto (__dn_count_labels)
78 libresolv_hidden_proto (__p_secstodate)
79
80diff --git a/resolv/Makefile b/resolv/Makefile
81index fdc37ed..01086d5 100644
82--- a/resolv/Makefile
83+++ b/resolv/Makefile
84@@ -46,6 +46,7 @@ tests += \
85 tst-res_hconf_reorder \
86 tst-res_use_inet6 \
87 tst-resolv-basic \
88+ tst-resolv-edns \
89 tst-resolv-network \
90 tst-resolv-search \
91
92@@ -124,6 +125,7 @@ $(objpfx)tst-bug18665-tcp: $(objpfx)libresolv.so $(shared-thread-library)
93 $(objpfx)tst-bug18665: $(objpfx)libresolv.so $(shared-thread-library)
94 $(objpfx)tst-res_use_inet6: $(objpfx)libresolv.so $(shared-thread-library)
95 $(objpfx)tst-resolv-basic: $(objpfx)libresolv.so $(shared-thread-library)
96+$(objpfx)tst-resolv-edns: $(objpfx)libresolv.so $(shared-thread-library)
97 $(objpfx)tst-resolv-network: $(objpfx)libresolv.so $(shared-thread-library)
98 $(objpfx)tst-resolv-qtypes: $(objpfx)libresolv.so $(shared-thread-library)
99 $(objpfx)tst-resolv-search: $(objpfx)libresolv.so $(shared-thread-library)
100diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c
101index d80b531..5a0bb10 100644
102--- a/resolv/res_mkquery.c
103+++ b/resolv/res_mkquery.c
104@@ -69,7 +69,7 @@
105 #include <netinet/in.h>
106 #include <arpa/nameser.h>
107 #include <netdb.h>
108-#include <resolv.h>
109+#include <resolv/resolv-internal.h>
110 #include <stdio.h>
111 #include <string.h>
112 #include <sys/time.h>
113@@ -243,7 +243,30 @@ __res_nopt(res_state statp,
114 *cp++ = 0; /* "." */
115
116 NS_PUT16(T_OPT, cp); /* TYPE */
117- NS_PUT16(MIN(anslen, 0xffff), cp); /* CLASS = UDP payload size */
118+
119+ /* Lowering the advertised buffer size based on the actual
120+ answer buffer size is desirable because the server will
121+ minimize the reply to fit into the UDP packet (and A
122+ non-minimal response might not fit the buffer).
123+
124+ The RESOLV_EDNS_BUFFER_SIZE limit could still result in TCP
125+ fallback and a non-minimal response which has to be
126+ hard-truncated in the stub resolver, but this is price to
127+ pay for avoiding fragmentation. (This issue does not
128+ affect the nss_dns functions because they use the stub
129+ resolver in such a way that it allocates a properly sized
130+ response buffer.) */
131+ {
132+ uint16_t buffer_size;
133+ if (anslen < 512)
134+ buffer_size = 512;
135+ else if (anslen > RESOLV_EDNS_BUFFER_SIZE)
136+ buffer_size = RESOLV_EDNS_BUFFER_SIZE;
137+ else
138+ buffer_size = anslen;
139+ NS_PUT16 (buffer_size, cp);
140+ }
141+
142 *cp++ = NOERROR; /* extended RCODE */
143 *cp++ = 0; /* EDNS version */
144
145@@ -261,4 +284,3 @@ __res_nopt(res_state statp,
146
147 return cp - buf;
148 }
149-libresolv_hidden_def (__res_nopt)
150diff --git a/resolv/res_query.c b/resolv/res_query.c
151index 07dc6f6..57156d0 100644
152--- a/resolv/res_query.c
153+++ b/resolv/res_query.c
154@@ -77,6 +77,7 @@
155 #include <stdio.h>
156 #include <stdlib.h>
157 #include <string.h>
158+#include <resolv/resolv-internal.h>
159
160 /* Options. Leave them on. */
161 /* #undef DEBUG */
162@@ -146,7 +147,10 @@ __libc_res_nquery(res_state statp,
163 if ((oflags & RES_F_EDNS0ERR) == 0
164 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
165 {
166- n = __res_nopt(statp, n, query1, bufsize, anslen / 2);
167+ /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
168+ buffer can be reallocated. */
169+ n = __res_nopt (statp, n, query1, bufsize,
170+ RESOLV_EDNS_BUFFER_SIZE);
171 if (n < 0)
172 goto unspec_nomem;
173 }
174@@ -167,8 +171,10 @@ __libc_res_nquery(res_state statp,
175 if (n > 0
176 && (oflags & RES_F_EDNS0ERR) == 0
177 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
178- n = __res_nopt(statp, n, query2, bufsize - nused - n,
179- anslen / 2);
180+ /* Use RESOLV_EDNS_BUFFER_SIZE because the receive
181+ buffer can be reallocated. */
182+ n = __res_nopt (statp, n, query2, bufsize,
183+ RESOLV_EDNS_BUFFER_SIZE);
184 nquery2 = n;
185 }
186
187@@ -182,7 +188,16 @@ __libc_res_nquery(res_state statp,
188 if (n > 0
189 && (oflags & RES_F_EDNS0ERR) == 0
190 && (statp->options & (RES_USE_EDNS0|RES_USE_DNSSEC)) != 0)
191- n = __res_nopt(statp, n, query1, bufsize, anslen);
192+ {
193+ /* Use RESOLV_EDNS_BUFFER_SIZE if the receive buffer
194+ can be reallocated. */
195+ size_t advertise;
196+ if (answerp == NULL)
197+ advertise = anslen;
198+ else
199+ advertise = RESOLV_EDNS_BUFFER_SIZE;
200+ n = __res_nopt (statp, n, query1, bufsize, advertise);
201+ }
202
203 nquery1 = n;
204 }
205diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h
206index 99fc17c..76fbe2f 100644
207--- a/resolv/resolv-internal.h
208+++ b/resolv/resolv-internal.h
209@@ -32,4 +32,22 @@ res_use_inet6 (void)
210 return _res.options & DEPRECATED_RES_USE_INET6;
211 }
212
213+enum
214+ {
215+ /* The advertized EDNS buffer size. The value 1200 is derived
216+ from the IPv6 minimum MTU (1280 bytes) minus some arbitrary
217+ space for tunneling overhead. If the DNS server does not react
218+ to ICMP Fragmentation Needed But DF Set messages, this should
219+ avoid all UDP fragments on current networks. Avoiding UDP
220+ fragments is desirable because it prevents fragmentation-based
221+ spoofing attacks because the randomness in a DNS packet is
222+ concentrated in the first fragment (with the headers) and does
223+ not protect subsequent fragments. */
224+ RESOLV_EDNS_BUFFER_SIZE = 1200,
225+ };
226+
227+/* Add an OPT record to a DNS query. */
228+int __res_nopt (res_state, int n0, unsigned char *buf, int buflen,
229+ int anslen) attribute_hidden;
230+
231 #endif /* _RESOLV_INTERNAL_H */
232diff --git a/resolv/tst-resolv-edns.c b/resolv/tst-resolv-edns.c
233new file mode 100644
234index 0000000..f17dbc3
235--- /dev/null
236+++ b/resolv/tst-resolv-edns.c
237@@ -0,0 +1,501 @@
238+/* Test EDNS handling in the stub resolver.
239+ Copyright (C) 2016-2017 Free Software Foundation, Inc.
240+ This file is part of the GNU C Library.
241+
242+ The GNU C Library is free software; you can redistribute it and/or
243+ modify it under the terms of the GNU Lesser General Public
244+ License as published by the Free Software Foundation; either
245+ version 2.1 of the License, or (at your option) any later version.
246+
247+ The GNU C Library is distributed in the hope that it will be useful,
248+ but WITHOUT ANY WARRANTY; without even the implied warranty of
249+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
250+ Lesser General Public License for more details.
251+
252+ You should have received a copy of the GNU Lesser General Public
253+ License along with the GNU C Library; if not, see
254+ <http://www.gnu.org/licenses/>. */
255+
256+#include <errno.h>
257+#include <netdb.h>
258+#include <stdio.h>
259+#include <stdlib.h>
260+#include <string.h>
261+#include <support/check.h>
262+#include <support/resolv_test.h>
263+#include <support/support.h>
264+#include <support/test-driver.h>
265+#include <support/xthread.h>
266+
267+/* Data produced by a test query. */
268+struct response_data
269+{
270+ char *qname;
271+ uint16_t qtype;
272+ struct resolv_edns_info edns;
273+};
274+
275+/* Global array used by put_response and get_response to record
276+ response data. The test DNS server returns the index of the array
277+ element which contains the actual response data. This enables the
278+ test case to return arbitrary amounts of data with the limited
279+ number of bits which fit into an IP addres.
280+
281+ The volatile specifier is needed because the test case accesses
282+ these variables from a callback function called from a function
283+ which is marked as __THROW (i.e., a leaf function which actually is
284+ not). */
285+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
286+static struct response_data ** volatile response_data_array;
287+volatile static size_t response_data_count;
288+
289+/* Extract information from the query, store it in a struct
290+ response_data object, and return its index in the
291+ response_data_array. */
292+static unsigned int
293+put_response (const struct resolv_response_context *ctx,
294+ const char *qname, uint16_t qtype)
295+{
296+ xpthread_mutex_lock (&mutex);
297+ ++response_data_count;
298+ /* We only can represent 2**24 indexes in 10.0.0.0/8. */
299+ TEST_VERIFY (response_data_count < (1 << 24));
300+ response_data_array = xrealloc
301+ (response_data_array, sizeof (*response_data_array) * response_data_count);
302+ unsigned int index = response_data_count - 1;
303+ struct response_data *data = xmalloc (sizeof (*data));
304+ *data = (struct response_data)
305+ {
306+ .qname = xstrdup (qname),
307+ .qtype = qtype,
308+ .edns = ctx->edns,
309+ };
310+ response_data_array[index] = data;
311+ xpthread_mutex_unlock (&mutex);
312+ return index;
313+}
314+
315+/* Verify the index into the response_data array and return the data
316+ at it. */
317+static struct response_data *
318+get_response (unsigned int index)
319+{
320+ xpthread_mutex_lock (&mutex);
321+ TEST_VERIFY_EXIT (index < response_data_count);
322+ struct response_data *result = response_data_array[index];
323+ xpthread_mutex_unlock (&mutex);
324+ return result;
325+}
326+
327+/* Deallocate all response data. */
328+static void
329+free_response_data (void)
330+{
331+ xpthread_mutex_lock (&mutex);
332+ size_t count = response_data_count;
333+ struct response_data **array = response_data_array;
334+ for (unsigned int i = 0; i < count; ++i)
335+ {
336+ struct response_data *data = array[i];
337+ free (data->qname);
338+ free (data);
339+ }
340+ free (array);
341+ response_data_array = NULL;
342+ response_data_count = 0;
343+ xpthread_mutex_unlock (&mutex);
344+}
345+
346+#define EDNS_PROBE_EXAMPLE "edns-probe.example"
347+
348+static void
349+response (const struct resolv_response_context *ctx,
350+ struct resolv_response_builder *b,
351+ const char *qname, uint16_t qclass, uint16_t qtype)
352+{
353+ TEST_VERIFY_EXIT (qname != NULL);
354+
355+ /* The "tcp." prefix can be used to request TCP fallback. */
356+ const char *qname_compare = qname;
357+ bool force_tcp;
358+ if (strncmp ("tcp.", qname_compare, strlen ("tcp.")) == 0)
359+ {
360+ force_tcp = true;
361+ qname_compare += strlen ("tcp.");
362+ }
363+ else
364+ force_tcp = false;
365+
366+ enum {edns_probe} requested_qname;
367+ if (strcmp (qname_compare, EDNS_PROBE_EXAMPLE) == 0)
368+ requested_qname = edns_probe;
369+ else
370+ {
371+ support_record_failure ();
372+ printf ("error: unexpected QNAME: %s\n", qname);
373+ return;
374+ }
375+ TEST_VERIFY_EXIT (qclass == C_IN);
376+ struct resolv_response_flags flags = {.tc = force_tcp && !ctx->tcp};
377+ resolv_response_init (b, flags);
378+ resolv_response_add_question (b, qname, qclass, qtype);
379+ if (flags.tc)
380+ return;
381+
382+ if (test_verbose)
383+ printf ("info: edns=%d payload_size=%d\n",
384+ ctx->edns.active, ctx->edns.payload_size);
385+
386+ /* Encode the response_data object in multiple address records.
387+ Each record carries two bytes of payload data, and an index. */
388+ resolv_response_section (b, ns_s_an);
389+ switch (requested_qname)
390+ {
391+ case edns_probe:
392+ {
393+ unsigned int index = put_response (ctx, qname, qtype);
394+ switch (qtype)
395+ {
396+ case T_A:
397+ {
398+ uint32_t addr = htonl (0x0a000000 | index);
399+ resolv_response_open_record (b, qname, qclass, qtype, 0);
400+ resolv_response_add_data (b, &addr, sizeof (addr));
401+ resolv_response_close_record (b);
402+ }
403+ break;
404+ case T_AAAA:
405+ {
406+ char addr[16]
407+ = {0x20, 0x01, 0xd, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0,
408+ index >> 16, index >> 8, index};
409+ resolv_response_open_record (b, qname, qclass, qtype, 0);
410+ resolv_response_add_data (b, &addr, sizeof (addr));
411+ resolv_response_close_record (b);
412+ }
413+ }
414+ }
415+ break;
416+ }
417+}
418+
419+/* Update *DATA with data from ADDRESS of SIZE. Set the corresponding
420+ flag in SHADOW for each byte written. */
421+static struct response_data *
422+decode_address (const void *address, size_t size)
423+{
424+ switch (size)
425+ {
426+ case 4:
427+ TEST_VERIFY (memcmp (address, "\x0a", 1) == 0);
428+ break;
429+ case 16:
430+ TEST_VERIFY (memcmp (address, "\x20\x01\x0d\xb8", 4) == 0);
431+ break;
432+ default:
433+ FAIL_EXIT1 ("unexpected address size %zu", size);
434+ }
435+ const unsigned char *addr = address;
436+ unsigned int index = addr[size - 3] * 256 * 256
437+ + addr[size - 2] * 256
438+ + addr[size - 1];
439+ return get_response (index);
440+}
441+
442+static struct response_data *
443+decode_hostent (struct hostent *e)
444+{
445+ TEST_VERIFY_EXIT (e != NULL);
446+ TEST_VERIFY_EXIT (e->h_addr_list[0] != NULL);
447+ TEST_VERIFY (e->h_addr_list[1] == NULL);
448+ return decode_address (e->h_addr_list[0], e->h_length);
449+}
450+
451+static struct response_data *
452+decode_addrinfo (struct addrinfo *ai, int family)
453+{
454+ struct response_data *data = NULL;
455+ while (ai != NULL)
456+ {
457+ if (ai->ai_family == family)
458+ {
459+ struct response_data *new_data;
460+ switch (family)
461+ {
462+ case AF_INET:
463+ {
464+ struct sockaddr_in *pin = (struct sockaddr_in *) ai->ai_addr;
465+ new_data = decode_address (&pin->sin_addr.s_addr, 4);
466+ }
467+ break;
468+ case AF_INET6:
469+ {
470+ struct sockaddr_in6 *pin = (struct sockaddr_in6 *) ai->ai_addr;
471+ new_data = decode_address (&pin->sin6_addr.s6_addr, 16);
472+ }
473+ break;
474+ default:
475+ FAIL_EXIT1 ("invalid address family %d", ai->ai_family);
476+ }
477+ if (data == NULL)
478+ data = new_data;
479+ else
480+ /* Check pointer equality because this should be the same
481+ response (same index). */
482+ TEST_VERIFY (data == new_data);
483+ }
484+ ai = ai->ai_next;
485+ }
486+ TEST_VERIFY_EXIT (data != NULL);
487+ return data;
488+}
489+
490+/* Updated by the main test loop in accordance with what is set in
491+ _res.options. */
492+static bool use_edns;
493+static bool use_dnssec;
494+
495+/* Verify the decoded response data against the flags above. */
496+static void
497+verify_response_data_payload (struct response_data *data,
498+ size_t expected_payload)
499+{
500+ bool edns = use_edns || use_dnssec;
501+ TEST_VERIFY (data->edns.active == edns);
502+ if (!edns)
503+ expected_payload = 0;
504+ if (data->edns.payload_size != expected_payload)
505+ {
506+ support_record_failure ();
507+ printf ("error: unexpected payload size %d (edns=%d)\n",
508+ (int) data->edns.payload_size, edns);
509+ }
510+ uint16_t expected_flags = 0;
511+ if (use_dnssec)
512+ expected_flags |= 0x8000; /* DO flag. */
513+ if (data->edns.flags != expected_flags)
514+ {
515+ support_record_failure ();
516+ printf ("error: unexpected EDNS flags 0x%04x (edns=%d)\n",
517+ (int) data->edns.flags, edns);
518+ }
519+}
520+
521+/* Same as verify_response_data_payload, but use the default
522+ payload. */
523+static void
524+verify_response_data (struct response_data *data)
525+{
526+ verify_response_data_payload (data, 1200);
527+}
528+
529+static void
530+check_hostent (struct hostent *e)
531+{
532+ TEST_VERIFY_EXIT (e != NULL);
533+ verify_response_data (decode_hostent (e));
534+}
535+
536+static void
537+do_ai (int family)
538+{
539+ struct addrinfo hints = { .ai_family = family };
540+ struct addrinfo *ai;
541+ int ret = getaddrinfo (EDNS_PROBE_EXAMPLE, "80", &hints, &ai);
542+ TEST_VERIFY_EXIT (ret == 0);
543+ switch (family)
544+ {
545+ case AF_INET:
546+ case AF_INET6:
547+ verify_response_data (decode_addrinfo (ai, family));
548+ break;
549+ case AF_UNSPEC:
550+ verify_response_data (decode_addrinfo (ai, AF_INET));
551+ verify_response_data (decode_addrinfo (ai, AF_INET6));
552+ break;
553+ default:
554+ FAIL_EXIT1 ("invalid address family %d", family);
555+ }
556+ freeaddrinfo (ai);
557+}
558+
559+enum res_op
560+{
561+ res_op_search,
562+ res_op_query,
563+ res_op_querydomain,
564+ res_op_nsearch,
565+ res_op_nquery,
566+ res_op_nquerydomain,
567+
568+ res_op_last = res_op_nquerydomain,
569+};
570+
571+static const char *
572+res_op_string (enum res_op op)
573+{
574+ switch (op)
575+ {
576+ case res_op_search:
577+ return "res_search";
578+ case res_op_query:
579+ return "res_query";
580+ case res_op_querydomain:
581+ return "res_querydomain";
582+ case res_op_nsearch:
583+ return "res_nsearch";
584+ case res_op_nquery:
585+ return "res_nquery";
586+ case res_op_nquerydomain:
587+ return "res_nquerydomain";
588+ }
589+ FAIL_EXIT1 ("invalid res_op value %d", (int) op);
590+}
591+
592+/* Call libresolv function OP to look up PROBE_NAME, with an answer
593+ buffer of SIZE bytes. Check that the advertised UDP buffer size is
594+ in fact EXPECTED_BUFFER_SIZE. */
595+static void
596+do_res_search (const char *probe_name, enum res_op op, size_t size,
597+ size_t expected_buffer_size)
598+{
599+ if (test_verbose)
600+ printf ("info: testing %s with buffer size %zu\n",
601+ res_op_string (op), size);
602+ unsigned char *buffer = xmalloc (size);
603+ int ret = -1;
604+ switch (op)
605+ {
606+ case res_op_search:
607+ ret = res_search (probe_name, C_IN, T_A, buffer, size);
608+ break;
609+ case res_op_query:
610+ ret = res_query (probe_name, C_IN, T_A, buffer, size);
611+ break;
612+ case res_op_nsearch:
613+ ret = res_nsearch (&_res, probe_name, C_IN, T_A, buffer, size);
614+ break;
615+ case res_op_nquery:
616+ ret = res_nquery (&_res, probe_name, C_IN, T_A, buffer, size);
617+ break;
618+ case res_op_querydomain:
619+ case res_op_nquerydomain:
620+ {
621+ char *example_stripped = xstrdup (probe_name);
622+ char *dot_example = strstr (example_stripped, ".example");
623+ if (dot_example != NULL && strcmp (dot_example, ".example") == 0)
624+ {
625+ /* Truncate the domain name. */
626+ *dot_example = '\0';
627+ if (op == res_op_querydomain)
628+ ret = res_querydomain
629+ (example_stripped, "example", C_IN, T_A, buffer, size);
630+ else
631+ ret = res_nquerydomain
632+ (&_res, example_stripped, "example", C_IN, T_A, buffer, size);
633+ }
634+ else
635+ FAIL_EXIT1 ("invalid probe name: %s", probe_name);
636+ free (example_stripped);
637+ }
638+ break;
639+ }
640+ TEST_VERIFY_EXIT (ret > 12);
641+ unsigned char *end = buffer + ret;
642+
643+ HEADER *hd = (HEADER *) buffer;
644+ TEST_VERIFY (ntohs (hd->qdcount) == 1);
645+ TEST_VERIFY (ntohs (hd->ancount) == 1);
646+ /* Skip over the header. */
647+ unsigned char *p = buffer + sizeof (*hd);
648+ /* Skip over the question. */
649+ ret = dn_skipname (p, end);
650+ TEST_VERIFY_EXIT (ret > 0);
651+ p += ret;
652+ TEST_VERIFY_EXIT (end - p >= 4);
653+ p += 4;
654+ /* Skip over the RNAME and the RR header, but stop at the RDATA
655+ length. */
656+ ret = dn_skipname (p, end);
657+ TEST_VERIFY_EXIT (ret > 0);
658+ p += ret;
659+ TEST_VERIFY_EXIT (end - p >= 2 + 2 + 4 + 2 + 4);
660+ p += 2 + 2 + 4;
661+ /* The IP address should be 4 bytes long. */
662+ TEST_VERIFY_EXIT (p[0] == 0);
663+ TEST_VERIFY_EXIT (p[1] == 4);
664+ /* Extract the address information. */
665+ p += 2;
666+ struct response_data *data = decode_address (p, 4);
667+
668+ verify_response_data_payload (data, expected_buffer_size);
669+
670+ free (buffer);
671+}
672+
673+static void
674+run_test (const char *probe_name)
675+{
676+ if (test_verbose)
677+ printf ("\ninfo: * use_edns=%d use_dnssec=%d\n",
678+ use_edns, use_dnssec);
679+ check_hostent (gethostbyname (probe_name));
680+ check_hostent (gethostbyname2 (probe_name, AF_INET));
681+ check_hostent (gethostbyname2 (probe_name, AF_INET6));
682+ do_ai (AF_UNSPEC);
683+ do_ai (AF_INET);
684+ do_ai (AF_INET6);
685+
686+ for (int op = 0; op <= res_op_last; ++op)
687+ {
688+ do_res_search (probe_name, op, 301, 512);
689+ do_res_search (probe_name, op, 511, 512);
690+ do_res_search (probe_name, op, 512, 512);
691+ do_res_search (probe_name, op, 513, 513);
692+ do_res_search (probe_name, op, 657, 657);
693+ do_res_search (probe_name, op, 1199, 1199);
694+ do_res_search (probe_name, op, 1200, 1200);
695+ do_res_search (probe_name, op, 1201, 1200);
696+ do_res_search (probe_name, op, 65535, 1200);
697+ }
698+}
699+
700+static int
701+do_test (void)
702+{
703+ for (int do_edns = 0; do_edns < 2; ++do_edns)
704+ for (int do_dnssec = 0; do_dnssec < 2; ++do_dnssec)
705+ for (int do_tcp = 0; do_tcp < 2; ++do_tcp)
706+ {
707+ struct resolv_test *aux = resolv_test_start
708+ ((struct resolv_redirect_config)
709+ {
710+ .response_callback = response,
711+ });
712+
713+ use_edns = do_edns;
714+ if (do_edns)
715+ _res.options |= RES_USE_EDNS0;
716+ use_dnssec = do_dnssec;
717+ if (do_dnssec)
718+ _res.options |= RES_USE_DNSSEC;
719+
720+ char *probe_name = xstrdup (EDNS_PROBE_EXAMPLE);
721+ if (do_tcp)
722+ {
723+ char *n = xasprintf ("tcp.%s", probe_name);
724+ free (probe_name);
725+ probe_name = n;
726+ }
727+
728+ run_test (probe_name);
729+
730+ free (probe_name);
731+ resolv_test_end (aux);
732+ }
733+
734+ free_response_data ();
735+ return 0;
736+}
737+
738+#include <support/test-driver.c>
739diff --git a/support/resolv_test.c b/support/resolv_test.c
740index 2d0ea3c..6b3554f 100644
741--- a/support/resolv_test.c
742+++ b/support/resolv_test.c
743@@ -428,6 +428,7 @@ struct query_info
744 char qname[MAXDNAME];
745 uint16_t qclass;
746 uint16_t qtype;
747+ struct resolv_edns_info edns;
748 };
749
750 /* Update *INFO from the specified DNS packet. */
751@@ -435,10 +436,26 @@ static void
752 parse_query (struct query_info *info,
753 const unsigned char *buffer, size_t length)
754 {
755- if (length < 12)
756+ HEADER hd;
757+ _Static_assert (sizeof (hd) == 12, "DNS header size");
758+ if (length < sizeof (hd))
759 FAIL_EXIT1 ("malformed DNS query: too short: %zu bytes", length);
760-
761- int ret = dn_expand (buffer, buffer + length, buffer + 12,
762+ memcpy (&hd, buffer, sizeof (hd));
763+
764+ if (ntohs (hd.qdcount) != 1)
765+ FAIL_EXIT1 ("malformed DNS query: wrong question count: %d",
766+ (int) ntohs (hd.qdcount));
767+ if (ntohs (hd.ancount) != 0)
768+ FAIL_EXIT1 ("malformed DNS query: wrong answer count: %d",
769+ (int) ntohs (hd.ancount));
770+ if (ntohs (hd.nscount) != 0)
771+ FAIL_EXIT1 ("malformed DNS query: wrong authority count: %d",
772+ (int) ntohs (hd.nscount));
773+ if (ntohs (hd.arcount) > 1)
774+ FAIL_EXIT1 ("malformed DNS query: wrong additional count: %d",
775+ (int) ntohs (hd.arcount));
776+
777+ int ret = dn_expand (buffer, buffer + length, buffer + sizeof (hd),
778 info->qname, sizeof (info->qname));
779 if (ret < 0)
780 FAIL_EXIT1 ("malformed DNS query: cannot uncompress QNAME");
781@@ -456,6 +473,37 @@ parse_query (struct query_info *info,
782 memcpy (&qtype_qclass, buffer + 12 + ret, sizeof (qtype_qclass));
783 info->qclass = ntohs (qtype_qclass.qclass);
784 info->qtype = ntohs (qtype_qclass.qtype);
785+
786+ memset (&info->edns, 0, sizeof (info->edns));
787+ if (ntohs (hd.arcount) > 0)
788+ {
789+ /* Parse EDNS record. */
790+ struct __attribute__ ((packed, aligned (1)))
791+ {
792+ uint8_t root;
793+ uint16_t rtype;
794+ uint16_t payload;
795+ uint8_t edns_extended_rcode;
796+ uint8_t edns_version;
797+ uint16_t flags;
798+ uint16_t rdatalen;
799+ } rr;
800+ _Static_assert (sizeof (rr) == 11, "EDNS record size");
801+
802+ if (remaining < 4 + sizeof (rr))
803+ FAIL_EXIT1 ("mailformed DNS query: no room for EDNS record");
804+ memcpy (&rr, buffer + 12 + ret + 4, sizeof (rr));
805+ if (rr.root != 0)
806+ FAIL_EXIT1 ("malformed DNS query: invalid OPT RNAME: %d\n", rr.root);
807+ if (rr.rtype != htons (41))
808+ FAIL_EXIT1 ("malformed DNS query: invalid OPT type: %d\n",
809+ ntohs (rr.rtype));
810+ info->edns.active = true;
811+ info->edns.extended_rcode = rr.edns_extended_rcode;
812+ info->edns.version = rr.edns_version;
813+ info->edns.flags = ntohs (rr.flags);
814+ info->edns.payload_size = ntohs (rr.payload);
815+ }
816 }
817
818
819@@ -585,6 +633,7 @@ server_thread_udp_process_one (struct resolv_test *obj, int server_index)
820 .query_length = length,
821 .server_index = server_index,
822 .tcp = false,
823+ .edns = qinfo.edns,
824 };
825 struct resolv_response_builder *b = response_builder_allocate (query, length);
826 obj->config.response_callback
827@@ -820,6 +869,7 @@ server_thread_tcp_client (void *arg)
828 .query_length = query_length,
829 .server_index = closure->server_index,
830 .tcp = true,
831+ .edns = qinfo.edns,
832 };
833 struct resolv_response_builder *b = response_builder_allocate
834 (query_buffer, query_length);
835diff --git a/support/resolv_test.h b/support/resolv_test.h
836index 7a9f1f7..6498751 100644
837--- a/support/resolv_test.h
838+++ b/support/resolv_test.h
839@@ -25,6 +25,16 @@
840
841 __BEGIN_DECLS
842
843+/* Information about EDNS properties of a DNS query. */
844+struct resolv_edns_info
845+{
846+ bool active;
847+ uint8_t extended_rcode;
848+ uint8_t version;
849+ uint16_t flags;
850+ uint16_t payload_size;
851+};
852+
853 /* This struct provides context information when the response callback
854 specified in struct resolv_redirect_config is invoked. */
855 struct resolv_response_context
856@@ -33,6 +43,7 @@ struct resolv_response_context
857 size_t query_length;
858 int server_index;
859 bool tcp;
860+ struct resolv_edns_info edns;
861 };
862
863 /* This opaque struct is used to construct responses from within the
864--
8651.9.1
866
diff --git a/recipes-core/glibc/glibc_%.bbappend b/recipes-core/glibc/glibc_%.bbappend
index 1ef0688..0e2cb2e 100644
--- a/recipes-core/glibc/glibc_%.bbappend
+++ b/recipes-core/glibc/glibc_%.bbappend
@@ -2,5 +2,6 @@
2FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" 2FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:"
3 3
4SRC_URI += "file://CVE-2017-1000366.patch \ 4SRC_URI += "file://CVE-2017-1000366.patch \
5 file://CVE-2017-12132.patch \
5 " 6 "
6 7