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

Apache a2ensite with "Error! Site Does Not Exists"

Problem

I am now using "Apache/2.4.9 (Ubuntu)". After editing apache configuration file for my virtual hosted site (mysite.com), I found an error while trying to enable my site:
>> sudo a2ensite mysite.com

however, it returns:
"Error: mysite.com does not exists"

Solution

So, I start to trace the problem. a2ensite is simply a perl script, we can open it with a text editor, and I saw:


This means that only filenames with ".conf" at the end is allowed. Therefore, I have to rename my setting file for mysite.com:
mv /etc/apache2/sites-available/mysite.com /etc/apache2/sites-available/mysite.com.conf
This time, it works!

Discussion

I don't understand why the developer of this code brought out this design. Lots of people are used to name the configuration with their site domain name with ".conf". And, the developer should display more error message if ".conf" is required instead of only showing "does not exists" message.

Reference

Tuesday, May 20, 2014

Post on Facebook Page using Facebook Graph API

Actually, Facebook offers a great API that is flexible and simple. However, I feel that is hard to get started if lacking the passion to read their document.

* I'm using hello.com as my host server for example here.

Preparation

  • Read Facebook Official Document - Access Token
  • Create a Facebook App on Developer Facebook
    • find out App ID and App Secret, you will need them later
    • put http://hello.com in Settings->Website->Site URL and Mobile Site URL
    • since we will need "manage_pages" permission, select it from the list and submit for approve by Facebook (it takes around a week)
  • Create a simple file that prints GET parameters on your server code. Ex, put <?php print_r($_GET);?> as index.php in hello.com

Variables

  • App ID
  • App Secret
  • URL (hello.com)
  • code
  • access_token
  • Facebook Page ID
  • message (the content you want to post on the page)

Facebook Graph APIs

  • https://www.facebook.com/dialog/oauth?client_id=<App ID>&redirect_uri=http://hello.com/&scope=manage_pages,publish_stream
    • you will get "code" from GET parameters in hello.com/index.php
  • https://graph.facebook.com/oauth/access_token?client_id=<App ID>&redirect_uri=http://hello.com/&client_secret=<App Secret>&code=<code>
    • you will get "access_token" and its expire time
  • https://graph.facebook.com/me/accounts?access_token=<access_token>
    • this checks the user's account information (his/her Facebook pages)
  • https://graph.facebook.com/<Facebook Page ID>/feed?access_token=<access_token>&message=<message>
    • the message is posted on the page

Reference

  

Wednesday, May 7, 2014

[OSDI] Adding Snapshot Feature for Block Devices in Kernel Code

It's the 8th lab for the OSDI course. This one is fun and interesting. We are asked to write a snapshot taking code in kernel code, so we can go back to the past status of a block device.

How to Implement

We are adding two new ioctl numbers to the block devices. One is SNAPSHOT, and another is ROLLBACK. By calling SNAPSHOT, the kernel code switch to the snapshot mode, and things are written into shadow pages instead of normal pages. By calling ROLLBACK, the shadow pages are removed, and we are back to the previous stage (snapshot).

Edit drivers/block/brd.c

I am editing the kernel code on version 2.6.32.60 (original here)

Global and Macros

  • pick not-used ioctl (I/O control) number for our custom ioctl number
  • MASK for distinguishing normal pages and shadow pages in page->index. I am using the most significant bit. If 1, it's a shadow page; otherwise, a normal page.
    • #define SHADOW_MASK (1 << ((sizeof(pgoff_t))*8-1))

Modify brd_lookup_page

Add the following to handle page lookup actions when snapshot is enabled:
    if( snapshot_enable ) {
        // is read request? yes
        // read from shadow page if exist
        rcu_read_lock();
        idx = (sector >> PAGE_SECTORS_SHIFT) | SHADOW_MASK; // shadow
        page = radix_tree_lookup(&brd->brd_pages, idx);
        rcu_read_unlock();

        BUG_ON(page && page->index != idx);
        if( page ){
            printk("[osdi] one shadow page is created\n");
            return page;
        }
    }
This means that when snapshot is enabled, we return its shadow page if exists.

Modify brd_insert_page

Add the following code at the beginning:
if (!snapshot_enable && page)   return page;
if (snapshot_enable && page && ((page->index & SHADOW_MASK)!=0))    return page;

And, add the following line before "radix_tree_insert":
if (snapshot_enable)    idx |= SHADOW_MASK;
So, this means that if it's under snapshot mode, we should always insert pages into shadow pages (if there's no a shadow page for it so far, then create one). If it's not under snapshot mode, then follow the old rules.

Modify brd_ioctl

Make sure that we detect the new ioctl numbers and call their handlers.

static int brd_ioctl(struct block_device *bdev, fmode_t mode,
                        unsigned int cmd, unsigned long arg)
{
    int error;
    struct brd_device *brd = bdev->bd_disk->private_data;

    if (cmd == BLKFLSBUF) {
            ...
        return error;
    }
    else if (cmd == SNAPSHOT) {
        error = snapshot_handler();
        return error;
    }
    else if (cmd == ROLLBACK) {
        error = rollback_handler(bdev);
        return error;
    }
    return -ENOTTY;
}

