diff options
Diffstat (limited to 'security/commoncap.c')
| -rw-r--r-- | security/commoncap.c | 78 | 
1 files changed, 63 insertions, 15 deletions
diff --git a/security/commoncap.c b/security/commoncap.c index 49c57fd60aea..f20e984ccfb4 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -27,6 +27,7 @@  #include <linux/sched.h>  #include <linux/prctl.h>  #include <linux/securebits.h> +#include <linux/user_namespace.h>  /*   * If a non-root user executes a setuid-root binary in @@ -67,6 +68,7 @@ EXPORT_SYMBOL(cap_netlink_recv);   * cap_capable - Determine whether a task has a particular effective capability   * @tsk: The task to query   * @cred: The credentials to use + * @ns:  The user namespace in which we need the capability   * @cap: The capability to check for   * @audit: Whether to write an audit message or not   * @@ -78,10 +80,30 @@ EXPORT_SYMBOL(cap_netlink_recv);   * cap_has_capability() returns 0 when a task has a capability, but the   * kernel's capable() and has_capability() returns 1 for this case.   */ -int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, -		int audit) +int cap_capable(struct task_struct *tsk, const struct cred *cred, +		struct user_namespace *targ_ns, int cap, int audit)  { -	return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; +	for (;;) { +		/* The creator of the user namespace has all caps. */ +		if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) +			return 0; + +		/* Do we have the necessary capabilities? */ +		if (targ_ns == cred->user->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) +			return -EPERM; + +		/* +		 *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->creator->user_ns; +	} + +	/* We never get here */  }  /** @@ -105,18 +127,30 @@ int cap_settime(const struct timespec *ts, const struct timezone *tz)   * @child: The process to be accessed   * @mode: The mode of attachment.   * + * If we are in the same or an ancestor user_ns and have all the target + * task's capabilities, then ptrace access is allowed. + * If we have the ptrace capability to the target user_ns, then ptrace + * access is allowed. + * Else denied. + *   * Determine whether a process may access another, returning 0 if permission   * granted, -ve if denied.   */  int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)  {  	int ret = 0; +	const struct cred *cred, *child_cred;  	rcu_read_lock(); -	if (!cap_issubset(__task_cred(child)->cap_permitted, -			  current_cred()->cap_permitted) && -	    !capable(CAP_SYS_PTRACE)) -		ret = -EPERM; +	cred = current_cred(); +	child_cred = __task_cred(child); +	if (cred->user->user_ns == child_cred->user->user_ns && +	    cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) +		goto out; +	if (ns_capable(child_cred->user->user_ns, CAP_SYS_PTRACE)) +		goto out; +	ret = -EPERM; +out:  	rcu_read_unlock();  	return ret;  } @@ -125,18 +159,30 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)   * cap_ptrace_traceme - Determine whether another process may trace the current   * @parent: The task proposed to be the tracer   * + * If parent is in the same or an ancestor user_ns and has all current's + * capabilities, then ptrace access is allowed. + * If parent has the ptrace capability to current's user_ns, then ptrace + * access is allowed. + * Else denied. + *   * Determine whether the nominated task is permitted to trace the current   * process, returning 0 if permission is granted, -ve if denied.   */  int cap_ptrace_traceme(struct task_struct *parent)  {  	int ret = 0; +	const struct cred *cred, *child_cred;  	rcu_read_lock(); -	if (!cap_issubset(current_cred()->cap_permitted, -			  __task_cred(parent)->cap_permitted) && -	    !has_capability(parent, CAP_SYS_PTRACE)) -		ret = -EPERM; +	cred = __task_cred(parent); +	child_cred = current_cred(); +	if (cred->user->user_ns == child_cred->user->user_ns && +	    cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) +		goto out; +	if (has_ns_capability(parent, child_cred->user->user_ns, CAP_SYS_PTRACE)) +		goto out; +	ret = -EPERM; +out:  	rcu_read_unlock();  	return ret;  } @@ -176,7 +222,8 @@ static inline int cap_inh_is_capped(void)  	/* they are so limited unless the current task has the CAP_SETPCAP  	 * capability  	 */ -	if (cap_capable(current, current_cred(), CAP_SETPCAP, +	if (cap_capable(current, current_cred(), +			current_cred()->user->user_ns, CAP_SETPCAP,  			SECURITY_CAP_AUDIT) == 0)  		return 0;  	return 1; @@ -828,7 +875,8 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,  		     & (new->securebits ^ arg2))			/*[1]*/  		    || ((new->securebits & SECURE_ALL_LOCKS & ~arg2))	/*[2]*/  		    || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS))	/*[3]*/ -		    || (cap_capable(current, current_cred(), CAP_SETPCAP, +		    || (cap_capable(current, current_cred(), +				    current_cred()->user->user_ns, CAP_SETPCAP,  				    SECURITY_CAP_AUDIT) != 0)		/*[4]*/  			/*  			 * [1] no changing of bits that are locked @@ -893,7 +941,7 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages)  {  	int cap_sys_admin = 0; -	if (cap_capable(current, current_cred(), CAP_SYS_ADMIN, +	if (cap_capable(current, current_cred(), &init_user_ns, CAP_SYS_ADMIN,  			SECURITY_CAP_NOAUDIT) == 0)  		cap_sys_admin = 1;  	return __vm_enough_memory(mm, pages, cap_sys_admin); @@ -920,7 +968,7 @@ int cap_file_mmap(struct file *file, unsigned long reqprot,  	int ret = 0;  	if (addr < dac_mmap_min_addr) { -		ret = cap_capable(current, current_cred(), CAP_SYS_RAWIO, +		ret = cap_capable(current, current_cred(), &init_user_ns, CAP_SYS_RAWIO,  				  SECURITY_CAP_AUDIT);  		/* set PF_SUPERPRIV if it turns out we allow the low mmap */  		if (ret == 0)  | 
