diff options
Diffstat (limited to 'kernel/kmod.c')
| -rw-r--r-- | kernel/kmod.c | 100 | 
1 files changed, 100 insertions, 0 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c index 5ae0ff38425..ad6a81c58b4 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -25,6 +25,7 @@  #include <linux/kmod.h>  #include <linux/slab.h>  #include <linux/completion.h> +#include <linux/cred.h>  #include <linux/file.h>  #include <linux/fdtable.h>  #include <linux/workqueue.h> @@ -43,6 +44,13 @@ extern int max_threads;  static struct workqueue_struct *khelper_wq; +#define CAP_BSET	(void *)1 +#define CAP_PI		(void *)2 + +static kernel_cap_t usermodehelper_bset = CAP_FULL_SET; +static kernel_cap_t usermodehelper_inheritable = CAP_FULL_SET; +static DEFINE_SPINLOCK(umh_sysctl_lock); +  #ifdef CONFIG_MODULES  /* @@ -132,6 +140,7 @@ EXPORT_SYMBOL(__request_module);  static int ____call_usermodehelper(void *data)  {  	struct subprocess_info *sub_info = data; +	struct cred *new;  	int retval;  	spin_lock_irq(¤t->sighand->siglock); @@ -153,6 +162,19 @@ static int ____call_usermodehelper(void *data)  			goto fail;  	} +	retval = -ENOMEM; +	new = prepare_kernel_cred(current); +	if (!new) +		goto fail; + +	spin_lock(&umh_sysctl_lock); +	new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset); +	new->cap_inheritable = cap_intersect(usermodehelper_inheritable, +					     new->cap_inheritable); +	spin_unlock(&umh_sysctl_lock); + +	commit_creds(new); +  	retval = kernel_execve(sub_info->path,  			       (const char *const *)sub_info->argv,  			       (const char *const *)sub_info->envp); @@ -420,6 +442,84 @@ unlock:  }  EXPORT_SYMBOL(call_usermodehelper_exec); +static int proc_cap_handler(struct ctl_table *table, int write, +			 void __user *buffer, size_t *lenp, loff_t *ppos) +{ +	struct ctl_table t; +	unsigned long cap_array[_KERNEL_CAPABILITY_U32S]; +	kernel_cap_t new_cap; +	int err, i; + +	if (write && (!capable(CAP_SETPCAP) || +		      !capable(CAP_SYS_MODULE))) +		return -EPERM; + +	/* +	 * convert from the global kernel_cap_t to the ulong array to print to +	 * userspace if this is a read. +	 */ +	spin_lock(&umh_sysctl_lock); +	for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++)  { +		if (table->data == CAP_BSET) +			cap_array[i] = usermodehelper_bset.cap[i]; +		else if (table->data == CAP_PI) +			cap_array[i] = usermodehelper_inheritable.cap[i]; +		else +			BUG(); +	} +	spin_unlock(&umh_sysctl_lock); + +	t = *table; +	t.data = &cap_array; + +	/* +	 * actually read or write and array of ulongs from userspace.  Remember +	 * these are least significant 32 bits first +	 */ +	err = proc_doulongvec_minmax(&t, write, buffer, lenp, ppos); +	if (err < 0) +		return err; + +	/* +	 * convert from the sysctl array of ulongs to the kernel_cap_t +	 * internal representation +	 */ +	for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++) +		new_cap.cap[i] = cap_array[i]; + +	/* +	 * Drop everything not in the new_cap (but don't add things) +	 */ +	spin_lock(&umh_sysctl_lock); +	if (write) { +		if (table->data == CAP_BSET) +			usermodehelper_bset = cap_intersect(usermodehelper_bset, new_cap); +		if (table->data == CAP_PI) +			usermodehelper_inheritable = cap_intersect(usermodehelper_inheritable, new_cap); +	} +	spin_unlock(&umh_sysctl_lock); + +	return 0; +} + +struct ctl_table usermodehelper_table[] = { +	{ +		.procname	= "bset", +		.data		= CAP_BSET, +		.maxlen		= _KERNEL_CAPABILITY_U32S * sizeof(unsigned long), +		.mode		= 0600, +		.proc_handler	= proc_cap_handler, +	}, +	{ +		.procname	= "inheritable", +		.data		= CAP_PI, +		.maxlen		= _KERNEL_CAPABILITY_U32S * sizeof(unsigned long), +		.mode		= 0600, +		.proc_handler	= proc_cap_handler, +	}, +	{ } +}; +  void __init usermodehelper_init(void)  {  	khelper_wq = create_singlethread_workqueue("khelper");  |