Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 18 Dec 2012 18:55:28 +0000 (10:55 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 18 Dec 2012 18:55:28 +0000 (10:55 -0800)
Pull (again) user namespace infrastructure changes from Eric Biederman:
 "Those bugs, those darn embarrasing bugs just want don't want to get
  fixed.

  Linus I just updated my mirror of your kernel.org tree and it appears
  you successfully pulled everything except the last 4 commits that fix
  those embarrasing bugs.

  When you get a chance can you please repull my branch"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
  userns: Fix typo in description of the limitation of userns_install
  userns: Add a more complete capability subset test to commit_creds
  userns: Require CAP_SYS_ADMIN for most uses of setns.
  Fix cap_capable to only allow owners in the parent user namespace to have caps.

fs/namespace.c
ipc/namespace.c
kernel/cred.c
kernel/pid_namespace.c
kernel/user_namespace.c
kernel/utsname.c
net/core/net_namespace.c
security/commoncap.c

index c1bbe86f4920057d0cee12b70512d318740ad08d..398a50ff243848fae1145c695d6decc801d80f7d 100644 (file)
@@ -2781,7 +2781,8 @@ static int mntns_install(struct nsproxy *nsproxy, void *ns)
        struct path root;
 
        if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
-           !nsown_capable(CAP_SYS_CHROOT))
+           !nsown_capable(CAP_SYS_CHROOT) ||
+           !nsown_capable(CAP_SYS_ADMIN))
                return -EPERM;
 
        if (fs->users != 1)
index cf3386a51de25509f15c85871d447e95feee07fe..7c1fa451b0b0d75ae5265fea8fbe1193cb865223 100644 (file)
@@ -170,7 +170,8 @@ static void ipcns_put(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))
+       if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
+           !nsown_capable(CAP_SYS_ADMIN))
                return -EPERM;
 
        /* Ditch state from the old ipc namespace */
index 8888afb846e95f36f5fa4ab27b82685785083bd8..e0573a43c7df62b1b37df319bd74ff377603ccf5 100644 (file)
@@ -372,6 +372,31 @@ error_put:
        return ret;
 }
 
+static bool cred_cap_issubset(const struct cred *set, const struct cred *subset)
+{
+       const struct user_namespace *set_ns = set->user_ns;
+       const struct user_namespace *subset_ns = subset->user_ns;
+
+       /* If the two credentials are in the same user namespace see if
+        * the capabilities of subset are a subset of set.
+        */
+       if (set_ns == subset_ns)
+               return cap_issubset(subset->cap_permitted, set->cap_permitted);
+
+       /* The credentials are in a different user namespaces
+        * therefore one is a subset of the other only if a set is an
+        * ancestor of subset and set->euid is owner of subset or one
+        * of subsets ancestors.
+        */
+       for (;subset_ns != &init_user_ns; subset_ns = subset_ns->parent) {
+               if ((set_ns == subset_ns->parent)  &&
+                   uid_eq(subset_ns->owner, set->euid))
+                       return true;
+       }
+
+       return false;
+}
+
 /**
  * commit_creds - Install new credentials upon the current task
  * @new: The credentials to be assigned
@@ -410,7 +435,7 @@ int commit_creds(struct cred *new)
            !gid_eq(old->egid, new->egid) ||
            !uid_eq(old->fsuid, new->fsuid) ||
            !gid_eq(old->fsgid, new->fsgid) ||
-           !cap_issubset(new->cap_permitted, old->cap_permitted)) {
+           !cred_cap_issubset(old, new)) {
                if (task->mm)
                        set_dumpable(task->mm, suid_dumpable);
                task->pdeath_signal = 0;
index 560da0dab230aab631dab7003597815fc4d21c6b..fdbd0cdf271ae4ce6ad016f8b826510865fe3600 100644 (file)
@@ -325,7 +325,8 @@ static int pidns_install(struct nsproxy *nsproxy, void *ns)
        struct pid_namespace *active = task_active_pid_ns(current);
        struct pid_namespace *ancestor, *new = ns;
 
-       if (!ns_capable(new->user_ns, CAP_SYS_ADMIN))
+       if (!ns_capable(new->user_ns, CAP_SYS_ADMIN) ||
+           !nsown_capable(CAP_SYS_ADMIN))
                return -EPERM;
 
        /*
index f5975ccf9348dffd7d12f7674a47cd47495f3bdd..2b042c42fbc415b24beb45b095fc6059612de0f8 100644 (file)
@@ -799,7 +799,7 @@ static int userns_install(struct nsproxy *nsproxy, void *ns)
        if (user_ns == current_user_ns())
                return -EINVAL;
 
-       /* Threaded many not enter a different user namespace */
+       /* Threaded processes may not enter a different user namespace */
        if (atomic_read(&current->mm->mm_users) > 1)
                return -EINVAL;
 
index f6336d51d64c40a1759519ae0561c9ba07438ea8..08b197e8c485e4cb79dcee37a1b578f45566ad68 100644 (file)
@@ -113,7 +113,8 @@ static int utsns_install(struct nsproxy *nsproxy, void *new)
 {
        struct uts_namespace *ns = new;
 
-       if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
+       if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
+           !nsown_capable(CAP_SYS_ADMIN))
                return -EPERM;
 
        get_uts_ns(ns);
index 2e9a3132b8dd11ac8a9851e8042d8982af9c20ef..8acce01b6dab90dede80bfb4bed6b4ce4a965108 100644 (file)
@@ -649,7 +649,8 @@ static int netns_install(struct nsproxy *nsproxy, void *ns)
 {
        struct net *net = ns;
 
-       if (!ns_capable(net->user_ns, CAP_SYS_ADMIN))
+       if (!ns_capable(net->user_ns, CAP_SYS_ADMIN) ||
+           !nsown_capable(CAP_SYS_ADMIN))
                return -EPERM;
 
        put_net(nsproxy->net_ns);
index 6dbae4650abe20208ff66eb27015e21b964d0344..7ee08c756d6b4b89e2c47a30775f233df43e4b78 100644 (file)
@@ -76,24 +76,33 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
 int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
                int cap, int audit)
 {
-       for (;;) {
-               /* The owner of the user namespace has all caps. */
-               if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, cred->euid))
-                       return 0;
+       struct user_namespace *ns = targ_ns;
 
+       /* See if cred has the capability in the target user namespace
+        * by examining the target user namespace and all of the target
+        * user namespace's parents.
+        */
+       for (;;) {
                /* Do we have the necessary capabilities? */
-               if (targ_ns == cred->user_ns)
+               if (ns == cred->user_ns)
                        return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
 
                /* Have we tried all of the parent namespaces? */
-               if (targ_ns == &init_user_ns)
+               if (ns == &init_user_ns)
                        return -EPERM;
 
+               /* 
+                * The owner of the user namespace in the parent of the
+                * user namespace has all caps.
+                */
+               if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
+                       return 0;
+
                /*
-                *If you have a capability in a parent user ns, then you have
+                * If you have a capability in a parent user ns, then you have
                 * it over all children user namespaces as well.
                 */
-               targ_ns = targ_ns->parent;
+               ns = ns->parent;
        }
 
        /* We never get here */