aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Mishin <dim@openvz.org>2006-12-05 16:43:50 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-12-06 21:39:02 -0500
commit74c9c0c17dea729d6089c0c82762babd02e65f84 (patch)
treefa3c59bcd8121b469013b49963fb561f0fa30903
parent79066ad32be5bb2edf16733aec36acf2af03fc99 (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>
-rw-r--r--net/ipv4/netfilter/arp_tables.c48
-rw-r--r--net/ipv4/netfilter/ip_tables.c68
-rw-r--r--net/ipv6/netfilter/ip6_tables.c59
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,
426static inline int standard_check(const struct arpt_entry_target *t, 441static 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;
651cleanup:
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
472static inline int 486static inline int
473standard_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
493static inline int
494check_match(struct ipt_entry_match *m, 487check_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;
740cleanup:
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;
1606err: 1588err:
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
511static inline int 525static inline int
512standard_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
532static inline int
533check_match(struct ip6t_entry_match *m, 526check_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;
780cleanup:
781 IP6T_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i);
782 return ret;
783} 770}
784 771
785/* Gets counters. */ 772/* Gets counters. */