components/php-5_3/php-sapi/patches/212_php_19556437.patch
changeset 3727 425608dcd0e3
equal deleted inserted replaced
3726:1c80a90dd005 3727:425608dcd0e3
       
     1 Fixed Sec Bug #67717 segfault in dns_get_record CVE-2014-3597
       
     2 
       
     3 Incomplete fix for CVE-2014-4049
       
     4 
       
     5 Check possible buffer overflow
       
     6 - pass real buffer end to dn_expand calls
       
     7 - check buffer len before each read
       
     8 
       
     9 
       
    10 diff --git a/ext/standard/dns.c b/ext/standard/dns.c
       
    11 index 214a7dc..0b5e69c 100644
       
    12 --- a/ext/standard/dns.c
       
    13 +++ b/ext/standard/dns.c
       
    14 @@ -412,8 +412,14 @@ PHP_FUNCTION(dns_check_record)
       
    15  
       
    16  #if HAVE_FULL_DNS_FUNCS
       
    17  
       
    18 +#define CHECKCP(n) do { \
       
    19 +	if (cp + n > end) { \
       
    20 +		return NULL; \
       
    21 +	} \
       
    22 +} while (0)
       
    23 +
       
    24  /* {{{ php_parserr */
       
    25 -static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int store, zval **subarray)
       
    26 +static u_char *php_parserr(u_char *cp, u_char *end, querybuf *answer, int type_to_fetch, int store, zval **subarray)
       
    27  {
       
    28  	u_short type, class, dlen;
       
    29  	u_long ttl;
       
    30 @@ -425,16 +431,18 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
    31  
       
    32  	*subarray = NULL;
       
    33  
       
    34 -	n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, sizeof(name) - 2);
       
    35 +	n = dn_expand(answer->qb2, end, cp, name, sizeof(name) - 2);
       
    36  	if (n < 0) {
       
    37  		return NULL;
       
    38  	}
       
    39  	cp += n;
       
    40  
       
    41 +	CHECKCP(10);
       
    42  	GETSHORT(type, cp);
       
    43  	GETSHORT(class, cp);
       
    44  	GETLONG(ttl, cp);
       
    45  	GETSHORT(dlen, cp);
       
    46 +	CHECKCP(dlen);
       
    47  	if (type_to_fetch != T_ANY && type != type_to_fetch) {
       
    48  		cp += dlen;
       
    49  		return cp;
       
    50 @@ -461,12 +469,14 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
    51  
       
    52  	switch (type) {
       
    53  		case DNS_T_A:
       
    54 +			CHECKCP(4);
       
    55  			add_assoc_string(*subarray, "type", "A", 1);
       
    56  			snprintf(name, sizeof(name), "%d.%d.%d.%d", cp[0], cp[1], cp[2], cp[3]);
       
    57  			add_assoc_string(*subarray, "ip", name, 1);
       
    58  			cp += dlen;
       
    59  			break;
       
    60  		case DNS_T_MX:
       
    61 +			CHECKCP(2);
       
    62  			add_assoc_string(*subarray, "type", "MX", 1);
       
    63  			GETSHORT(n, cp);
       
    64  			add_assoc_long(*subarray, "pri", n);
       
    65 @@ -485,7 +495,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
    66  			if (type == DNS_T_PTR) {
       
    67  				add_assoc_string(*subarray, "type", "PTR", 1);
       
    68  			}
       
    69 -			n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
       
    70 +			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
       
    71  			if (n < 0) {
       
    72  				return NULL;
       
    73  			}
       
    74 @@ -495,18 +505,22 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
    75  		case DNS_T_HINFO:
       
    76  			/* See RFC 1010 for values */
       
    77  			add_assoc_string(*subarray, "type", "HINFO", 1);
       
    78 +			CHECKCP(1);
       
    79  			n = *cp & 0xFF;
       
    80  			cp++;
       
    81 +			CHECKCP(n);
       
    82  			add_assoc_stringl(*subarray, "cpu", (char*)cp, n, 1);
       
    83  			cp += n;
       
    84 +			CHECKCP(1);
       
    85  			n = *cp & 0xFF;
       
    86  			cp++;
       
    87 +			CHECKCP(n);
       
    88  			add_assoc_stringl(*subarray, "os", (char*)cp, n, 1);
       
    89  			cp += n;
       
    90  			break;
       
    91  		case DNS_T_TXT:
       
    92  			{
       
    93 -				int ll = 0;
       
    94 +				int l1 = 0, l2 = 0;
       
    95  				zval *entries = NULL;
       
    96  
       
    97  				add_assoc_string(*subarray, "type", "TXT", 1);
       
    98 @@ -515,37 +529,41 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
    99  				MAKE_STD_ZVAL(entries);
       
   100  				array_init(entries);
       
   101  				
       
   102 -				while (ll < dlen) {
       
   103 -					n = cp[ll];
       
   104 -					if ((ll + n) >= dlen) {
       
   105 +				while (l1 < dlen) {
       
   106 +					n = cp[l1];
       
   107 +					if ((l1 + n) >= dlen) {
       
   108  						// Invalid chunk length, truncate
       
   109 -						n = dlen - (ll + 1);
       
   110 +						n = dlen - (l1 + 1);
       
   111 +					}
       
   112 +					if (n) {
       
   113 +						memcpy(tp + l2 , cp + l1 + 1, n);
       
   114 +						add_next_index_stringl(entries, cp + l1 + 1, n, 1);
       
   115  					}
       
   116 -					memcpy(tp + ll , cp + ll + 1, n);
       
   117 -					add_next_index_stringl(entries, cp + ll + 1, n, 1);
       
   118 -					ll = ll + n + 1;
       
   119 +					l1 = l1 + n + 1;
       
   120 +					l2 = l2 + n;
       
   121  				}
       
   122 -				tp[dlen] = '\0';
       
   123 +				tp[l2] = '\0';
       
   124  				cp += dlen;
       
   125  
       
   126 -				add_assoc_stringl(*subarray, "txt", tp, (dlen>0)?dlen - 1:0, 0);
       
   127 +				add_assoc_stringl(*subarray, "txt", tp, l2, 0);
       
   128  				add_assoc_zval(*subarray, "entries", entries);
       
   129  			}
       
   130  			break;
       
   131  		case DNS_T_SOA:
       
   132  			add_assoc_string(*subarray, "type", "SOA", 1);
       
   133 -			n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2);
       
   134 +			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
       
   135  			if (n < 0) {
       
   136  				return NULL;
       
   137  			}
       
   138  			cp += n;
       
   139  			add_assoc_string(*subarray, "mname", name, 1);
       
   140 -			n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) -2);
       
   141 +			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) -2);
       
   142  			if (n < 0) {
       
   143  				return NULL;
       
   144  			}
       
   145  			cp += n;
       
   146  			add_assoc_string(*subarray, "rname", name, 1);
       
   147 +			CHECKCP(5*4);
       
   148  			GETLONG(n, cp);
       
   149  			add_assoc_long(*subarray, "serial", n);
       
   150  			GETLONG(n, cp);
       
   151 @@ -559,6 +577,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
   152  			break;
       
   153  		case DNS_T_AAAA:
       
   154  			tp = (u_char*)name;
       
   155 +			CHECKCP(8*2);
       
   156  			for(i=0; i < 8; i++) {
       
   157  				GETSHORT(s, cp);
       
   158  				if (s != 0) {
       
   159 @@ -593,6 +612,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
   160  		case DNS_T_A6:
       
   161  			p = cp;
       
   162  			add_assoc_string(*subarray, "type", "A6", 1);
       
   163 +			CHECKCP(1);
       
   164  			n = ((int)cp[0]) & 0xFF;
       
   165  			cp++;
       
   166  			add_assoc_long(*subarray, "masklen", n);
       
   167 @@ -628,6 +648,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
   168  				cp++;
       
   169  			}
       
   170  			for (i = (n + 8) / 16; i < 8; i++) {
       
   171 +				CHECKCP(2);
       
   172  				GETSHORT(s, cp);
       
   173  				if (s != 0) {
       
   174  					if (tp > (u_char *)name) {
       
   175 @@ -657,7 +678,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
   176  			tp[0] = '\0';
       
   177  			add_assoc_string(*subarray, "ipv6", name, 1);
       
   178  			if (cp < p + dlen) {
       
   179 -				n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
       
   180 +				n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
       
   181  				if (n < 0) {
       
   182  					return NULL;
       
   183  				}
       
   184 @@ -666,6 +687,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
   185  			}
       
   186  			break;
       
   187  		case DNS_T_SRV:
       
   188 +			CHECKCP(3*2);
       
   189  			add_assoc_string(*subarray, "type", "SRV", 1);
       
   190  			GETSHORT(n, cp);
       
   191  			add_assoc_long(*subarray, "pri", n);
       
   192 @@ -673,7 +695,7 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
   193  			add_assoc_long(*subarray, "weight", n);
       
   194  			GETSHORT(n, cp);
       
   195  			add_assoc_long(*subarray, "port", n);
       
   196 -			n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
       
   197 +			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
       
   198  			if (n < 0) {
       
   199  				return NULL;
       
   200  			}
       
   201 @@ -681,21 +703,35 @@ static u_char *php_parserr(u_char *cp, querybuf *answer, int type_to_fetch, int
       
   202  			add_assoc_string(*subarray, "target", name, 1);
       
   203  			break;
       
   204  		case DNS_T_NAPTR:
       
   205 +			CHECKCP(2*2);
       
   206  			add_assoc_string(*subarray, "type", "NAPTR", 1);
       
   207  			GETSHORT(n, cp);
       
   208  			add_assoc_long(*subarray, "order", n);
       
   209  			GETSHORT(n, cp);
       
   210  			add_assoc_long(*subarray, "pref", n);
       
   211 +
       
   212 +			CHECKCP(1);
       
   213  			n = (cp[0] & 0xFF);
       
   214 -			add_assoc_stringl(*subarray, "flags", (char*)++cp, n, 1);
       
   215 +			cp++;
       
   216 +			CHECKCP(n);
       
   217 +			add_assoc_stringl(*subarray, "flags", (char*)cp, n, 1);
       
   218  			cp += n;
       
   219 +
       
   220 +			CHECKCP(1);
       
   221  			n = (cp[0] & 0xFF);
       
   222 -			add_assoc_stringl(*subarray, "services", (char*)++cp, n, 1);
       
   223 +			cp++;
       
   224 +			CHECKCP(n);
       
   225 +			add_assoc_stringl(*subarray, "services", (char*)cp, n, 1);
       
   226  			cp += n;
       
   227 +
       
   228 +			CHECKCP(1);
       
   229  			n = (cp[0] & 0xFF);
       
   230 -			add_assoc_stringl(*subarray, "regex", (char*)++cp, n, 1);
       
   231 +			cp++;
       
   232 +			CHECKCP(n);
       
   233 +			add_assoc_stringl(*subarray, "regex", (char*)cp, n, 1);
       
   234  			cp += n;
       
   235 -			n = dn_expand(answer->qb2, answer->qb2+65536, cp, name, (sizeof name) - 2);
       
   236 +
       
   237 +			n = dn_expand(answer->qb2, end, cp, name, (sizeof name) - 2);
       
   238  			if (n < 0) {
       
   239  				return NULL;
       
   240  			}
       
   241 @@ -888,7 +924,7 @@ PHP_FUNCTION(dns_get_record)
       
   242  			while (an-- && cp && cp < end) {
       
   243  				zval *retval;
       
   244  
       
   245 -				cp = php_parserr(cp, &answer, type_to_fetch, store_results, &retval);
       
   246 +				cp = php_parserr(cp, end, &answer, type_to_fetch, store_results, &retval);
       
   247  				if (retval != NULL && store_results) {
       
   248  					add_next_index_zval(return_value, retval);
       
   249  				}
       
   250 @@ -901,7 +937,7 @@ PHP_FUNCTION(dns_get_record)
       
   251  				while (ns-- > 0 && cp && cp < end) {
       
   252  					zval *retval = NULL;
       
   253  
       
   254 -					cp = php_parserr(cp, &answer, DNS_T_ANY, authns != NULL, &retval);
       
   255 +					cp = php_parserr(cp, end, &answer, DNS_T_ANY, authns != NULL, &retval);
       
   256  					if (retval != NULL) {
       
   257  						add_next_index_zval(authns, retval);
       
   258  					}
       
   259 @@ -913,7 +949,7 @@ PHP_FUNCTION(dns_get_record)
       
   260  				while (ar-- > 0 && cp && cp < end) {
       
   261  					zval *retval = NULL;
       
   262  
       
   263 -					cp = php_parserr(cp, &answer, DNS_T_ANY, 1, &retval);
       
   264 +					cp = php_parserr(cp, end, &answer, DNS_T_ANY, 1, &retval);
       
   265  					if (retval != NULL) {
       
   266  						add_next_index_zval(addtl, retval);
       
   267  					}