aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2012-10-15 17:01:07 -0400
committerRusty Russell <rusty@rustcorp.com.au>2012-12-13 21:35:22 -0500
commit34e1169d996ab148490c01b65b4ee371cf8ffba2 (patch)
tree3380af46682ce4396c1524bdba8badcab8a51046 /kernel
parent84ecfd15f5547c992c901df6ec14b4d507eb2c6e (diff)
module: add syscall to load module from fd
As part of the effort to create a stronger boundary between root and kernel, Chrome OS wants to be able to enforce that kernel modules are being loaded only from our read-only crypto-hash verified (dm_verity) root filesystem. Since the init_module syscall hands the kernel a module as a memory blob, no reasoning about the origin of the blob can be made. Earlier proposals for appending signatures to kernel modules would not be useful in Chrome OS, since it would involve adding an additional set of keys to our kernel and builds for no good reason: we already trust the contents of our root filesystem. We don't need to verify those kernel modules a second time. Having to do signature checking on module loading would slow us down and be redundant. All we need to know is where a module is coming from so we can say yes/no to loading it. If a file descriptor is used as the source of a kernel module, many more things can be reasoned about. In Chrome OS's case, we could enforce that the module lives on the filesystem we expect it to live on. In the case of IMA (or other LSMs), it would be possible, for example, to examine extended attributes that may contain signatures over the contents of the module. This introduces a new syscall (on x86), similar to init_module, that has only two arguments. The first argument is used as a file descriptor to the module and the second argument is a pointer to the NULL terminated string of module arguments. Signed-off-by: Kees Cook <keescook@chromium.org> Cc: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (merge fixes)
Diffstat (limited to 'kernel')
-rw-r--r--kernel/module.c367
-rw-r--r--kernel/sys_ni.c1
2 files changed, 220 insertions, 148 deletions
diff --git a/kernel/module.c b/kernel/module.c
index 6e48c3a4359..6d2c4e4ca1f 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -21,6 +21,7 @@
21#include <linux/ftrace_event.h> 21#include <linux/ftrace_event.h>
22#include <linux/init.h> 22#include <linux/init.h>
23#include <linux/kallsyms.h> 23#include <linux/kallsyms.h>
24#include <linux/file.h>
24#include <linux/fs.h> 25#include <linux/fs.h>
25#include <linux/sysfs.h> 26#include <linux/sysfs.h>
26#include <linux/kernel.h> 27#include <linux/kernel.h>
@@ -2425,18 +2426,17 @@ static inline void kmemleak_load_module(const struct module *mod,
2425#endif 2426#endif
2426 2427
2427#ifdef CONFIG_MODULE_SIG 2428#ifdef CONFIG_MODULE_SIG
2428static int module_sig_check(struct load_info *info, 2429static int module_sig_check(struct load_info *info)
2429 const void *mod, unsigned long *_len)
2430{ 2430{
2431 int err = -ENOKEY; 2431 int err = -ENOKEY;
2432 unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1; 2432 const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
2433 unsigned long len = *_len; 2433 const void *mod = info->hdr;
2434 2434
2435 if (len > markerlen && 2435 if (info->len > markerlen &&
2436 memcmp(mod + len - markerlen, MODULE_SIG_STRING, markerlen) == 0) { 2436 memcmp(mod + info->len - markerlen, MODULE_SIG_STRING, markerlen) == 0) {
2437 /* We truncate the module to discard the signature */ 2437 /* We truncate the module to discard the signature */
2438 *_len -= markerlen; 2438 info->len -= markerlen;
2439 err = mod_verify_sig(mod, _len); 2439 err = mod_verify_sig(mod, &info->len);
2440 } 2440 }
2441 2441
2442 if (!err) { 2442 if (!err) {
@@ -2454,59 +2454,97 @@ static int module_sig_check(struct load_info *info,
2454 return err; 2454 return err;
2455} 2455}
2456#else /* !CONFIG_MODULE_SIG */ 2456#else /* !CONFIG_MODULE_SIG */
2457static int module_sig_check(struct load_info *info, 2457static int module_sig_check(struct load_info *info)
2458 void *mod, unsigned long *len)
2459{ 2458{
2460 return 0; 2459 return 0;
2461} 2460}
2462#endif /* !CONFIG_MODULE_SIG */ 2461#endif /* !CONFIG_MODULE_SIG */
2463 2462
2464/* Sets info->hdr, info->len and info->sig_ok. */ 2463/* Sanity checks against invalid binaries, wrong arch, weird elf version. */
2465static int copy_and_check(struct load_info *info, 2464static int elf_header_check(struct load_info *info)
2466 const void __user *umod, unsigned long len,
2467 const char __user *uargs)
2468{ 2465{
2469 int err; 2466 if (info->len < sizeof(*(info->hdr)))
2470 Elf_Ehdr *hdr; 2467 return -ENOEXEC;
2468
2469 if (memcmp(info->hdr->e_ident, ELFMAG, SELFMAG) != 0
2470 || info->hdr->e_type != ET_REL
2471 || !elf_check_arch(info->hdr)
2472 || info->hdr->e_shentsize != sizeof(Elf_Shdr))
2473 return -ENOEXEC;
2474
2475 if (info->hdr->e_shoff >= info->len
2476 || (info->hdr->e_shnum * sizeof(Elf_Shdr) >
2477 info->len - info->hdr->e_shoff))
2478 return -ENOEXEC;
2471 2479
2472 if (len < sizeof(*hdr)) 2480 return 0;
2481}
2482
2483/* Sets info->hdr and info->len. */
2484static int copy_module_from_user(const void __user *umod, unsigned long len,
2485 struct load_info *info)
2486{
2487 info->len = len;
2488 if (info->len < sizeof(*(info->hdr)))
2473 return -ENOEXEC; 2489 return -ENOEXEC;
2474 2490
2475 /* Suck in entire file: we'll want most of it. */ 2491 /* Suck in entire file: we'll want most of it. */
2476 if ((hdr = vmalloc(len)) == NULL) 2492 info->hdr = vmalloc(info->len);
2493 if (!info->hdr)
2477 return -ENOMEM; 2494 return -ENOMEM;
2478 2495
2479 if (copy_from_user(hdr, umod, len) != 0) { 2496 if (copy_from_user(info->hdr, umod, info->len) != 0) {
2480 err = -EFAULT; 2497 vfree(info->hdr);
2481 goto free_hdr; 2498 return -EFAULT;
2482 } 2499 }
2483 2500
2484 err = module_sig_check(info, hdr, &len); 2501 return 0;
2502}
2503
2504/* Sets info->hdr and info->len. */
2505static int copy_module_from_fd(int fd, struct load_info *info)
2506{
2507 struct file *file;
2508 int err;
2509 struct kstat stat;
2510 loff_t pos;
2511 ssize_t bytes = 0;
2512
2513 file = fget(fd);
2514 if (!file)
2515 return -ENOEXEC;
2516
2517 err = vfs_getattr(file->f_vfsmnt, file->f_dentry, &stat);
2485 if (err) 2518 if (err)
2486 goto free_hdr; 2519 goto out;
2487 2520
2488 /* Sanity checks against insmoding binaries or wrong arch, 2521 if (stat.size > INT_MAX) {
2489 weird elf version */ 2522 err = -EFBIG;
2490 if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0 2523 goto out;
2491 || hdr->e_type != ET_REL
2492 || !elf_check_arch(hdr)
2493 || hdr->e_shentsize != sizeof(Elf_Shdr)) {
2494 err = -ENOEXEC;
2495 goto free_hdr;
2496 } 2524 }
2497 2525 info->hdr = vmalloc(stat.size);
2498 if (hdr->e_shoff >= len || 2526 if (!info->hdr) {
2499 hdr->e_shnum * sizeof(Elf_Shdr) > len - hdr->e_shoff) { 2527 err = -ENOMEM;
2500 err = -ENOEXEC; 2528 goto out;
2501 goto free_hdr;
2502 } 2529 }
2503 2530
2504 info->hdr = hdr; 2531 pos = 0;
2505 info->len = len; 2532 while (pos < stat.size) {
2506 return 0; 2533 bytes = kernel_read(file, pos, (char *)(info->hdr) + pos,
2534 stat.size - pos);
2535 if (bytes < 0) {
2536 vfree(info->hdr);
2537 err = bytes;
2538 goto out;
2539 }
2540 if (bytes == 0)
2541 break;
2542 pos += bytes;
2543 }
2544 info->len = pos;
2507 2545
2508free_hdr: 2546out:
2509 vfree(hdr); 2547 fput(file);
2510 return err; 2548 return err;
2511} 2549}
2512 2550
@@ -2945,33 +2983,123 @@ static bool finished_loading(const char *name)
2945 return ret; 2983 return ret;
2946} 2984}
2947 2985
2986/* Call module constructors. */
2987static void do_mod_ctors(struct module *mod)
2988{
2989#ifdef CONFIG_CONSTRUCTORS
2990 unsigned long i;
2991
2992 for (i = 0; i < mod->num_ctors; i++)
2993 mod->ctors[i]();
2994#endif
2995}
2996
2997/* This is where the real work happens */
2998static int do_init_module(struct module *mod)
2999{
3000 int ret = 0;
3001
3002 blocking_notifier_call_chain(&module_notify_list,
3003 MODULE_STATE_COMING, mod);
3004
3005 /* Set RO and NX regions for core */
3006 set_section_ro_nx(mod->module_core,
3007 mod->core_text_size,
3008 mod->core_ro_size,
3009 mod->core_size);
3010
3011 /* Set RO and NX regions for init */
3012 set_section_ro_nx(mod->module_init,
3013 mod->init_text_size,
3014 mod->init_ro_size,
3015 mod->init_size);
3016
3017 do_mod_ctors(mod);