diff options
Diffstat (limited to 'net/ipv6/netfilter')
-rw-r--r-- | net/ipv6/netfilter/Kconfig | 2 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_queue.c | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 47 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_ah.c | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_frag.c | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_hbh.c | 7 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6t_rt.c | 7 |
7 files changed, 59 insertions, 25 deletions
diff --git a/net/ipv6/netfilter/Kconfig b/net/ipv6/netfilter/Kconfig index 4bc4e5b3379..d7c45a9c15f 100644 --- a/net/ipv6/netfilter/Kconfig +++ b/net/ipv6/netfilter/Kconfig @@ -40,7 +40,7 @@ config IP6_NF_QUEUE To compile it as a module, choose M here. If unsure, say N. config IP6_NF_IPTABLES - tristate "IP6 tables support (required for filtering/masq/NAT)" + tristate "IP6 tables support (required for filtering)" depends on NETFILTER_XTABLES help ip6tables is a general, extensible packet identification framework. diff --git a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c index 9510c24ca8d..9fec832ee08 100644 --- a/net/ipv6/netfilter/ip6_queue.c +++ b/net/ipv6/netfilter/ip6_queue.c @@ -349,9 +349,10 @@ ipq_mangle_ipv6(ipq_verdict_msg_t *v, struct ipq_queue_entry *e) if (v->data_len < sizeof(*user_iph)) return 0; diff = v->data_len - e->skb->len; - if (diff < 0) - skb_trim(e->skb, v->data_len); - else if (diff > 0) { + if (diff < 0) { + if (pskb_trim(e->skb, v->data_len)) + return -ENOMEM; + } else if (diff > 0) { if (v->data_len > 0xFFFF) return -EINVAL; if (diff > skb_tailroom(e->skb)) { diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 4ab368fa0b8..204e02162d4 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -111,7 +111,7 @@ ip6_packet_match(const struct sk_buff *skb, const char *outdev, const struct ip6t_ip6 *ip6info, unsigned int *protoff, - int *fragoff) + int *fragoff, int *hotdrop) { size_t i; unsigned long ret; @@ -169,9 +169,11 @@ ip6_packet_match(const struct sk_buff *skb, unsigned short _frag_off; protohdr = ipv6_find_hdr(skb, protoff, -1, &_frag_off); - if (protohdr < 0) + if (protohdr < 0) { + if (_frag_off == 0) + *hotdrop = 1; return 0; - + } *fragoff = _frag_off; dprintf("Packet protocol %hi ?= %s%hi.\n", @@ -290,7 +292,7 @@ ip6t_do_table(struct sk_buff **pskb, IP_NF_ASSERT(e); IP_NF_ASSERT(back); if (ip6_packet_match(*pskb, indev, outdev, &e->ipv6, - &protoff, &offset)) { + &protoff, &offset, &hotdrop)) { struct ip6t_entry_target *t; if (IP6T_MATCH_ITERATE(e, do_match, @@ -584,12 +586,19 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size, return -EINVAL; } + if (e->target_offset + sizeof(struct ip6t_entry_target) > + e->next_offset) + return -EINVAL; + j = 0; ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j); if (ret != 0) goto cleanup_matches; t = ip6t_get_target(e); + ret = -EINVAL; + if (e->target_offset + t->u.target_size > e->next_offset) + goto cleanup_matches; target = try_then_request_module(xt_find_target(AF_INET6, t->u.user.name, t->u.user.revision), @@ -749,19 +758,17 @@ translate_table(const char *name, } } - if (!mark_source_chains(newinfo, valid_hooks, entry0)) - return -ELOOP; - /* Finally, each sanity check must pass */ i = 0; ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size, check_entry, name, size, &i); - if (ret != 0) { - IP6T_ENTRY_ITERATE(entry0, newinfo->size, - cleanup_entry, &i); - return ret; - } + if (ret != 0) + goto cleanup; + + ret = -ELOOP; + if (!mark_source_chains(newinfo, valid_hooks, entry0)) + goto cleanup; /* And one copy for every other CPU */ for_each_possible_cpu(i) { @@ -769,6 +776,9 @@ translate_table(const char *name, memcpy(newinfo->entries[i], entry0, newinfo->size); } + return 0; +cleanup: + IP6T_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i); return ret; } @@ -1438,6 +1448,9 @@ static void __exit ip6_tables_fini(void) * If target header is found, its offset is set in *offset and return protocol * number. Otherwise, return -1. * + * If the first fragment doesn't contain the final protocol header or + * NEXTHDR_NONE it is considered invalid. + * * Note that non-1st fragment is special case that "the protocol number * of last header" is "next header" field in Fragment header. In this case, * *offset is meaningless and fragment offset is stored in *fragoff if fragoff @@ -1461,12 +1474,12 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { if (target < 0) break; - return -1; + return -ENOENT; } hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); if (hp == NULL) - return -1; + return -EBADMSG; if (nexthdr == NEXTHDR_FRAGMENT) { unsigned short _frag_off, *fp; fp = skb_header_pointer(skb, @@ -1475,18 +1488,18 @@ int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, sizeof(_frag_off), &_frag_off); if (fp == NULL) - return -1; + return -EBADMSG; _frag_off = ntohs(*fp) & ~0x7; if (_frag_off) { if (target < 0 && ((!ipv6_ext_hdr(hp->nexthdr)) || - nexthdr == NEXTHDR_NONE)) { + hp->nexthdr == NEXTHDR_NONE)) { if (fragoff) *fragoff = _frag_off; return hp->nexthdr; } - return -1; + return -ENOENT; } hdrlen = 8; } else if (nexthdr == NEXTHDR_AUTH) diff --git a/net/ipv6/netfilter/ip6t_ah.c b/net/ipv6/netfilter/ip6t_ah.c index ec1b1608156..46486645eb7 100644 --- a/net/ipv6/netfilter/ip6t_ah.c +++ b/net/ipv6/netfilter/ip6t_ah.c @@ -54,9 +54,14 @@ match(const struct sk_buff *skb, const struct ip6t_ah *ahinfo = matchinfo; unsigned int ptr; unsigned int hdrlen = 0; + int err; - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL) < 0) + err = ipv6_find_hdr(skb, &ptr, NEXTHDR_AUTH, NULL); + if (err < 0) { + if (err != -ENOENT) + *hotdrop = 1; return 0; + } ah = skb_header_pointer(skb, ptr, sizeof(_ah), &_ah); if (ah == NULL) { diff --git a/net/ipv6/netfilter/ip6t_frag.c b/net/ipv6/netfilter/ip6t_frag.c index 78d9c8b9e28..cd22eaaccdc 100644 --- a/net/ipv6/netfilter/ip6t_frag.c +++ b/net/ipv6/netfilter/ip6t_frag.c @@ -52,9 +52,14 @@ match(const struct sk_buff *skb, struct frag_hdr _frag, *fh; const struct ip6t_frag *fraginfo = matchinfo; unsigned int ptr; + int err; - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL) < 0) + err = ipv6_find_hdr(skb, &ptr, NEXTHDR_FRAGMENT, NULL); + if (err < 0) { + if (err != -ENOENT) + *hotdrop = 1; return 0; + } fh = skb_header_pointer(skb, ptr, sizeof(_frag), &_frag); if (fh == NULL) { diff --git a/net/ipv6/netfilter/ip6t_hbh.c b/net/ipv6/netfilter/ip6t_hbh.c index d32a205e3af..3f25babe044 100644 --- a/net/ipv6/netfilter/ip6t_hbh.c +++ b/net/ipv6/netfilter/ip6t_hbh.c @@ -65,9 +65,14 @@ match(const struct sk_buff *skb, u8 _opttype, *tp = NULL; u8 _optlen, *lp = NULL; unsigned int optlen; + int err; - if (ipv6_find_hdr(skb, &ptr, match->data, NULL) < 0) + err = ipv6_find_hdr(skb, &ptr, match->data, NULL); + if (err < 0) { + if (err != -ENOENT) + *hotdrop = 1; return 0; + } oh = skb_header_pointer(skb, ptr, sizeof(_optsh), &_optsh); if (oh == NULL) { diff --git a/net/ipv6/netfilter/ip6t_rt.c b/net/ipv6/netfilter/ip6t_rt.c index bcb2e168a5b..54d7d14134f 100644 --- a/net/ipv6/netfilter/ip6t_rt.c +++ b/net/ipv6/netfilter/ip6t_rt.c @@ -58,9 +58,14 @@ match(const struct sk_buff *skb, unsigned int hdrlen = 0; unsigned int ret = 0; struct in6_addr *ap, _addr; + int err; - if (ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL) < 0) + err = ipv6_find_hdr(skb, &ptr, NEXTHDR_ROUTING, NULL); + if (err < 0) { + if (err != -ENOENT) + *hotdrop = 1; return 0; + } rh = skb_header_pointer(skb, ptr, sizeof(_route), &_route); if (rh == NULL) { |