summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'kernel')
-rw-r--r--kernel/cred.c27
-rw-r--r--kernel/pid_namespace.c3
-rw-r--r--kernel/user_namespace.c2
-rw-r--r--kernel/utsname.c3
4 files changed, 31 insertions, 4 deletions
diff --git a/kernel/cred.c b/kernel/cred.c
index 8888afb846e9..e0573a43c7df 100644
--- a/kernel/cred.c
+++ b/kernel/cred.c
@@ -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;
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index 560da0dab230..fdbd0cdf271a 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 f5975ccf9348..2b042c42fbc4 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(&current->mm->mm_users) > 1)
return -EINVAL;
diff --git a/kernel/utsname.c b/kernel/utsname.c
index f6336d51d64c..08b197e8c485 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);