diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2011-03-20 02:48:05 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2011-03-27 20:59:04 -0400 |
commit | e0bccd315db0c2f919e7fcf9cb60db21d9986f52 (patch) | |
tree | 8cf512f43221087f964c0f55c7665e293e96921b | |
parent | be20250c13f88375345ad99950190685eda51eb8 (diff) |
rose: Add length checks to CALL_REQUEST parsing
Define some constant offsets for CALL_REQUEST based on the description
at <http://www.techfest.com/networking/wan/x25plp.htm> and the
definition of ROSE as using 10-digit (5-byte) addresses. Use them
consistently. Validate all implicit and explicit facilities lengths.
Validate the address length byte rather than either trusting or
assuming its value.
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/rose.h | 8 | ||||
-rw-r--r-- | net/rose/af_rose.c | 8 | ||||
-rw-r--r-- | net/rose/rose_loopback.c | 13 | ||||
-rw-r--r-- | net/rose/rose_route.c | 20 | ||||
-rw-r--r-- | net/rose/rose_subr.c | 91 |
5 files changed, 93 insertions, 47 deletions
diff --git a/include/net/rose.h b/include/net/rose.h index 5ba9f02731eb..555dd198aab7 100644 --- a/include/net/rose.h +++ b/include/net/rose.h | |||
@@ -14,6 +14,12 @@ | |||
14 | 14 | ||
15 | #define ROSE_MIN_LEN 3 | 15 | #define ROSE_MIN_LEN 3 |
16 | 16 | ||
17 | #define ROSE_CALL_REQ_ADDR_LEN_OFF 3 | ||
18 | #define ROSE_CALL_REQ_ADDR_LEN_VAL 0xAA /* each address is 10 digits */ | ||
19 | #define ROSE_CALL_REQ_DEST_ADDR_OFF 4 | ||
20 | #define ROSE_CALL_REQ_SRC_ADDR_OFF 9 | ||
21 | #define ROSE_CALL_REQ_FACILITIES_OFF 14 | ||
22 | |||
17 | #define ROSE_GFI 0x10 | 23 | #define ROSE_GFI 0x10 |
18 | #define ROSE_Q_BIT 0x80 | 24 | #define ROSE_Q_BIT 0x80 |
19 | #define ROSE_D_BIT 0x40 | 25 | #define ROSE_D_BIT 0x40 |
@@ -214,7 +220,7 @@ extern void rose_requeue_frames(struct sock *); | |||
214 | extern int rose_validate_nr(struct sock *, unsigned short); | 220 | extern int rose_validate_nr(struct sock *, unsigned short); |
215 | extern void rose_write_internal(struct sock *, int); | 221 | extern void rose_write_internal(struct sock *, int); |
216 | extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); | 222 | extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); |
217 | extern int rose_parse_facilities(unsigned char *, struct rose_facilities_struct *); | 223 | extern int rose_parse_facilities(unsigned char *, unsigned int, struct rose_facilities_struct *); |
218 | extern void rose_disconnect(struct sock *, int, int, int); | 224 | extern void rose_disconnect(struct sock *, int, int, int); |
219 | 225 | ||
220 | /* rose_timer.c */ | 226 | /* rose_timer.c */ |
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index 5ee0c62046a0..a80aef6e3d1f 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c | |||
@@ -978,7 +978,7 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros | |||
978 | struct sock *make; | 978 | struct sock *make; |
979 | struct rose_sock *make_rose; | 979 | struct rose_sock *make_rose; |
980 | struct rose_facilities_struct facilities; | 980 | struct rose_facilities_struct facilities; |
981 | int n, len; | 981 | int n; |
982 | 982 | ||
983 | skb->sk = NULL; /* Initially we don't know who it's for */ | 983 | skb->sk = NULL; /* Initially we don't know who it's for */ |
984 | 984 | ||
@@ -987,9 +987,9 @@ int rose_rx_call_request(struct sk_buff *skb, struct net_device *dev, struct ros | |||
987 | */ | 987 | */ |
988 | memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); | 988 | memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); |
989 | 989 | ||
990 | len = (((skb->data[3] >> 4) & 0x0F) + 1) >> 1; | 990 | if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF, |
991 | len += (((skb->data[3] >> 0) & 0x0F) + 1) >> 1; | 991 | skb->len - ROSE_CALL_REQ_FACILITIES_OFF, |
992 | if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { | 992 | &facilities)) { |
993 | rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); | 993 | rose_transmit_clear_request(neigh, lci, ROSE_INVALID_FACILITY, 76); |
994 | return 0; | 994 | return 0; |
995 | } | 995 | } |
diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c index ae4a9d99aec7..344456206b70 100644 --- a/net/rose/rose_loopback.c +++ b/net/rose/rose_loopback.c | |||
@@ -73,9 +73,20 @@ static void rose_loopback_timer(unsigned long param) | |||
73 | unsigned int lci_i, lci_o; | 73 | unsigned int lci_i, lci_o; |
74 | 74 | ||
75 | while ((skb = skb_dequeue(&loopback_queue)) != NULL) { | 75 | while ((skb = skb_dequeue(&loopback_queue)) != NULL) { |
76 | if (skb->len < ROSE_MIN_LEN) { | ||
77 | kfree_skb(skb); | ||
78 | continue; | ||
79 | } | ||
76 | lci_i = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); | 80 | lci_i = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); |
77 | frametype = skb->data[2]; | 81 | frametype = skb->data[2]; |
78 | dest = (rose_address *)(skb->data + 4); | 82 | if (frametype == ROSE_CALL_REQUEST && |
83 | (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF || | ||
84 | skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] != | ||
85 | ROSE_CALL_REQ_ADDR_LEN_VAL)) { | ||
86 | kfree_skb(skb); | ||
87 | continue; | ||
88 | } | ||
89 | dest = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF); | ||
79 | lci_o = ROSE_DEFAULT_MAXVC + 1 - lci_i; | 90 | lci_o = ROSE_DEFAULT_MAXVC + 1 - lci_i; |
80 | 91 | ||
81 | skb_reset_transport_header(skb); | 92 | skb_reset_transport_header(skb); |
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 88a77e90e7e8..08dcd2f29cdc 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c | |||
@@ -861,7 +861,7 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) | |||
861 | unsigned int lci, new_lci; | 861 | unsigned int lci, new_lci; |
862 | unsigned char cause, diagnostic; | 862 | unsigned char cause, diagnostic; |
863 | struct net_device *dev; | 863 | struct net_device *dev; |
864 | int len, res = 0; | 864 | int res = 0; |
865 | char buf[11]; | 865 | char buf[11]; |
866 | 866 | ||
867 | #if 0 | 867 | #if 0 |
@@ -869,10 +869,17 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) | |||
869 | return res; | 869 | return res; |
870 | #endif | 870 | #endif |
871 | 871 | ||
872 | if (skb->len < ROSE_MIN_LEN) | ||
873 | return res; | ||
872 | frametype = skb->data[2]; | 874 | frametype = skb->data[2]; |
873 | lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); | 875 | lci = ((skb->data[0] << 8) & 0xF00) + ((skb->data[1] << 0) & 0x0FF); |
874 | src_addr = (rose_address *)(skb->data + 9); | 876 | if (frametype == ROSE_CALL_REQUEST && |
875 | dest_addr = (rose_address *)(skb->data + 4); | 877 | (skb->len <= ROSE_CALL_REQ_FACILITIES_OFF || |
878 | skb->data[ROSE_CALL_REQ_ADDR_LEN_OFF] != | ||
879 | ROSE_CALL_REQ_ADDR_LEN_VAL)) | ||
880 | return res; | ||
881 | src_addr = (rose_address *)(skb->data + ROSE_CALL_REQ_SRC_ADDR_OFF); | ||
882 | dest_addr = (rose_address *)(skb->data + ROSE_CALL_REQ_DEST_ADDR_OFF); | ||
876 | 883 | ||
877 | spin_lock_bh(&rose_neigh_list_lock); | 884 | spin_lock_bh(&rose_neigh_list_lock); |
878 | spin_lock_bh(&rose_route_list_lock); | 885 | spin_lock_bh(&rose_route_list_lock); |
@@ -1010,12 +1017,11 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) | |||
1010 | goto out; | 1017 | goto out; |
1011 | } | 1018 | } |
1012 | 1019 | ||
1013 | len = (((skb->data[3] >> 4) & 0x0F) + 1) >> 1; | ||
1014 | len += (((skb->data[3] >> 0) & 0x0F) + 1) >> 1; | ||
1015 | |||
1016 | memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); | 1020 | memset(&facilities, 0x00, sizeof(struct rose_facilities_struct)); |
1017 | 1021 | ||
1018 | if (!rose_parse_facilities(skb->data + len + 4, &facilities)) { | 1022 | if (!rose_parse_facilities(skb->data + ROSE_CALL_REQ_FACILITIES_OFF, |
1023 | skb->len - ROSE_CALL_REQ_FACILITIES_OFF, | ||
1024 | &facilities)) { | ||
1019 | rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); | 1025 | rose_transmit_clear_request(rose_neigh, lci, ROSE_INVALID_FACILITY, 76); |
1020 | goto out; | 1026 | goto out; |
1021 | } | 1027 | } |
diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 174d51c9ce37..f6c71caa94b9 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c | |||
@@ -142,7 +142,7 @@ void rose_write_internal(struct sock *sk, int frametype) | |||
142 | *dptr++ = ROSE_GFI | lci1; | 142 | *dptr++ = ROSE_GFI | lci1; |
143 | *dptr++ = lci2; | 143 | *dptr++ = lci2; |
144 | *dptr++ = frametype; | 144 | *dptr++ = frametype; |
145 | *dptr++ = 0xAA; | 145 | *dptr++ = ROSE_CALL_REQ_ADDR_LEN_VAL; |
146 | memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN); | 146 | memcpy(dptr, &rose->dest_addr, ROSE_ADDR_LEN); |
147 | dptr += ROSE_ADDR_LEN; | 147 | dptr += ROSE_ADDR_LEN; |
148 | memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); | 148 | memcpy(dptr, &rose->source_addr, ROSE_ADDR_LEN); |
@@ -246,12 +246,16 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct * | |||
246 | do { | 246 | do { |
247 | switch (*p & 0xC0) { | 247 | switch (*p & 0xC0) { |
248 | case 0x00: | 248 | case 0x00: |
249 | if (len < 2) | ||
250 | return -1; | ||
249 | p += 2; | 251 | p += 2; |
250 | n += 2; | 252 | n += 2; |
251 | len -= 2; | 253 | len -= 2; |
252 | break; | 254 | break; |
253 | 255 | ||
254 | case 0x40: | 256 | case 0x40: |
257 | if (len < 3) | ||
258 | return -1; | ||
255 | if (*p == FAC_NATIONAL_RAND) | 259 | if (*p == FAC_NATIONAL_RAND) |
256 | facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF); | 260 | facilities->rand = ((p[1] << 8) & 0xFF00) + ((p[2] << 0) & 0x00FF); |
257 | p += 3; | 261 | p += 3; |
@@ -260,32 +264,48 @@ static int rose_parse_national(unsigned char *p, struct rose_facilities_struct * | |||
260 | break; | 264 | break; |
261 | 265 | ||
262 | case 0x80: | 266 | case 0x80: |
267 | if (len < 4) | ||
268 | return -1; | ||
263 | p += 4; | 269 | p += 4; |
264 | n += 4; | 270 | n += 4; |
265 | len -= 4; | 271 | len -= 4; |
266 | break; | 272 | break; |
267 | 273 | ||
268 | case 0xC0: | 274 | case 0xC0: |
275 | if (len < 2) | ||
276 | return -1; | ||
269 | l = p[1]; | 277 | l = p[1]; |
278 | if (len < 2 + l) | ||
279 | return -1; | ||
270 | if (*p == FAC_NATIONAL_DEST_DIGI) { | 280 | if (*p == FAC_NATIONAL_DEST_DIGI) { |
271 | if (!fac_national_digis_received) { | 281 | if (!fac_national_digis_received) { |
282 | if (l < AX25_ADDR_LEN) | ||
283 | return -1; | ||
272 | memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN); | 284 | memcpy(&facilities->source_digis[0], p + 2, AX25_ADDR_LEN); |
273 | facilities->source_ndigis = 1; | 285 | facilities->source_ndigis = 1; |
274 | } | 286 | } |
275 | } | 287 | } |
276 | else if (*p == FAC_NATIONAL_SRC_DIGI) { | 288 | else if (*p == FAC_NATIONAL_SRC_DIGI) { |
277 | if (!fac_national_digis_received) { | 289 | if (!fac_national_digis_received) { |
290 | if (l < AX25_ADDR_LEN) | ||
291 | return -1; | ||
278 | memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); | 292 | memcpy(&facilities->dest_digis[0], p + 2, AX25_ADDR_LEN); |
279 | facilities->dest_ndigis = 1; | 293 | facilities->dest_ndigis = 1; |
280 | } | 294 | } |
281 | } | 295 | } |
282 | else if (*p == FAC_NATIONAL_FAIL_CALL) { | 296 | else if (*p == FAC_NATIONAL_FAIL_CALL) { |
297 | if (l < AX25_ADDR_LEN) | ||
298 | return -1; | ||
283 | memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); | 299 | memcpy(&facilities->fail_call, p + 2, AX25_ADDR_LEN); |
284 | } | 300 | } |
285 | else if (*p == FAC_NATIONAL_FAIL_ADD) { | 301 | else if (*p == FAC_NATIONAL_FAIL_ADD) { |
302 | if (l < 1 + ROSE_ADDR_LEN) | ||
303 | return -1; | ||
286 | memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); | 304 | memcpy(&facilities->fail_addr, p + 3, ROSE_ADDR_LEN); |
287 | } | 305 | } |
288 | else if (*p == FAC_NATIONAL_DIGIS) { | 306 | else if (*p == FAC_NATIONAL_DIGIS) { |
307 | if (l % AX25_ADDR_LEN) | ||
308 | return -1; | ||
289 | fac_national_digis_received = 1; | 309 | fac_national_digis_received = 1; |
290 | facilities->source_ndigis = 0; | 310 | facilities->source_ndigis = 0; |
291 | facilities->dest_ndigis = 0; | 311 | facilities->dest_ndigis = 0; |
@@ -319,24 +339,32 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac | |||
319 | do { | 339 | do { |
320 | switch (*p & 0xC0) { | 340 | switch (*p & 0xC0) { |
321 | case 0x00: | 341 | case 0x00: |
342 | if (len < 2) | ||
343 | return -1; | ||
322 | p += 2; | 344 | p += 2; |
323 | n += 2; | 345 | n += 2; |
324 | len -= 2; | 346 | len -= 2; |
325 | break; | 347 | break; |
326 | 348 | ||
327 | case 0x40: | 349 | case 0x40: |
350 | if (len < 3) | ||
351 | return -1; | ||
328 | p += 3; | 352 | p += 3; |
329 | n += 3; | 353 | n += 3; |
330 | len -= 3; | 354 | len -= 3; |
331 | break; | 355 | break; |
332 | 356 | ||
333 | case 0x80: | 357 | case 0x80: |
358 | if (len < 4) | ||
359 | return -1; | ||
334 | p += 4; | 360 | p += 4; |
335 | n += 4; | 361 | n += 4; |
336 | len -= 4; | 362 | len -= 4; |
337 | break; | 363 | break; |
338 | 364 | ||
339 | case 0xC0: | 365 | case 0xC0: |
366 | if (len < 2) | ||
367 | return -1; | ||
340 | l = p[1]; | 368 | l = p[1]; |
341 | 369 | ||
342 | /* Prevent overflows*/ | 370 | /* Prevent overflows*/ |
@@ -365,49 +393,44 @@ static int rose_parse_ccitt(unsigned char *p, struct rose_facilities_struct *fac | |||
365 | return n; | 393 | return n; |
366 | } | 394 | } |
367 | 395 | ||
368 | int rose_parse_facilities(unsigned char *p, | 396 | int rose_parse_facilities(unsigned char *p, unsigned packet_len, |
369 | struct rose_facilities_struct *facilities) | 397 | struct rose_facilities_struct *facilities) |
370 | { | 398 | { |
371 | int facilities_len, len; | 399 | int facilities_len, len; |
372 | 400 | ||
373 | facilities_len = *p++; | 401 | facilities_len = *p++; |
374 | 402 | ||
375 | if (facilities_len == 0) | 403 | if (facilities_len == 0 || (unsigned)facilities_len > packet_len) |
376 | return 0; | 404 | return 0; |
377 | 405 | ||
378 | while (facilities_len > 0) { | 406 | while (facilities_len >= 3 && *p == 0x00) { |
379 | if (*p == 0x00) { | 407 | facilities_len--; |
380 | facilities_len--; | 408 | p++; |
381 | p++; | 409 | |
382 | 410 | switch (*p) { | |
383 | switch (*p) { | 411 | case FAC_NATIONAL: /* National */ |
384 | case FAC_NATIONAL: /* National */ | 412 | len = rose_parse_national(p + 1, facilities, facilities_len - 1); |
385 | len = rose_parse_national(p + 1, facilities, facilities_len - 1); | 413 | break; |
386 | if (len < 0) | 414 | |
387 | return 0; | 415 | case FAC_CCITT: /* CCITT */ |
388 | facilities_len -= len + 1; | 416 | len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); |
389 | p += len + 1; | 417 | break; |
390 | break; | 418 | |
391 | 419 | default: | |
392 | case FAC_CCITT: /* CCITT */ | 420 | printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); |
393 | len = rose_parse_ccitt(p + 1, facilities, facilities_len - 1); | 421 | len = 1; |
394 | if (len < 0) | 422 | break; |
395 | return 0; | 423 | } |
396 | facilities_len -= len + 1; | 424 | |
397 | p += len + 1; | 425 | if (len < 0) |
398 | break; | 426 | return 0; |
399 | 427 | if (WARN_ON(len >= facilities_len)) | |
400 | default: | 428 | return 0; |
401 | printk(KERN_DEBUG "ROSE: rose_parse_facilities - unknown facilities family %02X\n", *p); | 429 | facilities_len -= len + 1; |
402 | facilities_len--; | 430 | p += len + 1; |
403 | p++; | ||
404 | break; | ||
405 | } | ||
406 | } else | ||
407 | break; /* Error in facilities format */ | ||
408 | } | 431 | } |
409 | 432 | ||
410 | return 1; | 433 | return facilities_len == 0; |
411 | } | 434 | } |
412 | 435 | ||
413 | static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose) | 436 | static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose) |