|
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 } |