diff options
Diffstat (limited to 'recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch')
-rw-r--r-- | recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch | 348 |
1 files changed, 348 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch b/recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch new file mode 100644 index 0000000..68289f2 --- /dev/null +++ b/recipes-kernel/linux/files/0001-net-sctp-CVE-2014-3673.patch | |||
@@ -0,0 +1,348 @@ | |||
1 | From bbd951a21e0fd555cd9ede44c7196af09d04d171 Mon Sep 17 00:00:00 2001 | ||
2 | From: Daniel Borkmann <dborkman@redhat.com> | ||
3 | Date: Thu, 9 Oct 2014 22:55:31 +0200 | ||
4 | Subject: [PATCH] net: sctp: fix skb_over_panic when receiving malformed ASCONF | ||
5 | chunks | ||
6 | |||
7 | commit 9de7922bc709eee2f609cd01d98aaedc4cf5ea74 upstream. | ||
8 | |||
9 | Commit 6f4c618ddb0 ("SCTP : Add paramters validity check for | ||
10 | ASCONF chunk") added basic verification of ASCONF chunks, however, | ||
11 | it is still possible to remotely crash a server by sending a | ||
12 | special crafted ASCONF chunk, even up to pre 2.6.12 kernels: | ||
13 | |||
14 | skb_over_panic: text:ffffffffa01ea1c3 len:31056 put:30768 | ||
15 | head:ffff88011bd81800 data:ffff88011bd81800 tail:0x7950 | ||
16 | end:0x440 dev:<NULL> | ||
17 | ------------[ cut here ]------------ | ||
18 | kernel BUG at net/core/skbuff.c:129! | ||
19 | [...] | ||
20 | Call Trace: | ||
21 | <IRQ> | ||
22 | [<ffffffff8144fb1c>] skb_put+0x5c/0x70 | ||
23 | [<ffffffffa01ea1c3>] sctp_addto_chunk+0x63/0xd0 [sctp] | ||
24 | [<ffffffffa01eadaf>] sctp_process_asconf+0x1af/0x540 [sctp] | ||
25 | [<ffffffff8152d025>] ? _read_unlock_bh+0x15/0x20 | ||
26 | [<ffffffffa01e0038>] sctp_sf_do_asconf+0x168/0x240 [sctp] | ||
27 | [<ffffffffa01e3751>] sctp_do_sm+0x71/0x1210 [sctp] | ||
28 | [<ffffffff8147645d>] ? fib_rules_lookup+0xad/0xf0 | ||
29 | [<ffffffffa01e6b22>] ? sctp_cmp_addr_exact+0x32/0x40 [sctp] | ||
30 | [<ffffffffa01e8393>] sctp_assoc_bh_rcv+0xd3/0x180 [sctp] | ||
31 | [<ffffffffa01ee986>] sctp_inq_push+0x56/0x80 [sctp] | ||
32 | [<ffffffffa01fcc42>] sctp_rcv+0x982/0xa10 [sctp] | ||
33 | [<ffffffffa01d5123>] ? ipt_local_in_hook+0x23/0x28 [iptable_filter] | ||
34 | [<ffffffff8148bdc9>] ? nf_iterate+0x69/0xb0 | ||
35 | [<ffffffff81496d10>] ? ip_local_deliver_finish+0x0/0x2d0 | ||
36 | [<ffffffff8148bf86>] ? nf_hook_slow+0x76/0x120 | ||
37 | [<ffffffff81496d10>] ? ip_local_deliver_finish+0x0/0x2d0 | ||
38 | [<ffffffff81496ded>] ip_local_deliver_finish+0xdd/0x2d0 | ||
39 | [<ffffffff81497078>] ip_local_deliver+0x98/0xa0 | ||
40 | [<ffffffff8149653d>] ip_rcv_finish+0x12d/0x440 | ||
41 | [<ffffffff81496ac5>] ip_rcv+0x275/0x350 | ||
42 | [<ffffffff8145c88b>] __netif_receive_skb+0x4ab/0x750 | ||
43 | [<ffffffff81460588>] netif_receive_skb+0x58/0x60 | ||
44 | |||
45 | This can be triggered e.g., through a simple scripted nmap | ||
46 | connection scan injecting the chunk after the handshake, for | ||
47 | example, ... | ||
48 | |||
49 | -------------- INIT[ASCONF; ASCONF_ACK] -------------> | ||
50 | <----------- INIT-ACK[ASCONF; ASCONF_ACK] ------------ | ||
51 | -------------------- COOKIE-ECHO --------------------> | ||
52 | <-------------------- COOKIE-ACK --------------------- | ||
53 | ------------------ ASCONF; UNKNOWN ------------------> | ||
54 | |||
55 | ... where ASCONF chunk of length 280 contains 2 parameters ... | ||
56 | |||
57 | 1) Add IP address parameter (param length: 16) | ||
58 | 2) Add/del IP address parameter (param length: 255) | ||
59 | |||
60 | ... followed by an UNKNOWN chunk of e.g. 4 bytes. Here, the | ||
61 | Address Parameter in the ASCONF chunk is even missing, too. | ||
62 | This is just an example and similarly-crafted ASCONF chunks | ||
63 | could be used just as well. | ||
64 | |||
65 | The ASCONF chunk passes through sctp_verify_asconf() as all | ||
66 | parameters passed sanity checks, and after walking, we ended | ||
67 | up successfully at the chunk end boundary, and thus may invoke | ||
68 | sctp_process_asconf(). Parameter walking is done with | ||
69 | WORD_ROUND() to take padding into account. | ||
70 | |||
71 | In sctp_process_asconf()'s TLV processing, we may fail in | ||
72 | sctp_process_asconf_param() e.g., due to removal of the IP | ||
73 | address that is also the source address of the packet containing | ||
74 | the ASCONF chunk, and thus we need to add all TLVs after the | ||
75 | failure to our ASCONF response to remote via helper function | ||
76 | sctp_add_asconf_response(), which basically invokes a | ||
77 | sctp_addto_chunk() adding the error parameters to the given | ||
78 | skb. | ||
79 | |||
80 | When walking to the next parameter this time, we proceed | ||
81 | with ... | ||
82 | |||
83 | length = ntohs(asconf_param->param_hdr.length); | ||
84 | asconf_param = (void *)asconf_param + length; | ||
85 | |||
86 | ... instead of the WORD_ROUND()'ed length, thus resulting here | ||
87 | in an off-by-one that leads to reading the follow-up garbage | ||
88 | parameter length of 12336, and thus throwing an skb_over_panic | ||
89 | for the reply when trying to sctp_addto_chunk() next time, | ||
90 | which implicitly calls the skb_put() with that length. | ||
91 | |||
92 | Fix it by using sctp_walk_params() [ which is also used in | ||
93 | INIT parameter processing ] macro in the verification *and* | ||
94 | in ASCONF processing: it will make sure we don't spill over, | ||
95 | that we walk parameters WORD_ROUND()'ed. Moreover, we're being | ||
96 | more defensive and guard against unknown parameter types and | ||
97 | missized addresses. | ||
98 | |||
99 | Joint work with Vlad Yasevich. | ||
100 | |||
101 | Fixes CVE-2014-3673 | ||
102 | Upstream-Status: Backport | ||
103 | |||
104 | Fixes: b896b82be4ae ("[SCTP] ADDIP: Support for processing incoming ASCONF_ACK chunks.") | ||
105 | Signed-off-by: Daniel Borkmann <dborkman@redhat.com> | ||
106 | Signed-off-by: Vlad Yasevich <vyasevich@gmail.com> | ||
107 | Acked-by: Neil Horman <nhorman@tuxdriver.com> | ||
108 | Signed-off-by: David S. Miller <davem@davemloft.net> | ||
109 | Cc: Josh Boyer <jwboyer@fedoraproject.org> | ||
110 | Signed-off-by: Jiri Slaby <jslaby@suse.cz> | ||
111 | Signed-off-by: Sona Sarmadi <sona.sarmadi@enea.com> | ||
112 | --- | ||
113 | include/net/sctp/sm.h | 6 +-- | ||
114 | net/sctp/sm_make_chunk.c | 99 +++++++++++++++++++++++++++--------------------- | ||
115 | net/sctp/sm_statefuns.c | 18 +-------- | ||
116 | 3 files changed, 60 insertions(+), 63 deletions(-) | ||
117 | |||
118 | diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h | ||
119 | index 4ef75af..c91b6f5 100644 | ||
120 | --- a/include/net/sctp/sm.h | ||
121 | +++ b/include/net/sctp/sm.h | ||
122 | @@ -249,9 +249,9 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *, | ||
123 | int, __be16); | ||
124 | struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, | ||
125 | union sctp_addr *addr); | ||
126 | -int sctp_verify_asconf(const struct sctp_association *asoc, | ||
127 | - struct sctp_paramhdr *param_hdr, void *chunk_end, | ||
128 | - struct sctp_paramhdr **errp); | ||
129 | +bool sctp_verify_asconf(const struct sctp_association *asoc, | ||
130 | + struct sctp_chunk *chunk, bool addr_param_needed, | ||
131 | + struct sctp_paramhdr **errp); | ||
132 | struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, | ||
133 | struct sctp_chunk *asconf); | ||
134 | int sctp_process_asconf_ack(struct sctp_association *asoc, | ||
135 | diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c | ||
136 | index e342387..d800160 100644 | ||
137 | --- a/net/sctp/sm_make_chunk.c | ||
138 | +++ b/net/sctp/sm_make_chunk.c | ||
139 | @@ -3126,50 +3126,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, | ||
140 | return SCTP_ERROR_NO_ERROR; | ||
141 | } | ||
142 | |||
143 | -/* Verify the ASCONF packet before we process it. */ | ||
144 | -int sctp_verify_asconf(const struct sctp_association *asoc, | ||
145 | - struct sctp_paramhdr *param_hdr, void *chunk_end, | ||
146 | - struct sctp_paramhdr **errp) { | ||
147 | - sctp_addip_param_t *asconf_param; | ||
148 | +/* Verify the ASCONF packet before we process it. */ | ||
149 | +bool sctp_verify_asconf(const struct sctp_association *asoc, | ||
150 | + struct sctp_chunk *chunk, bool addr_param_needed, | ||
151 | + struct sctp_paramhdr **errp) | ||
152 | +{ | ||
153 | + sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr; | ||
154 | union sctp_params param; | ||
155 | - int length, plen; | ||
156 | - | ||
157 | - param.v = (sctp_paramhdr_t *) param_hdr; | ||
158 | - while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) { | ||
159 | - length = ntohs(param.p->length); | ||
160 | - *errp = param.p; | ||
161 | + bool addr_param_seen = false; | ||
162 | |||
163 | - if (param.v > chunk_end - length || | ||
164 | - length < sizeof(sctp_paramhdr_t)) | ||
165 | - return 0; | ||
166 | + sctp_walk_params(param, addip, addip_hdr.params) { | ||
167 | + size_t length = ntohs(param.p->length); | ||
168 | |||
169 | + *errp = param.p; | ||
170 | switch (param.p->type) { | ||
171 | + case SCTP_PARAM_ERR_CAUSE: | ||
172 | + break; | ||
173 | + case SCTP_PARAM_IPV4_ADDRESS: | ||
174 | + if (length != sizeof(sctp_ipv4addr_param_t)) | ||
175 | + return false; | ||
176 | + addr_param_seen = true; | ||
177 | + break; | ||
178 | + case SCTP_PARAM_IPV6_ADDRESS: | ||
179 | + if (length != sizeof(sctp_ipv6addr_param_t)) | ||
180 | + return false; | ||
181 | + addr_param_seen = true; | ||
182 | + break; | ||
183 | case SCTP_PARAM_ADD_IP: | ||
184 | case SCTP_PARAM_DEL_IP: | ||
185 | case SCTP_PARAM_SET_PRIMARY: | ||
186 | - asconf_param = (sctp_addip_param_t *)param.v; | ||
187 | - plen = ntohs(asconf_param->param_hdr.length); | ||
188 | - if (plen < sizeof(sctp_addip_param_t) + | ||
189 | - sizeof(sctp_paramhdr_t)) | ||
190 | - return 0; | ||
191 | + /* In ASCONF chunks, these need to be first. */ | ||
192 | + if (addr_param_needed && !addr_param_seen) | ||
193 | + return false; | ||
194 | + length = ntohs(param.addip->param_hdr.length); | ||
195 | + if (length < sizeof(sctp_addip_param_t) + | ||
196 | + sizeof(sctp_paramhdr_t)) | ||
197 | + return false; | ||
198 | break; | ||
199 | case SCTP_PARAM_SUCCESS_REPORT: | ||
200 | case SCTP_PARAM_ADAPTATION_LAYER_IND: | ||
201 | if (length != sizeof(sctp_addip_param_t)) | ||
202 | - return 0; | ||
203 | - | ||
204 | + return false; | ||
205 | break; | ||
206 | default: | ||
207 | - break; | ||
208 | + /* This is unkown to us, reject! */ | ||
209 | + return false; | ||
210 | } | ||
211 | - | ||
212 | - param.v += WORD_ROUND(length); | ||
213 | } | ||
214 | |||
215 | - if (param.v != chunk_end) | ||
216 | - return 0; | ||
217 | + /* Remaining sanity checks. */ | ||
218 | + if (addr_param_needed && !addr_param_seen) | ||
219 | + return false; | ||
220 | + if (!addr_param_needed && addr_param_seen) | ||
221 | + return false; | ||
222 | + if (param.v != chunk->chunk_end) | ||
223 | + return false; | ||
224 | |||
225 | - return 1; | ||
226 | + return true; | ||
227 | } | ||
228 | |||
229 | /* Process an incoming ASCONF chunk with the next expected serial no. and | ||
230 | @@ -3178,16 +3191,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc, | ||
231 | struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, | ||
232 | struct sctp_chunk *asconf) | ||
233 | { | ||
234 | + sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr; | ||
235 | + bool all_param_pass = true; | ||
236 | + union sctp_params param; | ||
237 | sctp_addiphdr_t *hdr; | ||
238 | union sctp_addr_param *addr_param; | ||
239 | sctp_addip_param_t *asconf_param; | ||
240 | struct sctp_chunk *asconf_ack; | ||
241 | - | ||
242 | __be16 err_code; | ||
243 | int length = 0; | ||
244 | int chunk_len; | ||
245 | __u32 serial; | ||
246 | - int all_param_pass = 1; | ||
247 | |||
248 | chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); | ||
249 | hdr = (sctp_addiphdr_t *)asconf->skb->data; | ||
250 | @@ -3215,9 +3229,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, | ||
251 | goto done; | ||
252 | |||
253 | /* Process the TLVs contained within the ASCONF chunk. */ | ||
254 | - while (chunk_len > 0) { | ||
255 | + sctp_walk_params(param, addip, addip_hdr.params) { | ||
256 | + /* Skip preceeding address parameters. */ | ||
257 | + if (param.p->type == SCTP_PARAM_IPV4_ADDRESS || | ||
258 | + param.p->type == SCTP_PARAM_IPV6_ADDRESS) | ||
259 | + continue; | ||
260 | + | ||
261 | err_code = sctp_process_asconf_param(asoc, asconf, | ||
262 | - asconf_param); | ||
263 | + param.addip); | ||
264 | /* ADDIP 4.1 A7) | ||
265 | * If an error response is received for a TLV parameter, | ||
266 | * all TLVs with no response before the failed TLV are | ||
267 | @@ -3225,28 +3244,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, | ||
268 | * the failed response are considered unsuccessful unless | ||
269 | * a specific success indication is present for the parameter. | ||
270 | */ | ||
271 | - if (SCTP_ERROR_NO_ERROR != err_code) | ||
272 | - all_param_pass = 0; | ||
273 | - | ||
274 | + if (err_code != SCTP_ERROR_NO_ERROR) | ||
275 | + all_param_pass = false; | ||
276 | if (!all_param_pass) | ||
277 | - sctp_add_asconf_response(asconf_ack, | ||
278 | - asconf_param->crr_id, err_code, | ||
279 | - asconf_param); | ||
280 | + sctp_add_asconf_response(asconf_ack, param.addip->crr_id, | ||
281 | + err_code, param.addip); | ||
282 | |||
283 | /* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add | ||
284 | * an IP address sends an 'Out of Resource' in its response, it | ||
285 | * MUST also fail any subsequent add or delete requests bundled | ||
286 | * in the ASCONF. | ||
287 | */ | ||
288 | - if (SCTP_ERROR_RSRC_LOW == err_code) | ||
289 | + if (err_code == SCTP_ERROR_RSRC_LOW) | ||
290 | goto done; | ||
291 | - | ||
292 | - /* Move to the next ASCONF param. */ | ||
293 | - length = ntohs(asconf_param->param_hdr.length); | ||
294 | - asconf_param = (void *)asconf_param + length; | ||
295 | - chunk_len -= length; | ||
296 | } | ||
297 | - | ||
298 | done: | ||
299 | asoc->peer.addip_serial++; | ||
300 | |||
301 | diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c | ||
302 | index 62623cc..bf12098 100644 | ||
303 | --- a/net/sctp/sm_statefuns.c | ||
304 | +++ b/net/sctp/sm_statefuns.c | ||
305 | @@ -3595,9 +3595,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, | ||
306 | struct sctp_chunk *asconf_ack = NULL; | ||
307 | struct sctp_paramhdr *err_param = NULL; | ||
308 | sctp_addiphdr_t *hdr; | ||
309 | - union sctp_addr_param *addr_param; | ||
310 | __u32 serial; | ||
311 | - int length; | ||
312 | |||
313 | if (!sctp_vtag_verify(chunk, asoc)) { | ||
314 | sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, | ||
315 | @@ -3622,17 +3620,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, | ||
316 | hdr = (sctp_addiphdr_t *)chunk->skb->data; | ||
317 | serial = ntohl(hdr->serial); | ||
318 | |||
319 | - addr_param = (union sctp_addr_param *)hdr->params; | ||
320 | - length = ntohs(addr_param->p.length); | ||
321 | - if (length < sizeof(sctp_paramhdr_t)) | ||
322 | - return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, | ||
323 | - (void *)addr_param, commands); | ||
324 | - | ||
325 | /* Verify the ASCONF chunk before processing it. */ | ||
326 | - if (!sctp_verify_asconf(asoc, | ||
327 | - (sctp_paramhdr_t *)((void *)addr_param + length), | ||
328 | - (void *)chunk->chunk_end, | ||
329 | - &err_param)) | ||
330 | + if (!sctp_verify_asconf(asoc, chunk, true, &err_param)) | ||
331 | return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, | ||
332 | (void *)err_param, commands); | ||
333 | |||
334 | @@ -3750,10 +3739,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, | ||
335 | rcvd_serial = ntohl(addip_hdr->serial); | ||
336 | |||
337 | /* Verify the ASCONF-ACK chunk before processing it. */ | ||
338 | - if (!sctp_verify_asconf(asoc, | ||
339 | - (sctp_paramhdr_t *)addip_hdr->params, | ||
340 | - (void *)asconf_ack->chunk_end, | ||
341 | - &err_param)) | ||
342 | + if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param)) | ||
343 | return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, | ||
344 | (void *)err_param, commands); | ||
345 | |||
346 | -- | ||
347 | 1.9.1 | ||
348 | |||