diff options
author | Dmitry Mishin <dim@openvz.org> | 2006-10-30 18:12:55 -0500 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-10-30 18:24:44 -0500 |
commit | 590bdf7fd2292b47c428111cb1360e312eff207e (patch) | |
tree | c44b60a5e40b5e16e3478aecb839825b4a602ced | |
parent | 844dc7c88046ecd2e52596730d7cc400d6c3ad67 (diff) |
[NETFILTER]: Missed and reordered checks in {arp,ip,ip6}_tables
There is a number of issues in parsing user-provided table in
translate_table(). Malicious user with CAP_NET_ADMIN may crash system by
passing special-crafted table to the *_tables.
The first issue is that mark_source_chains() function is called before entry
content checks. In case of standard target, mark_source_chains() function
uses t->verdict field in order to determine new position. But the check, that
this field leads no further, than the table end, is in check_entry(), which
is called later, than mark_source_chains().
The second issue, that there is no check that target_offset points inside
entry. If so, *_ITERATE_MATCH macro will follow further, than the entry
ends. As a result, we'll have oops or memory disclosure.
And the third issue, that there is no check that the target is completely
inside entry. Results are the same, as in previous issue.
Signed-off-by: Dmitry Mishin <dim@openvz.org>
Acked-by: Kirill Korotaev <dev@openvz.org>
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 25 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 30 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 24 |
3 files changed, 54 insertions, 25 deletions
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 0849f1cced13..413c2d0a1f3d 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c | |||
@@ -466,7 +466,13 @@ static inline int check_entry(struct arpt_entry *e, const char *name, unsigned i | |||
466 | return -EINVAL; | 466 | return -EINVAL; |
467 | } | 467 | } |
468 | 468 | ||
469 | if (e->target_offset + sizeof(struct arpt_entry_target) > e->next_offset) | ||
470 | return -EINVAL; | ||
471 | |||
469 | t = arpt_get_target(e); | 472 | t = arpt_get_target(e); |
473 | if (e->target_offset + t->u.target_size > e->next_offset) | ||
474 | return -EINVAL; | ||
475 | |||
470 | target = try_then_request_module(xt_find_target(NF_ARP, t->u.user.name, | 476 | target = try_then_request_module(xt_find_target(NF_ARP, t->u.user.name, |
471 | t->u.user.revision), | 477 | t->u.user.revision), |
472 | "arpt_%s", t->u.user.name); | 478 | "arpt_%s", t->u.user.name); |
@@ -621,20 +627,18 @@ static int translate_table(const char *name, | |||
621 | } | 627 | } |
622 | } | 628 | } |
623 | 629 | ||
624 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) { | ||
625 | duprintf("Looping hook\n"); | ||
626 | return -ELOOP; | ||
627 | } | ||
628 | |||
629 | /* Finally, each sanity check must pass */ | 630 | /* Finally, each sanity check must pass */ |
630 | i = 0; | 631 | i = 0; |
631 | ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size, | 632 | ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size, |
632 | check_entry, name, size, &i); | 633 | check_entry, name, size, &i); |
633 | 634 | ||
634 | if (ret != 0) { | 635 | if (ret != 0) |
635 | ARPT_ENTRY_ITERATE(entry0, newinfo->size, | 636 | goto cleanup; |
636 | cleanup_entry, &i); | 637 | |
637 | return ret; | 638 | ret = -ELOOP; |
639 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) { | ||
640 | duprintf("Looping hook\n"); | ||
641 | goto cleanup; | ||
638 | } | 642 | } |
639 | 643 | ||
640 | /* And one copy for every other CPU */ | 644 | /* And one copy for every other CPU */ |
@@ -643,6 +647,9 @@ static int translate_table(const char *name, | |||
643 | memcpy(newinfo->entries[i], entry0, newinfo->size); | 647 | memcpy(newinfo->entries[i], entry0, newinfo->size); |
644 | } | 648 | } |
645 | 649 | ||
650 | return 0; | ||
651 | cleanup: | ||
652 | ARPT_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i); | ||
646 | return ret; | 653 | return ret; |
647 | } | 654 | } |
648 | 655 | ||
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 4b90927619b8..e2c7f6e024c5 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -547,12 +547,18 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size, | |||
547 | return -EINVAL; | 547 | return -EINVAL; |
548 | } | 548 | } |
549 | 549 | ||
550 | if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset) | ||
551 | return -EINVAL; | ||
552 | |||
550 | j = 0; | 553 | j = 0; |
551 | ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j); | 554 | ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j); |
552 | if (ret != 0) | 555 | if (ret != 0) |
553 | goto cleanup_matches; | 556 | goto cleanup_matches; |
554 | 557 | ||
555 | t = ipt_get_target(e); | 558 | t = ipt_get_target(e); |
559 | ret = -EINVAL; | ||
560 | if (e->target_offset + t->u.target_size > e->next_offset) | ||
561 | goto cleanup_matches; | ||
556 | target = try_then_request_module(xt_find_target(AF_INET, | 562 | target = try_then_request_module(xt_find_target(AF_INET, |
557 | t->u.user.name, | 563 | t->u.user.name, |
558 | t->u.user.revision), | 564 | t->u.user.revision), |
@@ -712,19 +718,17 @@ translate_table(const char *name, | |||
712 | } | 718 | } |
713 | } | 719 | } |
714 | 720 | ||
715 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) | ||
716 | return -ELOOP; | ||
717 | |||
718 | /* Finally, each sanity check must pass */ | 721 | /* Finally, each sanity check must pass */ |
719 | i = 0; | 722 | i = 0; |
720 | ret = IPT_ENTRY_ITERATE(entry0, newinfo->size, | 723 | ret = IPT_ENTRY_ITERATE(entry0, newinfo->size, |
721 | check_entry, name, size, &i); | 724 | check_entry, name, size, &i); |
722 | 725 | ||
723 | if (ret != 0) { | 726 | if (ret != 0) |
724 | IPT_ENTRY_ITERATE(entry0, newinfo->size, | 727 | goto cleanup; |
725 | cleanup_entry, &i); | 728 | |
726 | return ret; | 729 | ret = -ELOOP; |
727 | } | 730 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) |
731 | goto cleanup; | ||
728 | 732 | ||
729 | /* And one copy for every other CPU */ | 733 | /* And one copy for every other CPU */ |
730 | for_each_possible_cpu(i) { | 734 | for_each_possible_cpu(i) { |
@@ -732,6 +736,9 @@ translate_table(const char *name, | |||
732 | memcpy(newinfo->entries[i], entry0, newinfo->size); | 736 | memcpy(newinfo->entries[i], entry0, newinfo->size); |
733 | } | 737 | } |
734 | 738 | ||
739 | return 0; | ||
740 | cleanup: | ||
741 | IPT_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i); | ||
735 | return ret; | 742 | return ret; |
736 | } | 743 | } |
737 | 744 | ||
@@ -1463,6 +1470,10 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1463 | return -EINVAL; | 1470 | return -EINVAL; |
1464 | } | 1471 | } |
1465 | 1472 | ||
1473 | if (e->target_offset + sizeof(struct compat_xt_entry_target) > | ||
1474 | e->next_offset) | ||
1475 | return -EINVAL; | ||
1476 | |||
1466 | off = 0; | 1477 | off = 0; |
1467 | entry_offset = (void *)e - (void *)base; | 1478 | entry_offset = (void *)e - (void *)base; |
1468 | j = 0; | 1479 | j = 0; |
@@ -1472,6 +1483,9 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1472 | goto cleanup_matches; | 1483 | goto cleanup_matches; |
1473 | 1484 | ||
1474 | t = ipt_get_target(e); | 1485 | t = ipt_get_target(e); |
1486 | ret = -EINVAL; | ||
1487 | if (e->target_offset + t->u.target_size > e->next_offset) | ||
1488 | goto cleanup_matches; | ||
1475 | target = try_then_request_module(xt_find_target(AF_INET, | 1489 | target = try_then_request_module(xt_find_target(AF_INET, |
1476 | t->u.user.name, | 1490 | t->u.user.name, |
1477 | t->u.user.revision), | 1491 | t->u.user.revision), |
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 53bf977cca63..167c2ea88f6b 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c | |||
@@ -586,12 +586,19 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size, | |||
586 | return -EINVAL; | 586 | return -EINVAL; |
587 | } | 587 | } |
588 | 588 | ||
589 | if (e->target_offset + sizeof(struct ip6t_entry_target) > | ||
590 | e->next_offset) | ||
591 | return -EINVAL; | ||
592 | |||
589 | j = 0; | 593 | j = 0; |
590 | ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j); | 594 | ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j); |
591 | if (ret != 0) | 595 | if (ret != 0) |
592 | goto cleanup_matches; | 596 | goto cleanup_matches; |
593 | 597 | ||
594 | t = ip6t_get_target(e); | 598 | t = ip6t_get_target(e); |
599 | ret = -EINVAL; | ||
600 | if (e->target_offset + t->u.target_size > e->next_offset) | ||
601 | goto cleanup_matches; | ||
595 | target = try_then_request_module(xt_find_target(AF_INET6, | 602 | target = try_then_request_module(xt_find_target(AF_INET6, |
596 | t->u.user.name, | 603 | t->u.user.name, |
597 | t->u.user.revision), | 604 | t->u.user.revision), |
@@ -751,19 +758,17 @@ translate_table(const char *name, | |||
751 | } | 758 | } |
752 | } | 759 | } |
753 | 760 | ||
754 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) | ||
755 | return -ELOOP; | ||
756 | |||
757 | /* Finally, each sanity check must pass */ | 761 | /* Finally, each sanity check must pass */ |
758 | i = 0; | 762 | i = 0; |
759 | ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size, | 763 | ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size, |
760 | check_entry, name, size, &i); | 764 | check_entry, name, size, &i); |
761 | 765 | ||
762 | if (ret != 0) { | 766 | if (ret != 0) |
763 | IP6T_ENTRY_ITERATE(entry0, newinfo->size, | 767 | goto cleanup; |
764 | cleanup_entry, &i); | 768 | |
765 | return ret; | 769 | ret = -ELOOP; |
766 | } | 770 | if (!mark_source_chains(newinfo, valid_hooks, entry0)) |
771 | goto cleanup; | ||
767 | 772 | ||
768 | /* And one copy for every other CPU */ | 773 | /* And one copy for every other CPU */ |
769 | for_each_possible_cpu(i) { | 774 | for_each_possible_cpu(i) { |
@@ -771,6 +776,9 @@ translate_table(const char *name, | |||
771 | memcpy(newinfo->entries[i], entry0, newinfo->size); | 776 | memcpy(newinfo->entries[i], entry0, newinfo->size); |
772 | } | 777 | } |
773 | 778 | ||
779 | return 0; | ||
780 | cleanup: | ||
781 | IP6T_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i); | ||
774 | return ret; | 782 | return ret; |
775 | } | 783 | } |
776 | 784 | ||