[PATCH linux 1/3] net/ipv4: split group_range logic to kernel/group_range.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Akihiro Suda <akihiro.suda.cz@xxxxxxxxxxxxx>

The logic can be reused for other sysctls in future.

Signed-off-by: Akihiro Suda <akihiro.suda.cz@xxxxxxxxxxxxx>
---
 include/linux/group_range.h | 24 ++++++++++
 include/net/netns/ipv4.h    |  9 +---
 include/net/ping.h          |  6 ---
 kernel/Makefile             |  2 +-
 kernel/group_range.c        | 91 +++++++++++++++++++++++++++++++++++++
 net/ipv4/ping.c             | 39 ++--------------
 net/ipv4/sysctl_net_ipv4.c  | 56 ++---------------------
 7 files changed, 125 insertions(+), 102 deletions(-)
 create mode 100644 include/linux/group_range.h
 create mode 100644 kernel/group_range.c

diff --git a/include/linux/group_range.h b/include/linux/group_range.h
new file mode 100644
index 000000000000..5bd837eced95
--- /dev/null
+++ b/include/linux/group_range.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_GROUP_RANGE_H
+#define _LINUX_GROUP_RANGE_H
+
+#include <linux/seqlock.h>
+#include <linux/uidgid.h>
+
+/*
+ * gid_t is either uint or ushort.  We want to pass it to
+ * proc_dointvec_minmax(), so it must not be larger than MAX_INT
+ */
+#define GROUP_RANGE_MAX (((gid_t)~0U) >> 1)
+
+struct group_range {
+	seqlock_t       lock;
+	kgid_t          range[2];
+};
+
+typedef struct group_range* (*sysctl_group_range_func_t)(struct ctl_table *);
+int sysctl_group_range(sysctl_group_range_func_t fn, struct ctl_table *table,
+				 int write, void *buffer, size_t *lenp, loff_t *ppos);
+
+bool check_current_group_range(struct group_range *gr);
+#endif /* _LINUX_GROUP_RANGE_H */
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index db762e35aca9..75d745a7c6e1 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -6,11 +6,11 @@
 #ifndef __NETNS_IPV4_H__
 #define __NETNS_IPV4_H__
 
-#include <linux/uidgid.h>
 #include <net/inet_frag.h>
 #include <linux/rcupdate.h>
 #include <linux/seqlock.h>
 #include <linux/siphash.h>
+#include <linux/group_range.h>
 
 struct ctl_table_header;
 struct ipv4_devconf;
@@ -24,11 +24,6 @@ struct local_ports {
 	bool		warned;
 };
 
