aboutsummaryrefslogtreecommitdiff
path: root/net/bridge/netfilter/ebtables.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bridge/netfilter/ebtables.c')
-rw-r--r--net/bridge/netfilter/ebtables.c148
1 files changed, 59 insertions, 89 deletions
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
index cbc9f395ab1..1059ed3bc25 100644
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -118,10 +118,10 @@ ebt_dev_check(const char *entry, const struct net_device *device)
/* 1 is the wildcard token */
while (entry[i] != '\0' && entry[i] != 1 && entry[i] == devname[i])
i++;
- return (devname[i] != entry[i] && entry[i] != 1);
+ return devname[i] != entry[i] && entry[i] != 1;
}
-#define FWINV2(bool,invflg) ((bool) ^ !!(e->invflags & invflg))
+#define FWINV2(bool, invflg) ((bool) ^ !!(e->invflags & invflg))
/* process standard matches */
static inline int
ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
@@ -138,7 +138,7 @@ ebt_basic_match(const struct ebt_entry *e, const struct sk_buff *skb,
ethproto = h->h_proto;
if (e->bitmask & EBT_802_3) {
- if (FWINV2(ntohs(ethproto) >= 1536, EBT_IPROTO))
+ if (FWINV2(ntohs(ethproto) >= ETH_P_802_3_MIN, EBT_IPROTO))
return 1;
} else if (!(e->bitmask & EBT_NOPROTO) &&
FWINV2(e->ethproto != ethproto, EBT_IPROTO))
@@ -1044,10 +1044,9 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl,
if (repl->num_counters &&
copy_to_user(repl->counters, counterstmp,
repl->num_counters * sizeof(struct ebt_counter))) {
- ret = -EFAULT;
+ /* Silent error, can't fail, new table is already in place */
+ net_warn_ratelimited("ebtables: counters copy to user failed while replacing table\n");
}
- else
- ret = 0;
/* decrease module count and free resources */
EBT_ENTRY_ITERATE(table->entries, table->entries_size,
@@ -1107,6 +1106,8 @@ static int do_replace(struct net *net, const void __user *user,
if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter))
return -ENOMEM;
+ tmp.name[sizeof(tmp.name) - 1] = 0;
+
countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids;
newinfo = vmalloc(sizeof(*newinfo) + countersize);
if (!newinfo)
@@ -1147,7 +1148,7 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table)
void *p;
if (input_table == NULL || (repl = input_table->table) == NULL ||
- repl->entries == 0 || repl->entries_size == 0 ||
+ repl->entries == NULL || repl->entries_size == 0 ||
repl->counters != NULL || input_table->private != NULL) {
BUGPRINT("Bad table data for ebt_register_table!!!\n");
return ERR_PTR(-EINVAL);
@@ -1196,7 +1197,8 @@ ebt_register_table(struct net *net, const struct ebt_table *input_table)
if (table->check && table->check(newinfo, table->valid_hooks)) {
BUGPRINT("The table doesn't like its own initial data, lol\n");
- return ERR_PTR(-EINVAL);
+ ret = -EINVAL;
+ goto free_chainstack;
}
table->private = newinfo;
@@ -1332,7 +1334,12 @@ static inline int ebt_make_matchname(const struct ebt_entry_match *m,
const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)m - base);
- if (copy_to_user(hlp, m->u.match->name, EBT_FUNCTION_MAXNAMELEN))
+ char name[EBT_FUNCTION_MAXNAMELEN] = {};
+
+ /* ebtables expects 32 bytes long names but xt_match names are 29 bytes
+ long. Copy 29 bytes and fill remaining bytes with zeroes. */
+ strlcpy(name, m->u.match->name, sizeof(name));
+ if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
@@ -1341,7 +1348,10 @@ static inline int ebt_make_watchername(const struct ebt_entry_watcher *w,
const char *base, char __user *ubase)
{
char __user *hlp = ubase + ((char *)w - base);
- if (copy_to_user(hlp , w->u.watcher->name, EBT_FUNCTION_MAXNAMELEN))
+ char name[EBT_FUNCTION_MAXNAMELEN] = {};
+
+ strlcpy(name, w->u.watcher->name, sizeof(name));
+ if (copy_to_user(hlp , name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
@@ -1352,6 +1362,7 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
int ret;
char __user *hlp;
const struct ebt_entry_target *t;
+ char name[EBT_FUNCTION_MAXNAMELEN] = {};
if (e->bitmask == 0)
return 0;
@@ -1365,7 +1376,8 @@ ebt_make_names(struct ebt_entry *e, const char *base, char __user *ubase)
ret = EBT_WATCHER_ITERATE(e, ebt_make_watchername, base, ubase);
if (ret != 0)
return ret;
- if (copy_to_user(hlp, t->u.target->name, EBT_FUNCTION_MAXNAMELEN))
+ strlcpy(name, t->u.target->name, sizeof(name));
+ if (copy_to_user(hlp, name, EBT_FUNCTION_MAXNAMELEN))
return -EFAULT;
return 0;
}
@@ -1428,7 +1440,7 @@ static int copy_everything_to_user(struct ebt_table *t, void __user *user,
return -EFAULT;
if (*len != sizeof(struct ebt_replace) + entries_size +
- (tmp.num_counters? nentries * sizeof(struct ebt_counter): 0))
+ (tmp.num_counters ? nentries * sizeof(struct ebt_counter) : 0))
return -EINVAL;
if (tmp.nentries != nentries) {
@@ -1459,16 +1471,17 @@ static int do_ebt_set_ctl(struct sock *sk,
int cmd, void __user *user, unsigned int len)
{
int ret;
+ struct net *net = sock_net(sk);
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
- switch(cmd) {
+ switch (cmd) {
case EBT_SO_SET_ENTRIES:
- ret = do_replace(sock_net(sk), user, len);
+ ret = do_replace(net, user, len);
break;
case EBT_SO_SET_COUNTERS:
- ret = update_counters(sock_net(sk), user, len);
+ ret = update_counters(net, user, len);
break;
default:
ret = -EINVAL;
@@ -1481,21 +1494,22 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
int ret;
struct ebt_replace tmp;
struct ebt_table *t;
+ struct net *net = sock_net(sk);
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&tmp, user, sizeof(tmp)))
return -EFAULT;
- t = find_table_lock(sock_net(sk), tmp.name, &ret, &ebt_mutex);
+ t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
if (!t)
return ret;
- switch(cmd) {
+ switch (cmd) {
case EBT_SO_GET_INFO:
case EBT_SO_GET_INIT_INFO:
- if (*len != sizeof(struct ebt_replace)){
+ if (*len != sizeof(struct ebt_replace)) {
ret = -EINVAL;
mutex_unlock(&ebt_mutex);
break;
@@ -1510,7 +1524,7 @@ static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len)
tmp.valid_hooks = t->table->valid_hooks;
}
mutex_unlock(&ebt_mutex);
- if (copy_to_user(user, &tmp, *len) != 0){
+ if (copy_to_user(user, &tmp, *len) != 0) {
BUGPRINT("c2u Didn't work\n");
ret = -EFAULT;
break;
@@ -1764,6 +1778,7 @@ static int compat_table_info(const struct ebt_table_info *info,
newinfo->entries_size = size;
+ xt_compat_init_offsets(NFPROTO_BRIDGE, info->nentries);
return EBT_ENTRY_ITERATE(entries, size, compat_calc_entry, info,
entries, newinfo);
}
@@ -1879,21 +1894,17 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
struct xt_match *match;
struct xt_target *wt;
void *dst = NULL;
- int off, pad = 0, ret = 0;
- unsigned int size_kern, entry_offset, match_size = mwt->match_size;
+ int off, pad = 0;
+ unsigned int size_kern, match_size = mwt->match_size;
strlcpy(name, mwt->u.name, sizeof(name));
if (state->buf_kern_start)
dst = state->buf_kern_start + state->buf_kern_offset;
- entry_offset = (unsigned char *) mwt - base;
switch (compat_mwt) {
case EBT_COMPAT_MATCH:
- match = try_then_request_module(xt_find_match(NFPROTO_BRIDGE,
- name, 0), "ebt_%s", name);
- if (match == NULL)
- return -ENOENT;
+ match = xt_request_find_match(NFPROTO_BRIDGE, name, 0);
if (IS_ERR(match))
return PTR_ERR(match);
@@ -1912,10 +1923,7 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
break;
case EBT_COMPAT_WATCHER: /* fallthrough */
case EBT_COMPAT_TARGET:
- wt = try_then_request_module(xt_find_target(NFPROTO_BRIDGE,
- name, 0), "ebt_%s", name);
- if (wt == NULL)
- return -ENOENT;
+ wt = xt_request_find_target(NFPROTO_BRIDGE, name, 0);
if (IS_ERR(wt))
return PTR_ERR(wt);
off = xt_compat_target_offset(wt);
@@ -1930,13 +1938,9 @@ static int compat_mtw_from_user(struct compat_ebt_entry_mwt *mwt,
size_kern = wt->targetsize;
module_put(wt->me);
break;
- }
- if (!dst) {
- ret = xt_compat_add_offset(NFPROTO_BRIDGE, entry_offset,
- off + ebt_compat_entry_padsize());
- if (ret < 0)
- return ret;
+ default:
+ return -EINVAL;
}
state->buf_kern_offset += match_size + off;
@@ -2013,50 +2017,6 @@ static int ebt_size_mwt(struct compat_ebt_entry_mwt *match32,
return growth;
}
-#define EBT_COMPAT_WATCHER_ITERATE(e, fn, args...) \
-({ \
- unsigned int __i; \
- int __ret = 0; \
- struct compat_ebt_entry_mwt *__watcher; \
- \
- for (__i = e->watchers_offset; \
- __i < (e)->target_offset; \
- __i += __watcher->watcher_size + \
- sizeof(struct compat_ebt_entry_mwt)) { \
- __watcher = (void *)(e) + __i; \
- __ret = fn(__watcher , ## args); \
- if (__ret != 0) \
- break; \
- } \
- if (__ret == 0) { \
- if (__i != (e)->target_offset) \
- __ret = -EINVAL; \
- } \
- __ret; \
-})
-
-#define EBT_COMPAT_MATCH_ITERATE(e, fn, args...) \
-({ \
- unsigned int __i; \
- int __ret = 0; \
- struct compat_ebt_entry_mwt *__match; \
- \
- for (__i = sizeof(struct ebt_entry); \
- __i < (e)->watchers_offset; \
- __i += __match->match_size + \
- sizeof(struct compat_ebt_entry_mwt)) { \
- __match = (void *)(e) + __i; \
- __ret = fn(__match , ## args); \
- if (__ret != 0) \
- break; \
- } \
- if (__ret == 0) { \
- if (__i != (e)->watchers_offset) \
- __ret = -EINVAL; \
- } \
- __ret; \
-})
-
/* called for all ebt_entry structures. */
static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
unsigned int *total,
@@ -2129,6 +2089,14 @@ static int size_entry_mwt(struct ebt_entry *entry, const unsigned char *base,
}
}
+ if (state->buf_kern_start == NULL) {
+ unsigned int offset = buf_start - (char *) base;
+
+ ret = xt_compat_add_offset(NFPROTO_BRIDGE, offset, new_offset);
+ if (ret < 0)
+ return ret;
+ }
+
startoff = state->buf_user_offset - startoff;
BUG_ON(*total < startoff);
@@ -2237,6 +2205,7 @@ static int compat_do_replace(struct net *net, void __user *user,
xt_compat_lock(NFPROTO_BRIDGE);
+ xt_compat_init_offsets(NFPROTO_BRIDGE, tmp.nentries);
ret = compat_copy_entries(entries_tmp, tmp.entries_size, &state);
if (ret < 0)
goto out_unlock;
@@ -2311,16 +2280,17 @@ static int compat_do_ebt_set_ctl(struct sock *sk,
int cmd, void __user *user, unsigned int len)
{
int ret;
+ struct net *net = sock_net(sk);
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
switch (cmd) {
case EBT_SO_SET_ENTRIES:
- ret = compat_do_replace(sock_net(sk), user, len);
+ ret = compat_do_replace(net, user, len);
break;
case EBT_SO_SET_COUNTERS:
- ret = compat_update_counters(sock_net(sk), user, len);
+ ret = compat_update_counters(net, user, len);
break;
default:
ret = -EINVAL;
@@ -2334,8 +2304,9 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
int ret;
struct compat_ebt_replace tmp;
struct ebt_table *t;
+ struct net *net = sock_net(sk);
- if (!capable(CAP_NET_ADMIN))
+ if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
/* try real handler in case userland supplied needed padding */
@@ -2346,7 +2317,7 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
if (copy_from_user(&tmp, user, sizeof(tmp)))
return -EFAULT;
- t = find_table_lock(sock_net(sk), tmp.name, &ret, &ebt_mutex);
+ t = find_table_lock(net, tmp.name, &ret, &ebt_mutex);
if (!t)
return ret;
@@ -2403,8 +2374,7 @@ static int compat_do_ebt_get_ctl(struct sock *sk, int cmd,
}
#endif
-static struct nf_sockopt_ops ebt_sockopts =
-{
+static struct nf_sockopt_ops ebt_sockopts = {
.pf = PF_INET,
.set_optmin = EBT_BASE_CTL,
.set_optmax = EBT_SO_SET_MAX + 1,