提交 590bdf7f 编写于 作者: D Dmitry Mishin 提交者: David S. Miller

[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: NDmitry Mishin <dim@openvz.org>
Acked-by: NKirill Korotaev <dev@openvz.org>
Signed-off-by: NPatrick McHardy <kaber@trash.net>
Signed-off-by: NDavid S. Miller <davem@davemloft.net>
上级 844dc7c8
...@@ -466,7 +466,13 @@ static inline int check_entry(struct arpt_entry *e, const char *name, unsigned i ...@@ -466,7 +466,13 @@ static inline int check_entry(struct arpt_entry *e, const char *name, unsigned i
return -EINVAL; return -EINVAL;
} }
if (e->target_offset + sizeof(struct arpt_entry_target) > e->next_offset)
return -EINVAL;
t = arpt_get_target(e); t = arpt_get_target(e);
if (e->target_offset + t->u.target_size > e->next_offset)
return -EINVAL;
target = try_then_request_module(xt_find_target(NF_ARP, t->u.user.name, target = try_then_request_module(xt_find_target(NF_ARP, t->u.user.name,
t->u.user.revision), t->u.user.revision),
"arpt_%s", t->u.user.name); "arpt_%s", t->u.user.name);
...@@ -621,20 +627,18 @@ static int translate_table(const char *name, ...@@ -621,20 +627,18 @@ static int translate_table(const char *name,
} }
} }
if (!mark_source_chains(newinfo, valid_hooks, entry0)) {
duprintf("Looping hook\n");
return -ELOOP;
}
/* Finally, each sanity check must pass */ /* Finally, each sanity check must pass */
i = 0; i = 0;
ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size, ret = ARPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i); check_entry, name, size, &i);
if (ret != 0) { if (ret != 0)
ARPT_ENTRY_ITERATE(entry0, newinfo->size, goto cleanup;
cleanup_entry, &i);
return ret; ret = -ELOOP;
if (!mark_source_chains(newinfo, valid_hooks, entry0)) {
duprintf("Looping hook\n");
goto cleanup;
} }
/* And one copy for every other CPU */ /* And one copy for every other CPU */
...@@ -643,6 +647,9 @@ static int translate_table(const char *name, ...@@ -643,6 +647,9 @@ static int translate_table(const char *name,
memcpy(newinfo->entries[i], entry0, newinfo->size); memcpy(newinfo->entries[i], entry0, newinfo->size);
} }
return 0;
cleanup:
ARPT_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i);
return ret; return ret;
} }
......
...@@ -547,12 +547,18 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size, ...@@ -547,12 +547,18 @@ check_entry(struct ipt_entry *e, const char *name, unsigned int size,
return -EINVAL; return -EINVAL;
} }
if (e->target_offset + sizeof(struct ipt_entry_target) > e->next_offset)
return -EINVAL;
j = 0; j = 0;
ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j); ret = IPT_MATCH_ITERATE(e, check_match, name, &e->ip, e->comefrom, &j);
if (ret != 0) if (ret != 0)
goto cleanup_matches; goto cleanup_matches;
t = ipt_get_target(e); t = ipt_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_INET, target = try_then_request_module(xt_find_target(AF_INET,
t->u.user.name, t->u.user.name,
t->u.user.revision), t->u.user.revision),
...@@ -712,19 +718,17 @@ translate_table(const char *name, ...@@ -712,19 +718,17 @@ translate_table(const char *name,
} }
} }
if (!mark_source_chains(newinfo, valid_hooks, entry0))
return -ELOOP;
/* Finally, each sanity check must pass */ /* Finally, each sanity check must pass */
i = 0; i = 0;
ret = IPT_ENTRY_ITERATE(entry0, newinfo->size, ret = IPT_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i); check_entry, name, size, &i);
if (ret != 0) { if (ret != 0)
IPT_ENTRY_ITERATE(entry0, newinfo->size, goto cleanup;
cleanup_entry, &i);
return ret; ret = -ELOOP;
} if (!mark_source_chains(newinfo, valid_hooks, entry0))
goto cleanup;
/* And one copy for every other CPU */ /* And one copy for every other CPU */
for_each_possible_cpu(i) { for_each_possible_cpu(i) {
...@@ -732,6 +736,9 @@ translate_table(const char *name, ...@@ -732,6 +736,9 @@ translate_table(const char *name,
memcpy(newinfo->entries[i], entry0, newinfo->size); memcpy(newinfo->entries[i], entry0, newinfo->size);
} }
return 0;
cleanup:
IPT_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i);
return ret; return ret;
} }
...@@ -1463,6 +1470,10 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, ...@@ -1463,6 +1470,10 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e,
return -EINVAL; return -EINVAL;
} }
if (e->target_offset + sizeof(struct compat_xt_entry_target) >
e->next_offset)
return -EINVAL;
off = 0; off = 0;
entry_offset = (void *)e - (void *)base; entry_offset = (void *)e - (void *)base;
j = 0; j = 0;
...@@ -1472,6 +1483,9 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e, ...@@ -1472,6 +1483,9 @@ check_compat_entry_size_and_hooks(struct ipt_entry *e,
goto cleanup_matches; goto cleanup_matches;
t = ipt_get_target(e); t = ipt_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_INET, target = try_then_request_module(xt_find_target(AF_INET,
t->u.user.name, t->u.user.name,
t->u.user.revision), t->u.user.revision),
......
...@@ -586,12 +586,19 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size, ...@@ -586,12 +586,19 @@ check_entry(struct ip6t_entry *e, const char *name, unsigned int size,
return -EINVAL; return -EINVAL;
} }
if (e->target_offset + sizeof(struct ip6t_entry_target) >
e->next_offset)
return -EINVAL;
j = 0; j = 0;
ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j); ret = IP6T_MATCH_ITERATE(e, check_match, name, &e->ipv6, e->comefrom, &j);
if (ret != 0) if (ret != 0)
goto cleanup_matches; goto cleanup_matches;
t = ip6t_get_target(e); 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, target = try_then_request_module(xt_find_target(AF_INET6,
t->u.user.name, t->u.user.name,
t->u.user.revision), t->u.user.revision),
...@@ -751,19 +758,17 @@ translate_table(const char *name, ...@@ -751,19 +758,17 @@ translate_table(const char *name,
} }
} }
if (!mark_source_chains(newinfo, valid_hooks, entry0))
return -ELOOP;
/* Finally, each sanity check must pass */ /* Finally, each sanity check must pass */
i = 0; i = 0;
ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size, ret = IP6T_ENTRY_ITERATE(entry0, newinfo->size,
check_entry, name, size, &i); check_entry, name, size, &i);
if (ret != 0) { if (ret != 0)
IP6T_ENTRY_ITERATE(entry0, newinfo->size, goto cleanup;
cleanup_entry, &i);
return ret; ret = -ELOOP;
} if (!mark_source_chains(newinfo, valid_hooks, entry0))
goto cleanup;
/* And one copy for every other CPU */ /* And one copy for every other CPU */
for_each_possible_cpu(i) { for_each_possible_cpu(i) {
...@@ -771,6 +776,9 @@ translate_table(const char *name, ...@@ -771,6 +776,9 @@ translate_table(const char *name,
memcpy(newinfo->entries[i], entry0, newinfo->size); memcpy(newinfo->entries[i], entry0, newinfo->size);
} }
return 0;
cleanup:
IP6T_ENTRY_ITERATE(entry0, newinfo->size, cleanup_entry, &i);
return ret; return ret;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册