-struct ping_group_range {
-	seqlock_t	lock;
-	kgid_t		range[2];
-};
-
 struct inet_hashinfo;
 
 struct inet_timewait_death_row {
@@ -204,7 +199,7 @@ struct netns_ipv4 {
 	int sysctl_igmp_max_msf;
 	int sysctl_igmp_qrv;
 
-	struct ping_group_range ping_group_range;
+	struct group_range ping_group_range;
 
 	atomic_t dev_addr_genid;
 
diff --git a/include/net/ping.h b/include/net/ping.h
index 9233ad3de0ad..37b1d7baeb7b 100644
--- a/include/net/ping.h
+++ b/include/net/ping.h
@@ -16,12 +16,6 @@
 #define PING_HTABLE_SIZE 	64
 #define PING_HTABLE_MASK 	(PING_HTABLE_SIZE-1)
 
-/*
- * gid_t is either uint or ushort.  We want to pass it to
- * proc_dointvec_minmax(), so it must not be larger than MAX_INT
- */
-#define GID_T_MAX (((gid_t)~0U) >> 1)
-
 /* Compatibility glue so we can support IPv6 when it's compiled as a module */
 struct pingv6_ops {
 	int (*ipv6_recv_error)(struct sock *sk, struct msghdr *msg, int len,
diff --git a/kernel/Makefile b/kernel/Makefile
index b69c95315480..fb3a812cf92e 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@ obj-y     = fork.o exec_domain.o panic.o \
 	    extable.o params.o \
 	    kthread.o sys_ni.o nsproxy.o \
 	    notifier.o ksysfs.o cred.o reboot.o \
-	    async.o range.o smpboot.o ucount.o regset.o
+	    async.o range.o smpboot.o ucount.o regset.o group_range.o
 
 obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o
 obj-$(CONFIG_MULTIUSER) += groups.o
diff --git a/kernel/group_range.c b/kernel/group_range.c
new file mode 100644
index 000000000000..b5c7d35d680b
--- /dev/null
+++ b/kernel/group_range.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/cred.h>
+#include <linux/group_range.h>
+#include <linux/uidgid.h>
+#include <linux/seqlock.h>
+#include <linux/sysctl.h>
+
+static void get_group_range(struct group_range *gr, kgid_t *low, kgid_t *high)
+{
+	unsigned int seq;
+
+	do {
+		seq = read_seqbegin(&gr->lock);
+
+		*low = gr->range[0];
+		*high = gr->range[1];
+	} while (read_seqretry(&gr->lock, seq));
+}
+
+static void set_group_range(struct group_range *gr, kgid_t low, kgid_t high)
+{
+	write_seqlock(&gr->lock);
+	gr->range[0] = low;
+	gr->range[1] = high;
+	write_sequnlock(&gr->lock);
+}
+
+static int group_range_min[] = { 0, 0 };
+static int group_range_max[] = { GROUP_RANGE_MAX, GROUP_RANGE_MAX };
+
+int sysctl_group_range(sysctl_group_range_func_t fn, struct ctl_table *table,
+				 int write, void *buffer, size_t *lenp, loff_t *ppos)
+{
+	struct group_range *gr = fn(table);
+	struct user_namespace *user_ns = current_user_ns();
+	int ret;
+	gid_t urange[2];
+	kgid_t low, high;
+	struct ctl_table tmp = {
+		.data = &urange,
+		.maxlen = sizeof(urange),
+		.mode = table->mode,
+		.extra1 = &group_range_min,
+		.extra2 = &group_range_max,
+	};
+
+	get_group_range(gr, &low, &high);
+	urange[0] = from_kgid_munged(user_ns, low);
+	urange[1] = from_kgid_munged(user_ns, high);
+	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
+
+	if (write && ret == 0) {
+		low = make_kgid(user_ns, urange[0]);
+		high = make_kgid(user_ns, urange[1]);
+		if (!gid_valid(low) || !gid_valid(high))
+			return -EINVAL;
+		if (urange[1] < urange[0] || gid_lt(high, low)) {
+			low = make_kgid(&init_user_ns, 1);
+			high = make_kgid(&init_user_ns, 0);
+		}
+		set_group_range(gr, low, high);
+	}
+
+	return ret;
+}
+
+bool check_current_group_range(struct group_range *gr)
+{
+	kgid_t group = current_egid();
+	struct group_info *group_info;
+	int i;
+	kgid_t low, high;
+	bool ret = true;
+
+	get_group_range(gr, &low, &high);
+	if (gid_lte(low, group) && gid_lte(group, high))
+		return true;
+
+	group_info = get_current_groups();
+	for (i = 0; i < group_info->ngroups; i++) {
+		kgid_t gid = group_info->gid[i];
+
+		if (gid_lte(low, gid) && gid_lte(gid, high))
+			goto out_release_group;
+	}
+	ret = false;
+out_release_group:
+	put_group_info(group_info);
+	return ret;
+}
diff --git a/net/ipv4/ping.c b/net/ipv4/ping.c
index 5178a3f3cb53..6e23771c5234 100644
--- a/net/ipv4/ping.c
+++ b/net/ipv4/ping.c
@@ -244,50 +244,17 @@ exit:
 	return sk;
 }
 
-static void inet_get_ping_group_range_net(struct net *net, kgid_t *low,
-					  kgid_t *high)
-{
-	kgid_t *data = net->ipv4.ping_group_range.range;
-	unsigned int seq;
-
-	do {
-		seq = read_seqbegin(&net->ipv4.ping_group_range.lock);
-
-		*low = data[0];
-		*high = data[1];
-	} while (read_seqretry(&net->ipv4.ping_group_range.lock, seq));
-}
-
-
 int ping_init_sock(struct sock *sk)
 {
 	struct net *net = sock_net(sk);
-	kgid_t group = current_egid();
-	struct group_info *group_info;
-	int i;
-	kgid_t low, high;
-	int ret = 0;
 
 	if (sk->sk_family == AF_INET6)
 		sk->sk_ipv6only = 1;
 
-	inet_get_ping_group_range_net(net, &low, &high);
-	if (gid_lte(low, group) && gid_lte(group, high))
-		return 0;
-
-	group_info = get_current_groups();
-	for (i = 0; i < group_info->ngroups; i++) {
-		kgid_t gid = group_info->gid[i];
+	if (!check_current_group_range(&net->ipv4.ping_group_range))
+		return -EACCES;
 
-		if (gid_lte(low, gid) && gid_lte(gid, high))
-			goto out_release_group;
-	}
-
-	ret = -EACCES;
-
-out_release_group:
-	put_group_info(group_info);
-	return ret;
+	return 0;
 }
 EXPORT_SYMBOL_GPL(ping_init_sock);
 
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index 40fe70fc2015..ad355ab265db 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -34,8 +34,6 @@ static int ip_ttl_min = 1;
 static int ip_ttl_max = 255;
 static int tcp_syn_retries_min = 1;
 static int tcp_syn_retries_max = MAX_TCP_SYNCNT;
-static int ip_ping_group_range_min[] = { 0, 0 };
-static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX };
 static u32 u32_max_div_HZ = UINT_MAX / HZ;
 static int one_day_secs = 24 * 3600;
 static u32 fib_multipath_hash_fields_all_mask __maybe_unused =
@@ -133,66 +131,20 @@ static int ipv4_privileged_ports(struct ctl_table *table, int write,
 	return ret;
 }
 
-static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high)
+static struct group_range *ipv4_ping_group_range_func(struct ctl_table *table)
 {
-	kgid_t *data = table->data;
 	struct net *net =
 		container_of(table->data, struct net, ipv4.ping_group_range.range);
-	unsigned int seq;
-	do {
-		seq = read_seqbegin(&net->ipv4.ping_group_range.lock);
 
-		*low = data[0];
-		*high = data[1];
-	} while (read_seqretry(&net->ipv4.ping_group_range.lock, seq));
-}
-
-/* Update system visible IP port range */
-static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t high)
-{
-	kgid_t *data = table->data;
-	struct net *net =
-		container_of(table->data, struct net, ipv4.ping_group_range.range);
-	write_seqlock(&net->ipv4.ping_group_range.lock);
-	data[0] = low;
-	data[1] = high;
-	write_sequnlock(&net->ipv4.ping_group_range.lock);
+	return &net->ipv4.ping_group_range;
 }
 
 /* Validate changes from /proc interface. */
 static int ipv4_ping_group_range(struct ctl_table *table, int write,
 				 void *buffer, size_t *lenp, loff_t *ppos)
 {
-	struct user_namespace *user_ns = current_user_ns();
-	int ret;
-	gid_t urange[2];
-	kgid_t low, high;
-	struct ctl_table tmp = {
-		.data = &urange,
-		.maxlen = sizeof(urange),
-		.mode = table->mode,
-		.extra1 = &ip_ping_group_range_min,
-		.extra2 = &ip_ping_group_range_max,
-	};
-
-	inet_get_ping_group_range_table(table, &low, &high);
-	urange[0] = from_kgid_munged(user_ns, low);
-	urange[1] = from_kgid_munged(user_ns, high);
-	ret = proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
-
-	if (write && ret == 0) {
-		low = make_kgid(user_ns, urange[0]);
-		high = make_kgid(user_ns, urange[1]);
-		if (!gid_valid(low) || !gid_valid(high))
-			return -EINVAL;
-		if (urange[1] < urange[0] || gid_lt(high, low)) {
-			low = make_kgid(&init_user_ns, 1);
-			high = make_kgid(&init_user_ns, 0);
-		}
-		set_ping_group_range(table, low, high);
-	}
-
-	return ret;
+	return sysctl_group_range(ipv4_ping_group_range_func, table,
+		write, buffer, lenp, ppos);
 }
 
 static int ipv4_fwd_update_priority(struct ctl_table *table, int write,
-- 
2.38.4






[Index of Archives]     [Cgroups]     [Netdev]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite Forum]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux