diff options
author | Dmitry Mishin <dim@openvz.org> | 2006-12-05 16:43:50 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-12-06 21:39:02 -0500 |
commit | 74c9c0c17dea729d6089c0c82762babd02e65f84 (patch) | |
tree | fa3c59bcd8121b469013b49963fb561f0fa30903 /net | |
parent | 79066ad32be5bb2edf16733aec36acf2af03fc99 (diff) |
[NETFILTER]: Fix {ip,ip6,arp}_tables hook validation
Commit 590bdf7fd2292b47c428111cb1360e312eff207e introduced a regression
in match/target hook validation. mark_source_chains builds a bitmask
for each rule representing the hooks it can be reached from, which is
then used by the matches and targets to make sure they are only called
from valid hooks. The patch moved the match/target specific validation
before the mark_source_chains call, at which point the mask is always zero.
This patch returns back to the old order and moves the standard checks
to mark_source_chains. This allows to get rid of a special case for
standard targets as a nice side-effect.
Signed-off-by: Dmitry Mishin <dim@openvz.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 48 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 68 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 59 |
3 files changed, 72 insertions, 103 deletions
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 413c2d0a1f3d..71b76ade00e1 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c | |||
@@ -375,6 +375,13 @@ static int mark_source_chains(struct xt_table_info *newinfo, | |||
375 | && unconditional(&e->arp)) { | 375 | && unconditional(&e->arp)) { |
376 | unsigned int oldpos, size; | 376 | unsigned int oldpos, size; |
377 | 377 | ||
378 | if (t->verdict < -NF_MAX_VERDICT - 1) { | ||
379 | duprintf("mark_source_chains: bad " | ||
380 | "negative verdict (%i)\n", | ||
381 | t->verdict); | ||
382 | return 0; | ||
383 | } | ||
384 | |||
378 | /* Return: backtrack through the last | 385 | /* Return: backtrack through the last |
379 | * big jump. | 386 | * big jump. |
380 | */ | 387 | */ |
@@ -404,6 +411,14 @@ static int mark_source_chains(struct xt_table_info *newinfo, | |||
404 | if (strcmp(t->target.u.user.name, | 411 | if (strcmp(t->target.u.user.name, |
405 | ARPT_STANDARD_TARGET) == 0 | 412 | ARPT_STANDARD_TARGET) == 0 |
406 | && newpos >= 0) { | 413 | && newpos >= 0) { |
414 | if (newpos > newinfo->size - | ||
415 | sizeof(struct arpt_entry)) { | ||
416 | duprintf("mark_source_chains: " | ||
417 | "bad verdict (%i)\n", | ||
418 | newpos); | ||
419 | return 0; | ||
420 | } | ||
421 | |||
407 | /* This a jump; chase it. */ | 422 | /* This a jump; chase it. */ |
408 | duprintf("Jump rule %u -> %u\n", | 423 | duprintf("Jump rule %u -> %u\n", |
409 | pos, newpos); | 424 | pos, newpos); |
@@ -426,8 +441,6 @@ static int mark_source_chains(struct xt_table_info *newinfo, | |||
426 | static inline int standard_check(const struct arpt_entry_target *t, | 441 | static inline int standard_check(const struct arpt_entry_target *t, |
427 | unsigned int max_offset) | 442 | unsigned int max_offset) |
428 | { | 443 | { |
429 | struct arpt_standard_target *targ = (void *)t; | ||
430 | |||
431 | /* Check standard info. */ | 444 | /* Check standard info. */ |
432 | if (t->u.target_size | 445 | if (t->u.target_size |
433 | != ARPT_ALIGN(sizeof(struct arpt_standard_target))) { | 446 | != ARPT_ALIGN(sizeof(struct arpt_standard_target))) { |
@@ -437,18 +450,6 @@ static inline int standard_check(const struct arpt_entry_target *t, | |||
437 | return 0; | 450 | return 0; |
438 | } | 451 | } |
439 | 452 | ||
440 | if (targ->verdict >= 0 | ||
441 | && targ->verdict > max_offset - sizeof(struct arpt_entry)) { | ||
442 | duprintf("arpt_standard_check: bad verdict (%i)\n", | ||
443 | targ->verdict); | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | if (targ->verdict < -NF_MAX_VERDICT - 1) { | ||
448 | duprintf("arpt_standard_check: bad negative verdict (%i)\n", | ||
449 | targ->verdict); | ||
450 | return 0; | ||
451 | } | ||
452 | return 1; | 453 | return 1; |
453 | } | 454 | } |
454 | 455 | ||
@@ -627,18 +628,20 @@ static int translate_table(const char *name, | |||
627 | } | 628 | } |
628 | } | 629 | } |
629 | 630 | ||
631 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) { | ||
632 | duprintf("Looping hook\n"); | ||
633 | return -ELOOP; | ||
634 | } | ||
635 | |||
630 | /* Finally, each sanity check must pass */ | 636 | /* Finally, each sanity check must pass */ |
631 | i = 0; | 637 | i = 0; |
632 | ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size, | 638 | ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size, |
633 | check_entry, name, size, &i); | 639 | check_entry, name, size, &i); |
634 | 640 | ||
635 | if (ret != 0) | 641 | if (ret != 0) { |
636 | goto cleanup; | 642 | ARPT_ENTRY_ITERATE(entry0, newinfo->size, |
637 | 643 | cleanup_entry, &i); | |
638 | ret = -ELOOP; | 644 | return ret; |
639 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) { | ||
640 | duprintf("Looping hook\n"); | ||
641 | goto cleanup; | ||
642 | } | 645 | } |
643 | 646 | ||
644 | /* And one copy for every other CPU */ | 647 | /* And one copy for every other CPU */ |
@@ -647,9 +650,6 @@ static int translate_table(const char *name, | |||
647 | memcpy(newinfo->entries[i], entry0, newinfo->size); | 650 | memcpy(newinfo->entries[i], entry0, newinfo->size); |
648 | } | 651 | } |
649 | 652 | ||
650 | return 0; | ||
651 | cleanup: | ||
652 | ARPT_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i); | ||
653 | return ret; | 653 | return ret; |
654 | } | 654 | } |
655 | 655 | ||
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 8a455439b128..2bddf8491982 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -401,6 +401,13 @@ mark_source_chains(struct xt_table_info *newinfo, | |||
401 | && unconditional(&e->ip)) { | 401 | && unconditional(&e->ip)) { |
402 | unsigned int oldpos, size; | 402 | unsigned int oldpos, size; |
403 | 403 | ||
404 | if (t->verdict < -NF_MAX_VERDICT - 1) { | ||
405 | duprintf("mark_source_chains: bad " | ||
406 | "negative verdict (%i)\n", | ||
407 | t->verdict); | ||
408 | return 0; | ||
409 | } | ||
410 | |||
404 | /* Return: backtrack through the last | 411 | /* Return: backtrack through the last |
405 | big jump. */ | 412 | big jump. */ |
406 | do { | 413 | do { |
@@ -438,6 +445,13 @@ mark_source_chains(struct xt_table_info *newinfo, | |||
438 | if (strcmp(t->target.u.user.name, | 445 | if (strcmp(t->target.u.user.name, |
439 | IPT_STANDARD_TARGET) == 0 | 446 | IPT_STANDARD_TARGET) == 0 |
440 | && newpos >= 0) { | 447 | && newpos >= 0) { |
448 | if (newpos > newinfo->size - | ||
449 | sizeof(struct ipt_entry)) { | ||
450 | duprintf("mark_source_chains: " | ||
451 | "bad verdict (%i)\n", | ||
452 | newpos); | ||
453 | return 0; | ||
454 | } | ||
441 | /* This a jump; chase it. */ | 455 | /* This a jump; chase it. */ |
442 | duprintf("Jump rule %u -> %u\n", | 456 | duprintf("Jump rule %u -> %u\n", |
443 | pos, newpos); | 457 | pos, newpos); |
@@ -470,27 +484,6 @@ cleanup_match(struct ipt_entry_match *m, unsigned int *i) | |||
470 | } | 484 | } |
471 | 485 | ||
472 | static inline int | 486 | static inline int |
473 | standard_check(const struct ipt_entry_target *t, | ||
474 | unsigned int max_offset) | ||
475 | { | ||
476 | struct ipt_standard_target *targ = (void *)t; | ||
477 | |||
478 | /* Check standard info. */ | ||
479 | if (targ->verdict >= 0 | ||
480 | && targ->verdict > max_offset - sizeof(struct ipt_entry)) { | ||
481 | duprintf("ipt_standard_check: bad verdict (%i)\n", | ||
482 | targ->verdict); | ||
483 | return 0; | ||
484 | } | ||
485 | if (targ->verdict < -NF_MAX_VERDICT - 1) { | ||
486 | duprintf("ipt_standard_check: bad negative verdict (%i)\n", | ||
487 | targ->verdict); | ||
488 | return 0; | ||
489 | } | ||
490 | return 1; | ||
491 | } | ||
492 | |||
493 | static inline int | ||
494 | check_match(struct ipt_entry_match *m, | 487 | check_match(struct ipt_entry_match *m, |
495 | const char *name, | 488 | const char *name, |
496 | const struct ipt_ip *ip, | 489 | const struct ipt_ip *ip, |
@@ -576,12 +569,7 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size, | |||
576 | if (ret) | 569 | if (ret) |
577 | goto err; | 570 | goto err; |
578 | 571 | ||
579 | if (t->u.kernel.target == &ipt_standard_target) { | 572 | if (t->u.kernel.target->checkentry |
580 | if (!standard_check(t, size)) { | ||
581 | ret = -EINVAL; | ||
582 | goto err; | ||
583 | } | ||
584 | } else if (t->u.kernel.target->checkentry | ||
585 | && !t->u.kernel.target->checkentry(name, e, target, t->data, | 573 | && !t->u.kernel.target->checkentry(name, e, target, t->data, |
586 | e->comefrom)) { | 574 | e->comefrom)) { |
587 | duprintf("ip_tables: check failed for `%s'.\n", | 575 | duprintf("ip_tables: check failed for `%s'.\n", |
@@ -718,17 +706,19 @@ translate_table(const char *name, | |||
718 | } | 706 | } |
719 | } | 707 | } |
720 | 708 | ||
709 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) | ||
710 | return -ELOOP; | ||
711 | |||
721 | /* Finally, each sanity check must pass */ | 712 | /* Finally, each sanity check must pass */ |
722 | i = 0; | 713 | i = 0; |
723 | ret = IPT_ENTRY_ITERATE(entry0, newinfo->size, | 714 | ret = IPT_ENTRY_ITERATE(entry0, newinfo->size, |
724 | check_entry, name, size, &i); | 715 | check_entry, name, size, &i); |
725 | 716 | ||
726 | if (ret != 0) | 717 | if (ret != 0) { |
727 | goto cleanup; | 718 | IPT_ENTRY_ITERATE(entry0, newinfo->size, |
728 | 719 | cleanup_entry, &i); | |
729 | ret = -ELOOP; | 720 | return ret; |
730 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) | 721 | } |
731 | goto cleanup; | ||
732 | 722 | ||
733 | /* And one copy for every other CPU */ | 723 | /* And one copy for every other CPU */ |
734 | for_each_possible_cpu(i) { | 724 | for_each_possible_cpu(i) { |
@@ -736,9 +726,6 @@ translate_table(const char *name, | |||
736 | memcpy(newinfo->entries[i], entry0, newinfo->size); | 726 | memcpy(newinfo->entries[i], entry0, newinfo->size); |
737 | } | 727 | } |
738 | 728 | ||
739 | return 0; | ||
740 | cleanup: | ||
741 | IPT_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i); | ||
742 | return ret; | 729 | return ret; |
743 | } | 730 | } |
744 | 731 | ||
@@ -1591,18 +1578,13 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | |||
1591 | if (ret) | 1578 | if (ret) |
1592 | goto err; | 1579 | goto err; |
1593 | 1580 | ||
1594 | ret = -EINVAL; | 1581 | if (t->u.kernel.target->checkentry |
1595 | if (t->u.kernel.target == &ipt_standard_target) { | ||
1596 | if (!standard_check(t, *size)) | ||
1597 | goto err; | ||
1598 | } else if (t->u.kernel.target->checkentry | ||
1599 | && !t->u.kernel.target->checkentry(name, de, target, | 1582 | && !t->u.kernel.target->checkentry(name, de, target, |
1600 | t->data, de->comefrom)) { | 1583 | t->data, de->comefrom)) { |
1601 | duprintf("ip_tables: compat: check failed for `%s'.\n", | 1584 | duprintf("ip_tables: compat: check failed for `%s'.\n", |
1602 | t->u.kernel.target->name); | 1585 | t->u.kernel.target->name); |
1603 | goto err; | 1586 | ret = -EINVAL; |
1604 | } | 1587 | } |
1605 | ret = 0; | ||
1606 | err: | 1588 | err: |
1607 | return ret; | 1589 | return ret; |
1608 | } | 1590 | } |
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index f63fb86d7c7b..4eec4b3988b8 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -440,6 +440,13 @@ mark_source_chains(struct xt_table_info *newinfo, | |||
440 | && unconditional(&e->ipv6)) { | 440 | && unconditional(&e->ipv6)) { |
441 | unsigned int oldpos, size; | 441 | unsigned int oldpos, size; |
442 | 442 | ||
443 | if (t->verdict < -NF_MAX_VERDICT - 1) { | ||
444 | duprintf("mark_source_chains: bad " | ||
445 | "negative verdict (%i)\n", | ||
446 | t->verdict); | ||
447 | return 0; | ||
448 | } | ||
449 | |||
443 | /* Return: backtrack through the last | 450 | /* Return: backtrack through the last |
444 | big jump. */ | 451 | big jump. */ |
445 | do { | 452 | do { |
@@ -477,6 +484,13 @@ mark_source_chains(struct xt_table_info *newinfo, | |||
477 | if (strcmp(t->target.u.user.name, | 484 | if (strcmp(t->target.u.user.name, |
478 | IP6T_STANDARD_TARGET) == 0 | 485 | IP6T_STANDARD_TARGET) == 0 |
479 | && newpos >= 0) { | 486 | && newpos >= 0) { |
487 | if (newpos > newinfo->size - | ||
488 | sizeof(struct ip6t_entry)) { | ||
489 | duprintf("mark_source_chains: " | ||
490 | "bad verdict (%i)\n", | ||
491 | newpos); | ||
492 | return 0; | ||
493 | } | ||
480 | /* This a jump; chase it. */ | 494 | /* This a jump; chase it. */ |
481 | duprintf("Jump rule %u -> %u\n", | 495 | duprintf("Jump rule %u -> %u\n", |
482 | pos, newpos); | 496 | pos, newpos); |
@@ -509,27 +523,6 @@ cleanup_match(struct ip6t_entry_match *m, unsigned int *i) | |||
509 | } | 523 | } |
510 | 524 | ||
511 | static inline int | 525 | static inline int |
512 | standard_check(const struct ip6t_entry_target *t, | ||
513 | unsigned int max_offset) | ||
514 | { | ||
515 | struct ip6t_standard_target *targ = (void *)t; | ||
516 | |||
517 | /* Check standard info. */ | ||
518 | if (targ->verdict >= 0 | ||
519 | && targ->verdict > max_offset - sizeof(struct ip6t_entry)) { | ||
520 | duprintf("ip6t_standard_check: bad verdict (%i)\n", | ||
521 | targ->verdict); | ||
522 | return 0; | ||
523 | } | ||
524 | if (targ->verdict < -NF_MAX_VERDICT - 1) { | ||
525 | duprintf("ip6t_standard_check: bad negative verdict (%i)\n", | ||
526 | targ->verdict); | ||
527 | return 0; | ||
528 | } | ||
529 | return 1; | ||
530 | } | ||
531 | |||
532 | static inline int | ||
533 | check_match(struct ip6t_entry_match *m, | 526 | check_match(struct ip6t_entry_match *m, |
534 | const char *name, | 527 | const char *name, |
535 | const struct ip6t_ip6 *ipv6, | 528 | const struct ip6t_ip6 *ipv6, |
@@ -616,12 +609,7 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size, | |||
616 | if (ret) | 609 | if (ret) |
617 | goto err; | 610 | goto err; |
618 | 611 | ||
619 | if (t->u.kernel.target == &ip6t_standard_target) { | 612 | if (t->u.kernel.target->checkentry |
620 | if (!standard_check(t, size)) { | ||
621 | ret = -EINVAL; | ||
622 | goto err; | ||
623 | } | ||
624 | } else if (t->u.kernel.target->checkentry | ||
625 | && !t->u.kernel.target->checkentry(name, e, target, t->data, | 613 | && !t->u.kernel.target->checkentry(name, e, target, t->data, |
626 | e->comefrom)) { | 614 | e->comefrom)) { |
627 | duprintf("ip_tables: check failed for `%s'.\n", | 615 | duprintf("ip_tables: check failed for `%s'.\n", |
@@ -758,17 +746,19 @@ translate_table(const char *name, | |||
758 | } | 746 | } |
759 | } | 747 | } |
760 | 748 | ||
749 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) | ||
750 | return -ELOOP; | ||
751 | |||
761 | /* Finally, each sanity check must pass */ | 752 | /* Finally, each sanity check must pass */ |
762 | i = 0; | 753 | i = 0; |
763 | ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size, | 754 | ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size, |
764 | check_entry, name, size, &i); | 755 | check_entry, name, size, &i); |
765 | 756 | ||
766 | if (ret != 0) | 757 | if (ret != 0) { |
767 | goto cleanup; | 758 | IP6T_ENTRY_ITERATE(entry0, newinfo->size, |
768 | 759 | cleanup_entry, &i); | |
769 | ret = -ELOOP; | 760 | return ret; |
770 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) | 761 | } |
771 | goto cleanup; | ||
772 | 762 | ||
773 | /* And one copy for every other CPU */ | 763 | /* And one copy for every other CPU */ |
774 | for_each_possible_cpu(i) { | 764 | for_each_possible_cpu(i) { |
@@ -777,9 +767,6 @@ translate_table(const char *name, | |||
777 | } | 767 | } |
778 | 768 | ||
779 | return 0; | 769 | return 0; |
780 | cleanup: | ||
781 | IP6T_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i); | ||
782 | return ret; | ||
783 | } | 770 | } |
784 | 771 | ||
785 | /* Gets counters. */ | 772 | /* Gets counters. */ |