diff --git a/fs/namespace.c b/fs/namespace.c index c1bbe86f4920057d0cee12b70512d318740ad08d..398a50ff243848fae1145c695d6decc801d80f7d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -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) diff --git a/ipc/namespace.c b/ipc/namespace.c index cf3386a51de25509f15c85871d447e95feee07fe..7c1fa451b0b0d75ae5265fea8fbe1193cb865223 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c @@ -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 */ diff --git a/kernel/cred.c b/kernel/cred.c index 8888afb846e95f36f5fa4ab27b82685785083bd8..e0573a43c7df62b1b37df319bd74ff377603ccf5 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -372,6 +372,31 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) 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; diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c index 560da0dab230aab631dab7003597815fc4d21c6b..fdbd0cdf271ae4ce6ad016f8b826510865fe3600 100644 --- a/kernel/pid_namespace.c +++ b/kernel/pid_namespace.c @@ -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; /* diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index f5975ccf9348dffd7d12f7674a47cd47495f3bdd..2b042c42fbc415b24beb45b095fc6059612de0f8 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -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(¤t->mm->mm_users) > 1) return -EINVAL; diff --git a/kernel/utsname.c b/kernel/utsname.c index f6336d51d64c40a1759519ae0561c9ba07438ea8..08b197e8c485e4cb79dcee37a1b578f45566ad68 100644 --- a/kernel/utsname.c +++ b/kernel/utsname.c @@ -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); diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 2e9a3132b8dd11ac8a9851e8042d8982af9c20ef..8acce01b6dab90dede80bfb4bed6b4ce4a965108 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -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); diff --git a/security/commoncap.c b/security/commoncap.c index 6dbae4650abe20208ff66eb27015e21b964d0344..7ee08c756d6b4b89e2c47a30775f233df43e4b78 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -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 */