Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
29 | magnus | 1 | #! /bin/sh /usr/share/dpatch/dpatch-run |
2 | ## 50_permanent_include_errors.dpatch by Shevek <shevek@anarres.org>, edited by Magnus Holmgren |
||
3 | ## |
||
4 | ## DP: Fix CVE-2008-2469 - buffer overflows handling DNS responses. |
||
5 | |||
6 | @DPATCH@ |
||
7 | |||
8 | --- libspf2/src/libspf2/spf_dns_resolv.c.orig 2008-09-20 19:36:57.000000000 +0200 |
||
9 | +++ libspf2/src/libspf2/spf_dns_resolv.c 2008-09-20 19:39:08.000000000 +0200 |
||
10 | @@ -110,7 +110,8 @@ |
||
11 | int nrec; |
||
12 | int cnt; |
||
13 | |||
14 | - u_char response[2048]; |
||
15 | + u_char *responsebuf; |
||
16 | + size_t responselen; |
||
17 | |||
18 | int dns_len; |
||
19 | |||
20 | @@ -127,11 +128,13 @@ |
||
21 | char name_buf[ NS_MAXDNAME ]; |
||
22 | int prio; |
||
23 | |||
24 | - int rdlen; |
||
25 | - const u_char *rdata, *rdata_end; |
||
26 | + size_t rdlen; |
||
27 | + const u_char *rdata; |
||
28 | |||
29 | +#if HAVE_DECL_RES_NINIT |
||
30 | void *res_spec; |
||
31 | struct __res_state *res_state; |
||
32 | +#endif |
||
33 | |||
34 | SPF_ASSERT_NOTNULL(spf_dns_server); |
||
35 | |||
36 | @@ -140,10 +143,12 @@ |
||
37 | SPF_ASSERT_NOTNULL(spfhook); |
||
38 | #endif |
||
39 | |||
40 | +#if HAVE_DECL_RES_NINIT |
||
41 | res_spec = pthread_getspecific(res_state_key); |
||
42 | if (res_spec == NULL) { |
||
43 | res_state = (struct __res_state *) |
||
44 | malloc(sizeof(struct __res_state)); |
||
45 | + memset(res_state, 0, sizeof(struct __res_state)); |
||
46 | if (res_ninit(res_state) != 0) { |
||
47 | SPF_error("Failed to call res_ninit()"); |
||
48 | } |
||
49 | @@ -152,20 +157,45 @@ |
||
50 | else { |
||
51 | res_state = (struct __res_state *)res_spec; |
||
52 | } |
||
53 | +#endif |
||
54 | + |
||
55 | + responselen = 2048; |
||
56 | + responsebuf = (u_char *)malloc(responselen); |
||
57 | + memset(responsebuf, 0, responselen); |
||
58 | + |
||
59 | + /* |
||
60 | + * Retry the lookup until our response buffer is big enough. |
||
61 | + * |
||
62 | + * This loop repeats until either we fail a lookup or we succeed. |
||
63 | + * The size of the response buffer is monotonic increasing, so eventually we |
||
64 | + * must either succeed, or we try to malloc more RAM than we can. |
||
65 | + * |
||
66 | + * The Linux man pages do not describe res_nquery adequately. Solaris says: |
||
67 | + * |
||
68 | + * The res_nquery() and res_query() routines return a length that may be bigger |
||
69 | + * than anslen. In that case, retry the query with a larger buf. The answer to the |
||
70 | + * second query may be larger still], so it is recommended that you supply a buf |
||
71 | + * larger than the answer returned by the previous query. answer must be large |
||
72 | + * enough to receive a maximum UDP response from the server or parts of the answer |
||
73 | + * will be silently discarded. The default maximum UDP response size is 512 bytes. |
||
74 | + */ |
||
75 | + for (;;) { |
||
76 | |||
77 | /* |
||
78 | * try resolving the name |
||
79 | */ |
||
80 | #if HAVE_DECL_RES_NINIT |
||
81 | dns_len = res_nquery(res_state, domain, ns_c_in, rr_type, |
||
82 | - response, sizeof(response)); |
||
83 | + responsebuf, responselen); |
||
84 | #else |
||
85 | dns_len = res_query(domain, ns_c_in, rr_type, |
||
86 | - response, sizeof(response)); |
||
87 | + responsebuf, responselen); |
||
88 | #endif |
||
89 | |||
90 | if ( dns_len < 0 ) { |
||
91 | + /* We failed to perform a lookup. */ |
||
92 | /* This block returns unconditionally. */ |
||
93 | + free(responsebuf); |
||
94 | if ( spf_dns_server->debug ) |
||
95 | SPF_debugf( "query failed: err = %d %s (%d): %s", |
||
96 | dns_len, hstrerror( SPF_h_errno ), SPF_h_errno, |
||
97 | @@ -178,6 +208,25 @@ |
||
98 | return SPF_dns_rr_new_init(spf_dns_server, |
||
99 | domain, rr_type, 0, SPF_h_errno); |
||
100 | } |
||
101 | + else if (dns_len > responselen) { |
||
102 | + /* We managed a lookup but our buffer was too small. */ |
||
103 | + responselen = dns_len + (dns_len >> 1); |
||
104 | +#if 0 |
||
105 | + /* Sanity-trap - we should never hit this. */ |
||
106 | + if (responselen > 1048576) { /* One megabyte. */ |
||
107 | + free(responsebuf); |
||
108 | + return SPF_dns_rr_new_init(spf_dns_server, |
||
109 | + domain, rr_type, 0, SPF_h_errno); |
||
110 | + } |
||
111 | +#endif |
||
112 | + responsebuf = realloc(responsebuf, responselen); |
||
113 | + } |
||
114 | + else { |
||
115 | + /* We managed a lookup, and our buffer was large enough. */ |
||
116 | + responselen = dns_len; |
||
117 | + break; |
||
118 | + } |
||
119 | + } |
||
120 | |||
121 | /* |
||
122 | * initialize stuff |
||
123 | @@ -185,12 +234,13 @@ |
||
124 | spfrr = SPF_dns_rr_new_init(spf_dns_server, |
||
125 | domain, rr_type, 0, NETDB_SUCCESS); |
||
126 | |||
127 | - err = ns_initparse( response, dns_len, &ns_handle ); |
||
128 | + err = ns_initparse(responsebuf, responselen, &ns_handle); |
||
129 | |||
130 | if ( err < 0 ) { /* 0 or -1 */ |
||
131 | if ( spf_dns_server->debug ) |
||
132 | SPF_debugf( "ns_initparse failed: err = %d %s (%d)", |
||
133 | err, strerror( errno ), errno ); |
||
134 | + free(responsebuf); |
||
135 | return spfrr; |
||
136 | } |
||
137 | |||
138 | @@ -226,6 +276,7 @@ |
||
139 | if ( spf_dns_server->debug > 1 ) |
||
140 | SPF_debugf( "ns_parserr failed: err = %d %s (%d)", |
||
141 | err, strerror( errno ), errno ); |
||
142 | + free(responsebuf); |
||
143 | return spfrr; |
||
144 | } |
||
145 | |||
146 | @@ -257,8 +308,8 @@ |
||
147 | break; |
||
148 | |||
149 | case ns_t_ns: |
||
150 | - err = ns_name_uncompress( response, |
||
151 | - response + sizeof( response ), |
||
152 | + err = ns_name_uncompress( responsebuf, |
||
153 | + responsebuf + responselen, |
||
154 | rdata, |
||
155 | name_buf, sizeof( name_buf ) ); |
||
156 | if ( err < 0 ) /* 0 or -1 */ |
||
157 | @@ -271,8 +322,8 @@ |
||
158 | break; |
||
159 | |||
160 | case ns_t_cname: |
||
161 | - err = ns_name_uncompress( response, |
||
162 | - response + sizeof( response ), |
||
163 | + err = ns_name_uncompress( responsebuf, |
||
164 | + responsebuf + responselen, |
||
165 | rdata, |
||
166 | name_buf, sizeof( name_buf ) ); |
||
167 | if ( err < 0 ) /* 0 or -1 */ |
||
168 | @@ -286,8 +337,8 @@ |
||
169 | |||
170 | case ns_t_mx: |
||
171 | prio = ns_get16( rdata ); |
||
172 | - err = ns_name_uncompress( response, |
||
173 | - response + sizeof( response ), |
||
174 | + err = ns_name_uncompress( responsebuf, |
||
175 | + responsebuf + sizeof( responselen ), |
||
176 | rdata + NS_INT16SZ, |
||
177 | name_buf, sizeof( name_buf ) ); |
||
178 | if ( err < 0 ) /* 0 or -1 */ |
||
179 | @@ -300,14 +351,13 @@ |
||
180 | break; |
||
181 | |||
182 | case ns_t_txt: |
||
183 | - rdata_end = rdata + rdlen; |
||
184 | SPF_debugf( "TXT: (%d) \"%.*s\"", |
||
185 | rdlen, rdlen-1, rdata+1 ); |
||
186 | break; |
||
187 | |||
188 | case ns_t_ptr: |
||
189 | - err = ns_name_uncompress( response, |
||
190 | - response + sizeof( response ), |
||
191 | + err = ns_name_uncompress( responsebuf, |
||
192 | + responsebuf + responselen, |
||
193 | rdata, |
||
194 | name_buf, sizeof( name_buf ) ); |
||
195 | if ( err < 0 ) /* 0 or -1 */ |
||
196 | @@ -341,18 +391,21 @@ |
||
197 | { |
||
198 | case ns_t_a: |
||
199 | if ( SPF_dns_rr_buf_realloc( spfrr, cnt, |
||
200 | - sizeof( spfrr->rr[cnt]->a ) ) != SPF_E_SUCCESS ) |
||
201 | + sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) { |
||
202 | + free(responsebuf); |
||
203 | return spfrr; |
||
204 | - memmove( &spfrr->rr[cnt]->a, rdata, sizeof( spfrr->rr[cnt]->a ) ); |
||
205 | + } |
||
206 | + memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a)); |
||
207 | cnt++; |
||
208 | break; |
||
209 | |||
210 | case ns_t_aaaa: |
||
211 | if ( SPF_dns_rr_buf_realloc( spfrr, cnt, |
||
212 | - sizeof( spfrr->rr[cnt]->aaaa ) ) != SPF_E_SUCCESS ) |
||
213 | + sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) { |
||
214 | + free(responsebuf); |
||
215 | return spfrr; |
||
216 | - memmove( &spfrr->rr[cnt]->aaaa, rdata, sizeof( spfrr->rr[cnt]->aaaa ) ); |
||
217 | - |
||
218 | + } |
||
219 | + memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa)); |
||
220 | cnt++; |
||
221 | break; |
||
222 | |||
223 | @@ -364,8 +417,8 @@ |
||
224 | break; |
||
225 | |||
226 | case ns_t_mx: |
||
227 | - err = ns_name_uncompress( response, |
||
228 | - response + sizeof( response ), |
||
229 | + err = ns_name_uncompress(responsebuf, |
||
230 | + responsebuf + responselen, |
||
231 | rdata + NS_INT16SZ, |
||
232 | name_buf, sizeof( name_buf ) ); |
||
233 | if ( err < 0 ) /* 0 or -1 */ |
||
234 | @@ -373,12 +426,15 @@ |
||
235 | if ( spf_dns_server->debug > 1 ) |
||
236 | SPF_debugf( "ns_name_uncompress failed: err = %d %s (%d)", |
||
237 | err, strerror( errno ), errno ); |
||
238 | + free(responsebuf); |
||
239 | return spfrr; |
||
240 | } |
||
241 | |||
242 | if ( SPF_dns_rr_buf_realloc( spfrr, cnt, |
||
243 | - strlen( name_buf ) + 1 ) != SPF_E_SUCCESS ) |
||
244 | + strlen(name_buf) + 1 ) != SPF_E_SUCCESS) { |
||
245 | + free(responsebuf); |
||
246 | return spfrr; |
||
247 | + } |
||
248 | strcpy( spfrr->rr[cnt]->mx, name_buf ); |
||
249 | |||
250 | cnt++; |
||
251 | @@ -390,8 +446,12 @@ |
||
252 | u_char *src, *dst; |
||
253 | size_t len; |
||
254 | |||
255 | - if ( SPF_dns_rr_buf_realloc( spfrr, cnt, rdlen ) != SPF_E_SUCCESS ) |
||
256 | + /* Just rdlen is enough because there is at least one |
||
257 | + * length byte. */ |
||
258 | + if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) { |
||
259 | + free(responsebuf); |
||
260 | return spfrr; |
||
261 | + } |
||
262 | |||
263 | dst = (u_char *)(spfrr->rr[cnt]->txt); |
||
264 | len = 0; |
||
265 | @@ -400,15 +460,22 @@ |
||
266 | { |
||
267 | len = *src; |
||
268 | src++; |
||
269 | + rdlen--; |
||
270 | + |
||
271 | + /* Avoid buffer overrun if len is junk. */ |
||
272 | + if (len > rdlen) |
||
273 | + len = rdlen; |
||
274 | memcpy( dst, src, len ); |
||
275 | dst += len; |
||
276 | src += len; |
||
277 | - rdlen -= len + 1; |
||
278 | + rdlen -= len; |
||
279 | } |
||
280 | *dst = '\0'; |
||
281 | } else { |
||
282 | - if ( SPF_dns_rr_buf_realloc( spfrr, cnt, 1 ) != SPF_E_SUCCESS ) |
||
283 | + if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) { |
||
284 | + free(responsebuf); |
||
285 | return spfrr; |
||
286 | + } |
||
287 | spfrr->rr[cnt]->txt[0] = '\0'; |
||
288 | } |
||
289 | |||
290 | @@ -416,8 +483,8 @@ |
||
291 | break; |
||
292 | |||
293 | case ns_t_ptr: |
||
294 | - err = ns_name_uncompress( response, |
||
295 | - response + sizeof( response ), |
||
296 | + err = ns_name_uncompress(responsebuf, |
||
297 | + responsebuf + responselen, |
||
298 | rdata, |
||
299 | name_buf, sizeof( name_buf ) ); |
||
300 | if ( err < 0 ) /* 0 or -1 */ |
||
301 | @@ -425,12 +492,15 @@ |
||
302 | if ( spf_dns_server->debug > 1 ) |
||
303 | SPF_debugf( "ns_name_uncompress failed: err = %d %s (%d)", |
||
304 | err, strerror( errno ), errno ); |
||
305 | + free(responsebuf); |
||
306 | return spfrr; |
||
307 | } |
||
308 | |||
309 | if ( SPF_dns_rr_buf_realloc( spfrr, cnt, |
||
310 | - strlen( name_buf ) + 1 ) != SPF_E_SUCCESS ) |
||
311 | + strlen(name_buf) + 1) != SPF_E_SUCCESS) { |
||
312 | + free(responsebuf); |
||
313 | return spfrr; |
||
314 | + } |
||
315 | strcpy( spfrr->rr[cnt]->ptr, name_buf ); |
||
316 | |||
317 | cnt++; |
||
318 | @@ -447,6 +517,7 @@ |
||
319 | if ( spfrr->num_rr == 0 ) |
||
320 | spfrr->herrno = NO_DATA; |
||
321 | |||
322 | + free(responsebuf); |
||
323 | return spfrr; |
||
324 | } |
||
325 |