netfilter: x_tables: fix unconditional helper
[ Upstream commit 54d83fc74a
]
Ben Hawkes says:
In the mark_source_chains function (net/ipv4/netfilter/ip_tables.c) it
is possible for a user-supplied ipt_entry structure to have a large
next_offset field. This field is not bounds checked prior to writing a
counter value at the supplied offset.
Problem is that mark_source_chains should not have been called --
the rule doesn't have a next entry, so its supposed to return
an absolute verdict of either ACCEPT or DROP.
However, the function conditional() doesn't work as the name implies.
It only checks that the rule is using wildcard address matching.
However, an unconditional rule must also not be using any matches
(no -m args).
The underflow validator only checked the addresses, therefore
passing the 'unconditional absolute verdict' test, while
mark_source_chains also tested for presence of matches, and thus
proceeeded to the next (not-existent) rule.
Unify this so that all the callers have same idea of 'unconditional rule'.
Reported-by: Ben Hawkes <hawkes@google.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
This commit is contained in:
parent
a1c49d8cf9
commit
850c377e0e
|
@ -354,11 +354,12 @@ unsigned int arpt_do_table(struct sk_buff *skb,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* All zeroes == unconditional rule. */
|
/* All zeroes == unconditional rule. */
|
||||||
static inline bool unconditional(const struct arpt_arp *arp)
|
static inline bool unconditional(const struct arpt_entry *e)
|
||||||
{
|
{
|
||||||
static const struct arpt_arp uncond;
|
static const struct arpt_arp uncond;
|
||||||
|
|
||||||
return memcmp(arp, &uncond, sizeof(uncond)) == 0;
|
return e->target_offset == sizeof(struct arpt_entry) &&
|
||||||
|
memcmp(&e->arp, &uncond, sizeof(uncond)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Figures out from what hook each rule can be called: returns 0 if
|
/* Figures out from what hook each rule can be called: returns 0 if
|
||||||
|
@ -397,11 +398,10 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
|
||||||
|= ((1 << hook) | (1 << NF_ARP_NUMHOOKS));
|
|= ((1 << hook) | (1 << NF_ARP_NUMHOOKS));
|
||||||
|
|
||||||
/* Unconditional return/END. */
|
/* Unconditional return/END. */
|
||||||
if ((e->target_offset == sizeof(struct arpt_entry) &&
|
if ((unconditional(e) &&
|
||||||
(strcmp(t->target.u.user.name,
|
(strcmp(t->target.u.user.name,
|
||||||
XT_STANDARD_TARGET) == 0) &&
|
XT_STANDARD_TARGET) == 0) &&
|
||||||
t->verdict < 0 && unconditional(&e->arp)) ||
|
t->verdict < 0) || visited) {
|
||||||
visited) {
|
|
||||||
unsigned int oldpos, size;
|
unsigned int oldpos, size;
|
||||||
|
|
||||||
if ((strcmp(t->target.u.user.name,
|
if ((strcmp(t->target.u.user.name,
|
||||||
|
@ -540,7 +540,7 @@ static bool check_underflow(const struct arpt_entry *e)
|
||||||
const struct xt_entry_target *t;
|
const struct xt_entry_target *t;
|
||||||
unsigned int verdict;
|
unsigned int verdict;
|
||||||
|
|
||||||
if (!unconditional(&e->arp))
|
if (!unconditional(e))
|
||||||
return false;
|
return false;
|
||||||
t = arpt_get_target_c(e);
|
t = arpt_get_target_c(e);
|
||||||
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
|
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
|
||||||
|
@ -587,7 +587,7 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
|
||||||
newinfo->hook_entry[h] = hook_entries[h];
|
newinfo->hook_entry[h] = hook_entries[h];
|
||||||
if ((unsigned char *)e - base == underflows[h]) {
|
if ((unsigned char *)e - base == underflows[h]) {
|
||||||
if (!check_underflow(e)) {
|
if (!check_underflow(e)) {
|
||||||
pr_err("Underflows must be unconditional and "
|
pr_debug("Underflows must be unconditional and "
|
||||||
"use the STANDARD target with "
|
"use the STANDARD target with "
|
||||||
"ACCEPT/DROP\n");
|
"ACCEPT/DROP\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -168,11 +168,12 @@ get_entry(const void *base, unsigned int offset)
|
||||||
|
|
||||||
/* All zeroes == unconditional rule. */
|
/* All zeroes == unconditional rule. */
|
||||||
/* Mildly perf critical (only if packet tracing is on) */
|
/* Mildly perf critical (only if packet tracing is on) */
|
||||||
static inline bool unconditional(const struct ipt_ip *ip)
|
static inline bool unconditional(const struct ipt_entry *e)
|
||||||
{
|
{
|
||||||
static const struct ipt_ip uncond;
|
static const struct ipt_ip uncond;
|
||||||
|
|
||||||
return memcmp(ip, &uncond, sizeof(uncond)) == 0;
|
return e->target_offset == sizeof(struct ipt_entry) &&
|
||||||
|
memcmp(&e->ip, &uncond, sizeof(uncond)) == 0;
|
||||||
#undef FWINV
|
#undef FWINV
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,11 +230,10 @@ get_chainname_rulenum(const struct ipt_entry *s, const struct ipt_entry *e,
|
||||||
} else if (s == e) {
|
} else if (s == e) {
|
||||||
(*rulenum)++;
|
(*rulenum)++;
|
||||||
|
|
||||||
if (s->target_offset == sizeof(struct ipt_entry) &&
|
if (unconditional(s) &&
|
||||||
strcmp(t->target.u.kernel.target->name,
|
strcmp(t->target.u.kernel.target->name,
|
||||||
XT_STANDARD_TARGET) == 0 &&
|
XT_STANDARD_TARGET) == 0 &&
|
||||||
t->verdict < 0 &&
|
t->verdict < 0) {
|
||||||
unconditional(&s->ip)) {
|
|
||||||
/* Tail of chains: STANDARD target (return/policy) */
|
/* Tail of chains: STANDARD target (return/policy) */
|
||||||
*comment = *chainname == hookname
|
*comment = *chainname == hookname
|
||||||
? comments[NF_IP_TRACE_COMMENT_POLICY]
|
? comments[NF_IP_TRACE_COMMENT_POLICY]
|
||||||
|
@ -471,11 +471,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
|
||||||
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
|
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
|
||||||
|
|
||||||
/* Unconditional return/END. */
|
/* Unconditional return/END. */
|
||||||
if ((e->target_offset == sizeof(struct ipt_entry) &&
|
if ((unconditional(e) &&
|
||||||
(strcmp(t->target.u.user.name,
|
(strcmp(t->target.u.user.name,
|
||||||
XT_STANDARD_TARGET) == 0) &&
|
XT_STANDARD_TARGET) == 0) &&
|
||||||
t->verdict < 0 && unconditional(&e->ip)) ||
|
t->verdict < 0) || visited) {
|
||||||
visited) {
|
|
||||||
unsigned int oldpos, size;
|
unsigned int oldpos, size;
|
||||||
|
|
||||||
if ((strcmp(t->target.u.user.name,
|
if ((strcmp(t->target.u.user.name,
|
||||||
|
@ -702,7 +701,7 @@ static bool check_underflow(const struct ipt_entry *e)
|
||||||
const struct xt_entry_target *t;
|
const struct xt_entry_target *t;
|
||||||
unsigned int verdict;
|
unsigned int verdict;
|
||||||
|
|
||||||
if (!unconditional(&e->ip))
|
if (!unconditional(e))
|
||||||
return false;
|
return false;
|
||||||
t = ipt_get_target_c(e);
|
t = ipt_get_target_c(e);
|
||||||
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
|
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
|
||||||
|
@ -750,7 +749,7 @@ check_entry_size_and_hooks(struct ipt_entry *e,
|
||||||
newinfo->hook_entry[h] = hook_entries[h];
|
newinfo->hook_entry[h] = hook_entries[h];
|
||||||
if ((unsigned char *)e - base == underflows[h]) {
|
if ((unsigned char *)e - base == underflows[h]) {
|
||||||
if (!check_underflow(e)) {
|
if (!check_underflow(e)) {
|
||||||
pr_err("Underflows must be unconditional and "
|
pr_debug("Underflows must be unconditional and "
|
||||||
"use the STANDARD target with "
|
"use the STANDARD target with "
|
||||||
"ACCEPT/DROP\n");
|
"ACCEPT/DROP\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
|
@ -198,11 +198,12 @@ get_entry(const void *base, unsigned int offset)
|
||||||
|
|
||||||
/* All zeroes == unconditional rule. */
|
/* All zeroes == unconditional rule. */
|
||||||
/* Mildly perf critical (only if packet tracing is on) */
|
/* Mildly perf critical (only if packet tracing is on) */
|
||||||
static inline bool unconditional(const struct ip6t_ip6 *ipv6)
|
static inline bool unconditional(const struct ip6t_entry *e)
|
||||||
{
|
{
|
||||||
static const struct ip6t_ip6 uncond;
|
static const struct ip6t_ip6 uncond;
|
||||||
|
|
||||||
return memcmp(ipv6, &uncond, sizeof(uncond)) == 0;
|
return e->target_offset == sizeof(struct ip6t_entry) &&
|
||||||
|
memcmp(&e->ipv6, &uncond, sizeof(uncond)) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const struct xt_entry_target *
|
static inline const struct xt_entry_target *
|
||||||
|
@ -258,11 +259,10 @@ get_chainname_rulenum(const struct ip6t_entry *s, const struct ip6t_entry *e,
|
||||||
} else if (s == e) {
|
} else if (s == e) {
|
||||||
(*rulenum)++;
|
(*rulenum)++;
|
||||||
|
|
||||||
if (s->target_offset == sizeof(struct ip6t_entry) &&
|
if (unconditional(s) &&
|
||||||
strcmp(t->target.u.kernel.target->name,
|
strcmp(t->target.u.kernel.target->name,
|
||||||
XT_STANDARD_TARGET) == 0 &&
|
XT_STANDARD_TARGET) == 0 &&
|
||||||
t->verdict < 0 &&
|
t->verdict < 0) {
|
||||||
unconditional(&s->ipv6)) {
|
|
||||||
/* Tail of chains: STANDARD target (return/policy) */
|
/* Tail of chains: STANDARD target (return/policy) */
|
||||||
*comment = *chainname == hookname
|
*comment = *chainname == hookname
|
||||||
? comments[NF_IP6_TRACE_COMMENT_POLICY]
|
? comments[NF_IP6_TRACE_COMMENT_POLICY]
|
||||||
|
@ -484,11 +484,10 @@ mark_source_chains(const struct xt_table_info *newinfo,
|
||||||
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
|
e->comefrom |= ((1 << hook) | (1 << NF_INET_NUMHOOKS));
|
||||||
|
|
||||||
/* Unconditional return/END. */
|
/* Unconditional return/END. */
|
||||||
if ((e->target_offset == sizeof(struct ip6t_entry) &&
|
if ((unconditional(e) &&
|
||||||
(strcmp(t->target.u.user.name,
|
(strcmp(t->target.u.user.name,
|
||||||
XT_STANDARD_TARGET) == 0) &&
|
XT_STANDARD_TARGET) == 0) &&
|
||||||
t->verdict < 0 &&
|
t->verdict < 0) || visited) {
|
||||||
unconditional(&e->ipv6)) || visited) {
|
|
||||||
unsigned int oldpos, size;
|
unsigned int oldpos, size;
|
||||||
|
|
||||||
if ((strcmp(t->target.u.user.name,
|
if ((strcmp(t->target.u.user.name,
|
||||||
|
@ -716,7 +715,7 @@ static bool check_underflow(const struct ip6t_entry *e)
|
||||||
const struct xt_entry_target *t;
|
const struct xt_entry_target *t;
|
||||||
unsigned int verdict;
|
unsigned int verdict;
|
||||||
|
|
||||||
if (!unconditional(&e->ipv6))
|
if (!unconditional(e))
|
||||||
return false;
|
return false;
|
||||||
t = ip6t_get_target_c(e);
|
t = ip6t_get_target_c(e);
|
||||||
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
|
if (strcmp(t->u.user.name, XT_STANDARD_TARGET) != 0)
|
||||||
|
@ -764,7 +763,7 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
|
||||||
newinfo->hook_entry[h] = hook_entries[h];
|
newinfo->hook_entry[h] = hook_entries[h];
|
||||||
if ((unsigned char *)e - base == underflows[h]) {
|
if ((unsigned char *)e - base == underflows[h]) {
|
||||||
if (!check_underflow(e)) {
|
if (!check_underflow(e)) {
|
||||||
pr_err("Underflows must be unconditional and "
|
pr_debug("Underflows must be unconditional and "
|
||||||
"use the STANDARD target with "
|
"use the STANDARD target with "
|
||||||
"ACCEPT/DROP\n");
|
"ACCEPT/DROP\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
Loading…
Reference in a new issue