Wednesday, May 21, 2014

[OSDI] ramfs XOR Encryption/Decryption while Read/Write in Kernel Code


This time, in our OSDI lab, we are asked to implement a XOR Encryption/Decryption while someone is read/write the ramfs.

Setup the Flag (switch on/off the encryption/decryption feature)

Register a proc_dir_entry in fs/ramfs/inode.c to listen the external controls of the flag. A proc_read and a proc_write function is required. So...

Global Variables & Macros

#define MAX_PROC_SIZE 100
int ramfs_flag;
static struct proc_dir_entry *proc_entry;

(register) Modify init_ramfs_fs

static int __init init_ramfs_fs(void)
{
    // ODSI lab 10
    proc_entry = create_proc_entry("flag", 0644, NULL);
    proc_entry->read_proc = my_read;
    proc_entry->write_proc = my_write;
    ramfs_flag = 0;
    //
    return register_filesystem(&ramfs_fs_type);
}

(remove) Modifiy exit_ramfs_fs

static void __exit exit_ramfs_fs(void)
{
    // ODSI lab 10
    remove_proc_entry("flag", NULL);
    unregister_filesystem(&ramfs_fs_type);
}

(read) Add proc_read Handler

static int my_read(char *buf, char **start, off_t offset, int count, int *eof, void *data) {
    int len=0;
    len = sprintf(buf,"%d\n", ramfs_flag);

    return len;
}

(write) Add proc_write Handler

static int my_write(struct file *file, const char *buf, unsigned long count, void *data) {

    char t_data[MAX_PROC_SIZE];
    if(copy_from_user(t_data, buf, count))      return -EFAULT;

    if(t_data[0] != '0' && t_data[0] != '1'){
        printk("garbage ignored\n");
        return count;   // just ignore
    }
    printk("my_write get : %s\n", t_data);

    // success setup
    if(t_data[0] == '0')                        ramfs_flag = 0;
    else if(t_data[0] == '1')                   ramfs_flag = 1;
    printk("ramfs_flag = %d ramfs_addr = %p\n", ramfs_flag, &ramfs_flag);
    return count;

}

Implement the Encryption/Decryption when the Flag is Up

Make our Flag Accessible by the MMU (fs/ramfs/internal.h)

extern const struct address_space_operations ramfs_aops;
extern const struct inode_operations ramfs_file_inode_operations;
extern int ramfs_flag;

Switch to Our Custom Handlers for MMU Read/Write (mm/file-mmu.c)

const struct file_operations ramfs_file_operations = {
 .read  = do_sync_read,
 .aio_read = my_aio_read,   // OSDI lab 10
 .write  = do_sync_write,
 .aio_write = my_aio_write,   // OSDI lab 10
 .mmap  = generic_file_mmap,
 .fsync  = simple_sync_file,
 .splice_read = generic_file_splice_read,
 .splice_write = generic_file_splice_write,
 .llseek  = generic_file_llseek,
};

Encryption (mm/file-mmu.c)

ssize_t my_aio_write(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) {

    printk("OSDI: custom write\n");
    if(ramfs_flag && iov!=NULL) {
        size_t i;
        char *ib = (char *)iov->iov_base;
        for( i=0 ; i < iov->iov_len ; i++ )   ib[i] ^= ENCODE_KEY;
        printk("ramfs_flag is up\n");
    }

    return generic_file_aio_write(iocb, iov, nr_segs, pos);
}

Decryption (mm/file-mmu.c)

ssize_t my_aio_read(struct kiocb *iocb, const struct iovec *iov, unsigned long nr_segs, loff_t pos) {
    ssize_t r;
    printk("OSDI: custom read\n");
    r = generic_file_aio_read(iocb, iov, nr_segs, pos);
    if(ramfs_flag) {
        size_t i;
        char *ib = (char *)iov->iov_base;
        for( i=0 ; i < iov->iov_len ; i++ )   ib[i] ^= ENCODE_KEY;
        printk("ramfs_flag is up\n");
    }
    return r;
}

p.s. ENCODE_KEY can be any character size constant (ex. 0x25)

Run & Test

It should be something like this:
>> mount -t ramfs ramfs /mnt/ramfs/
>> cd /mnt/ramfs/
>> cat /proc/flag
0
>> echo 1 > /proc/flag
>> cat /proc/flag
1
>> echo hello > test
>> cat test
hello
>> echo 0 > /proc/flag
>> cat test
M@IIJ/

More