diff options
author | Sona Sarmadi <sona.sarmadi@enea.com> | 2017-09-11 13:15:37 +0200 |
---|---|---|
committer | Martin Borg <martin.borg@enea.com> | 2017-09-11 15:56:34 +0200 |
commit | c5b7ab645351fd3767d096a3c500355a9a896878 (patch) | |
tree | 1e3155d6aa55c045af8c494cb69607f9c1816a04 | |
parent | dd30c9d0cab364eecd2ad387817568f699231733 (diff) | |
download | meta-nfv-access-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.patch | 866 | ||||
-rw-r--r-- | recipes-core/glibc/glibc_%.bbappend | 1 |
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 @@ | |||
1 | From 44cf81c6c008316876cfcc8208ae982621949e0e Mon Sep 17 00:00:00 2001 | ||
2 | From: Sona Sarmadi <sona.sarmadi@enea.com> | ||
3 | Date: Mon, 11 Sep 2017 10:55:45 +0200 | ||
4 | Subject: [PATCH] glibc: CVE-2017-12132 | ||
5 | |||
6 | From e14a27723cc3a154d67f3f26e719d08c0ba9ad25 Mon Sep 17 00:00:00 2001 | ||
7 | From: Florian Weimer <fweimer@redhat.com> | ||
8 | Date: Thu, 13 Apr 2017 13:09:38 +0200 | ||
9 | Subject: [PATCH] resolv: Reduce EDNS payload size to 1200 bytes [BZ #21361] | ||
10 | |||
11 | This hardens the stub resolver against fragmentation-based attacks. | ||
12 | |||
13 | CVE: CVE-2017-12132 | ||
14 | Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=glibc.git;a=patch;h=e14a27723cc3a154d67f3f26e719d08c0ba9ad25] | ||
15 | |||
16 | Signed-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 | |||
30 | diff --git a/ChangeLog b/ChangeLog | ||
31 | index 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] | ||
59 | diff --git a/include/resolv.h b/include/resolv.h | ||
60 | index 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 | |||
80 | diff --git a/resolv/Makefile b/resolv/Makefile | ||
81 | index 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) | ||
100 | diff --git a/resolv/res_mkquery.c b/resolv/res_mkquery.c | ||
101 | index 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) | ||
150 | diff --git a/resolv/res_query.c b/resolv/res_query.c | ||
151 | index 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 | } | ||
205 | diff --git a/resolv/resolv-internal.h b/resolv/resolv-internal.h | ||
206 | index 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 */ | ||
232 | diff --git a/resolv/tst-resolv-edns.c b/resolv/tst-resolv-edns.c | ||
233 | new file mode 100644 | ||
234 | index 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> | ||
739 | diff --git a/support/resolv_test.c b/support/resolv_test.c | ||
740 | index 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); | ||
835 | diff --git a/support/resolv_test.h b/support/resolv_test.h | ||
836 | index 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 | -- | ||
865 | 1.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 @@ | |||
2 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" | 2 | FILESEXTRAPATHS_prepend := "${THISDIR}/${PN}:" |
3 | 3 | ||
4 | SRC_URI += "file://CVE-2017-1000366.patch \ | 4 | SRC_URI += "file://CVE-2017-1000366.patch \ |
5 | file://CVE-2017-12132.patch \ | ||
5 | " | 6 | " |
6 | 7 | ||