aboutsummaryrefslogtreecommitdiffstats
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
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)
-rw-r--r--arch/x86/syscalls/syscall_32.tbl1
-rw-r--r--arch/x86/syscalls/syscall_64.tbl1
-rw-r--r--include/linux/syscalls.h1
-rw-r--r--kernel/module.c367
-rw-r--r--kernel/sys_ni.c1
5 files changed, 223 insertions, 148 deletions
diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl
index a47103fbc692..83b3838417ed 100644
--- a/arch/x86/syscalls/syscall_32.tbl
+++ b/arch/x86/syscalls/syscall_32.tbl
@@ -356,3 +356,4 @@
356347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv 356347 i386 process_vm_readv sys_process_vm_readv compat_sys_process_vm_readv
357348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev 357348 i386 process_vm_writev sys_process_vm_writev compat_sys_process_vm_writev
358349 i386 kcmp sys_kcmp 358349 i386 kcmp sys_kcmp
359350 i386 finit_module sys_finit_module
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index a582bfed95bb..7c58c84b7bc8 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -319,6 +319,7 @@
319310 64 process_vm_readv sys_process_vm_readv 319310 64 process_vm_readv sys_process_vm_readv
320311 64 process_vm_writev sys_process_vm_writev 320311 64 process_vm_writev sys_process_vm_writev
321312 common kcmp sys_kcmp 321312 common kcmp sys_kcmp
322313 common finit_module sys_finit_module
322 323
323# 324#
324# x32-specific system call numbers start at 512 to avoid cache impact 325# x32-specific system call numbers start at 512 to avoid cache impact
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 727f0cd73921..32bc035bcd68 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -868,4 +868,5 @@ asmlinkage long sys_process_vm_writev(pid_t pid,
868 868
869asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, 869asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type,
870 unsigned long idx1, unsigned long idx2); 870 unsigned long idx1, unsigned long idx2);
871asmlinkage long sys_finit_module(int fd, const char __user *uargs);
871#endif 872#endif
diff --git a/kernel/module.c b/kernel/module.c
index 6e48c3a43599..6d2c4e4ca1f5 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) {
</