Sealing the kernel

John Langford (l_k_account@gs176.sp.cs.cmu.edu)
Tue, 26 Oct 1999 10:58:56 -0400 (EDT)


I decided to implement an idea from phrack-55, namely making a module to
seal the kernel so that arbitrary kernel-mode access is not available to
root. This has some obvious use for paranoid sysadmins against trojan
kernel modules. The code is pretty simple - it just overwrites the
pointer to sys_create_module, sys_init_module, and the open function for
/dev/mem (and /dev/kmem) (Did I miss anything?).

Dimitris and I would like to push this idea a little bit farther. We want
to allow only a few pre-selected modules to be loadable. The obvious way
to do this is to give the 'seal' module the md5sum of allowed modules then
calculate the md5sum of a loaded module in kernel, loading the module if
the md5sum matches.

Unfortunately, a problem develops here, and this is where we would like
some advice. We want the md5sum to run in the kernel because we are
thinking of kernel mode as "safe" and anything in user mode as "unsafe".
But the relocation of the module happens in insmod in (untrusted) user
mode. Naturally, modules relocated to different addresses have different
md5sums. There are several possible work arounds:

1. Try to guess the most likely eventual module relocation locations and
allow the md5sum of the relocated module to match any one of the
possibilities.
- messy and likely to fail to work.
2. Shift module relocation into kernel mode doing the md5sum as soon as it
is seen.
- feasible but a fairly large change in the way things are done. All of
a sudden the create_module syscall is useless.
3. Make create_module pass back a dummy relocation (0x00000000?) then
complete the relocation in kernel mode if the module passes an md5sum.
- inelegant, but maintains backwards compatibility
4. Try to just do an md5sum on unrelocated portions of the module
- nasty and it's unclear that this is "safe".

Are there other methods? What's best?

-John

Here's the initial code we have:
/*
* seal.o - seal the kernel so no further modules may be loaded.
* Also disables /dev/kmem
* gcc -O6 -c seal.c -I/usr/src/linux/include -DMODULE -D__KERNEL__
* jcl@cmu.edu & dmarg@cs.cmu.edu
*/

#include <linux/config.h>
#include <linux/module.h>
#include <linux/version.h>

#include <linux/mm.h>

char *db;
MODULE_PARM(db, "s");

typedef int (*sysfun_p)();
extern sysfun_p sys_call_table[];

#define PASS 0
#define DEBUG 1

sysfun_p realsys_create_module;
sysfun_p realsys_init_module;
sysfun_p mem_open;

struct md5_name {
char *modname;
char md5sum[16];
} *md5map;

int no_create_module(const char *name_user, size_t size)
{
printk(KERN_DEBUG "seal: Attempt to create module.\n");
if ( PASS ) realsys_create_module(name_user, size);
else
return -EPERM;
}

int no_init_module(const char *name_user, struct module *mod_user)
{
printk(KERN_DEBUG "seal: Attempt to init module.\n");
if ( PASS ) realsys_init_module(name_user, mod_user);
else
return -EPERM;
}

int no_delete_module(const char *name_user)
{
printk(KERN_DEBUG "seal: Attempt to delete module.\n");
return -EPERM;
}

int no_open_mem(struct inode * bar, struct file * foo)
{
printk(KERN_DEBUG "seal: Attempt to open /dev/mem or /dev/kmem.\n");
return -EPERM;
}

int init_module() {
struct dentry * dentry;
int result, fd;
struct stat stat_buf;
int flen;
int nlines;

printk(KERN_DEBUG "seal: In init_modules.\n");
realsys_create_module = sys_call_table[127];
realsys_init_module = sys_call_table[128];
sys_call_table[127] = no_create_module;
sys_call_table[128] = no_init_module;

dentry = open_namei("/dev/mem",0,0);
if (IS_ERR(dentry)) {
printk(KERN_DEBUG "seal: Can not seal /dev/mem\n");
return -EFAULT;
}
else
if (dentry->d_inode && dentry->d_inode->i_op && dentry->d_inode->i_op->default_file_ops &&
dentry->d_inode->i_op->default_file_ops->open) {
mem_open = dentry->d_inode->i_op->default_file_ops->open;
dentry->d_inode->i_op->default_file_ops->open = no_open_mem;
}

return 0;
}

void cleanup_module() {
struct dentry * dentry;

printk(KERN_DEBUG "seal: In cleanup_module.\n");
if (DEBUG) {
sys_call_table[127]=realsys_create_module;
sys_call_table[128]=realsys_init_module;

dentry = open_namei("/dev/mem",0,0);
if (IS_ERR(dentry))
printk(KERN_DEBUG "seal: Can not seal /dev/mem\n");
else
if (dentry->d_inode && dentry->d_inode->i_op && dentry->d_inode->i_op->default_file_ops &&
dentry->d_inode->i_op->default_file_ops->open) {
dentry->d_inode->i_op->default_file_ops->open = mem_open;
}
}
}

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/