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 /net/rose/rose_subr.c | |
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>
Diffstat (limited to 'net/rose/rose_subr.c')
-rw-r--r-- | net/rose/rose_subr.c | 91 |
1 files changed, 57 insertions, 34 deletions
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) |