SNAPSHOT & ROLLBACK Handlers

static int snapshot_handler() {
    snapshot_enable = TRUE;
    return 0;
}

static int rollback_handler(struct block_device *bdev) {
    int error = 0;
    struct brd_device *brd = bdev->bd_disk->private_data;

    snapshot_enable = FALSE;
    brd_free_shadow_pages(brd);

    if(error)   printk("[osdi] error = %d\n", error);
    return error;
}

* brd_free_shadow_pages is almost as same as brd_free_pages but adding "if( (pages[i]->index & SHADOW_MASK)==0 )" in the beginning of the for loop.

Full Code ( drivers/block/brd.c )

In this codepad, please let me know if the link is not working.

User Programs

To control our devices, we need to write our user programs. Here, we will use /dev/ram0 as our block device.

SNAPSHOT.c

#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>

#include <stdio.h>

#define SNAPSHOT _IO(0x10, 30)

int main() {

    int fb;

    fb = open("/dev/ram0", O_RDWR);
    if( fb<0 ) {
        printf("can't access the device\n");
        return -1;
    }
    ioctl(fb, SNAPSHOT, 0);
    close(fb);

    return 0;
}

ROLLBACK.c

#include<unistd.h>
#include<sys/ioctl.h>
#include<fcntl.h>

#include <stdio.h>

#define ROLLBACK _IO(0x10, 31)

int main() {

    int fb;

    fb = open("/dev/ram0", O_RDWR);
    if( fb<0 ) {
        printf("can't access the device\n");
        return -1;
    }
    ioctl(fb, ROLLBACK, 0);
    close(fb);

    return 0;
}

Makefile

CC=gcc
all: ROLLBACK.c SNAPSHOT.c
        -$(CC) ROLLBACK.c -o ROLLBACK
        -$(CC) SNAPSHOT.c -o SNAPSHOT

Run & Test

  • sudo mke2fs -m 0 /dev/ram0 # format the ram partition to ext2
  • sudo mkdir /ramdisk/
  • sudo mount /dev/ram0 /ramdisk/
  • sudo mount # check if it's mounted successfully
  • sudo mkdir /ramdisk/TEST1
  • sudo umount /ramdisk/
We've created a directory called "TEST1", and umount the ram disk. It's time to take a snapshot!
  • sudo ./SNAPSHOT
  • sudo mount /dev/ram0 /ramdisk/
  • sudo mkdir /ramdisk/TEST2
  • sudo umount /ramdisk/
Now, we've create the second directory called "TEST2", and it's time to test rollback.
  • sudo ./ROLLBACK
  • sudo mount /dev/ram0 /ramdisk/
  • ls /ramdisk 
In the last command, we should only see the folder "TEST1" without "TEST2". So, the snapshot works!

Question Raised

Q: Why we always umount the device when running our SNAPSHOT or ROLLBACK user program in this lab?
A: Since we are checking if the device is being used or not in ROLLBACK, we should umount before asking the device to ROLLBACK.

Tuesday, May 6, 2014

Vim + LaTeX

Intro

I've played around LaTeX this morning. There are different tools for compiling LaTeX files.

Online Web Tools

Writing LaTeX online is possible. It's easy to share the files and no need to install any extra software on the machine. However, it has a long latency for PDF preview. Some good web-based LaTeX editors are as below:
writeLATEX

Desktop GUI Tools

Texmaker

Desktop Command Tools

Under Fedora, install:
  • sudo yum install tetex-latex
Simple setup in .vimrc:
  • map \t <ESC>:!pdflatex %<CR><CR>

Sunday, May 4, 2014

Defend from DirBuster (avoid brute force directories and files names on web/application servers)

* Trying to brute force directories and files names on deployed servers is ILLEGAL!

Some fake hackers like to hack deployed servers by using tools like "DirBuster"; however, it's easy to defend. Also remember that applying brute force on deployed server is ILLEGAL and that will get you into trouble.

I am using IP 123.123.123.123 as the attacker's IP here.

1. Ban the attacker's IP

In the apache setting file add:
<Location />
        Order deny,allow
        Deny from 123.123.123.123
</Location>

2. Setup mod_evasive

Follow the instruction here, which is:
  • apt-get install apache2-utils
  • make sure module configuration is on in Apache setting:
    • Include mods-enabled/*.load
    • Include mods-enabled/*.conf
  • configure DOS parameters by adding following into .conf file of the site
<IfModule mod_evasive20.c>
DOSHashTableSize 3097
DOSPageCount 2
DOSSiteCount 50
DOSPageInterval 1
DOSSiteInterval 1
DOSBlockingPeriod 60
DOSEmailNotify someone@somewhere.com
</IfModule>
To test, run this perl script: https://github.com/KoHead/mod_evasive/blob/master/test.pl

3. Setup Nagios with notifications

Setup a system monitoring program on the server, so if there's anything abnormal the administrator will receive emails immediately. Check: http://www.nagios.org/

Saturday, May 3, 2014

Start VMWare Fusion from Script

I am now using VMWare Fusion, and not willing to switch between the terminal and VMWare manager. So, I came up this script which allow me to start the VM and ssh into the machine by one-line command.

Start
./kali start
Stop
./kali stop
Status
./kali status