diff options
Diffstat (limited to 'kernel/capability.c')
| -rw-r--r-- | kernel/capability.c | 191 | 
1 files changed, 159 insertions, 32 deletions
diff --git a/kernel/capability.c b/kernel/capability.c index 2f05303715a..a5cf13c018c 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -7,13 +7,16 @@   * 30 May 2002:	Cleanup, Robert M. Love <rml@tech9.net>   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/audit.h>  #include <linux/capability.h>  #include <linux/mm.h> -#include <linux/module.h> +#include <linux/export.h>  #include <linux/security.h>  #include <linux/syscalls.h>  #include <linux/pid_namespace.h> +#include <linux/user_namespace.h>  #include <asm/uaccess.h>  /* @@ -21,12 +24,7 @@   */  const kernel_cap_t __cap_empty_set = CAP_EMPTY_SET; -const kernel_cap_t __cap_full_set = CAP_FULL_SET; -const kernel_cap_t __cap_init_eff_set = CAP_INIT_EFF_SET; -  EXPORT_SYMBOL(__cap_empty_set); -EXPORT_SYMBOL(__cap_full_set); -EXPORT_SYMBOL(__cap_init_eff_set);  int file_caps_enabled = 1; @@ -45,15 +43,10 @@ __setup("no_file_caps", file_caps_disable);  static void warn_legacy_capability_use(void)  { -	static int warned; -	if (!warned) { -		char name[sizeof(current->comm)]; - -		printk(KERN_INFO "warning: `%s' uses 32-bit capabilities" -		       " (legacy support in use)\n", -		       get_task_comm(name, current)); -		warned = 1; -	} +	char name[sizeof(current->comm)]; + +	pr_info_once("warning: `%s' uses 32-bit capabilities (legacy support in use)\n", +		     get_task_comm(name, current));  }  /* @@ -74,16 +67,10 @@ static void warn_legacy_capability_use(void)  static void warn_deprecated_v2(void)  { -	static int warned; - -	if (!warned) { -		char name[sizeof(current->comm)]; +	char name[sizeof(current->comm)]; -		printk(KERN_INFO "warning: `%s' uses deprecated v2" -		       " capabilities in a way that may be insecure.\n", -		       get_task_comm(name, current)); -		warned = 1; -	} +	pr_info_once("warning: `%s' uses deprecated v2 capabilities in a way that may be insecure\n", +		     get_task_comm(name, current));  }  /* @@ -201,7 +188,7 @@ SYSCALL_DEFINE2(capget, cap_user_header_t, header, cap_user_data_t, dataptr)  		 *  		 * An alternative would be to return an error here  		 * (-ERANGE), but that causes legacy applications to -		 * unexpectidly fail; the capget/modify/capset aborts +		 * unexpectedly fail; the capget/modify/capset aborts  		 * before modification is attempted and the application  		 * fails.  		 */ @@ -280,7 +267,7 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data)  	if (ret < 0)  		goto error; -	audit_log_capset(pid, new, current_cred()); +	audit_log_capset(new, current_cred());  	return commit_creds(new); @@ -290,7 +277,88 @@ error:  }  /** - * capable - Determine if the current task has a superior capability in effect + * has_ns_capability - Does a task have a capability in a specific user ns + * @t: The task in question + * @ns: target user namespace + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect to the specified user namespace, false if not. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +bool has_ns_capability(struct task_struct *t, +		       struct user_namespace *ns, int cap) +{ +	int ret; + +	rcu_read_lock(); +	ret = security_capable(__task_cred(t), ns, cap); +	rcu_read_unlock(); + +	return (ret == 0); +} + +/** + * has_capability - Does a task have a capability in init_user_ns + * @t: The task in question + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect to the initial user namespace, false if not. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +bool has_capability(struct task_struct *t, int cap) +{ +	return has_ns_capability(t, &init_user_ns, cap); +} + +/** + * has_ns_capability_noaudit - Does a task have a capability (unaudited) + * in a specific user ns. + * @t: The task in question + * @ns: target user namespace + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect to the specified user namespace, false if not. + * Do not write an audit message for the check. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +bool has_ns_capability_noaudit(struct task_struct *t, +			       struct user_namespace *ns, int cap) +{ +	int ret; + +	rcu_read_lock(); +	ret = security_capable_noaudit(__task_cred(t), ns, cap); +	rcu_read_unlock(); + +	return (ret == 0); +} + +/** + * has_capability_noaudit - Does a task have a capability (unaudited) in the + * initial user ns + * @t: The task in question + * @cap: The capability to be tested for + * + * Return true if the specified task has the given superior capability + * currently in effect to init_user_ns, false if not.  Don't write an + * audit message for the check. + * + * Note that this does not set PF_SUPERPRIV on the task. + */ +bool has_capability_noaudit(struct task_struct *t, int cap) +{ +	return has_ns_capability_noaudit(t, &init_user_ns, cap); +} + +/** + * ns_capable - Determine if the current task has a superior capability in effect + * @ns:  The usernamespace we want the capability in   * @cap: The capability to be tested for   *   * Return true if the current task has the given superior capability currently @@ -299,17 +367,76 @@ error:   * This sets PF_SUPERPRIV on the task if the capability is available on the   * assumption that it's about to be used.   */ -int capable(int cap) +bool ns_capable(struct user_namespace *ns, int cap)  {  	if (unlikely(!cap_valid(cap))) { -		printk(KERN_CRIT "capable() called with invalid cap=%u\n", cap); +		pr_crit("capable() called with invalid cap=%u\n", cap);  		BUG();  	} -	if (security_capable(cap) == 0) { +	if (security_capable(current_cred(), ns, cap) == 0) {  		current->flags |= PF_SUPERPRIV; -		return 1; +		return true;  	} -	return 0; +	return false; +} +EXPORT_SYMBOL(ns_capable); + +/** + * file_ns_capable - Determine if the file's opener had a capability in effect + * @file:  The file we want to check + * @ns:  The usernamespace we want the capability in + * @cap: The capability to be tested for + * + * Return true if task that opened the file had a capability in effect + * when the file was opened. + * + * This does not set PF_SUPERPRIV because the caller may not + * actually be privileged. + */ +bool file_ns_capable(const struct file *file, struct user_namespace *ns, +		     int cap) +{ +	if (WARN_ON_ONCE(!cap_valid(cap))) +		return false; + +	if (security_capable(file->f_cred, ns, cap) == 0) +		return true; + +	return false; +} +EXPORT_SYMBOL(file_ns_capable); + +/** + * capable - Determine if the current task has a superior capability in effect + * @cap: The capability to be tested for + * + * Return true if the current task has the given superior capability currently + * available for use, false if not. + * + * This sets PF_SUPERPRIV on the task if the capability is available on the + * assumption that it's about to be used. + */ +bool capable(int cap) +{ +	return ns_capable(&init_user_ns, cap);  }  EXPORT_SYMBOL(capable); + +/** + * capable_wrt_inode_uidgid - Check nsown_capable and uid and gid mapped + * @inode: The inode in question + * @cap: The capability in question + * + * Return true if the current task has the given capability targeted at + * its own user namespace and that the given inode's uid and gid are + * mapped into the current user namespace. + */ +bool capable_wrt_inode_uidgid(const struct inode *inode, int cap) +{ +	struct user_namespace *ns = current_user_ns(); + +	return ns_capable(ns, cap) && kuid_has_mapping(ns, inode->i_uid) && +		kgid_has_mapping(ns, inode->i_gid); +} +EXPORT_SYMBOL(capable_wrt_inode_uidgid);  | 
