Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Sep 24, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/linux/netfilter/x_tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ void xt_unregister_match(struct xt_match *target);
int xt_register_matches(struct xt_match *match, unsigned int n);
void xt_unregister_matches(struct xt_match *match, unsigned int n);

int xt_check_entry_offsets(const void *base, const char *elems,
unsigned int target_offset,
unsigned int next_offset);

int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
bool inv_proto);
int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
Expand Down Expand Up @@ -488,6 +492,9 @@ void xt_compat_target_from_user(struct xt_entry_target *t, void **dstptr,
unsigned int *size);
int xt_compat_target_to_user(const struct xt_entry_target *t,
void __user **dstptr, unsigned int *size);
int xt_compat_check_entry_offsets(const void *base, const char *elems,
unsigned int target_offset,
unsigned int next_offset);

#endif /* CONFIG_COMPAT */
#endif /* _X_TABLES_H */
32 changes: 20 additions & 12 deletions net/ipv4/netfilter/arp_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,14 +474,12 @@ static int mark_source_chains(const struct xt_table_info *newinfo,
return 1;
}

static inline int check_entry(const struct arpt_entry *e, const char *name)
static inline int check_entry(const struct arpt_entry *e)
{
const struct xt_entry_target *t;

if (!arp_checkentry(&e->arp)) {
duprintf("arp_tables: arp check failed %p %s.\n", e, name);
if (!arp_checkentry(&e->arp))
return -EINVAL;
}

if (e->target_offset + sizeof(struct xt_entry_target) > e->next_offset)
return -EINVAL;
Expand Down Expand Up @@ -522,10 +520,6 @@ find_check_entry(struct arpt_entry *e, const char *name, unsigned int size)
struct xt_target *target;
int ret;

ret = check_entry(e, name);
if (ret)
return ret;

e->counters.pcnt = xt_percpu_counter_alloc();
if (IS_ERR_VALUE(e->counters.pcnt))
return -ENOMEM;
Expand Down Expand Up @@ -576,9 +570,11 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
unsigned int valid_hooks)
{
unsigned int h;
int err;

if ((unsigned long)e % __alignof__(struct arpt_entry) != 0 ||
(unsigned char *)e + sizeof(struct arpt_entry) >= limit) {
(unsigned char *)e + sizeof(struct arpt_entry) >= limit ||
(unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p\n", e);
return -EINVAL;
}
Expand All @@ -590,6 +586,14 @@ static inline int check_entry_size_and_hooks(struct arpt_entry *e,
return -EINVAL;
}

if (!arp_checkentry(&e->arp))
return -EINVAL;

err = xt_check_entry_offsets(e, e->elems, e->target_offset,
e->next_offset);
if (err)
return err;

/* Check hooks & underflows */
for (h = 0; h < NF_ARP_NUMHOOKS; h++) {
if (!(valid_hooks & (1 << h)))
Expand Down Expand Up @@ -1233,7 +1237,8 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,

duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_arpt_entry) != 0 ||
(unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit) {
(unsigned char *)e + sizeof(struct compat_arpt_entry) >= limit ||
(unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p, limit = %p\n", e, limit);
return -EINVAL;
}
Expand All @@ -1245,8 +1250,11 @@ check_compat_entry_size_and_hooks(struct compat_arpt_entry *e,
return -EINVAL;
}

/* For purposes of check_entry casting the compat entry is fine */
ret = check_entry((struct arpt_entry *)e, name);
if (!arp_checkentry(&e->arp))
return -EINVAL;

ret = xt_compat_check_entry_offsets(e, e->elems, e->target_offset,
e->next_offset);
if (ret)
return ret;

Expand Down
32 changes: 20 additions & 12 deletions net/ipv4/netfilter/ip_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -569,14 +569,12 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
}

static int
check_entry(const struct ipt_entry *e, const char *name)
check_entry(const struct ipt_entry *e)
{
const struct xt_entry_target *t;

if (!ip_checkentry(&e->ip)) {
duprintf("ip check failed %p %s.\n", e, name);
if (!ip_checkentry(&e->ip))
return -EINVAL;
}

if (e->target_offset + sizeof(struct xt_entry_target) >
e->next_offset)
Expand Down Expand Up @@ -666,10 +664,6 @@ find_check_entry(struct ipt_entry *e, struct net *net, const char *name,
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;

ret = check_entry(e, name);
if (ret)
return ret;

e->counters.pcnt = xt_percpu_counter_alloc();
if (IS_ERR_VALUE(e->counters.pcnt))
return -ENOMEM;
Expand Down Expand Up @@ -741,9 +735,11 @@ check_entry_size_and_hooks(struct ipt_entry *e,
unsigned int valid_hooks)
{
unsigned int h;
int err;

if ((unsigned long)e % __alignof__(struct ipt_entry) != 0 ||
(unsigned char *)e + sizeof(struct ipt_entry) >= limit) {
(unsigned char *)e + sizeof(struct ipt_entry) >= limit ||
(unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p\n", e);
return -EINVAL;
}
Expand All @@ -755,6 +751,14 @@ check_entry_size_and_hooks(struct ipt_entry *e,
return -EINVAL;
}

if (!ip_checkentry(&e->ip))
return -EINVAL;

err = xt_check_entry_offsets(e, e->elems, e->target_offset,
e->next_offset);
if (err)
return err;

/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if (!(valid_hooks & (1 << h)))
Expand Down Expand Up @@ -1493,7 +1497,8 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,

duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ipt_entry) != 0 ||
(unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit) {
(unsigned char *)e + sizeof(struct compat_ipt_entry) >= limit ||
(unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p, limit = %p\n", e, limit);
return -EINVAL;
}
Expand All @@ -1505,8 +1510,11 @@ check_compat_entry_size_and_hooks(struct compat_ipt_entry *e,
return -EINVAL;
}

/* For purposes of check_entry casting the compat entry is fine */
ret = check_entry((struct ipt_entry *)e, name);
if (!ip_checkentry(&e->ip))
return -EINVAL;

ret = xt_compat_check_entry_offsets(e, e->elems,
e->target_offset, e->next_offset);
if (ret)
return ret;

Expand Down
32 changes: 20 additions & 12 deletions net/ipv6/netfilter/ip6_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -581,14 +581,12 @@ static void cleanup_match(struct xt_entry_match *m, struct net *net)
}

static int
check_entry(const struct ip6t_entry *e, const char *name)
check_entry(const struct ip6t_entry *e)
{
const struct xt_entry_target *t;

if (!ip6_checkentry(&e->ipv6)) {
duprintf("ip_tables: ip check failed %p %s.\n", e, name);
if (!ip6_checkentry(&e->ipv6))
return -EINVAL;
}

if (e->target_offset + sizeof(struct xt_entry_target) >
e->next_offset)
Expand Down Expand Up @@ -679,10 +677,6 @@ find_check_entry(struct ip6t_entry *e, struct net *net, const char *name,
struct xt_mtchk_param mtpar;
struct xt_entry_match *ematch;

ret = check_entry(e, name);
if (ret)
return ret;

e->counters.pcnt = xt_percpu_counter_alloc();
if (IS_ERR_VALUE(e->counters.pcnt))
return -ENOMEM;
Expand Down Expand Up @@ -753,9 +747,11 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
unsigned int valid_hooks)
{
unsigned int h;
int err;

if ((unsigned long)e % __alignof__(struct ip6t_entry) != 0 ||
(unsigned char *)e + sizeof(struct ip6t_entry) >= limit) {
(unsigned char *)e + sizeof(struct ip6t_entry) >= limit ||
(unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p\n", e);
return -EINVAL;
}
Expand All @@ -767,6 +763,14 @@ check_entry_size_and_hooks(struct ip6t_entry *e,
return -EINVAL;
}

if (!ip6_checkentry(&e->ipv6))
return -EINVAL;

err = xt_check_entry_offsets(e, e->elems, e->target_offset,
e->next_offset);
if (err)
return err;

/* Check hooks & underflows */
for (h = 0; h < NF_INET_NUMHOOKS; h++) {
if (!(valid_hooks & (1 << h)))
Expand Down Expand Up @@ -1505,7 +1509,8 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,

duprintf("check_compat_entry_size_and_hooks %p\n", e);
if ((unsigned long)e % __alignof__(struct compat_ip6t_entry) != 0 ||
(unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit) {
(unsigned char *)e + sizeof(struct compat_ip6t_entry) >= limit ||
(unsigned char *)e + e->next_offset > limit) {
duprintf("Bad offset %p, limit = %p\n", e, limit);
return -EINVAL;
}
Expand All @@ -1517,8 +1522,11 @@ check_compat_entry_size_and_hooks(struct compat_ip6t_entry *e,
return -EINVAL;
}

/* For purposes of check_entry casting the compat entry is fine */
ret = check_entry((struct ip6t_entry *)e, name);
if (!ip6_checkentry(&e->ipv6))
return -EINVAL;

ret = xt_compat_check_entry_offsets(e, e->elems,
e->target_offset, e->next_offset);
if (ret)
return ret;

Expand Down
87 changes: 87 additions & 0 deletions net/netfilter/x_tables.c
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,95 @@ int xt_compat_match_to_user(const struct xt_entry_match *m,
return 0;
}
EXPORT_SYMBOL_GPL(xt_compat_match_to_user);

/* non-compat version may have padding after verdict */
struct compat_xt_standard_target {
struct compat_xt_entry_target t;
compat_uint_t verdict;
};

int xt_compat_check_entry_offsets(const void *base, const char *elems,
unsigned int target_offset,
unsigned int next_offset)
{
long size_of_base_struct = elems - (const char *)base;
const struct compat_xt_entry_target *t;
const char *e = base;

if (target_offset < size_of_base_struct)
return -EINVAL;

if (target_offset + sizeof(*t) > next_offset)
return -EINVAL;

t = (void *)(e + target_offset);
if (t->u.target_size < sizeof(*t))
return -EINVAL;

if (target_offset + t->u.target_size > next_offset)
return -EINVAL;

if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
target_offset + sizeof(struct compat_xt_standard_target) != next_offset)
return -EINVAL;

return 0;
}
EXPORT_SYMBOL(xt_compat_check_entry_offsets);
#endif /* CONFIG_COMPAT */

/**
* xt_check_entry_offsets - validate arp/ip/ip6t_entry
*
* @base: pointer to arp/ip/ip6t_entry
* @elems: pointer to first xt_entry_match, i.e. ip(6)t_entry->elems
* @target_offset: the arp/ip/ip6_t->target_offset
* @next_offset: the arp/ip/ip6_t->next_offset
*
* validates that target_offset and next_offset are sane.
* Also see xt_compat_check_entry_offsets for CONFIG_COMPAT version.
*
* This function does not validate the targets or matches themselves, it
* only tests that all the offsets and sizes are correct.
*
* The arp/ip/ip6t_entry structure @base must have passed following tests:
* - it must point to a valid memory location
* - base to base + next_offset must be accessible, i.e. not exceed allocated
* length.
*
* Return: 0 on success, negative errno on failure.
*/
int xt_check_entry_offsets(const void *base,
const char *elems,
unsigned int target_offset,
unsigned int next_offset)
{
long size_of_base_struct = elems - (const char *)base;
const struct xt_entry_target *t;
const char *e = base;

/* target start is within the ip/ip6/arpt_entry struct */
if (target_offset < size_of_base_struct)
return -EINVAL;

if (target_offset + sizeof(*t) > next_offset)
return -EINVAL;

t = (void *)(e + target_offset);
if (t->u.target_size < sizeof(*t))
return -EINVAL;

if (target_offset + t->u.target_size > next_offset)
return -EINVAL;

if (strcmp(t->u.user.name, XT_STANDARD_TARGET) == 0 &&
target_offset + sizeof(struct xt_standard_target) != next_offset)
return -EINVAL;

return 0;
}
EXPORT_SYMBOL(xt_check_entry_offsets);

int xt_check_target(struct xt_tgchk_param *par,
unsigned int size, u_int8_t proto, bool inv_proto)
{
Expand Down