aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2011-04-01 17:07:50 -0400
committerJames Morris <jmorris@namei.org>2011-04-03 20:31:04 -0400
commit17f60a7da150fdd0cfb9756f86a262daa72c835f (patch)
tree1c5ece75d66bed0766de861cde6eb18ee7ea4cf2 /kernel
parentcfc64fd91fabed099a4c3df58559f4b7efe9bcce (diff)
capabilites: allow the application of capability limits to usermode helpers
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> Acked-by: Serge E. Hallyn <serge.hallyn@canonical.com> Acked-by: Andrew G. Morgan <morgan@kernel.org> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/kmod.c100
-rw-r--r--kernel/sysctl.c6
2 files changed, 106 insertions, 0 deletions
diff --git a/kernel/kmod.c b/kernel/kmod.c
index 9cd0591c96a2..06fdea2819b6 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -25,6 +25,7 @@
25#include <linux/kmod.h> 25#include <linux/kmod.h>
26#include <linux/slab.h> 26#include <linux/slab.h>
27#include <linux/completion.h> 27#include <linux/completion.h>
28#include <linux/cred.h>
28#include <linux/file.h> 29#include <linux/file.h>
29#include <linux/fdtable.h> 30#include <linux/fdtable.h>
30#include <linux/workqueue.h> 31#include <linux/workqueue.h>
@@ -43,6 +44,13 @@ extern int max_threads;
43 44
44static struct workqueue_struct *khelper_wq; 45static struct workqueue_struct *khelper_wq;
45 46
47#define CAP_BSET (void *)1
48#define CAP_PI (void *)2
49
50static kernel_cap_t usermodehelper_bset = CAP_FULL_SET;
51static kernel_cap_t usermodehelper_inheritable = CAP_FULL_SET;
52static DEFINE_SPINLOCK(umh_sysctl_lock);
53
46#ifdef CONFIG_MODULES 54#ifdef CONFIG_MODULES
47 55
48/* 56/*
@@ -132,6 +140,7 @@ EXPORT_SYMBOL(__request_module);
132static int ____call_usermodehelper(void *data) 140static int ____call_usermodehelper(void *data)
133{ 141{
134 struct subprocess_info *sub_info = data; 142 struct subprocess_info *sub_info = data;
143 struct cred *new;
135 int retval; 144 int retval;
136 145
137 spin_lock_irq(&current->sighand->siglock); 146 spin_lock_irq(&current->sighand->siglock);
@@ -153,6 +162,19 @@ static int ____call_usermodehelper(void *data)
153 goto fail; 162 goto fail;
154 } 163 }
155 164
165 retval = -ENOMEM;
166 new = prepare_kernel_cred(current);
167 if (!new)
168 goto fail;
169
170 spin_lock(&umh_sysctl_lock);
171 new->cap_bset = cap_intersect(usermodehelper_bset, new->cap_bset);
172 new->cap_inheritable = cap_intersect(usermodehelper_inheritable,
173 new->cap_inheritable);
174 spin_unlock(&umh_sysctl_lock);
175
176 commit_creds(new);
177
156 retval = kernel_execve(sub_info->path, 178 retval = kernel_execve(sub_info->path,
157 (const char *const *)sub_info->argv, 179 (const char *const *)sub_info->argv,
158 (const char *const *)sub_info->envp); 180 (const char *const *)sub_info->envp);
@@ -418,6 +440,84 @@ unlock:
418} 440}
419EXPORT_SYMBOL(call_usermodehelper_exec); 441EXPORT_SYMBOL(call_usermodehelper_exec);
420 442
443static int proc_cap_handler(struct ctl_table *table, int write,
444 void __user *buffer, size_t *lenp, loff_t *ppos)
445{
446 struct ctl_table t;
447 unsigned long cap_array[_KERNEL_CAPABILITY_U32S];
448 kernel_cap_t new_cap;
449 int err, i;
450
451 if (write && (!capable(CAP_SETPCAP) ||
452 !capable(CAP_SYS_MODULE)))
453 return -EPERM;
454
455 /*
456 * convert from the global kernel_cap_t to the ulong array to print to
457 * userspace if this is a read.
458 */
459 spin_lock(&umh_sysctl_lock);
460 for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++) {
461 if (table->data == CAP_BSET)
462 cap_array[i] = usermodehelper_bset.cap[i];
463 else if (table->data == CAP_PI)
464 cap_array[i] = usermodehelper_inheritable.cap[i];
465 else
466 BUG();
467 }
468 spin_unlock(&umh_sysctl_lock);
469
470 t = *table;
471 t.data = &cap_array;
472
473 /*
474 * actually read or write and array of ulongs from userspace. Remember
475 * these are least significant 32 bits first
476 */
477 err = proc_doulongvec_minmax(&t, write, buffer, lenp, ppos);
478 if (err < 0)
479 return err;
480
481 /*
482 * convert from the sysctl array of ulongs to the kernel_cap_t
483 * internal representation
484 */
485 for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++)
486 new_cap.cap[i] = cap_array[i];
487
488 /*
489 * Drop everything not in the new_cap (but don't add things)
490 */
491 spin_lock(&umh_sysctl_lock);
492 if (write) {
493 if (table->data == CAP_BSET)
494 usermodehelper_bset = cap_intersect(usermodehelper_bset, new_cap);
495 if (table->data == CAP_PI)
496 usermodehelper_inheritable = cap_intersect(usermodehelper_inheritable, new_cap);
497 }
498 spin_unlock(&umh_sysctl_lock);
499
500 return 0;
501}
502
503struct ctl_table usermodehelper_table[] = {
504 {
505 .procname = "bset",
506 .data = CAP_BSET,
507 .maxlen = _KERNEL_CAPABILITY_U32S * sizeof(unsigned long),
508 .mode = 0600,
509 .proc_handler = proc_cap_handler,
510 },
511 {
512 .procname = "inheritable",
513 .data = CAP_PI,
514 .maxlen = _KERNEL_CAPABILITY_U32S * sizeof(unsigned long),
515 .mode = 0600,
516 .proc_handler = proc_cap_handler,
517 },
518 { }
519};
520
421void __init usermodehelper_init(void) 521void __init usermodehelper_init(void)
422{ 522{
423 khelper_wq = create_singlethread_workqueue("khelper"); 523 khelper_wq = create_singlethread_workqueue("khelper");
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index c0bb32414b17..965134bed6cd 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -56,6 +56,7 @@
56#include <linux/kprobes.h> 56#include <linux/kprobes.h>
57#include <linux/pipe_fs_i.h> 57#include <linux/pipe_fs_i.h>
58#include <linux/oom.h> 58#include <linux/oom.h>
59#include <linux/kmod.h>
59 60
60#include <asm/uaccess.h> 61#include <asm/uaccess.h>
61#include <asm/processor.h> 62#include <asm/processor.h>
@@ -616,6 +617,11 @@ static struct ctl_table kern_table[] = {
616 .child = random_table, 617 .child = random_table,
617 }, 618 },
618 { 619 {
620 .procname = "usermodehelper",
621 .mode = 0555,
622 .child = usermodehelper_table,
623 },
624 {
619 .procname = "overflowuid", 625 .procname = "overflowuid",
620 .data = &overflowuid, 626 .data = &overflowuid,
621 .maxlen = sizeof(int), 627 .maxlen = sizeof(int),