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_NINITvoid *res_spec;struct __res_state *res_state;+#endifSPF_ASSERT_NOTNULL(spf_dns_server);@@ -140,10 +143,12 @@SPF_ASSERT_NOTNULL(spfhook);#endif+#if HAVE_DECL_RES_NINITres_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_NINITdns_len = res_nquery(res_state, domain, ns_c_in, rr_type,- response, sizeof(response));+ responsebuf, responselen);#elsedns_len = res_query(domain, ns_c_in, rr_type,- response, sizeof(response));+ responsebuf, responselen);#endifif ( 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;}