aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Westphal <fw@strlen.de>2016-04-01 09:37:59 -0400
committerPablo Neira Ayuso <pablo@netfilter.org>2016-04-13 18:30:41 -0400
commitd7591f0c41ce3e67600a982bab6989ef0f07b3ce (patch)
tree54d520c0e6f35befce9b424d4cc8a518fe31f0ad
parentaded9f3e9fa8db559c5b7661bbb497754270e754 (diff)
netfilter: x_tables: introduce and use xt_copy_counters_from_user
The three variants use same copy&pasted code, condense this into a helper and use that. Make sure info.name is 0-terminated. Signed-off-by: Florian Westphal <fw@strlen.de> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/linux/netfilter/x_tables.h3
-rw-r--r--net/ipv4/netfilter/arp_tables.c48
-rw-r--r--net/ipv4/netfilter/ip_tables.c48
-rw-r--r--net/ipv6/netfilter/ip6_tables.c49
-rw-r--r--net/netfilter/x_tables.c74
5 files changed, 92 insertions, 130 deletions
diff --git a/include/linux/netfilter/x_tables.h b/include/linux/netfilter/x_tables.h
index e2da9b90f1b8..4dd9306c9d56 100644
--- a/include/linux/netfilter/x_tables.h
+++ b/include/linux/netfilter/x_tables.h
@@ -251,6 +251,9 @@ int xt_check_match(struct xt_mtchk_param *, unsigned int size, u_int8_t proto,
251int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto, 251int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto,
252 bool inv_proto); 252 bool inv_proto);
253 253
254void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
255 struct xt_counters_info *info, bool compat);
256
254struct xt_table *xt_register_table(struct net *net, 257struct xt_table *xt_register_table(struct net *net,
255 const struct xt_table *table, 258 const struct xt_table *table,
256 struct xt_table_info *bootstrap, 259 struct xt_table_info *bootstrap,
diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c
index 8cefb7a2606b..60f5161abcb4 100644
--- a/net/ipv4/netfilter/arp_tables.c
+++ b/net/ipv4/netfilter/arp_tables.c
@@ -1123,55 +1123,17 @@ static int do_add_counters(struct net *net, const void __user *user,
1123 unsigned int i; 1123 unsigned int i;
1124 struct xt_counters_info tmp; 1124 struct xt_counters_info tmp;
1125 struct xt_counters *paddc; 1125 struct xt_counters *paddc;
1126 unsigned int num_counters;
1127 const char *name;
1128 int size;
1129 void *ptmp;
1130 struct xt_table *t; 1126 struct xt_table *t;
1131 const struct xt_table_info *private; 1127 const struct xt_table_info *private;
1132 int ret = 0; 1128 int ret = 0;
1133 struct arpt_entry *iter; 1129 struct arpt_entry *iter;
1134 unsigned int addend; 1130 unsigned int addend;
1135#ifdef CONFIG_COMPAT
1136 struct compat_xt_counters_info compat_tmp;
1137
1138 if (compat) {
1139 ptmp = &compat_tmp;
1140 size = sizeof(struct compat_xt_counters_info);
1141 } else
1142#endif
1143 {
1144 ptmp = &tmp;
1145 size = sizeof(struct xt_counters_info);
1146 }
1147 1131
1148 if (copy_from_user(ptmp, user, size) != 0) 1132 paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1149 return -EFAULT; 1133 if (IS_ERR(paddc))
1150 1134 return PTR_ERR(paddc);
1151#ifdef CONFIG_COMPAT
1152 if (compat) {
1153 num_counters = compat_tmp.num_counters;
1154 name = compat_tmp.name;
1155 } else
1156#endif
1157 {
1158 num_counters = tmp.num_counters;
1159 name = tmp.name;
1160 }
1161
1162 if (len != size + num_counters * sizeof(struct xt_counters))
1163 return -EINVAL;
1164
1165 paddc = vmalloc(len - size);
1166 if (!paddc)
1167 return -ENOMEM;
1168
1169 if (copy_from_user(paddc, user + size, len - size) != 0) {
1170 ret = -EFAULT;
1171 goto free;
1172 }
1173 1135
1174 t = xt_find_table_lock(net, NFPROTO_ARP, name); 1136 t = xt_find_table_lock(net, NFPROTO_ARP, tmp.name);
1175 if (IS_ERR_OR_NULL(t)) { 1137 if (IS_ERR_OR_NULL(t)) {
1176 ret = t ? PTR_ERR(t) : -ENOENT; 1138 ret = t ? PTR_ERR(t) : -ENOENT;
1177 goto free; 1139 goto free;
@@ -1179,7 +1141,7 @@ static int do_add_counters(struct net *net, const void __user *user,
1179 1141
1180 local_bh_disable(); 1142 local_bh_disable();
1181 private = t->private; 1143 private = t->private;
1182 if (private->number != num_counters) { 1144 if (private->number != tmp.num_counters) {
1183 ret = -EINVAL; 1145 ret = -EINVAL;
1184 goto unlock_up_free; 1146 goto unlock_up_free;
1185 } 1147 }
diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c
index 9340ce0a7549..735d1ee8c1ab 100644
--- a/net/ipv4/netfilter/ip_tables.c
+++ b/net/ipv4/netfilter/ip_tables.c
@@ -1307,55 +1307,17 @@ do_add_counters(struct net *net, const void __user *user,
1307 unsigned int i; 1307 unsigned int i;
1308 struct xt_counters_info tmp; 1308 struct xt_counters_info tmp;
1309 struct xt_counters *paddc; 1309 struct xt_counters *paddc;
1310 unsigned int num_counters;
1311 const char *name;
1312 int size;
1313 void *ptmp;
1314 struct xt_table *t; 1310 struct xt_table *t;
1315 const struct xt_table_info *private; 1311 const struct xt_table_info *private;
1316 int ret = 0; 1312 int ret = 0;
1317 struct ipt_entry *iter; 1313 struct ipt_entry *iter;
1318 unsigned int addend; 1314 unsigned int addend;
1319#ifdef CONFIG_COMPAT
1320 struct compat_xt_counters_info compat_tmp;
1321
1322 if (compat) {
1323 ptmp = &compat_tmp;
1324 size = sizeof(struct compat_xt_counters_info);
1325 } else
1326#endif
1327 {
1328 ptmp = &tmp;
1329 size = sizeof(struct xt_counters_info);
1330 }
1331 1315
1332 if (copy_from_user(ptmp, user, size) != 0) 1316 paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1333 return -EFAULT; 1317 if (IS_ERR(paddc))
1334 1318 return PTR_ERR(paddc);
1335#ifdef CONFIG_COMPAT
1336 if (compat) {
1337 num_counters = compat_tmp.num_counters;
1338 name = compat_tmp.name;
1339 } else
1340#endif
1341 {
1342 num_counters = tmp.num_counters;
1343 name = tmp.name;
1344 }
1345
1346 if (len != size + num_counters * sizeof(struct xt_counters))
1347 return -EINVAL;
1348
1349 paddc = vmalloc(len - size);
1350 if (!paddc)
1351 return -ENOMEM;
1352
1353 if (copy_from_user(paddc, user + size, len - size) != 0) {
1354 ret = -EFAULT;
1355 goto free;
1356 }
1357 1319
1358 t = xt_find_table_lock(net, AF_INET, name); 1320 t = xt_find_table_lock(net, AF_INET, tmp.name);
1359 if (IS_ERR_OR_NULL(t)) { 1321 if (IS_ERR_OR_NULL(t)) {
1360 ret = t ? PTR_ERR(t) : -ENOENT; 1322 ret = t ? PTR_ERR(t) : -ENOENT;
1361 goto free; 1323 goto free;
@@ -1363,7 +1325,7 @@ do_add_counters(struct net *net, const void __user *user,
1363 1325
1364 local_bh_disable(); 1326 local_bh_disable();
1365 private = t->private; 1327 private = t->private;
1366 if (private->number != num_counters) { 1328 if (private->number != tmp.num_counters) {
1367 ret = -EINVAL; 1329 ret = -EINVAL;
1368 goto unlock_up_free; 1330 goto unlock_up_free;
1369 } 1331 }
diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c
index aa010856a255..73e606c719ef 100644
--- a/net/ipv6/netfilter/ip6_tables.c
+++ b/net/ipv6/netfilter/ip6_tables.c
@@ -1319,55 +1319,16 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
1319 unsigned int i; 1319 unsigned int i;
1320 struct xt_counters_info tmp; 1320 struct xt_counters_info tmp;
1321 struct xt_counters *paddc; 1321 struct xt_counters *paddc;
1322 unsigned int num_counters;
1323 char *name;
1324 int size;
1325 void *ptmp;
1326 struct xt_table *t; 1322 struct xt_table *t;
1327 const struct xt_table_info *private; 1323 const struct xt_table_info *private;
1328 int ret = 0; 1324 int ret = 0;
1329 struct ip6t_entry *iter; 1325 struct ip6t_entry *iter;
1330 unsigned int addend; 1326 unsigned int addend;
1331#ifdef CONFIG_COMPAT
1332 struct compat_xt_counters_info compat_tmp;
1333
1334 if (compat) {
1335 ptmp = &compat_tmp;
1336 size = sizeof(struct compat_xt_counters_info);
1337 } else
1338#endif
1339 {
1340 ptmp = &tmp;
1341 size = sizeof(struct xt_counters_info);
1342 }
1343
1344 if (copy_from_user(ptmp, user, size) != 0)
1345 return -EFAULT;
1346
1347#ifdef CONFIG_COMPAT
1348 if (compat) {
1349 num_counters = compat_tmp.num_counters;
1350 name = compat_tmp.name;
1351 } else
1352#endif
1353 {
1354 num_counters = tmp.num_counters;
1355 name = tmp.name;
1356 }
1357
1358 if (len != size + num_counters * sizeof(struct xt_counters))
1359 return -EINVAL;
1360
1361 paddc = vmalloc(len - size);
1362 if (!paddc)
1363 return -ENOMEM;
1364
1365 if (copy_from_user(paddc, user + size, len - size) != 0) {
1366 ret = -EFAULT;
1367 goto free;
1368 }
1369 1327
1370 t = xt_find_table_lock(net, AF_INET6, name); 1328 paddc = xt_copy_counters_from_user(user, len, &tmp, compat);
1329 if (IS_ERR(paddc))
1330 return PTR_ERR(paddc);
1331 t = xt_find_table_lock(net, AF_INET6, tmp.name);
1371 if (IS_ERR_OR_NULL(t)) { 1332 if (IS_ERR_OR_NULL(t)) {
1372 ret = t ? PTR_ERR(t) : -ENOENT; 1333 ret = t ? PTR_ERR(t) : -ENOENT;
1373 goto free; 1334 goto free;
@@ -1375,7 +1336,7 @@ do_add_counters(struct net *net, const void __user *user, unsigned int len,
1375 1336
1376 local_bh_disable(); 1337 local_bh_disable();
1377 private = t->private; 1338 private = t->private;
1378 if (private->number != num_counters) { 1339 if (private->number != tmp.num_counters) {
1379 ret = -EINVAL; 1340 ret = -EINVAL;
1380 goto unlock_up_free; 1341 goto unlock_up_free;
1381 } 1342 }
diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c
index 9ec23ffa43b4..c69c892231d7 100644
--- a/net/netfilter/x_tables.c
+++ b/net/netfilter/x_tables.c
@@ -752,6 +752,80 @@ int xt_check_target(struct xt_tgchk_param *par,
752} 752}
753EXPORT_SYMBOL_GPL(xt_check_target); 753EXPORT_SYMBOL_GPL(xt_check_target);
754 754
755/**
756 * xt_copy_counters_from_user - copy counters and metadata from userspace
757 *
758 * @user: src pointer to userspace memory
759 * @len: alleged size of userspace memory
760 * @info: where to store the xt_counters_info metadata
761 * @compat: true if we setsockopt call is done by 32bit task on 64bit kernel
762 *
763 * Copies counter meta data from @user and stores it in @info.
764 *
765 * vmallocs memory to hold the counters, then copies the counter data
766 * from @user to the new memory and returns a pointer to it.
767 *
768 * If @compat is true, @info gets converted automatically to the 64bit
769 * representation.
770 *
771 * The metadata associated with the counters is stored in @info.
772 *
773 * Return: returns pointer that caller has to test via IS_ERR().
774 * If IS_ERR is false, caller has to vfree the pointer.
775 */
776void *xt_copy_counters_from_user(const void __user *user, unsigned int len,
777 struct xt_counters_info *info, bool compat)
778{
779 void *mem;
780 u64 size;
781
782#ifdef CONFIG_COMPAT
783 if (compat) {
784 /* structures only differ in size due to alignment */
785 struct compat_xt_counters_info compat_tmp;
786
787 if (len <= sizeof(compat_tmp))
788 return ERR_PTR(-EINVAL);
789
790 len -= sizeof(compat_tmp);
791 if (copy_from_user(&compat_tmp, user, sizeof(compat_tmp)) != 0)
792 return ERR_PTR(-EFAULT);
793
794 strlcpy(info->name, compat_tmp.name, sizeof(info->name));
795 info->num_counters = compat_tmp.num_counters;
796 user += sizeof(compat_tmp);
797 } else
798#endif
799 {
800 if (len <= sizeof(*info))
801 return ERR_PTR(-EINVAL);
802
803 len -= sizeof(*info);
804 if (copy_from_user(info, user, sizeof(*info)) != 0)
805 return ERR_PTR(-EFAULT);
806
807 info->name[sizeof(info->name) - 1] = '\0';
808 user += sizeof(*info);
809 }
810
811 size = sizeof(struct xt_counters);
812 size *= info->num_counters;
813
814 if (size != (u64)len)
815 return ERR_PTR(-EINVAL);
816
817 mem = vmalloc(len);
818 if (!mem)
819 return ERR_PTR(-ENOMEM);
820
821 if (copy_from_user(mem, user, len) == 0)
822 return mem;
823
824 vfree(mem);
825 return ERR_PTR(-EFAULT);
826}
827EXPORT_SYMBOL_GPL(xt_copy_counters_from_user);
828
755#ifdef CONFIG_COMPAT 829#ifdef CONFIG_COMPAT
756int xt_compat_target_offset(const struct xt_target *target) 830int xt_compat_target_offset(const struct xt_target *target)
757{ 831{