diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 176 |
1 files changed, 79 insertions, 97 deletions
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 0ff2956d35e5..83ebbeb80b6e 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c | |||
@@ -484,7 +484,47 @@ cleanup_match(struct ipt_entry_match *m, unsigned int *i) | |||
484 | } | 484 | } |
485 | 485 | ||
486 | static inline int | 486 | static inline int |
487 | check_match(struct ipt_entry_match *m, | 487 | check_entry(struct ipt_entry *e, const char *name) |
488 | { | ||
489 | struct ipt_entry_target *t; | ||
490 | |||
491 | if (!ip_checkentry(&e->ip)) { | ||
492 | duprintf("ip_tables: ip check failed %p %s.\n", e, name); | ||
493 | return -EINVAL; | ||
494 | } | ||
495 | |||
496 | if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset) | ||
497 | return -EINVAL; | ||
498 | |||
499 | t = ipt_get_target(e); | ||
500 | if (e->target_offset + t->u.target_size > e->next_offset) | ||
501 | return -EINVAL; | ||
502 | |||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | static inline int check_match(struct ipt_entry_match *m, const char *name, | ||
507 | const struct ipt_ip *ip, unsigned int hookmask) | ||
508 | { | ||
509 | struct ipt_match *match; | ||
510 | int ret; | ||
511 | |||
512 | match = m->u.kernel.match; | ||
513 | ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m), | ||
514 | name, hookmask, ip->proto, | ||
515 | ip->invflags & IPT_INV_PROTO); | ||
516 | if (!ret && m->u.kernel.match->checkentry | ||
517 | && !m->u.kernel.match->checkentry(name, ip, match, m->data, | ||
518 | hookmask)) { | ||
519 | duprintf("ip_tables: check failed for `%s'.\n", | ||
520 | m->u.kernel.match->name); | ||
521 | ret = -EINVAL; | ||
522 | } | ||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | static inline int | ||
527 | find_check_match(struct ipt_entry_match *m, | ||
488 | const char *name, | 528 | const char *name, |
489 | const struct ipt_ip *ip, | 529 | const struct ipt_ip *ip, |
490 | unsigned int hookmask, | 530 | unsigned int hookmask, |
@@ -497,26 +537,15 @@ check_match(struct ipt_entry_match *m, | |||
497 | m->u.user.revision), | 537 | m->u.user.revision), |
498 | "ipt_%s", m->u.user.name); | 538 | "ipt_%s", m->u.user.name); |
499 | if (IS_ERR(match) || !match) { | 539 | if (IS_ERR(match) || !match) { |
500 | duprintf("check_match: `%s' not found\n", m->u.user.name); | 540 | duprintf("find_check_match: `%s' not found\n", m->u.user.name); |
501 | return match ? PTR_ERR(match) : -ENOENT; | 541 | return match ? PTR_ERR(match) : -ENOENT; |
502 | } | 542 | } |
503 | m->u.kernel.match = match; | 543 | m->u.kernel.match = match; |
504 | 544 | ||
505 | ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m), | 545 | ret = check_match(m, name, ip, hookmask); |
506 | name, hookmask, ip->proto, | ||
507 | ip->invflags & IPT_INV_PROTO); | ||
508 | if (ret) | 546 | if (ret) |
509 | goto err; | 547 | goto err; |
510 | 548 | ||
511 | if (m->u.kernel.match->checkentry | ||
512 | && !m->u.kernel.match->checkentry(name, ip, match, m->data, | ||
513 | hookmask)) { | ||
514 | duprintf("ip_tables: check failed for `%s'.\n", | ||
515 | m->u.kernel.match->name); | ||
516 | ret = -EINVAL; | ||
517 | goto err; | ||
518 | } | ||
519 | |||
520 | (*i)++; | 549 | (*i)++; |
521 | return 0; | 550 | return 0; |
522 | err: | 551 | err: |
@@ -524,10 +553,29 @@ err: | |||
524 | return ret; | 553 | return ret; |
525 | } | 554 | } |
526 | 555 | ||
527 | static struct ipt_target ipt_standard_target; | 556 | static inline int check_target(struct ipt_entry *e, const char *name) |
557 | { | ||
558 | struct ipt_entry_target *t; | ||
559 | struct ipt_target *target; | ||
560 | int ret; | ||
561 | |||
562 | t = ipt_get_target(e); | ||
563 | target = t->u.kernel.target; | ||
564 | ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), | ||
565 | name, e->comefrom, e->ip.proto, | ||
566 | e->ip.invflags & IPT_INV_PROTO); | ||
567 | if (!ret && t->u.kernel.target->checkentry | ||
568 | && !t->u.kernel.target->checkentry(name, e, target, | ||
569 | t->data, e->comefrom)) { | ||
570 | duprintf("ip_tables: check failed for `%s'.\n", | ||
571 | t->u.kernel.target->name); | ||
572 | ret = -EINVAL; | ||
573 | } | ||
574 | return ret; | ||
575 | } | ||
528 | 576 | ||
529 | static inline int | 577 | static inline int |
530 | check_entry(struct ipt_entry *e, const char *name, unsigned int size, | 578 | find_check_entry(struct ipt_entry *e, const char *name, unsigned int size, |
531 | unsigned int *i) | 579 | unsigned int *i) |
532 | { | 580 | { |
533 | struct ipt_entry_target *t; | 581 | struct ipt_entry_target *t; |
@@ -535,49 +583,32 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size, | |||
535 | int ret; | 583 | int ret; |
536 | unsigned int j; | 584 | unsigned int j; |
537 | 585 | ||
538 | if (!ip_checkentry(&e->ip)) { | 586 | ret = check_entry(e, name); |
539 | duprintf("ip_tables: ip check failed %p %s.\n", e, name); | 587 | if (ret) |
540 | return -EINVAL; | 588 | return ret; |
541 | } | ||
542 | |||
543 | if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset) | ||
544 | return -EINVAL; | ||
545 | 589 | ||
546 | j = 0; | 590 | j = 0; |
547 | ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j); | 591 | ret = IPT_MATCH_ITERATE(e, find_check_match, name, &e->ip, |
592 | e->comefrom, &j); | ||
548 | if (ret != 0) | 593 | if (ret != 0) |
549 | goto cleanup_matches; | 594 | goto cleanup_matches; |
550 | 595 | ||
551 | t = ipt_get_target(e); | 596 | t = ipt_get_target(e); |
552 | ret = -EINVAL; | ||
553 | if (e->target_offset + t->u.target_size > e->next_offset) | ||
554 | goto cleanup_matches; | ||
555 | target = try_then_request_module(xt_find_target(AF_INET, | 597 | target = try_then_request_module(xt_find_target(AF_INET, |
556 | t->u.user.name, | 598 | t->u.user.name, |
557 | t->u.user.revision), | 599 | t->u.user.revision), |
558 | "ipt_%s", t->u.user.name); | 600 | "ipt_%s", t->u.user.name); |
559 | if (IS_ERR(target) || !target) { | 601 | if (IS_ERR(target) || !target) { |
560 | duprintf("check_entry: `%s' not found\n", t->u.user.name); | 602 | duprintf("find_check_entry: `%s' not found\n", t->u.user.name); |
561 | ret = target ? PTR_ERR(target) : -ENOENT; | 603 | ret = target ? PTR_ERR(target) : -ENOENT; |
562 | goto cleanup_matches; | 604 | goto cleanup_matches; |
563 | } | 605 | } |
564 | t->u.kernel.target = target; | 606 | t->u.kernel.target = target; |
565 | 607 | ||
566 | ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), | 608 | ret = check_target(e, name); |
567 | name, e->comefrom, e->ip.proto, | ||
568 | e->ip.invflags & IPT_INV_PROTO); | ||
569 | if (ret) | 609 | if (ret) |
570 | goto err; | 610 | goto err; |
571 | 611 | ||
572 | if (t->u.kernel.target->checkentry | ||
573 | && !t->u.kernel.target->checkentry(name, e, target, t->data, | ||
574 | e->comefrom)) { | ||
575 | duprintf("ip_tables: check failed for `%s'.\n", | ||
576 | t->u.kernel.target->name); | ||
577 | ret = -EINVAL; | ||
578 | goto err; | ||
579 | } | ||
580 | |||
581 | (*i)++; | 612 | (*i)++; |
582 | return 0; | 613 | return 0; |
583 | err: | 614 | err: |
@@ -712,7 +743,7 @@ translate_table(const char *name, | |||
712 | /* Finally, each sanity check must pass */ | 743 | /* Finally, each sanity check must pass */ |
713 | i = 0; | 744 | i = 0; |
714 | ret = IPT_ENTRY_ITERATE(entry0, newinfo->size, | 745 | ret = IPT_ENTRY_ITERATE(entry0, newinfo->size, |
715 | check_entry, name, size, &i); | 746 | find_check_entry, name, size, &i); |
716 | 747 | ||
717 | if (ret != 0) { | 748 | if (ret != 0) { |
718 | IPT_ENTRY_ITERATE(entry0, newinfo->size, | 749 | IPT_ENTRY_ITERATE(entry0, newinfo->size, |
@@ -1452,14 +1483,9 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1452 | return -EINVAL; | 1483 | return -EINVAL; |
1453 | } | 1484 | } |
1454 | 1485 | ||
1455 | if (!ip_checkentry(&e->ip)) { | 1486 | ret = check_entry(e, name); |
1456 | duprintf("ip_tables: ip check failed %p %s.\n", e, name); | 1487 | if (ret) |
1457 | return -EINVAL; | 1488 | return ret; |
1458 | } | ||
1459 | |||
1460 | if (e->target_offset + sizeof(struct compat_xt_entry_target) > | ||
1461 | e->next_offset) | ||
1462 | return -EINVAL; | ||
1463 | 1489 | ||
1464 | off = 0; | 1490 | off = 0; |
1465 | entry_offset = (void *)e - (void *)base; | 1491 | entry_offset = (void *)e - (void *)base; |
@@ -1470,15 +1496,13 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, | |||
1470 | goto cleanup_matches; | 1496 | goto cleanup_matches; |
1471 | 1497 | ||
1472 | t = ipt_get_target(e); | 1498 | t = ipt_get_target(e); |
1473 | ret = -EINVAL; | ||
1474 | if (e->target_offset + t->u.target_size > e->next_offset) | ||
1475 | goto cleanup_matches; | ||
1476 | target = try_then_request_module(xt_find_target(AF_INET, | 1499 | target = try_then_request_module(xt_find_target(AF_INET, |
1477 | t->u.user.name, | 1500 | t->u.user.name, |
1478 | t->u.user.revision), | 1501 | t->u.user.revision), |
1479 | "ipt_%s", t->u.user.name); | 1502 | "ipt_%s", t->u.user.name); |
1480 | if (IS_ERR(target) || !target) { | 1503 | if (IS_ERR(target) || !target) { |
1481 | duprintf("check_entry: `%s' not found\n", t->u.user.name); | 1504 | duprintf("check_compat_entry_size_and_hooks: `%s' not found\n", |
1505 | t->u.user.name); | ||
1482 | ret = target ? PTR_ERR(target) : -ENOENT; | 1506 | ret = target ? PTR_ERR(target) : -ENOENT; |
1483 | goto cleanup_matches; | 1507 | goto cleanup_matches; |
1484 | } | 1508 | } |
@@ -1555,57 +1579,15 @@ static int compat_copy_entry_from_user(struct ipt_entry *e, void **dstptr, | |||
1555 | return ret; | 1579 | return ret; |
1556 | } | 1580 | } |
1557 | 1581 | ||
1558 | static inline int compat_check_match(struct ipt_entry_match *m, const char *name, | ||
1559 | const struct ipt_ip *ip, unsigned int hookmask) | ||
1560 | { | ||
1561 | struct ipt_match *match; | ||
1562 | int ret; | ||
1563 | |||
1564 | match = m->u.kernel.match; | ||
1565 | ret = xt_check_match(match, AF_INET, m->u.match_size - sizeof(*m), | ||
1566 | name, hookmask, ip->proto, | ||
1567 | ip->invflags & IPT_INV_PROTO); | ||
1568 | if (!ret && m->u.kernel.match->checkentry | ||
1569 | && !m->u.kernel.match->checkentry(name, ip, match, m->data, | ||
1570 | hookmask)) { | ||
1571 | duprintf("ip_tables: compat: check failed for `%s'.\n", | ||
1572 | m->u.kernel.match->name); | ||
1573 | ret = -EINVAL; | ||
1574 | } | ||
1575 | return ret; | ||
1576 | } | ||
1577 | |||
1578 | static inline int compat_check_target(struct ipt_entry *e, const char *name) | ||
1579 | { | ||
1580 | struct ipt_entry_target *t; | ||
1581 | struct ipt_target *target; | ||
1582 | int ret; | ||
1583 | |||
1584 | t = ipt_get_target(e); | ||
1585 | target = t->u.kernel.target; | ||
1586 | ret = xt_check_target(target, AF_INET, t->u.target_size - sizeof(*t), | ||
1587 | name, e->comefrom, e->ip.proto, | ||
1588 | e->ip.invflags & IPT_INV_PROTO); | ||
1589 | if (!ret && t->u.kernel.target->checkentry | ||
1590 | && !t->u.kernel.target->checkentry(name, e, target, | ||
1591 | t->data, e->comefrom)) { | ||
1592 | duprintf("ip_tables: compat: check failed for `%s'.\n", | ||
1593 | t->u.kernel.target->name); | ||
1594 | ret = -EINVAL; | ||
1595 | } | ||
1596 | return ret; | ||
1597 | } | ||
1598 | |||
1599 | static inline int compat_check_entry(struct ipt_entry *e, const char *name) | 1582 | static inline int compat_check_entry(struct ipt_entry *e, const char *name) |
1600 | { | 1583 | { |
1601 | int ret; | 1584 | int ret; |
1602 | 1585 | ||
1603 | ret = IPT_MATCH_ITERATE(e, compat_check_match, name, &e->ip, | 1586 | ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom); |
1604 | e->comefrom); | ||
1605 | if (ret) | 1587 | if (ret) |
1606 | return ret; | 1588 | return ret; |
1607 | 1589 | ||
1608 | return compat_check_target(e, name); | 1590 | return check_target(e, name); |
1609 | } | 1591 | } |
1610 | 1592 | ||
1611 | static int | 1593 | static int |