Commit fc832ad3 authored by Serge E. Hallyn's avatar Serge E. Hallyn Committed by Linus Torvalds

userns: user namespaces: convert all capable checks in kernel/sys.c

This allows setuid/setgid in containers.  It also fixes some corner cases
where kernel logic foregoes capability checks when uids are equivalent.
The latter will need to be done throughout the whole kernel.

Changelog:
	Jan 11: Use nsown_capable() as suggested by Bastian Blank.
	Jan 11: Fix logic errors in uid checks pointed out by Bastian.
	Feb 15: allow prlimit to current (was regression in previous version)
	Feb 23: remove debugging printks, uninline set_one_prio_perm and
		make it bool, and document its return value.
Signed-off-by: default avatarSerge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@free.fr>
Acked-by: default avatarDavid Howells <dhowells@redhat.com>
Cc: James Morris <jmorris@namei.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 3263245d
...@@ -119,17 +119,34 @@ EXPORT_SYMBOL(cad_pid); ...@@ -119,17 +119,34 @@ EXPORT_SYMBOL(cad_pid);
void (*pm_power_off_prepare)(void); void (*pm_power_off_prepare)(void);
/*
* Returns true if current's euid is same as p's uid or euid,
* or has CAP_SYS_NICE to p's user_ns.
*
* Called with rcu_read_lock, creds are safe
*/
static bool set_one_prio_perm(struct task_struct *p)
{
const struct cred *cred = current_cred(), *pcred = __task_cred(p);
if (pcred->user->user_ns == cred->user->user_ns &&
(pcred->uid == cred->euid ||
pcred->euid == cred->euid))
return true;
if (ns_capable(pcred->user->user_ns, CAP_SYS_NICE))
return true;
return false;
}
/* /*
* set the priority of a task * set the priority of a task
* - the caller must hold the RCU read lock * - the caller must hold the RCU read lock
*/ */
static int set_one_prio(struct task_struct *p, int niceval, int error) static int set_one_prio(struct task_struct *p, int niceval, int error)
{ {
const struct cred *cred = current_cred(), *pcred = __task_cred(p);
int no_nice; int no_nice;
if (pcred->uid != cred->euid && if (!set_one_prio_perm(p)) {
pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) {
error = -EPERM; error = -EPERM;
goto out; goto out;
} }
...@@ -506,7 +523,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid) ...@@ -506,7 +523,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)
if (rgid != (gid_t) -1) { if (rgid != (gid_t) -1) {
if (old->gid == rgid || if (old->gid == rgid ||
old->egid == rgid || old->egid == rgid ||
capable(CAP_SETGID)) nsown_capable(CAP_SETGID))
new->gid = rgid; new->gid = rgid;
else else
goto error; goto error;
...@@ -515,7 +532,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid) ...@@ -515,7 +532,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)
if (old->gid == egid || if (old->gid == egid ||
old->egid == egid || old->egid == egid ||
old->sgid == egid || old->sgid == egid ||
capable(CAP_SETGID)) nsown_capable(CAP_SETGID))
new->egid = egid; new->egid = egid;
else else
goto error; goto error;
...@@ -550,7 +567,7 @@ SYSCALL_DEFINE1(setgid, gid_t, gid) ...@@ -550,7 +567,7 @@ SYSCALL_DEFINE1(setgid, gid_t, gid)
old = current_cred(); old = current_cred();
retval = -EPERM; retval = -EPERM;
if (capable(CAP_SETGID)) if (nsown_capable(CAP_SETGID))
new->gid = new->egid = new->sgid = new->fsgid = gid; new->gid = new->egid = new->sgid = new->fsgid = gid;
else if (gid == old->gid || gid == old->sgid) else if (gid == old->gid || gid == old->sgid)
new->egid = new->fsgid = gid; new->egid = new->fsgid = gid;
...@@ -617,7 +634,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) ...@@ -617,7 +634,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
new->uid = ruid; new->uid = ruid;
if (old->uid != ruid && if (old->uid != ruid &&
old->euid != ruid && old->euid != ruid &&
!capable(CAP_SETUID)) !nsown_capable(CAP_SETUID))
goto error; goto error;
} }
...@@ -626,7 +643,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid) ...@@ -626,7 +643,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
if (old->uid != euid && if (old->uid != euid &&
old->euid != euid && old->euid != euid &&
old->suid != euid && old->suid != euid &&
!capable(CAP_SETUID)) !nsown_capable(CAP_SETUID))
goto error; goto error;
} }
...@@ -674,7 +691,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid) ...@@ -674,7 +691,7 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
old = current_cred(); old = current_cred();
retval = -EPERM; retval = -EPERM;
if (capable(CAP_SETUID)) { if (nsown_capable(CAP_SETUID)) {
new->suid = new->uid = uid; new->suid = new->uid = uid;
if (uid != old->uid) { if (uid != old->uid) {
retval = set_user(new); retval = set_user(new);
...@@ -716,7 +733,7 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid) ...@@ -716,7 +733,7 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
old = current_cred(); old = current_cred();
retval = -EPERM; retval = -EPERM;
if (!capable(CAP_SETUID)) { if (!nsown_capable(CAP_SETUID)) {
if (ruid != (uid_t) -1 && ruid != old->uid && if (ruid != (uid_t) -1 && ruid != old->uid &&
ruid != old->euid && ruid != old->suid) ruid != old->euid && ruid != old->suid)
goto error; goto error;
...@@ -780,7 +797,7 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid) ...@@ -780,7 +797,7 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)
old = current_cred(); old = current_cred();
retval = -EPERM; retval = -EPERM;
if (!capable(CAP_SETGID)) { if (!nsown_capable(CAP_SETGID)) {
if (rgid != (gid_t) -1 && rgid != old->gid && if (rgid != (gid_t) -1 && rgid != old->gid &&
rgid != old->egid && rgid != old->sgid) rgid != old->egid && rgid != old->sgid)
goto error; goto error;
...@@ -840,7 +857,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid) ...@@ -840,7 +857,7 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
if (uid == old->uid || uid == old->euid || if (uid == old->uid || uid == old->euid ||
uid == old->suid || uid == old->fsuid || uid == old->suid || uid == old->fsuid ||
capable(CAP_SETUID)) { nsown_capable(CAP_SETUID)) {
if (uid != old_fsuid) { if (uid != old_fsuid) {
new->fsuid = uid; new->fsuid = uid;
if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0) if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
...@@ -873,7 +890,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid) ...@@ -873,7 +890,7 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
if (gid == old->gid || gid == old->egid || if (gid == old->gid || gid == old->egid ||
gid == old->sgid || gid == old->fsgid || gid == old->sgid || gid == old->fsgid ||
capable(CAP_SETGID)) { nsown_capable(CAP_SETGID)) {
if (gid != old_fsgid) { if (gid != old_fsgid) {
new->fsgid = gid; new->fsgid = gid;
goto change_okay; goto change_okay;
...@@ -1183,6 +1200,7 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len) ...@@ -1183,6 +1200,7 @@ SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN)) if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN) if (len < 0 || len > __NEW_UTS_LEN)
return -EINVAL; return -EINVAL;
down_write(&uts_sem); down_write(&uts_sem);
...@@ -1230,7 +1248,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len) ...@@ -1230,7 +1248,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
int errno; int errno;
char tmp[__NEW_UTS_LEN]; char tmp[__NEW_UTS_LEN];
if (!capable(CAP_SYS_ADMIN)) if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
if (len < 0 || len > __NEW_UTS_LEN) if (len < 0 || len > __NEW_UTS_LEN)
return -EINVAL; return -EINVAL;
...@@ -1345,6 +1363,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource, ...@@ -1345,6 +1363,8 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
rlim = tsk->signal->rlim + resource; rlim = tsk->signal->rlim + resource;
task_lock(tsk->group_leader); task_lock(tsk->group_leader);
if (new_rlim) { if (new_rlim) {
/* Keep the capable check against init_user_ns until
cgroups can contain all limits */
if (new_rlim->rlim_max > rlim->rlim_max && if (new_rlim->rlim_max > rlim->rlim_max &&
!capable(CAP_SYS_RESOURCE)) !capable(CAP_SYS_RESOURCE))
retval = -EPERM; retval = -EPERM;
...@@ -1388,19 +1408,22 @@ static int check_prlimit_permission(struct task_struct *task) ...@@ -1388,19 +1408,22 @@ static int check_prlimit_permission(struct task_struct *task)
{ {
const struct cred *cred = current_cred(), *tcred; const struct cred *cred = current_cred(), *tcred;
tcred = __task_cred(task); if (current == task)
if (current != task && return 0;
(cred->uid != tcred->euid ||
cred->uid != tcred->suid ||
cred->uid != tcred->uid ||
cred->gid != tcred->egid ||
cred->gid != tcred->sgid ||
cred->gid != tcred->gid) &&
!capable(CAP_SYS_RESOURCE)) {
return -EPERM;
}
return 0; tcred = __task_cred(task);
if (cred->user->user_ns == tcred->user->user_ns &&
(cred->uid == tcred->euid &&
cred->uid == tcred->suid &&
cred->uid == tcred->uid &&
cred->gid == tcred->egid &&
cred->gid == tcred->sgid &&
cred->gid == tcred->gid))
return 0;
if (ns_capable(tcred->user->user_ns, CAP_SYS_RESOURCE))
return 0;
return -EPERM;
} }
SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource, SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
......
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