userns: Allow unprivileged use of setns.
Eric W. Biederman [Thu, 26 Jul 2012 08:13:20 +0000 (01:13 -0700)]
- Push the permission check from the core setns syscall into
  the setns install methods where the user namespace of the
  target namespace can be determined, and used in a ns_capable
  call.

Acked-by: Serge Hallyn <serge.hallyn@canonical.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>

ipc/namespace.c
kernel/nsproxy.c
kernel/utsname.c
net/core/net_namespace.c

index f362298..6ed33c0 100644 (file)
@@ -161,8 +161,12 @@ static void ipcns_put(void *ns)
        return put_ipc_ns(ns);
 }
 
-static int ipcns_install(struct nsproxy *nsproxy, void *ns)
+static int ipcns_install(struct nsproxy *nsproxy, void *new)
 {
+       struct ipc_namespace *ns = new;
+       if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
+               return -EPERM;
+
        /* Ditch state from the old ipc namespace */
        exit_sem(current);
        put_ipc_ns(nsproxy->ipc_ns);
index a214e0e..4357a0a 100644 (file)
@@ -242,9 +242,6 @@ SYSCALL_DEFINE2(setns, int, fd, int, nstype)
        struct file *file;
        int err;
 
-       if (!capable(CAP_SYS_ADMIN))
-               return -EPERM;
-
        file = proc_ns_fget(fd);
        if (IS_ERR(file))
                return PTR_ERR(file);
index 679d97a..4a9362f 100644 (file)
@@ -102,8 +102,13 @@ static void utsns_put(void *ns)
        put_uts_ns(ns);
 }
 
-static int utsns_install(struct nsproxy *nsproxy, void *ns)
+static int utsns_install(struct nsproxy *nsproxy, void *new)
 {
+       struct uts_namespace *ns = new;
+
+       if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
+               return -EPERM;
+
        get_uts_ns(ns);
        put_uts_ns(nsproxy->uts_ns);
        nsproxy->uts_ns = ns;
index 6456439..ec2870b 100644 (file)
@@ -630,8 +630,13 @@ static void netns_put(void *ns)
 
 static int netns_install(struct nsproxy *nsproxy, void *ns)
 {
+       struct net *net = ns;
+
+       if (!ns_capable(net->user_ns, CAP_SYS_ADMIN))
+               return -EPERM;
+
        put_net(nsproxy->net_ns);
-       nsproxy->net_ns = get_net(ns);
+       nsproxy->net_ns = get_net(net);
        return 0;
 }