* [PATCH] capabilites: allow the application of capability limits to usermode helpers
@ 2011-03-29 21:01 Eric Paris
0 siblings, 0 replies; 4+ messages in thread
From: Eric Paris @ 2011-03-29 21:01 UTC (permalink / raw)
To: linux-kernel, linux-security-module
Cc: greg, eparis, dhowells, jmorris, serge.hallyn
There is no way to limit the capabilities of usermodehelpers. This problem
reared its head recently when someone complained that any user with
cap_net_admin was able to load arbitrary kernel modules, even though the user
didn't have cap_sys_module. The reason is because the actual load is done by
a usermode helper and those always have the full cap set. This patch addes new
sysctls which allow us to bound the permissions of usermode helpers.
/proc/sys/kernel/usermodehelper/bset
/proc/sys/kernel/usermodehelper/inheritable
You must have CAP_SYS_MODULE and CAP_SET_PCAP to change these (changes are
&= ONLY). When the kernel launches a usermodehelper it will do so with these
as the bset and pI.
-v2: make globals static
create spinlock to protect globals
-v3: require both CAP_SET_PCAP and CAP_SYS_MODULE
Signed-off-by: Eric Paris <eparis@redhat.com>
No-objection-from: Serge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: David Howells <dhowells@redhat.com>
---
include/linux/kmod.h | 3 ++
kernel/kmod.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/sysctl.c | 6 +++
3 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index 6efd7a7..79bb98d 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -24,6 +24,7 @@
#include <linux/errno.h>
#include <linux/compiler.h>
#include <linux/workqueue.h>
+#include <linux/sysctl.h>
#define KMOD_PATH_LEN 256
@@ -109,6 +110,8 @@ call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
NULL, NULL, NULL);
}
+extern struct ctl_table usermodehelper_table[];
+
extern void usermodehelper_init(void);
extern int usermodehelper_disable(void);
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 9cd0591..3571d1e 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);
@@ -418,6 +440,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_SET_PCAP) ||
+ !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");
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index c0bb324..965134b 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -56,6 +56,7 @@
#include <linux/kprobes.h>
#include <linux/pipe_fs_i.h>
#include <linux/oom.h>
+#include <linux/kmod.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
@@ -616,6 +617,11 @@ static struct ctl_table kern_table[] = {
.child = random_table,
},
{
+ .procname = "usermodehelper",
+ .mode = 0555,
+ .child = usermodehelper_table,
+ },
+ {
.procname = "overflowuid",
.data = &overflowuid,
.maxlen = sizeof(int),
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] capabilites: allow the application of capability limits to usermode helpers
2011-03-30 17:04 ` Serge E. Hallyn
@ 2011-04-01 0:22 ` Andrew G. Morgan
0 siblings, 0 replies; 4+ messages in thread
From: Andrew G. Morgan @ 2011-04-01 0:22 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: Eric Paris, linux-kernel, linux-security-module, greg, dhowells,
jmorris, serge.hallyn
Acked-by: Andrew G. Morgan <morgan@kernel.org>
Cheers
Andrew
On Wed, Mar 30, 2011 at 10:04 AM, Serge E. Hallyn
<serge.hallyn@ubuntu.com> wrote:
> Quoting Eric Paris (eparis@redhat.com):
>> There is no way to limit the capabilities of usermodehelpers. This problem
>> reared its head recently when someone complained that any user with
>> cap_net_admin was able to load arbitrary kernel modules, even though the user
>> didn't have cap_sys_module. The reason is because the actual load is done by
>> a usermode helper and those always have the full cap set. This patch addes new
>> sysctls which allow us to bound the permissions of usermode helpers.
>>
>> /proc/sys/kernel/usermodehelper/bset
>> /proc/sys/kernel/usermodehelper/inheritable
>>
>> You must have CAP_SYS_MODULE and CAP_SETPCAP to change these (changes are
>> &= ONLY). When the kernel launches a usermodehelper it will do so with these
>> as the bset and pI.
>>
>> -v2: make globals static
>> create spinlock to protect globals
>>
>> -v3: require both CAP_SETPCAP and CAP_SYS_MODULE
>> -v4: fix the typo s/CAP_SET_PCAP/CAP_SETPCAP/ because I didn't commit
>> Signed-off-by: Eric Paris <eparis@redhat.com>
>> No-objection-from: Serge E. Hallyn <serge.hallyn@canonical.com>
>
> Acked-by: Serge E. Hallyn <serge.hallyn@canonical.com>
>
> thanks,
> -serge
>
>> Acked-by: David Howells <dhowells@redhat.com>
>> ---
>>
>> include/linux/kmod.h | 3 ++
>> kernel/kmod.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> kernel/sysctl.c | 6 +++
>> 3 files changed, 109 insertions(+), 0 deletions(-)
>>
>> diff --git a/include/linux/kmod.h b/include/linux/kmod.h
>> index 6efd7a7..79bb98d 100644
>> --- a/include/linux/kmod.h
>> +++ b/include/linux/kmod.h
>> @@ -24,6 +24,7 @@
>> #include <linux/errno.h>
>> #include <linux/compiler.h>
>> #include <linux/workqueue.h>
>> +#include <linux/sysctl.h>
>>
>> #define KMOD_PATH_LEN 256
>>
>> @@ -109,6 +110,8 @@ call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
>> NULL, NULL, NULL);
>> }
>>
>> +extern struct ctl_table usermodehelper_table[];
>> +
>> extern void usermodehelper_init(void);
>>
>> extern int usermodehelper_disable(void);
>> diff --git a/kernel/kmod.c b/kernel/kmod.c
>> index 9cd0591..06fdea2 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);
>> @@ -418,6 +440,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");
>> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
>> index c0bb324..965134b 100644
>> --- a/kernel/sysctl.c
>> +++ b/kernel/sysctl.c
>> @@ -56,6 +56,7 @@
>> #include <linux/kprobes.h>
>> #include <linux/pipe_fs_i.h>
>> #include <linux/oom.h>
>> +#include <linux/kmod.h>
>>
>> #include <asm/uaccess.h>
>> #include <asm/processor.h>
>> @@ -616,6 +617,11 @@ static struct ctl_table kern_table[] = {
>> .child = random_table,
>> },
>> {
>> + .procname = "usermodehelper",
>> + .mode = 0555,
>> + .child = usermodehelper_table,
>> + },
>> + {
>> .procname = "overflowuid",
>> .data = &overflowuid,
>> .maxlen = sizeof(int),
>>
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.10 (GNU/Linux)
>
> iQEcBAEBAgAGBQJNk2K6AAoJEHmllQITXQdF8AgH/RDWKdOXjySrBXaCc7LQR29M
> tPxdgl+/w7aKxsWF9iXxoIsMSKQXGhY1+J9QyoWpP9LWJ3njXcofZjg3x7uX94Ex
> JNnCrhjMFHhI6evPz00l0jqXA5lrg/LVqaDmpoTwJs+oKNj+3kHMQtdLpMrCKqjO
> RX6fNIthfYgBEdHPVN1h4nHdNurRRfsxXsrHso1Ljkc4DhaUsEKZaDRbf+mIM6zP
> Z+e6a/IwsYrfJ5tbA37tppActh3tcVcIEYj31g/peeZV1L5MHlkn0jemPIWyQJ8I
> zauWoBsWj5/uuIcGJ7tU6ae7iFLo1kWI1x03TwWSf6k2xeRfx3uwdf04mgrS1pY=
> =40oa
> -----END PGP SIGNATURE-----
>
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] capabilites: allow the application of capability limits to usermode helpers
2011-03-29 21:12 Eric Paris
@ 2011-03-30 17:04 ` Serge E. Hallyn
2011-04-01 0:22 ` Andrew G. Morgan
0 siblings, 1 reply; 4+ messages in thread
From: Serge E. Hallyn @ 2011-03-30 17:04 UTC (permalink / raw)
To: Eric Paris
Cc: linux-kernel, linux-security-module, greg, dhowells, jmorris,
serge.hallyn
[-- Attachment #1: Type: text/plain, Size: 6809 bytes --]
Quoting Eric Paris (eparis@redhat.com):
> There is no way to limit the capabilities of usermodehelpers. This problem
> reared its head recently when someone complained that any user with
> cap_net_admin was able to load arbitrary kernel modules, even though the user
> didn't have cap_sys_module. The reason is because the actual load is done by
> a usermode helper and those always have the full cap set. This patch addes new
> sysctls which allow us to bound the permissions of usermode helpers.
>
> /proc/sys/kernel/usermodehelper/bset
> /proc/sys/kernel/usermodehelper/inheritable
>
> You must have CAP_SYS_MODULE and CAP_SETPCAP to change these (changes are
> &= ONLY). When the kernel launches a usermodehelper it will do so with these
> as the bset and pI.
>
> -v2: make globals static
> create spinlock to protect globals
>
> -v3: require both CAP_SETPCAP and CAP_SYS_MODULE
> -v4: fix the typo s/CAP_SET_PCAP/CAP_SETPCAP/ because I didn't commit
> Signed-off-by: Eric Paris <eparis@redhat.com>
> No-objection-from: Serge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: Serge E. Hallyn <serge.hallyn@canonical.com>
thanks,
-serge
> Acked-by: David Howells <dhowells@redhat.com>
> ---
>
> include/linux/kmod.h | 3 ++
> kernel/kmod.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++
> kernel/sysctl.c | 6 +++
> 3 files changed, 109 insertions(+), 0 deletions(-)
>
> diff --git a/include/linux/kmod.h b/include/linux/kmod.h
> index 6efd7a7..79bb98d 100644
> --- a/include/linux/kmod.h
> +++ b/include/linux/kmod.h
> @@ -24,6 +24,7 @@
> #include <linux/errno.h>
> #include <linux/compiler.h>
> #include <linux/workqueue.h>
> +#include <linux/sysctl.h>
>
> #define KMOD_PATH_LEN 256
>
> @@ -109,6 +110,8 @@ call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
> NULL, NULL, NULL);
> }
>
> +extern struct ctl_table usermodehelper_table[];
> +
> extern void usermodehelper_init(void);
>
> extern int usermodehelper_disable(void);
> diff --git a/kernel/kmod.c b/kernel/kmod.c
> index 9cd0591..06fdea2 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);
> @@ -418,6 +440,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");
> diff --git a/kernel/sysctl.c b/kernel/sysctl.c
> index c0bb324..965134b 100644
> --- a/kernel/sysctl.c
> +++ b/kernel/sysctl.c
> @@ -56,6 +56,7 @@
> #include <linux/kprobes.h>
> #include <linux/pipe_fs_i.h>
> #include <linux/oom.h>
> +#include <linux/kmod.h>
>
> #include <asm/uaccess.h>
> #include <asm/processor.h>
> @@ -616,6 +617,11 @@ static struct ctl_table kern_table[] = {
> .child = random_table,
> },
> {
> + .procname = "usermodehelper",
> + .mode = 0555,
> + .child = usermodehelper_table,
> + },
> + {
> .procname = "overflowuid",
> .data = &overflowuid,
> .maxlen = sizeof(int),
>
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 490 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH] capabilites: allow the application of capability limits to usermode helpers
@ 2011-03-29 21:12 Eric Paris
2011-03-30 17:04 ` Serge E. Hallyn
0 siblings, 1 reply; 4+ messages in thread
From: Eric Paris @ 2011-03-29 21:12 UTC (permalink / raw)
To: linux-kernel, linux-security-module
Cc: greg, eparis, dhowells, jmorris, serge.hallyn
There is no way to limit the capabilities of usermodehelpers. This problem
reared its head recently when someone complained that any user with
cap_net_admin was able to load arbitrary kernel modules, even though the user
didn't have cap_sys_module. The reason is because the actual load is done by
a usermode helper and those always have the full cap set. This patch addes new
sysctls which allow us to bound the permissions of usermode helpers.
/proc/sys/kernel/usermodehelper/bset
/proc/sys/kernel/usermodehelper/inheritable
You must have CAP_SYS_MODULE and CAP_SETPCAP to change these (changes are
&= ONLY). When the kernel launches a usermodehelper it will do so with these
as the bset and pI.
-v2: make globals static
create spinlock to protect globals
-v3: require both CAP_SETPCAP and CAP_SYS_MODULE
-v4: fix the typo s/CAP_SET_PCAP/CAP_SETPCAP/ because I didn't commit
Signed-off-by: Eric Paris <eparis@redhat.com>
No-objection-from: Serge E. Hallyn <serge.hallyn@canonical.com>
Acked-by: David Howells <dhowells@redhat.com>
---
include/linux/kmod.h | 3 ++
kernel/kmod.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++
kernel/sysctl.c | 6 +++
3 files changed, 109 insertions(+), 0 deletions(-)
diff --git a/include/linux/kmod.h b/include/linux/kmod.h
index 6efd7a7..79bb98d 100644
--- a/include/linux/kmod.h
+++ b/include/linux/kmod.h
@@ -24,6 +24,7 @@
#include <linux/errno.h>
#include <linux/compiler.h>
#include <linux/workqueue.h>
+#include <linux/sysctl.h>
#define KMOD_PATH_LEN 256
@@ -109,6 +110,8 @@ call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
NULL, NULL, NULL);
}
+extern struct ctl_table usermodehelper_table[];
+
extern void usermodehelper_init(void);
extern int usermodehelper_disable(void);
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 9cd0591..06fdea2 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);
@@ -418,6 +440,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");
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index c0bb324..965134b 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -56,6 +56,7 @@
#include <linux/kprobes.h>
#include <linux/pipe_fs_i.h>
#include <linux/oom.h>
+#include <linux/kmod.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
@@ -616,6 +617,11 @@ static struct ctl_table kern_table[] = {
.child = random_table,
},
{
+ .procname = "usermodehelper",
+ .mode = 0555,
+ .child = usermodehelper_table,
+ },
+ {
.procname = "overflowuid",
.data = &overflowuid,
.maxlen = sizeof(int),
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2011-04-01 0:22 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-29 21:01 [PATCH] capabilites: allow the application of capability limits to usermode helpers Eric Paris
2011-03-29 21:12 Eric Paris
2011-03-30 17:04 ` Serge E. Hallyn
2011-04-01 0:22 ` Andrew G. Morgan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®