modphp: Security Advisory - php - CVE-2014-3597 Upstream-Status: Backport Signed-off-by: Yue Tao From 2fefae47716d501aec41c1102f3fd4531f070b05 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Tue, 19 Aug 2014 08:33:49 +0200 Subject: [PATCH] Fixed Sec Bug #67717 segfault in dns_get_record CVE-2014-3597 Incomplete fix for CVE-2014-4049 Check possible buffer overflow - pass real buffer end to dn_expand calls - check buffer len before each read --- ext/standard/dns.c | 84 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/ext/standard/dns.c b/ext/standard/dns.c index 214a7dc..0b5e69c 100644 --- a/ext/standard/dns.c +++ b/ext/standard/dns.c @@ -412,8 +412,14 @@ PHP_FUNCTION(dns_check_record) #if HAVE_FULL_DNS_FUNCS +#define CHECKCP(n) do { \ + if (cp + n > end) { \ + return NULL; \ + } \ +} while (0) + /* {{{ php_parserr */ -static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int store, int raw, zval **subarray) +static u_char *php_parserr(u_char *cp, u_char *end, querybuf *answer, int type_to_fetch, int store, int raw, zval **subarray) { u_short type, class, dlen; u_long ttl; @@ -425,16 +431,18 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int *subarray = NULL; - n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, sizeof(name) - 2); + n = dn_expand(answer->qb2, end, cp, name, sizeof(name) - 2); if (n < 0) { return NULL; } cp += n; + CHECKCP(10); GETSHORT(type, cp); GETSHORT(class, cp); GETLONG(ttl, cp); GETSHORT(dlen, cp); + CHECKCP(dlen); if (type_to_fetch != T_ANY && type != type_to_fetch) { cp += dlen; return cp; @@ -461,12 +469,14 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int switch (type) { case DNS_T_A: + CHECKCP(4); add_assoc_string(*subarray, "type", "A", 1); snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]); add_assoc_string(*subarray, "ip", name, 1); cp += dlen; break; case DNS_T_MX: + CHECKCP(2); add_assoc_string(*subarray, "type", "MX", 1); GETSHORT(n, cp); add_assoc_long(*subarray, "pri", n); @@ -485,7 +495,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int if (type == DNS_T_PTR) { add_assoc_string(*subarray, "type", "PTR", 1); } - n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2); if (n < 0) { return NULL; } @@ -495,18 +505,22 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int case DNS_T_HINFO: /* See RFC 1010 for values */ add_assoc_string(*subarray, "type", "HINFO", 1); + CHECKCP(1); n = *cp & 0xFF; cp++; + CHECKCP(n); add_assoc_stringl(*subarray, "cpu", (char*)cp, n, 1); cp += n; + CHECKCP(1); n = *cp & 0xFF; cp++; + CHECKCP(n); add_assoc_stringl(*subarray, "os", (char*)cp, n, 1); cp += n; break; case DNS_T_TXT: { - int ll = 0; + int l1 = 0, l2 = 0; zval *entries = NULL; add_assoc_string(*subarray, "type", "TXT", 1); @@ -515,37 +529,41 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int MAKE_STD_ZVAL(entries); array_init(entries); - while (ll < dlen) { - n = cp[ll]; - if ((ll + n) >= dlen) { + while (l1 < dlen) { + n = cp[l1]; + if ((l1 + n) >= dlen) { // Invalid chunk length, truncate - n = dlen - (ll + 1); + n = dlen - (l1 + 1); + } + if (n) { + memcpy(tp + l2 , cp + l1 + 1, n); + add_next_index_stringl(entries, cp + l1 + 1, n, 1); } - memcpy(tp + ll , cp + ll + 1, n); - add_next_index_stringl(entries, cp + ll + 1, n, 1); - ll = ll + n + 1; + l1 = l1 + n + 1; + l2 = l2 + n; } - tp[dlen] = '\0'; + tp[l2] = '\0'; cp += dlen; - add_assoc_stringl(*subarray, "txt", tp, (dlen>0)?dlen - 1:0, 0); + add_assoc_stringl(*subarray, "txt", tp, l2, 0); add_assoc_zval(*subarray, "entries", entries); } break; case DNS_T_SOA: add_assoc_string(*subarray, "type", "SOA", 1); - n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2); + n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2); if (n < 0) { return NULL; } cp += n; add_assoc_string(*subarray, "mname", name, 1); - n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2); + n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2); if (n < 0) { return NULL; } cp += n; add_assoc_string(*subarray, "rname", name, 1); + CHECKCP(5*4); GETLONG(n, cp); add_assoc_long(*subarray, "serial", n); GETLONG(n, cp); @@ -559,6 +577,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int break; case DNS_T_AAAA: tp = (u_char*)name; + CHECKCP(8*2); for(i=0; i < 8; i++) { GETSHORT(s, cp); if (s != 0) { @@ -593,6 +612,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int case DNS_T_A6: p = cp; add_assoc_string(*subarray, "type", "A6", 1); + CHECKCP(1); n = ((int)cp[0]) & 0xFF; cp++; add_assoc_long(*subarray, "masklen", n); @@ -628,6 +648,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int cp++; } for (i = (n + 8) / 16; i < 8; i++) { + CHECKCP(2); GETSHORT(s, cp); if (s != 0) { if (tp > (u_char *)name) { @@ -657,7 +678,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int tp[0] = '\0'; add_assoc_string(*subarray, "ipv6", name, 1); if (cp < p + dlen) { - n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2); if (n < 0) { return NULL; } @@ -666,6 +687,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int } break; case DNS_T_SRV: + CHECKCP(3*2); add_assoc_string(*subarray, "type", "SRV", 1); GETSHORT(n, cp); add_assoc_long(*subarray, "pri", n); @@ -673,7 +695,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int add_assoc_long(*subarray, "weight", n); GETSHORT(n, cp); add_assoc_long(*subarray, "port", n); - n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2); if (n < 0) { return NULL; } @@ -681,21 +703,35 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int add_assoc_string(*subarray, "target", name, 1); break; case DNS_T_NAPTR: + CHECKCP(2*2); add_assoc_string(*subarray, "type", "NAPTR", 1); GETSHORT(n, cp); add_assoc_long(*subarray, "order", n); GETSHORT(n, cp); add_assoc_long(*subarray, "pref", n); + + CHECKCP(1); n = (cp[0] & 0xFF); - add_assoc_stringl(*subarray, "flags", (char*)++cp, n, 1); + cp++; + CHECKCP(n); + add_assoc_stringl(*subarray, "flags", (char*)cp, n, 1); cp += n; + + CHECKCP(1); n = (cp[0] & 0xFF); - add_assoc_stringl(*subarray, "services", (char*)++cp, n, 1); + cp++; + CHECKCP(n); + add_assoc_stringl(*subarray, "services", (char*)cp, n, 1); cp += n; + + CHECKCP(1); n = (cp[0] & 0xFF); - add_assoc_stringl(*subarray, "regex", (char*)++cp, n, 1); + cp++; + CHECKCP(n); + add_assoc_stringl(*subarray, "regex", (char*)cp, n, 1); cp += n; - n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2); + + n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2); if (n < 0) { return NULL; } @@ -888,7 +924,7 @@ PHP_FUNCTION(dns_get_record) while (an-- && cp && cp < end) { zval *retval; - cp = php_parserr(cp, &answer, type_to_fetch, store_results, raw, &retval); + cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, raw, &retval); if (retval != NULL && store_results) { add_next_index_zval(return_value, retval); } @@ -901,7 +937,7 @@ PHP_FUNCTION(dns_get_record) while (ns-- > 0 && cp && cp < end) { zval *retval = NULL; - cp = php_parserr(cp, &answer, DNS_T_ANY, authns != NULL, raw, &retval); + cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, raw, &retval); if (retval != NULL) { add_next_index_zval(authns, retval); } @@ -913,7 +949,7 @@ PHP_FUNCTION(dns_get_record) while (ar-- > 0 && cp && cp < end) { zval *retval = NULL; - cp = php_parserr(cp, &answer, DNS_T_ANY, 1, raw, &retval); + cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, raw, &retval); if (retval != NULL) { add_next_index_zval(addtl, retval); } -- 1.7.9.5