|
@@ -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");
|