Blame | Last modification | View Log | RSS feed
#! /bin/sh /usr/share/dpatch/dpatch-run
## 50_permanent_include_errors.dpatch by Shevek <shevek@anarres.org>, edited by Magnus Holmgren
##
## DP: Fix CVE-2008-2469 - buffer overflows handling DNS responses.
@DPATCH@
--- libspf2/src/libspf2/spf_dns_resolv.c.orig 2008-09-20 19:36:57.000000000 +0200
+++ libspf2/src/libspf2/spf_dns_resolv.c 2008-09-20 19:39:08.000000000 +0200
@@ -110,7 +110,8 @@
int nrec;
int cnt;
- u_char response[2048];
+ u_char *responsebuf;
+ size_t responselen;
int dns_len;
@@ -127,11 +128,13 @@
char name_buf[ NS_MAXDNAME ];
int prio;
- int rdlen;
- const u_char *rdata, *rdata_end;
+ size_t rdlen;
+ const u_char *rdata;
+#if HAVE_DECL_RES_NINIT
void *res_spec;
struct __res_state *res_state;
+#endif
SPF_ASSERT_NOTNULL(spf_dns_server);
@@ -140,10 +143,12 @@
SPF_ASSERT_NOTNULL(spfhook);
#endif
+#if HAVE_DECL_RES_NINIT
res_spec = pthread_getspecific(res_state_key);
if (res_spec == NULL) {
res_state = (struct __res_state *)
malloc(sizeof(struct __res_state));
+ memset(res_state, 0, sizeof(struct __res_state));
if (res_ninit(res_state) != 0) {
SPF_error("Failed to call res_ninit()");
}
@@ -152,20 +157,45 @@
else {
res_state = (struct __res_state *)res_spec;
}
+#endif
+
+ responselen = 2048;
+ responsebuf = (u_char *)malloc(responselen);
+ memset(responsebuf, 0, responselen);
+
+ /*
+ * Retry the lookup until our response buffer is big enough.
+ *
+ * This loop repeats until either we fail a lookup or we succeed.
+ * The size of the response buffer is monotonic increasing, so eventually we
+ * must either succeed, or we try to malloc more RAM than we can.
+ *
+ * The Linux man pages do not describe res_nquery adequately. Solaris says:
+ *
+ * The res_nquery() and res_query() routines return a length that may be bigger
+ * than anslen. In that case, retry the query with a larger buf. The answer to the
+ * second query may be larger still], so it is recommended that you supply a buf
+ * larger than the answer returned by the previous query. answer must be large
+ * enough to receive a maximum UDP response from the server or parts of the answer
+ * will be silently discarded. The default maximum UDP response size is 512 bytes.
+ */
+ for (;;) {
/*
* try resolving the name
*/
#if HAVE_DECL_RES_NINIT
dns_len = res_nquery(res_state, domain, ns_c_in, rr_type,
- response, sizeof(response));
+ responsebuf, responselen);
#else
dns_len = res_query(domain, ns_c_in, rr_type,
- response, sizeof(response));
+ responsebuf, responselen);
#endif
if ( dns_len < 0 ) {
+ /* We failed to perform a lookup. */
/* This block returns unconditionally. */
+ free(responsebuf);
if ( spf_dns_server->debug )
SPF_debugf( "query failed: err = %d %s (%d): %s",
dns_len, hstrerror( SPF_h_errno ), SPF_h_errno,
@@ -178,6 +208,25 @@
return SPF_dns_rr_new_init(spf_dns_server,
domain, rr_type, 0, SPF_h_errno);
}
+ else if (dns_len > responselen) {
+ /* We managed a lookup but our buffer was too small. */
+ responselen = dns_len + (dns_len >> 1);
+#if 0
+ /* Sanity-trap - we should never hit this. */
+ if (responselen > 1048576) { /* One megabyte. */
+ free(responsebuf);
+ return SPF_dns_rr_new_init(spf_dns_server,
+ domain, rr_type, 0, SPF_h_errno);
+ }
+#endif
+ responsebuf = realloc(responsebuf, responselen);
+ }
+ else {
+ /* We managed a lookup, and our buffer was large enough. */
+ responselen = dns_len;
+ break;
+ }
+ }
/*
* initialize stuff
@@ -185,12 +234,13 @@
spfrr = SPF_dns_rr_new_init(spf_dns_server,
domain, rr_type, 0, NETDB_SUCCESS);
- err = ns_initparse( response, dns_len, &ns_handle );
+ err = ns_initparse(responsebuf, responselen, &ns_handle);
if ( err < 0 ) { /* 0 or -1 */
if ( spf_dns_server->debug )
SPF_debugf( "ns_initparse failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
@@ -226,6 +276,7 @@
if ( spf_dns_server->debug > 1 )
SPF_debugf( "ns_parserr failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
@@ -257,8 +308,8 @@
break;
case ns_t_ns:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -271,8 +322,8 @@
break;
case ns_t_cname:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -286,8 +337,8 @@
case ns_t_mx:
prio = ns_get16( rdata );
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + sizeof( responselen ),
rdata + NS_INT16SZ,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -300,14 +351,13 @@
break;
case ns_t_txt:
- rdata_end = rdata + rdlen;
SPF_debugf( "TXT: (%d) \"%.*s\"",
rdlen, rdlen-1, rdata+1 );
break;
case ns_t_ptr:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress( responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -341,18 +391,21 @@
{
case ns_t_a:
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- sizeof( spfrr->rr[cnt]->a ) ) != SPF_E_SUCCESS )
+ sizeof(spfrr->rr[cnt]->a)) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
- memmove( &spfrr->rr[cnt]->a, rdata, sizeof( spfrr->rr[cnt]->a ) );
+ }
+ memcpy(&spfrr->rr[cnt]->a, rdata, sizeof(spfrr->rr[cnt]->a));
cnt++;
break;
case ns_t_aaaa:
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- sizeof( spfrr->rr[cnt]->aaaa ) ) != SPF_E_SUCCESS )
+ sizeof(spfrr->rr[cnt]->aaaa)) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
- memmove( &spfrr->rr[cnt]->aaaa, rdata, sizeof( spfrr->rr[cnt]->aaaa ) );
-
+ }
+ memcpy(&spfrr->rr[cnt]->aaaa, rdata, sizeof(spfrr->rr[cnt]->aaaa));
cnt++;
break;
@@ -364,8 +417,8 @@
break;
case ns_t_mx:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress(responsebuf,
+ responsebuf + responselen,
rdata + NS_INT16SZ,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -373,12 +426,15 @@
if ( spf_dns_server->debug > 1 )
SPF_debugf( "ns_name_uncompress failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- strlen( name_buf ) + 1 ) != SPF_E_SUCCESS )
+ strlen(name_buf) + 1 ) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
strcpy( spfrr->rr[cnt]->mx, name_buf );
cnt++;
@@ -390,8 +446,12 @@
u_char *src, *dst;
size_t len;
- if ( SPF_dns_rr_buf_realloc( spfrr, cnt, rdlen ) != SPF_E_SUCCESS )
+ /* Just rdlen is enough because there is at least one
+ * length byte. */
+ if (SPF_dns_rr_buf_realloc(spfrr, cnt, rdlen) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
dst = (u_char *)(spfrr->rr[cnt]->txt);
len = 0;
@@ -400,15 +460,22 @@
{
len = *src;
src++;
+ rdlen--;
+
+ /* Avoid buffer overrun if len is junk. */
+ if (len > rdlen)
+ len = rdlen;
memcpy( dst, src, len );
dst += len;
src += len;
- rdlen -= len + 1;
+ rdlen -= len;
}
*dst = '\0';
} else {
- if ( SPF_dns_rr_buf_realloc( spfrr, cnt, 1 ) != SPF_E_SUCCESS )
+ if (SPF_dns_rr_buf_realloc(spfrr, cnt, 1) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
spfrr->rr[cnt]->txt[0] = '\0';
}
@@ -416,8 +483,8 @@
break;
case ns_t_ptr:
- err = ns_name_uncompress( response,
- response + sizeof( response ),
+ err = ns_name_uncompress(responsebuf,
+ responsebuf + responselen,
rdata,
name_buf, sizeof( name_buf ) );
if ( err < 0 ) /* 0 or -1 */
@@ -425,12 +492,15 @@
if ( spf_dns_server->debug > 1 )
SPF_debugf( "ns_name_uncompress failed: err = %d %s (%d)",
err, strerror( errno ), errno );
+ free(responsebuf);
return spfrr;
}
if ( SPF_dns_rr_buf_realloc( spfrr, cnt,
- strlen( name_buf ) + 1 ) != SPF_E_SUCCESS )
+ strlen(name_buf) + 1) != SPF_E_SUCCESS) {
+ free(responsebuf);
return spfrr;
+ }
strcpy( spfrr->rr[cnt]->ptr, name_buf );
cnt++;
@@ -447,6 +517,7 @@
if ( spfrr->num_rr == 0 )
spfrr->herrno = NO_DATA;
+ free(responsebuf);
return spfrr;
}