diff options
Diffstat (limited to 'kernel/sys.c')
| -rw-r--r-- | kernel/sys.c | 266 | 
1 files changed, 174 insertions, 92 deletions
diff --git a/kernel/sys.c b/kernel/sys.c index ba0ae8eea6f..6df42624e45 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -93,10 +93,8 @@  int overflowuid = DEFAULT_OVERFLOWUID;  int overflowgid = DEFAULT_OVERFLOWGID; -#ifdef CONFIG_UID16  EXPORT_SYMBOL(overflowuid);  EXPORT_SYMBOL(overflowgid); -#endif  /*   * the same as above, but for filesystems which can only store a 16-bit @@ -133,11 +131,10 @@ 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)) +	if (uid_eq(pcred->uid,  cred->euid) || +	    uid_eq(pcred->euid, cred->euid))  		return true; -	if (ns_capable(pcred->user->user_ns, CAP_SYS_NICE)) +	if (ns_capable(pcred->user_ns, CAP_SYS_NICE))  		return true;  	return false;  } @@ -177,6 +174,7 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval)  	const struct cred *cred = current_cred();  	int error = -EINVAL;  	struct pid *pgrp; +	kuid_t uid;  	if (which > PRIO_USER || which < PRIO_PROCESS)  		goto out; @@ -209,18 +207,19 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval)  			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);  			break;  		case PRIO_USER: -			user = (struct user_struct *) cred->user; +			uid = make_kuid(cred->user_ns, who); +			user = cred->user;  			if (!who) -				who = cred->uid; -			else if ((who != cred->uid) && -				 !(user = find_user(who))) +				uid = cred->uid; +			else if (!uid_eq(uid, cred->uid) && +				 !(user = find_user(uid)))  				goto out_unlock;	/* No processes for this user */  			do_each_thread(g, p) { -				if (__task_cred(p)->uid == who) +				if (uid_eq(task_uid(p), uid))  					error = set_one_prio(p, niceval, error);  			} while_each_thread(g, p); -			if (who != cred->uid) +			if (!uid_eq(uid, cred->uid))  				free_uid(user);		/* For find_user() */  			break;  	} @@ -244,6 +243,7 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who)  	const struct cred *cred = current_cred();  	long niceval, retval = -ESRCH;  	struct pid *pgrp; +	kuid_t uid;  	if (which > PRIO_USER || which < PRIO_PROCESS)  		return -EINVAL; @@ -274,21 +274,22 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who)  			} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);  			break;  		case PRIO_USER: -			user = (struct user_struct *) cred->user; +			uid = make_kuid(cred->user_ns, who); +			user = cred->user;  			if (!who) -				who = cred->uid; -			else if ((who != cred->uid) && -				 !(user = find_user(who))) +				uid = cred->uid; +			else if (!uid_eq(uid, cred->uid) && +				 !(user = find_user(uid)))  				goto out_unlock;	/* No processes for this user */  			do_each_thread(g, p) { -				if (__task_cred(p)->uid == who) { +				if (uid_eq(task_uid(p), uid)) {  					niceval = 20 - task_nice(p);  					if (niceval > retval)  						retval = niceval;  				}  			} while_each_thread(g, p); -			if (who != cred->uid) +			if (!uid_eq(uid, cred->uid))  				free_uid(user);		/* for find_user() */  			break;  	} @@ -553,9 +554,19 @@ void ctrl_alt_del(void)   */  SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)  { +	struct user_namespace *ns = current_user_ns();  	const struct cred *old;  	struct cred *new;  	int retval; +	kgid_t krgid, kegid; + +	krgid = make_kgid(ns, rgid); +	kegid = make_kgid(ns, egid); + +	if ((rgid != (gid_t) -1) && !gid_valid(krgid)) +		return -EINVAL; +	if ((egid != (gid_t) -1) && !gid_valid(kegid)) +		return -EINVAL;  	new = prepare_creds();  	if (!new) @@ -564,25 +575,25 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)  	retval = -EPERM;  	if (rgid != (gid_t) -1) { -		if (old->gid == rgid || -		    old->egid == rgid || +		if (gid_eq(old->gid, krgid) || +		    gid_eq(old->egid, krgid) ||  		    nsown_capable(CAP_SETGID)) -			new->gid = rgid; +			new->gid = krgid;  		else  			goto error;  	}  	if (egid != (gid_t) -1) { -		if (old->gid == egid || -		    old->egid == egid || -		    old->sgid == egid || +		if (gid_eq(old->gid, kegid) || +		    gid_eq(old->egid, kegid) || +		    gid_eq(old->sgid, kegid) ||  		    nsown_capable(CAP_SETGID)) -			new->egid = egid; +			new->egid = kegid;  		else  			goto error;  	}  	if (rgid != (gid_t) -1 || -	    (egid != (gid_t) -1 && egid != old->gid)) +	    (egid != (gid_t) -1 && !gid_eq(kegid, old->gid)))  		new->sgid = new->egid;  	new->fsgid = new->egid; @@ -600,9 +611,15 @@ error:   */  SYSCALL_DEFINE1(setgid, gid_t, gid)  { +	struct user_namespace *ns = current_user_ns();  	const struct cred *old;  	struct cred *new;  	int retval; +	kgid_t kgid; + +	kgid = make_kgid(ns, gid); +	if (!gid_valid(kgid)) +		return -EINVAL;  	new = prepare_creds();  	if (!new) @@ -611,9 +628,9 @@ SYSCALL_DEFINE1(setgid, gid_t, gid)  	retval = -EPERM;  	if (nsown_capable(CAP_SETGID)) -		new->gid = new->egid = new->sgid = new->fsgid = gid; -	else if (gid == old->gid || gid == old->sgid) -		new->egid = new->fsgid = gid; +		new->gid = new->egid = new->sgid = new->fsgid = kgid; +	else if (gid_eq(kgid, old->gid) || gid_eq(kgid, old->sgid)) +		new->egid = new->fsgid = kgid;  	else  		goto error; @@ -631,7 +648,7 @@ static int set_user(struct cred *new)  {  	struct user_struct *new_user; -	new_user = alloc_uid(current_user_ns(), new->uid); +	new_user = alloc_uid(new->uid);  	if (!new_user)  		return -EAGAIN; @@ -670,9 +687,19 @@ static int set_user(struct cred *new)   */  SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)  { +	struct user_namespace *ns = current_user_ns();  	const struct cred *old;  	struct cred *new;  	int retval; +	kuid_t kruid, keuid; + +	kruid = make_kuid(ns, ruid); +	keuid = make_kuid(ns, euid); + +	if ((ruid != (uid_t) -1) && !uid_valid(kruid)) +		return -EINVAL; +	if ((euid != (uid_t) -1) && !uid_valid(keuid)) +		return -EINVAL;  	new = prepare_creds();  	if (!new) @@ -681,29 +708,29 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)  	retval = -EPERM;  	if (ruid != (uid_t) -1) { -		new->uid = ruid; -		if (old->uid != ruid && -		    old->euid != ruid && +		new->uid = kruid; +		if (!uid_eq(old->uid, kruid) && +		    !uid_eq(old->euid, kruid) &&  		    !nsown_capable(CAP_SETUID))  			goto error;  	}  	if (euid != (uid_t) -1) { -		new->euid = euid; -		if (old->uid != euid && -		    old->euid != euid && -		    old->suid != euid && +		new->euid = keuid; +		if (!uid_eq(old->uid, keuid) && +		    !uid_eq(old->euid, keuid) && +		    !uid_eq(old->suid, keuid) &&  		    !nsown_capable(CAP_SETUID))  			goto error;  	} -	if (new->uid != old->uid) { +	if (!uid_eq(new->uid, old->uid)) {  		retval = set_user(new);  		if (retval < 0)  			goto error;  	}  	if (ruid != (uid_t) -1 || -	    (euid != (uid_t) -1 && euid != old->uid)) +	    (euid != (uid_t) -1 && !uid_eq(keuid, old->uid)))  		new->suid = new->euid;  	new->fsuid = new->euid; @@ -731,9 +758,15 @@ error:   */  SYSCALL_DEFINE1(setuid, uid_t, uid)  { +	struct user_namespace *ns = current_user_ns();  	const struct cred *old;  	struct cred *new;  	int retval; +	kuid_t kuid; + +	kuid = make_kuid(ns, uid); +	if (!uid_valid(kuid)) +		return -EINVAL;  	new = prepare_creds();  	if (!new) @@ -742,17 +775,17 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)  	retval = -EPERM;  	if (nsown_capable(CAP_SETUID)) { -		new->suid = new->uid = uid; -		if (uid != old->uid) { +		new->suid = new->uid = kuid; +		if (!uid_eq(kuid, old->uid)) {  			retval = set_user(new);  			if (retval < 0)  				goto error;  		} -	} else if (uid != old->uid && uid != new->suid) { +	} else if (!uid_eq(kuid, old->uid) && !uid_eq(kuid, new->suid)) {  		goto error;  	} -	new->fsuid = new->euid = uid; +	new->fsuid = new->euid = kuid;  	retval = security_task_fix_setuid(new, old, LSM_SETID_ID);  	if (retval < 0) @@ -772,9 +805,24 @@ error:   */  SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)  { +	struct user_namespace *ns = current_user_ns();  	const struct cred *old;  	struct cred *new;  	int retval; +	kuid_t kruid, keuid, ksuid; + +	kruid = make_kuid(ns, ruid); +	keuid = make_kuid(ns, euid); +	ksuid = make_kuid(ns, suid); + +	if ((ruid != (uid_t) -1) && !uid_valid(kruid)) +		return -EINVAL; + +	if ((euid != (uid_t) -1) && !uid_valid(keuid)) +		return -EINVAL; + +	if ((suid != (uid_t) -1) && !uid_valid(ksuid)) +		return -EINVAL;  	new = prepare_creds();  	if (!new) @@ -784,29 +832,29 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)  	retval = -EPERM;  	if (!nsown_capable(CAP_SETUID)) { -		if (ruid != (uid_t) -1 && ruid != old->uid && -		    ruid != old->euid  && ruid != old->suid) +		if (ruid != (uid_t) -1        && !uid_eq(kruid, old->uid) && +		    !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))  			goto error; -		if (euid != (uid_t) -1 && euid != old->uid && -		    euid != old->euid  && euid != old->suid) +		if (euid != (uid_t) -1        && !uid_eq(keuid, old->uid) && +		    !uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid))  			goto error; -		if (suid != (uid_t) -1 && suid != old->uid && -		    suid != old->euid  && suid != old->suid) +		if (suid != (uid_t) -1        && !uid_eq(ksuid, old->uid) && +		    !uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid))  			goto error;  	}  	if (ruid != (uid_t) -1) { -		new->uid = ruid; -		if (ruid != old->uid) { +		new->uid = kruid; +		if (!uid_eq(kruid, old->uid)) {  			retval = set_user(new);  			if (retval < 0)  				goto error;  		}  	}  	if (euid != (uid_t) -1) -		new->euid = euid; +		new->euid = keuid;  	if (suid != (uid_t) -1) -		new->suid = suid; +		new->suid = ksuid;  	new->fsuid = new->euid;  	retval = security_task_fix_setuid(new, old, LSM_SETID_RES); @@ -820,14 +868,19 @@ error:  	return retval;  } -SYSCALL_DEFINE3(getresuid, uid_t __user *, ruid, uid_t __user *, euid, uid_t __user *, suid) +SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t __user *, suidp)  {  	const struct cred *cred = current_cred();  	int retval; +	uid_t ruid, euid, suid; + +	ruid = from_kuid_munged(cred->user_ns, cred->uid); +	euid = from_kuid_munged(cred->user_ns, cred->euid); +	suid = from_kuid_munged(cred->user_ns, cred->suid); -	if (!(retval   = put_user(cred->uid,  ruid)) && -	    !(retval   = put_user(cred->euid, euid))) -		retval = put_user(cred->suid, suid); +	if (!(retval   = put_user(ruid, ruidp)) && +	    !(retval   = put_user(euid, euidp))) +		retval = put_user(suid, suidp);  	return retval;  } @@ -837,9 +890,22 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruid, uid_t __user *, euid, uid_t __u   */  SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)  { +	struct user_namespace *ns = current_user_ns();  	const struct cred *old;  	struct cred *new;  	int retval; +	kgid_t krgid, kegid, ksgid; + +	krgid = make_kgid(ns, rgid); +	kegid = make_kgid(ns, egid); +	ksgid = make_kgid(ns, sgid); + +	if ((rgid != (gid_t) -1) && !gid_valid(krgid)) +		return -EINVAL; +	if ((egid != (gid_t) -1) && !gid_valid(kegid)) +		return -EINVAL; +	if ((sgid != (gid_t) -1) && !gid_valid(ksgid)) +		return -EINVAL;  	new = prepare_creds();  	if (!new) @@ -848,23 +914,23 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)  	retval = -EPERM;  	if (!nsown_capable(CAP_SETGID)) { -		if (rgid != (gid_t) -1 && rgid != old->gid && -		    rgid != old->egid  && rgid != old->sgid) +		if (rgid != (gid_t) -1        && !gid_eq(krgid, old->gid) && +		    !gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid))  			goto error; -		if (egid != (gid_t) -1 && egid != old->gid && -		    egid != old->egid  && egid != old->sgid) +		if (egid != (gid_t) -1        && !gid_eq(kegid, old->gid) && +		    !gid_eq(kegid, old->egid) && !gid_eq(kegid, old->sgid))  			goto error; -		if (sgid != (gid_t) -1 && sgid != old->gid && -		    sgid != old->egid  && sgid != old->sgid) +		if (sgid != (gid_t) -1        && !gid_eq(ksgid, old->gid) && +		    !gid_eq(ksgid, old->egid) && !gid_eq(ksgid, old->sgid))  			goto error;  	}  	if (rgid != (gid_t) -1) -		new->gid = rgid; +		new->gid = krgid;  	if (egid != (gid_t) -1) -		new->egid = egid; +		new->egid = kegid;  	if (sgid != (gid_t) -1) -		new->sgid = sgid; +		new->sgid = ksgid;  	new->fsgid = new->egid;  	return commit_creds(new); @@ -874,14 +940,19 @@ error:  	return retval;  } -SYSCALL_DEFINE3(getresgid, gid_t __user *, rgid, gid_t __user *, egid, gid_t __user *, sgid) +SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t __user *, sgidp)  {  	const struct cred *cred = current_cred();  	int retval; +	gid_t rgid, egid, sgid; + +	rgid = from_kgid_munged(cred->user_ns, cred->gid); +	egid = from_kgid_munged(cred->user_ns, cred->egid); +	sgid = from_kgid_munged(cred->user_ns, cred->sgid); -	if (!(retval   = put_user(cred->gid,  rgid)) && -	    !(retval   = put_user(cred->egid, egid))) -		retval = put_user(cred->sgid, sgid); +	if (!(retval   = put_user(rgid, rgidp)) && +	    !(retval   = put_user(egid, egidp))) +		retval = put_user(sgid, sgidp);  	return retval;  } @@ -898,18 +969,24 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)  	const struct cred *old;  	struct cred *new;  	uid_t old_fsuid; +	kuid_t kuid; + +	old = current_cred(); +	old_fsuid = from_kuid_munged(old->user_ns, old->fsuid); + +	kuid = make_kuid(old->user_ns, uid); +	if (!uid_valid(kuid)) +		return old_fsuid;  	new = prepare_creds();  	if (!new) -		return current_fsuid(); -	old = current_cred(); -	old_fsuid = old->fsuid; +		return old_fsuid; -	if (uid == old->uid  || uid == old->euid  || -	    uid == old->suid || uid == old->fsuid || +	if (uid_eq(kuid, old->uid)  || uid_eq(kuid, old->euid)  || +	    uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) ||  	    nsown_capable(CAP_SETUID)) { -		if (uid != old_fsuid) { -			new->fsuid = uid; +		if (!uid_eq(kuid, old->fsuid)) { +			new->fsuid = kuid;  			if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)  				goto change_okay;  		} @@ -931,18 +1008,24 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)  	const struct cred *old;  	struct cred *new;  	gid_t old_fsgid; +	kgid_t kgid; + +	old = current_cred(); +	old_fsgid = from_kgid_munged(old->user_ns, old->fsgid); + +	kgid = make_kgid(old->user_ns, gid); +	if (!gid_valid(kgid)) +		return old_fsgid;  	new = prepare_creds();  	if (!new) -		return current_fsgid(); -	old = current_cred(); -	old_fsgid = old->fsgid; +		return old_fsgid; -	if (gid == old->gid  || gid == old->egid  || -	    gid == old->sgid || gid == old->fsgid || +	if (gid_eq(kgid, old->gid)  || gid_eq(kgid, old->egid)  || +	    gid_eq(kgid, old->sgid) || gid_eq(kgid, old->fsgid) ||  	    nsown_capable(CAP_SETGID)) { -		if (gid != old_fsgid) { -			new->fsgid = gid; +		if (!gid_eq(kgid, old->fsgid)) { +			new->fsgid = kgid;  			goto change_okay;  		}  	} @@ -1498,15 +1581,14 @@ static int check_prlimit_permission(struct task_struct *task)  		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)) +	if (uid_eq(cred->uid, tcred->euid) && +	    uid_eq(cred->uid, tcred->suid) && +	    uid_eq(cred->uid, tcred->uid)  && +	    gid_eq(cred->gid, tcred->egid) && +	    gid_eq(cred->gid, tcred->sgid) && +	    gid_eq(cred->gid, tcred->gid))  		return 0; -	if (ns_capable(tcred->user->user_ns, CAP_SYS_RESOURCE)) +	if (ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))  		return 0;  	return -EPERM;  |