diff options
author | Florian Westphal <fw@strlen.de> | 2016-04-01 09:37:59 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2016-04-13 18:30:41 -0400 |
commit | d7591f0c41ce3e67600a982bab6989ef0f07b3ce (patch) | |
tree | 54d520c0e6f35befce9b424d4cc8a518fe31f0ad | |
parent | aded9f3e9fa8db559c5b7661bbb497754270e754 (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.h | 3 | ||||
-rw-r--r-- | net/ipv4/netfilter/arp_tables.c | 48 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_tables.c | 48 | ||||
-rw-r--r-- | net/ipv6/netfilter/ip6_tables.c | 49 | ||||
-rw-r--r-- | net/netfilter/x_tables.c | 74 |
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, | |||
251 | int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto, | 251 | int xt_check_target(struct xt_tgchk_param *, unsigned int size, u_int8_t proto, |
252 | bool inv_proto); | 252 | bool inv_proto); |
253 | 253 | ||
254 | void *xt_copy_counters_from_user(const void __user *user, unsigned int len, | ||
255 | struct xt_counters_info *info, bool compat); | ||
256 | |||
254 | struct xt_table *xt_register_table(struct net *net, | 257 | struct 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 | } |
753 | EXPORT_SYMBOL_GPL(xt_check_target); | 753 | EXPORT_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 | */ | ||
776 | void *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 | } | ||
827 | EXPORT_SYMBOL_GPL(xt_copy_counters_from_user); | ||
828 | |||
755 | #ifdef CONFIG_COMPAT | 829 | #ifdef CONFIG_COMPAT |
756 | int xt_compat_target_offset(const struct xt_target *target) | 830 | int xt_compat_target_offset(const struct xt_target *target) |
757 | { | 831 | { |