Commit a2faf2fc authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace

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.
parents 4351654e 5155040e
...@@ -2781,7 +2781,8 @@ static int mntns_install(struct nsproxy *nsproxy, void *ns) ...@@ -2781,7 +2781,8 @@ static int mntns_install(struct nsproxy *nsproxy, void *ns)
struct path root; struct path root;
if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) || 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; return -EPERM;
if (fs->users != 1) if (fs->users != 1)
......
...@@ -170,7 +170,8 @@ static void ipcns_put(void *ns) ...@@ -170,7 +170,8 @@ static void ipcns_put(void *ns)
static int ipcns_install(struct nsproxy *nsproxy, void *new) static int ipcns_install(struct nsproxy *nsproxy, void *new)
{ {
struct ipc_namespace *ns = 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; return -EPERM;
/* Ditch state from the old ipc namespace */ /* Ditch state from the old ipc namespace */
......
...@@ -372,6 +372,31 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) ...@@ -372,6 +372,31 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
return ret; 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 * commit_creds - Install new credentials upon the current task
* @new: The credentials to be assigned * @new: The credentials to be assigned
...@@ -410,7 +435,7 @@ int commit_creds(struct cred *new) ...@@ -410,7 +435,7 @@ int commit_creds(struct cred *new)
!gid_eq(old->egid, new->egid) || !gid_eq(old->egid, new->egid) ||
!uid_eq(old->fsuid, new->fsuid) || !uid_eq(old->fsuid, new->fsuid) ||
!gid_eq(old->fsgid, new->fsgid) || !gid_eq(old->fsgid, new->fsgid) ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) { !cred_cap_issubset(old, new)) {
if (task->mm) if (task->mm)
set_dumpable(task->mm, suid_dumpable); set_dumpable(task->mm, suid_dumpable);
task->pdeath_signal = 0; task->pdeath_signal = 0;
......
...@@ -325,7 +325,8 @@ static int pidns_install(struct nsproxy *nsproxy, void *ns) ...@@ -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 *active = task_active_pid_ns(current);
struct pid_namespace *ancestor, *new = ns; 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; return -EPERM;
/* /*
......
...@@ -799,7 +799,7 @@ static int userns_install(struct nsproxy *nsproxy, void *ns) ...@@ -799,7 +799,7 @@ static int userns_install(struct nsproxy *nsproxy, void *ns)
if (user_ns == current_user_ns()) if (user_ns == current_user_ns())
return -EINVAL; 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) if (atomic_read(&current->mm->mm_users) > 1)
return -EINVAL; return -EINVAL;
......
...@@ -113,7 +113,8 @@ static int utsns_install(struct nsproxy *nsproxy, void *new) ...@@ -113,7 +113,8 @@ static int utsns_install(struct nsproxy *nsproxy, void *new)
{ {
struct uts_namespace *ns = 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; return -EPERM;
get_uts_ns(ns); get_uts_ns(ns);
......
...@@ -649,7 +649,8 @@ static int netns_install(struct nsproxy *nsproxy, void *ns) ...@@ -649,7 +649,8 @@ static int netns_install(struct nsproxy *nsproxy, void *ns)
{ {
struct net *net = 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; return -EPERM;
put_net(nsproxy->net_ns); put_net(nsproxy->net_ns);
......
...@@ -76,24 +76,33 @@ int cap_netlink_send(struct sock *sk, struct sk_buff *skb) ...@@ -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_capable(const struct cred *cred, struct user_namespace *targ_ns,
int cap, int audit) int cap, int audit)
{ {
for (;;) { struct user_namespace *ns = targ_ns;
/* The owner of the user namespace has all caps. */
if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, cred->euid))
return 0;
/* 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? */ /* 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; return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/* Have we tried all of the parent namespaces? */ /* Have we tried all of the parent namespaces? */
if (targ_ns == &init_user_ns) if (ns == &init_user_ns)
return -EPERM; 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. * it over all children user namespaces as well.
*/ */
targ_ns = targ_ns->parent; ns = ns->parent;
} }
/* We never get here */ /* We never get here */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment