diff -u libspf2-1.2.5/debian/changelog libspf2-1.2.5/debian/changelog --- libspf2-1.2.5/debian/changelog +++ libspf2-1.2.5/debian/changelog @@ -1,3 +1,12 @@ +libspf2 (1.2.5-4ubuntu3.1) feisty-security; urgency=high + + * SECURITY UPDATE: + * References CVE2008-2469 + * Add 50_dns_resolv_bufoverflow.dpatch to fix buffer overflows handling DNS + responses. (LP: #271025) + + -- Scott Kitterman Wed, 15 Oct 2008 00:28:47 -0400 + libspf2 (1.2.5-4ubuntu3) feisty; urgency=low * Added patch to change MX and PTR limits to match RFC (Closes LP: #92569) diff -u libspf2-1.2.5/debian/patches/00list libspf2-1.2.5/debian/patches/00list --- libspf2-1.2.5/debian/patches/00list +++ libspf2-1.2.5/debian/patches/00list @@ -1,0 +2 @@ +50_dns_resolv_bufoverflow only in patch2: unchanged: --- libspf2-1.2.5.orig/debian/patches/50_dns_resolv_bufoverflow.dpatch +++ libspf2-1.2.5/debian/patches/50_dns_resolv_bufoverflow.dpatch @@ -0,0 +1,325 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## 50_permanent_include_errors.dpatch by Shevek , edited by Magnus Holmgren +## +## DP: Fix CVE-2008-2469 - buffer overflows handling DNS responses. + +@DPATCH@ +diff -urNad libspf2-1.2.5~/src/libspf2/spf_dns_resolv.c libspf2-1.2.5/src/libspf2/spf_dns_resolv.c +--- libspf2-1.2.5~/src/libspf2/spf_dns_resolv.c 2005-02-18 21:38:12.000000000 -0500 ++++ libspf2-1.2.5/src/libspf2/spf_dns_resolv.c 2008-10-15 00:30:19.000000000 -0400 +@@ -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 = 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; + } +