Subversion Repositories

?revision_form?Rev ?revision_input??revision_submit??revision_endform?

Rev 29 | Details | Compare with Previous | 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