userns: Use kgids for sysctl_ping_group_range
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 24 May 2012 16:34:21 +0000 (10:34 -0600)
committerEric W. Biederman <ebiederm@xmission.com>
Wed, 15 Aug 2012 04:49:10 +0000 (21:49 -0700)
- Store sysctl_ping_group_range as a paire of kgid_t values
  instead of a pair of gid_t values.
- Move the kgid conversion work from ping_init_sock into ipv4_ping_group_range
- For invalid cases reset to the default disabled state.

With the kgid_t conversion made part of the original value sanitation
from userspace understand how the code will react becomes clearer
and it becomes possible to set the sysctl ping group range from
something other than the initial user namespace.

Cc: Vasiliy Kulikov <segoon@openwall.com>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
include/net/netns/ipv4.h
init/Kconfig
net/ipv4/ping.c
net/ipv4/sysctl_net_ipv4.c

index 1474dd65c66f558999a70f38c081dea247fbee5a..3516dc0cc61548190ece8b1b9e8407779eeb20a8 100644 (file)
@@ -5,6 +5,7 @@
 #ifndef __NETNS_IPV4_H__
 #define __NETNS_IPV4_H__
 
+#include <linux/uidgid.h>
 #include <net/inet_frag.h>
 
 struct tcpm_hash_bucket;
@@ -62,7 +63,7 @@ struct netns_ipv4 {
        int sysctl_icmp_ratemask;
        int sysctl_icmp_errors_use_inbound_ifaddr;
 
-       unsigned int sysctl_ping_group_range[2];
+       kgid_t sysctl_ping_group_range[2];
        long sysctl_tcp_mem[3];
 
        atomic_t rt_genid;
index 25a6ebb50c6408a51900fcccba3251cecccd4a12..f857f97bcef3ee8e4b83513f1a9283514c0c1833 100644 (file)
@@ -948,7 +948,6 @@ config UIDGID_CONVERTED
        depends on NETFILTER_XT_MATCH_RECENT = n
        depends on NETFILTER_XT_TARGET_LOG = n
        depends on NETFILTER_NETLINK_LOG = n
-       depends on INET = n
        depends on IPV6 = n
        depends on AF_RXRPC = n
        depends on NET_KEY = n
index bee5eeb676f8e019e292306c65a4b5f4fff10274..8f3d05424a3e8f2f318c36bf007fd17724533997 100644 (file)
@@ -185,10 +185,10 @@ exit:
        return sk;
 }
 
-static void inet_get_ping_group_range_net(struct net *net, gid_t *low,
-                                         gid_t *high)
+static void inet_get_ping_group_range_net(struct net *net, kgid_t *low,
+                                         kgid_t *high)
 {
-       gid_t *data = net->ipv4.sysctl_ping_group_range;
+       kgid_t *data = net->ipv4.sysctl_ping_group_range;
        unsigned int seq;
 
        do {
@@ -203,19 +203,13 @@ static void inet_get_ping_group_range_net(struct net *net, gid_t *low,
 static int ping_init_sock(struct sock *sk)
 {
        struct net *net = sock_net(sk);
-       gid_t group = current_egid();
-       gid_t range[2];
+       kgid_t group = current_egid();
        struct group_info *group_info = get_current_groups();
        int i, j, count = group_info->ngroups;
        kgid_t low, high;
 
-       inet_get_ping_group_range_net(net, range, range+1);
-       low = make_kgid(&init_user_ns, range[0]);
-       high = make_kgid(&init_user_ns, range[1]);
-       if (!gid_valid(low) || !gid_valid(high) || gid_lt(high, low))
-               return -EACCES;
-
-       if (range[0] <= group && group <= range[1])
+       inet_get_ping_group_range_net(net, &low, &high);
+       if (gid_lte(low, group) && gid_lte(group, high))
                return 0;
 
        for (i = 0; i < group_info->nblocks; i++) {
index 1b5ce96707a38124c9ae3e11b261995ce19b6668..3e78c79b5586de71059b9e14efdb0fb45146b217 100644 (file)
@@ -76,9 +76,9 @@ static int ipv4_local_port_range(ctl_table *table, int write,
 }
 
 
-static void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low, gid_t *high)
+static void inet_get_ping_group_range_table(struct ctl_table *table, kgid_t *low, kgid_t *high)
 {
-       gid_t *data = table->data;
+       kgid_t *data = table->data;
        unsigned int seq;
        do {
                seq = read_seqbegin(&sysctl_local_ports.lock);
@@ -89,12 +89,12 @@ static void inet_get_ping_group_range_table(struct ctl_table *table, gid_t *low,
 }
 
 /* Update system visible IP port range */
-static void set_ping_group_range(struct ctl_table *table, gid_t range[2])
+static void set_ping_group_range(struct ctl_table *table, kgid_t low, kgid_t high)
 {
-       gid_t *data = table->data;
+       kgid_t *data = table->data;
        write_seqlock(&sysctl_local_ports.lock);
-       data[0] = range[0];
-       data[1] = range[1];
+       data[0] = low;
+       data[1] = high;
        write_sequnlock(&sysctl_local_ports.lock);
 }
 
@@ -103,21 +103,33 @@ static int ipv4_ping_group_range(ctl_table *table, int write,
                                 void __user *buffer,
                                 size_t *lenp, loff_t *ppos)
 {
+       struct user_namespace *user_ns = current_user_ns();
        int ret;
-       gid_t range[2];
+       gid_t urange[2];
+       kgid_t low, high;
        ctl_table tmp = {
-               .data = &range,
-               .maxlen = sizeof(range),
+               .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, range, range + 1);
+       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)
-               set_ping_group_range(table, range);
+       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) ||
+                   (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;
 }
@@ -786,7 +798,7 @@ static struct ctl_table ipv4_net_table[] = {
        {
                .procname       = "ping_group_range",
                .data           = &init_net.ipv4.sysctl_ping_group_range,
-               .maxlen         = sizeof(init_net.ipv4.sysctl_ping_group_range),
+               .maxlen         = sizeof(gid_t)*2,
                .mode           = 0644,
                .proc_handler   = ipv4_ping_group_range,
        },
@@ -830,8 +842,8 @@ static __net_init int ipv4_sysctl_init_net(struct net *net)
         * Sane defaults - nobody may create ping sockets.
         * Boot scripts should set this to distro-specific group.
         */
-       net->ipv4.sysctl_ping_group_range[0] = 1;
-       net->ipv4.sysctl_ping_group_range[1] = 0;
+       net->ipv4.sysctl_ping_group_range[0] = make_kgid(&init_user_ns, 1);
+       net->ipv4.sysctl_ping_group_range[1] = make_kgid(&init_user_ns, 0);
 
        tcp_init_mem(net);