aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Hutchings <ben@decadent.org.uk>2011-03-20 02:48:05 -0400
committerDavid S. Miller <davem@davemloft.net>2011-03-27 20:59:04 -0400
commite0bccd315db0c2f919e7fcf9cb60db21d9986f52 (patch)
tree8cf512f43221087f964c0f55c7665e293e96921b
parentbe20250c13f88375345ad99950190685eda51eb8 (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.h8
-rw-r--r--net/rose/af_rose.c8
-rw-r--r--net/rose/rose_loopback.c13
-rw-r--r--net/rose/rose_route.c20
-rw-r--r--net/rose/rose_subr.c91
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 *);
214extern int rose_validate_nr(struct sock *, unsigned short); 220extern int rose_validate_nr(struct sock *, unsigned short);
215extern void rose_write_internal(struct sock *, int); 221extern void rose_write_internal(struct sock *, int);
216extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *); 222extern int rose_decode(struct sk_buff *, int *, int *, int *, int *, int *);
217extern int rose_parse_facilities(unsigned char *, struct rose_facilities_struct *); 223extern int rose_parse_facilities(unsigned char *, unsigned int, struct rose_facilities_struct *);
218extern void rose_disconnect(struct sock *, int, int, int); 224extern 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
368int rose_parse_facilities(unsigned char *p, 396int 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
413static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose) 436static int rose_create_facilities(unsigned char *buffer, struct rose_sock *rose)