.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * ebtables |
---|
3 | 4 | * |
---|
.. | .. |
---|
8 | 9 | * |
---|
9 | 10 | * This code is strongly inspired by the iptables code which is |
---|
10 | 11 | * Copyright (C) 1999 Paul `Rusty' Russell & Michael J. Neuling |
---|
11 | | - * |
---|
12 | | - * This program is free software; you can redistribute it and/or |
---|
13 | | - * modify it under the terms of the GNU General Public License |
---|
14 | | - * as published by the Free Software Foundation; either version |
---|
15 | | - * 2 of the License, or (at your option) any later version. |
---|
16 | 12 | */ |
---|
17 | 13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
---|
18 | 14 | #include <linux/kmod.h> |
---|
.. | .. |
---|
381 | 377 | par->match = match; |
---|
382 | 378 | par->matchinfo = m->data; |
---|
383 | 379 | ret = xt_check_match(par, m->match_size, |
---|
384 | | - e->ethproto, e->invflags & EBT_IPROTO); |
---|
| 380 | + ntohs(e->ethproto), e->invflags & EBT_IPROTO); |
---|
385 | 381 | if (ret < 0) { |
---|
386 | 382 | module_put(match->me); |
---|
387 | 383 | return ret; |
---|
.. | .. |
---|
418 | 414 | par->target = watcher; |
---|
419 | 415 | par->targinfo = w->data; |
---|
420 | 416 | ret = xt_check_target(par, w->watcher_size, |
---|
421 | | - e->ethproto, e->invflags & EBT_IPROTO); |
---|
| 417 | + ntohs(e->ethproto), e->invflags & EBT_IPROTO); |
---|
422 | 418 | if (ret < 0) { |
---|
423 | 419 | module_put(watcher->me); |
---|
424 | 420 | return ret; |
---|
.. | .. |
---|
744 | 740 | tgpar.target = target; |
---|
745 | 741 | tgpar.targinfo = t->data; |
---|
746 | 742 | ret = xt_check_target(&tgpar, t->target_size, |
---|
747 | | - e->ethproto, e->invflags & EBT_IPROTO); |
---|
| 743 | + ntohs(e->ethproto), e->invflags & EBT_IPROTO); |
---|
748 | 744 | if (ret < 0) { |
---|
749 | 745 | module_put(target->me); |
---|
750 | 746 | goto cleanup_watchers; |
---|
.. | .. |
---|
1003 | 999 | goto free_iterate; |
---|
1004 | 1000 | } |
---|
1005 | 1001 | |
---|
1006 | | - /* the table doesn't like it */ |
---|
1007 | | - if (t->check && (ret = t->check(newinfo, repl->valid_hooks))) |
---|
| 1002 | + if (repl->valid_hooks != t->valid_hooks) { |
---|
| 1003 | + ret = -EINVAL; |
---|
1008 | 1004 | goto free_unlock; |
---|
| 1005 | + } |
---|
1009 | 1006 | |
---|
1010 | 1007 | if (repl->num_counters && repl->num_counters != t->private->nentries) { |
---|
1011 | 1008 | ret = -EINVAL; |
---|
.. | .. |
---|
1050 | 1047 | vfree(table); |
---|
1051 | 1048 | vfree(counterstmp); |
---|
1052 | 1049 | |
---|
1053 | | -#ifdef CONFIG_AUDIT |
---|
1054 | | - if (audit_enabled) { |
---|
1055 | | - audit_log(audit_context(), GFP_KERNEL, |
---|
1056 | | - AUDIT_NETFILTER_CFG, |
---|
1057 | | - "table=%s family=%u entries=%u", |
---|
1058 | | - repl->name, AF_BRIDGE, repl->nentries); |
---|
1059 | | - } |
---|
1060 | | -#endif |
---|
1061 | | - return ret; |
---|
| 1050 | + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, |
---|
| 1051 | + AUDIT_XT_OP_REPLACE, GFP_KERNEL); |
---|
| 1052 | + return 0; |
---|
1062 | 1053 | |
---|
1063 | 1054 | free_unlock: |
---|
1064 | 1055 | mutex_unlock(&ebt_mutex); |
---|
.. | .. |
---|
1073 | 1064 | } |
---|
1074 | 1065 | |
---|
1075 | 1066 | /* replace the table */ |
---|
1076 | | -static int do_replace(struct net *net, const void __user *user, |
---|
1077 | | - unsigned int len) |
---|
| 1067 | +static int do_replace(struct net *net, sockptr_t arg, unsigned int len) |
---|
1078 | 1068 | { |
---|
1079 | 1069 | int ret, countersize; |
---|
1080 | 1070 | struct ebt_table_info *newinfo; |
---|
1081 | 1071 | struct ebt_replace tmp; |
---|
1082 | 1072 | |
---|
1083 | | - if (copy_from_user(&tmp, user, sizeof(tmp)) != 0) |
---|
| 1073 | + if (copy_from_sockptr(&tmp, arg, sizeof(tmp)) != 0) |
---|
1084 | 1074 | return -EFAULT; |
---|
1085 | 1075 | |
---|
1086 | 1076 | if (len != sizeof(tmp) + tmp.entries_size) |
---|
.. | .. |
---|
1099 | 1089 | tmp.name[sizeof(tmp.name) - 1] = 0; |
---|
1100 | 1090 | |
---|
1101 | 1091 | countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids; |
---|
1102 | | - newinfo = __vmalloc(sizeof(*newinfo) + countersize, GFP_KERNEL_ACCOUNT, |
---|
1103 | | - PAGE_KERNEL); |
---|
| 1092 | + newinfo = __vmalloc(sizeof(*newinfo) + countersize, GFP_KERNEL_ACCOUNT); |
---|
1104 | 1093 | if (!newinfo) |
---|
1105 | 1094 | return -ENOMEM; |
---|
1106 | 1095 | |
---|
1107 | 1096 | if (countersize) |
---|
1108 | 1097 | memset(newinfo->counters, 0, countersize); |
---|
1109 | 1098 | |
---|
1110 | | - newinfo->entries = __vmalloc(tmp.entries_size, GFP_KERNEL_ACCOUNT, |
---|
1111 | | - PAGE_KERNEL); |
---|
| 1099 | + newinfo->entries = __vmalloc(tmp.entries_size, GFP_KERNEL_ACCOUNT); |
---|
1112 | 1100 | if (!newinfo->entries) { |
---|
1113 | 1101 | ret = -ENOMEM; |
---|
1114 | 1102 | goto free_newinfo; |
---|
.. | .. |
---|
1134 | 1122 | mutex_lock(&ebt_mutex); |
---|
1135 | 1123 | list_del(&table->list); |
---|
1136 | 1124 | mutex_unlock(&ebt_mutex); |
---|
| 1125 | + audit_log_nfcfg(table->name, AF_BRIDGE, table->private->nentries, |
---|
| 1126 | + AUDIT_XT_OP_UNREGISTER, GFP_KERNEL); |
---|
1137 | 1127 | EBT_ENTRY_ITERATE(table->private->entries, table->private->entries_size, |
---|
1138 | 1128 | ebt_cleanup_entry, net, NULL); |
---|
1139 | 1129 | if (table->private->nentries) |
---|
.. | .. |
---|
1197 | 1187 | if (ret != 0) |
---|
1198 | 1188 | goto free_chainstack; |
---|
1199 | 1189 | |
---|
1200 | | - if (table->check && table->check(newinfo, table->valid_hooks)) { |
---|
1201 | | - ret = -EINVAL; |
---|
1202 | | - goto free_chainstack; |
---|
1203 | | - } |
---|
1204 | | - |
---|
1205 | 1190 | table->private = newinfo; |
---|
1206 | 1191 | rwlock_init(&table->lock); |
---|
1207 | 1192 | mutex_lock(&ebt_mutex); |
---|
.. | .. |
---|
1221 | 1206 | mutex_unlock(&ebt_mutex); |
---|
1222 | 1207 | |
---|
1223 | 1208 | WRITE_ONCE(*res, table); |
---|
1224 | | - |
---|
1225 | | - if (!ops) |
---|
1226 | | - return 0; |
---|
1227 | | - |
---|
1228 | 1209 | ret = nf_register_net_hooks(net, ops, hweight32(table->valid_hooks)); |
---|
1229 | 1210 | if (ret) { |
---|
1230 | 1211 | __ebt_unregister_table(net, table); |
---|
1231 | 1212 | *res = NULL; |
---|
1232 | 1213 | } |
---|
1233 | 1214 | |
---|
| 1215 | + audit_log_nfcfg(repl->name, AF_BRIDGE, repl->nentries, |
---|
| 1216 | + AUDIT_XT_OP_REGISTER, GFP_KERNEL); |
---|
1234 | 1217 | return ret; |
---|
1235 | 1218 | free_unlock: |
---|
1236 | 1219 | mutex_unlock(&ebt_mutex); |
---|
.. | .. |
---|
1245 | 1228 | return ret; |
---|
1246 | 1229 | } |
---|
1247 | 1230 | |
---|
1248 | | -void ebt_unregister_table(struct net *net, struct ebt_table *table, |
---|
1249 | | - const struct nf_hook_ops *ops) |
---|
| 1231 | +static struct ebt_table *__ebt_find_table(struct net *net, const char *name) |
---|
1250 | 1232 | { |
---|
1251 | | - if (ops) |
---|
| 1233 | + struct ebt_table *t; |
---|
| 1234 | + |
---|
| 1235 | + mutex_lock(&ebt_mutex); |
---|
| 1236 | + |
---|
| 1237 | + list_for_each_entry(t, &net->xt.tables[NFPROTO_BRIDGE], list) { |
---|
| 1238 | + if (strcmp(t->name, name) == 0) { |
---|
| 1239 | + mutex_unlock(&ebt_mutex); |
---|
| 1240 | + return t; |
---|
| 1241 | + } |
---|
| 1242 | + } |
---|
| 1243 | + |
---|
| 1244 | + mutex_unlock(&ebt_mutex); |
---|
| 1245 | + return NULL; |
---|
| 1246 | +} |
---|
| 1247 | + |
---|
| 1248 | +void ebt_unregister_table_pre_exit(struct net *net, const char *name, const struct nf_hook_ops *ops) |
---|
| 1249 | +{ |
---|
| 1250 | + struct ebt_table *table = __ebt_find_table(net, name); |
---|
| 1251 | + |
---|
| 1252 | + if (table) |
---|
1252 | 1253 | nf_unregister_net_hooks(net, ops, hweight32(table->valid_hooks)); |
---|
| 1254 | +} |
---|
| 1255 | +EXPORT_SYMBOL(ebt_unregister_table_pre_exit); |
---|
| 1256 | + |
---|
| 1257 | +void ebt_unregister_table(struct net *net, struct ebt_table *table) |
---|
| 1258 | +{ |
---|
1253 | 1259 | __ebt_unregister_table(net, table); |
---|
1254 | 1260 | } |
---|
1255 | 1261 | |
---|
1256 | 1262 | /* userspace just supplied us with counters */ |
---|
1257 | 1263 | static int do_update_counters(struct net *net, const char *name, |
---|
1258 | | - struct ebt_counter __user *counters, |
---|
1259 | | - unsigned int num_counters, |
---|
1260 | | - const void __user *user, unsigned int len) |
---|
| 1264 | + struct ebt_counter __user *counters, |
---|
| 1265 | + unsigned int num_counters, unsigned int len) |
---|
1261 | 1266 | { |
---|
1262 | 1267 | int i, ret; |
---|
1263 | 1268 | struct ebt_counter *tmp; |
---|
.. | .. |
---|
1300 | 1305 | return ret; |
---|
1301 | 1306 | } |
---|
1302 | 1307 | |
---|
1303 | | -static int update_counters(struct net *net, const void __user *user, |
---|
1304 | | - unsigned int len) |
---|
| 1308 | +static int update_counters(struct net *net, sockptr_t arg, unsigned int len) |
---|
1305 | 1309 | { |
---|
1306 | 1310 | struct ebt_replace hlp; |
---|
1307 | 1311 | |
---|
1308 | | - if (copy_from_user(&hlp, user, sizeof(hlp))) |
---|
| 1312 | + if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) |
---|
1309 | 1313 | return -EFAULT; |
---|
1310 | 1314 | |
---|
1311 | 1315 | if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) |
---|
1312 | 1316 | return -EINVAL; |
---|
1313 | 1317 | |
---|
1314 | 1318 | return do_update_counters(net, hlp.name, hlp.counters, |
---|
1315 | | - hlp.num_counters, user, len); |
---|
| 1319 | + hlp.num_counters, len); |
---|
1316 | 1320 | } |
---|
1317 | 1321 | |
---|
1318 | 1322 | static inline int ebt_obj_to_user(char __user *um, const char *_name, |
---|
.. | .. |
---|
1464 | 1468 | ebt_entry_to_user, entries, tmp.entries); |
---|
1465 | 1469 | } |
---|
1466 | 1470 | |
---|
1467 | | -static int do_ebt_set_ctl(struct sock *sk, |
---|
1468 | | - int cmd, void __user *user, unsigned int len) |
---|
1469 | | -{ |
---|
1470 | | - int ret; |
---|
1471 | | - struct net *net = sock_net(sk); |
---|
1472 | | - |
---|
1473 | | - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
---|
1474 | | - return -EPERM; |
---|
1475 | | - |
---|
1476 | | - switch (cmd) { |
---|
1477 | | - case EBT_SO_SET_ENTRIES: |
---|
1478 | | - ret = do_replace(net, user, len); |
---|
1479 | | - break; |
---|
1480 | | - case EBT_SO_SET_COUNTERS: |
---|
1481 | | - ret = update_counters(net, user, len); |
---|
1482 | | - break; |
---|
1483 | | - default: |
---|
1484 | | - ret = -EINVAL; |
---|
1485 | | - } |
---|
1486 | | - return ret; |
---|
1487 | | -} |
---|
1488 | | - |
---|
1489 | | -static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) |
---|
1490 | | -{ |
---|
1491 | | - int ret; |
---|
1492 | | - struct ebt_replace tmp; |
---|
1493 | | - struct ebt_table *t; |
---|
1494 | | - struct net *net = sock_net(sk); |
---|
1495 | | - |
---|
1496 | | - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
---|
1497 | | - return -EPERM; |
---|
1498 | | - |
---|
1499 | | - if (copy_from_user(&tmp, user, sizeof(tmp))) |
---|
1500 | | - return -EFAULT; |
---|
1501 | | - |
---|
1502 | | - tmp.name[sizeof(tmp.name) - 1] = '\0'; |
---|
1503 | | - |
---|
1504 | | - t = find_table_lock(net, tmp.name, &ret, &ebt_mutex); |
---|
1505 | | - if (!t) |
---|
1506 | | - return ret; |
---|
1507 | | - |
---|
1508 | | - switch (cmd) { |
---|
1509 | | - case EBT_SO_GET_INFO: |
---|
1510 | | - case EBT_SO_GET_INIT_INFO: |
---|
1511 | | - if (*len != sizeof(struct ebt_replace)) { |
---|
1512 | | - ret = -EINVAL; |
---|
1513 | | - mutex_unlock(&ebt_mutex); |
---|
1514 | | - break; |
---|
1515 | | - } |
---|
1516 | | - if (cmd == EBT_SO_GET_INFO) { |
---|
1517 | | - tmp.nentries = t->private->nentries; |
---|
1518 | | - tmp.entries_size = t->private->entries_size; |
---|
1519 | | - tmp.valid_hooks = t->valid_hooks; |
---|
1520 | | - } else { |
---|
1521 | | - tmp.nentries = t->table->nentries; |
---|
1522 | | - tmp.entries_size = t->table->entries_size; |
---|
1523 | | - tmp.valid_hooks = t->table->valid_hooks; |
---|
1524 | | - } |
---|
1525 | | - mutex_unlock(&ebt_mutex); |
---|
1526 | | - if (copy_to_user(user, &tmp, *len) != 0) { |
---|
1527 | | - ret = -EFAULT; |
---|
1528 | | - break; |
---|
1529 | | - } |
---|
1530 | | - ret = 0; |
---|
1531 | | - break; |
---|
1532 | | - |
---|
1533 | | - case EBT_SO_GET_ENTRIES: |
---|
1534 | | - case EBT_SO_GET_INIT_ENTRIES: |
---|
1535 | | - ret = copy_everything_to_user(t, user, len, cmd); |
---|
1536 | | - mutex_unlock(&ebt_mutex); |
---|
1537 | | - break; |
---|
1538 | | - |
---|
1539 | | - default: |
---|
1540 | | - mutex_unlock(&ebt_mutex); |
---|
1541 | | - ret = -EINVAL; |
---|
1542 | | - } |
---|
1543 | | - |
---|
1544 | | - return ret; |
---|
1545 | | -} |
---|
1546 | | - |
---|
1547 | 1471 | #ifdef CONFIG_COMPAT |
---|
1548 | 1472 | /* 32 bit-userspace compatibility definitions. */ |
---|
1549 | 1473 | struct compat_ebt_replace { |
---|
.. | .. |
---|
1570 | 1494 | compat_uptr_t ptr; |
---|
1571 | 1495 | } u; |
---|
1572 | 1496 | compat_uint_t match_size; |
---|
1573 | | - compat_uint_t data[0] __attribute__ ((aligned (__alignof__(struct compat_ebt_replace)))); |
---|
| 1497 | + compat_uint_t data[] __aligned(__alignof__(struct compat_ebt_replace)); |
---|
1574 | 1498 | }; |
---|
1575 | 1499 | |
---|
1576 | 1500 | /* account for possible padding between match_size and ->data */ |
---|
.. | .. |
---|
1948 | 1872 | size_kern = match_size; |
---|
1949 | 1873 | module_put(match->me); |
---|
1950 | 1874 | break; |
---|
1951 | | - case EBT_COMPAT_WATCHER: /* fallthrough */ |
---|
| 1875 | + case EBT_COMPAT_WATCHER: |
---|
1952 | 1876 | case EBT_COMPAT_TARGET: |
---|
1953 | 1877 | wt = xt_request_find_target(NFPROTO_BRIDGE, name, |
---|
1954 | 1878 | mwt->u.revision); |
---|
.. | .. |
---|
2077 | 2001 | return ret; |
---|
2078 | 2002 | |
---|
2079 | 2003 | offsets[0] = sizeof(struct ebt_entry); /* matches come first */ |
---|
2080 | | - memcpy(&offsets[1], &entry->watchers_offset, |
---|
2081 | | - sizeof(offsets) - sizeof(offsets[0])); |
---|
| 2004 | + memcpy(&offsets[1], &entry->offsets, sizeof(entry->offsets)); |
---|
2082 | 2005 | |
---|
2083 | 2006 | if (state->buf_kern_start) { |
---|
2084 | 2007 | buf_start = state->buf_kern_start + state->buf_kern_offset; |
---|
.. | .. |
---|
2173 | 2096 | |
---|
2174 | 2097 | |
---|
2175 | 2098 | static int compat_copy_ebt_replace_from_user(struct ebt_replace *repl, |
---|
2176 | | - void __user *user, unsigned int len) |
---|
| 2099 | + sockptr_t arg, unsigned int len) |
---|
2177 | 2100 | { |
---|
2178 | 2101 | struct compat_ebt_replace tmp; |
---|
2179 | 2102 | int i; |
---|
.. | .. |
---|
2181 | 2104 | if (len < sizeof(tmp)) |
---|
2182 | 2105 | return -EINVAL; |
---|
2183 | 2106 | |
---|
2184 | | - if (copy_from_user(&tmp, user, sizeof(tmp))) |
---|
| 2107 | + if (copy_from_sockptr(&tmp, arg, sizeof(tmp))) |
---|
2185 | 2108 | return -EFAULT; |
---|
2186 | 2109 | |
---|
2187 | 2110 | if (len != sizeof(tmp) + tmp.entries_size) |
---|
.. | .. |
---|
2208 | 2131 | return 0; |
---|
2209 | 2132 | } |
---|
2210 | 2133 | |
---|
2211 | | -static int compat_do_replace(struct net *net, void __user *user, |
---|
2212 | | - unsigned int len) |
---|
| 2134 | +static int compat_do_replace(struct net *net, sockptr_t arg, unsigned int len) |
---|
2213 | 2135 | { |
---|
2214 | 2136 | int ret, i, countersize, size64; |
---|
2215 | 2137 | struct ebt_table_info *newinfo; |
---|
.. | .. |
---|
2217 | 2139 | struct ebt_entries_buf_state state; |
---|
2218 | 2140 | void *entries_tmp; |
---|
2219 | 2141 | |
---|
2220 | | - ret = compat_copy_ebt_replace_from_user(&tmp, user, len); |
---|
| 2142 | + ret = compat_copy_ebt_replace_from_user(&tmp, arg, len); |
---|
2221 | 2143 | if (ret) { |
---|
2222 | 2144 | /* try real handler in case userland supplied needed padding */ |
---|
2223 | | - if (ret == -EINVAL && do_replace(net, user, len) == 0) |
---|
| 2145 | + if (ret == -EINVAL && do_replace(net, arg, len) == 0) |
---|
2224 | 2146 | ret = 0; |
---|
2225 | 2147 | return ret; |
---|
2226 | 2148 | } |
---|
.. | .. |
---|
2311 | 2233 | goto free_entries; |
---|
2312 | 2234 | } |
---|
2313 | 2235 | |
---|
2314 | | -static int compat_update_counters(struct net *net, void __user *user, |
---|
| 2236 | +static int compat_update_counters(struct net *net, sockptr_t arg, |
---|
2315 | 2237 | unsigned int len) |
---|
2316 | 2238 | { |
---|
2317 | 2239 | struct compat_ebt_replace hlp; |
---|
2318 | 2240 | |
---|
2319 | | - if (copy_from_user(&hlp, user, sizeof(hlp))) |
---|
| 2241 | + if (copy_from_sockptr(&hlp, arg, sizeof(hlp))) |
---|
2320 | 2242 | return -EFAULT; |
---|
2321 | 2243 | |
---|
2322 | 2244 | /* try real handler in case userland supplied needed padding */ |
---|
2323 | 2245 | if (len != sizeof(hlp) + hlp.num_counters * sizeof(struct ebt_counter)) |
---|
2324 | | - return update_counters(net, user, len); |
---|
| 2246 | + return update_counters(net, arg, len); |
---|
2325 | 2247 | |
---|
2326 | 2248 | return do_update_counters(net, hlp.name, compat_ptr(hlp.counters), |
---|
2327 | | - hlp.num_counters, user, len); |
---|
2328 | | -} |
---|
2329 | | - |
---|
2330 | | -static int compat_do_ebt_set_ctl(struct sock *sk, |
---|
2331 | | - int cmd, void __user *user, unsigned int len) |
---|
2332 | | -{ |
---|
2333 | | - int ret; |
---|
2334 | | - struct net *net = sock_net(sk); |
---|
2335 | | - |
---|
2336 | | - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
---|
2337 | | - return -EPERM; |
---|
2338 | | - |
---|
2339 | | - switch (cmd) { |
---|
2340 | | - case EBT_SO_SET_ENTRIES: |
---|
2341 | | - ret = compat_do_replace(net, user, len); |
---|
2342 | | - break; |
---|
2343 | | - case EBT_SO_SET_COUNTERS: |
---|
2344 | | - ret = compat_update_counters(net, user, len); |
---|
2345 | | - break; |
---|
2346 | | - default: |
---|
2347 | | - ret = -EINVAL; |
---|
2348 | | - } |
---|
2349 | | - return ret; |
---|
| 2249 | + hlp.num_counters, len); |
---|
2350 | 2250 | } |
---|
2351 | 2251 | |
---|
2352 | 2252 | static int compat_do_ebt_get_ctl(struct sock *sk, int cmd, |
---|
.. | .. |
---|
2357 | 2257 | struct ebt_table *t; |
---|
2358 | 2258 | struct net *net = sock_net(sk); |
---|
2359 | 2259 | |
---|
2360 | | - if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
---|
2361 | | - return -EPERM; |
---|
2362 | | - |
---|
2363 | | - /* try real handler in case userland supplied needed padding */ |
---|
2364 | | - if ((cmd == EBT_SO_GET_INFO || |
---|
2365 | | - cmd == EBT_SO_GET_INIT_INFO) && *len != sizeof(tmp)) |
---|
2366 | | - return do_ebt_get_ctl(sk, cmd, user, len); |
---|
| 2260 | + if ((cmd == EBT_SO_GET_INFO || cmd == EBT_SO_GET_INIT_INFO) && |
---|
| 2261 | + *len != sizeof(struct compat_ebt_replace)) |
---|
| 2262 | + return -EINVAL; |
---|
2367 | 2263 | |
---|
2368 | 2264 | if (copy_from_user(&tmp, user, sizeof(tmp))) |
---|
2369 | 2265 | return -EFAULT; |
---|
.. | .. |
---|
2426 | 2322 | } |
---|
2427 | 2323 | #endif |
---|
2428 | 2324 | |
---|
| 2325 | +static int do_ebt_get_ctl(struct sock *sk, int cmd, void __user *user, int *len) |
---|
| 2326 | +{ |
---|
| 2327 | + struct net *net = sock_net(sk); |
---|
| 2328 | + struct ebt_replace tmp; |
---|
| 2329 | + struct ebt_table *t; |
---|
| 2330 | + int ret; |
---|
| 2331 | + |
---|
| 2332 | + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
---|
| 2333 | + return -EPERM; |
---|
| 2334 | + |
---|
| 2335 | +#ifdef CONFIG_COMPAT |
---|
| 2336 | + /* try real handler in case userland supplied needed padding */ |
---|
| 2337 | + if (in_compat_syscall() && |
---|
| 2338 | + ((cmd != EBT_SO_GET_INFO && cmd != EBT_SO_GET_INIT_INFO) || |
---|
| 2339 | + *len != sizeof(tmp))) |
---|
| 2340 | + return compat_do_ebt_get_ctl(sk, cmd, user, len); |
---|
| 2341 | +#endif |
---|
| 2342 | + |
---|
| 2343 | + if (copy_from_user(&tmp, user, sizeof(tmp))) |
---|
| 2344 | + return -EFAULT; |
---|
| 2345 | + |
---|
| 2346 | + tmp.name[sizeof(tmp.name) - 1] = '\0'; |
---|
| 2347 | + |
---|
| 2348 | + t = find_table_lock(net, tmp.name, &ret, &ebt_mutex); |
---|
| 2349 | + if (!t) |
---|
| 2350 | + return ret; |
---|
| 2351 | + |
---|
| 2352 | + switch (cmd) { |
---|
| 2353 | + case EBT_SO_GET_INFO: |
---|
| 2354 | + case EBT_SO_GET_INIT_INFO: |
---|
| 2355 | + if (*len != sizeof(struct ebt_replace)) { |
---|
| 2356 | + ret = -EINVAL; |
---|
| 2357 | + mutex_unlock(&ebt_mutex); |
---|
| 2358 | + break; |
---|
| 2359 | + } |
---|
| 2360 | + if (cmd == EBT_SO_GET_INFO) { |
---|
| 2361 | + tmp.nentries = t->private->nentries; |
---|
| 2362 | + tmp.entries_size = t->private->entries_size; |
---|
| 2363 | + tmp.valid_hooks = t->valid_hooks; |
---|
| 2364 | + } else { |
---|
| 2365 | + tmp.nentries = t->table->nentries; |
---|
| 2366 | + tmp.entries_size = t->table->entries_size; |
---|
| 2367 | + tmp.valid_hooks = t->table->valid_hooks; |
---|
| 2368 | + } |
---|
| 2369 | + mutex_unlock(&ebt_mutex); |
---|
| 2370 | + if (copy_to_user(user, &tmp, *len) != 0) { |
---|
| 2371 | + ret = -EFAULT; |
---|
| 2372 | + break; |
---|
| 2373 | + } |
---|
| 2374 | + ret = 0; |
---|
| 2375 | + break; |
---|
| 2376 | + |
---|
| 2377 | + case EBT_SO_GET_ENTRIES: |
---|
| 2378 | + case EBT_SO_GET_INIT_ENTRIES: |
---|
| 2379 | + ret = copy_everything_to_user(t, user, len, cmd); |
---|
| 2380 | + mutex_unlock(&ebt_mutex); |
---|
| 2381 | + break; |
---|
| 2382 | + |
---|
| 2383 | + default: |
---|
| 2384 | + mutex_unlock(&ebt_mutex); |
---|
| 2385 | + ret = -EINVAL; |
---|
| 2386 | + } |
---|
| 2387 | + |
---|
| 2388 | + return ret; |
---|
| 2389 | +} |
---|
| 2390 | + |
---|
| 2391 | +static int do_ebt_set_ctl(struct sock *sk, int cmd, sockptr_t arg, |
---|
| 2392 | + unsigned int len) |
---|
| 2393 | +{ |
---|
| 2394 | + struct net *net = sock_net(sk); |
---|
| 2395 | + int ret; |
---|
| 2396 | + |
---|
| 2397 | + if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) |
---|
| 2398 | + return -EPERM; |
---|
| 2399 | + |
---|
| 2400 | + switch (cmd) { |
---|
| 2401 | + case EBT_SO_SET_ENTRIES: |
---|
| 2402 | +#ifdef CONFIG_COMPAT |
---|
| 2403 | + if (in_compat_syscall()) |
---|
| 2404 | + ret = compat_do_replace(net, arg, len); |
---|
| 2405 | + else |
---|
| 2406 | +#endif |
---|
| 2407 | + ret = do_replace(net, arg, len); |
---|
| 2408 | + break; |
---|
| 2409 | + case EBT_SO_SET_COUNTERS: |
---|
| 2410 | +#ifdef CONFIG_COMPAT |
---|
| 2411 | + if (in_compat_syscall()) |
---|
| 2412 | + ret = compat_update_counters(net, arg, len); |
---|
| 2413 | + else |
---|
| 2414 | +#endif |
---|
| 2415 | + ret = update_counters(net, arg, len); |
---|
| 2416 | + break; |
---|
| 2417 | + default: |
---|
| 2418 | + ret = -EINVAL; |
---|
| 2419 | + } |
---|
| 2420 | + return ret; |
---|
| 2421 | +} |
---|
| 2422 | + |
---|
2429 | 2423 | static struct nf_sockopt_ops ebt_sockopts = { |
---|
2430 | 2424 | .pf = PF_INET, |
---|
2431 | 2425 | .set_optmin = EBT_BASE_CTL, |
---|
2432 | 2426 | .set_optmax = EBT_SO_SET_MAX + 1, |
---|
2433 | 2427 | .set = do_ebt_set_ctl, |
---|
2434 | | -#ifdef CONFIG_COMPAT |
---|
2435 | | - .compat_set = compat_do_ebt_set_ctl, |
---|
2436 | | -#endif |
---|
2437 | 2428 | .get_optmin = EBT_BASE_CTL, |
---|
2438 | 2429 | .get_optmax = EBT_SO_GET_MAX + 1, |
---|
2439 | 2430 | .get = do_ebt_get_ctl, |
---|
2440 | | -#ifdef CONFIG_COMPAT |
---|
2441 | | - .compat_get = compat_do_ebt_get_ctl, |
---|
2442 | | -#endif |
---|
2443 | 2431 | .owner = THIS_MODULE, |
---|
2444 | 2432 | }; |
---|
2445 | 2433 | |
---|