Saturday, October 31, 2009

Spy mode inotify

Although linux inotify(7) user-space interface allows to catch events on file operations it doesn't provide a way to answer a simple question: who the hell is accessing my files. By 'who' I mean which process, of course. So if we want to get an answer for that question we have to go into kernel space. (Constantly doing 'lsof|grep' is out of question here.)

Little thinking give us a hope this can be done with quite simple kernel module. Indeed, inotify handler must be called just in the moment a file is accessed, meaning inotify handler is running in context of the process which is accessing a file. So our inotify handler can just output to dmesg desired fields from 'current' task. This should work until some sophisticated buffering is introduced between file operations and inotify handlers.

Kernel provides an easy way to use self-written inotify handler in a kernel module. See Documentation/filesystems/inotify.txt for details.

The code below just exhibits the described approach works. For trying it do
insmod spy_inotify.ko path=file_to_spy_on

Then access that file and watch dmesg for messages like the following
[spy_inotify] Catched inotify event for file ../../cpufreq.c. mask=32, pid=8183 executable="less"



spy_inotify.c
/*  spy_inotify - linux kernel `module for spying on inotify events
*  prints to dmesg _who_ exactly is doing something with a file
*
*  Copyright (C) 2009 Ruslan Savchenko
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License as published by
*  the Free Software Foundation; either version 2 of the License, or
*  (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program; if not, write to the Free Software
*  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/inotify.h>
#include <linux/sched.h>
#include <linux/string.h>

#define SPY_INOTIFY "[spy_inotify] "
#define MASK (IN_ACCESS | IN_ATTRIB | IN_CLOSE_WRITE | IN_CLOSE_NOWRITE \
| IN_CREATE | IN_DELETE | IN_DELETE_SELF | IN_MODIFY \
| IN_MOVE_SELF | IN_MOVED_FROM | IN_MOVED_TO | IN_OPEN )

static char *path = "/";
module_param(path, charp, 0);
MODULE_PARM_DESC(path, "Path to spy at");
static char comm[TASK_COMM_LEN];


void sinotify_event(struct inotify_watch *watch, u32 wd, u32 mask,
u32 cookie, const char *name, struct inode *inode);
void sinotify_destroy(struct inotify_watch *watch);


static struct inotify_operations si_op = {
sinotify_event, sinotify_destroy };


void sinotify_event(struct inotify_watch *watch, u32 wd, u32 mask,
u32 cookie, const char *name, struct inode *inode) {
task_lock(current);
strncpy(comm, current->comm, sizeof(current->comm));
task_unlock(current);
printk(KERN_INFO SPY_INOTIFY
"Catched inotify event for file %s. mask=%d, pid=%d executable=\"%s\"\n",
path, mask, current->tgid, comm);
}

static struct inotify_handle *ih = 0;
static struct inotify_watch watch;

void sinotify_destroy(struct inotify_watch *watch) {
}

int init_sinotify(void)
{
struct path s_path;
int err;

printk(KERN_INFO SPY_INOTIFY "Spying at %s\n", path);

if ((err = kern_path(path, LOOKUP_FOLLOW, &s_path)) != 0) {
printk(KERN_ERR SPY_INOTIFY "kern_path() returned %d\n", err);
goto out;
}

/* FIXME: crappy nullcheck and no actions on error. Odd */
if ((s_path.dentry == NULL) || (s_path.dentry->d_inode == NULL)) {
printk(KERN_ERR SPY_INOTIFY "NULL dentry or inode\n");
err = -ENOENT;
goto out;
}

ih = inotify_init(&si_op);
if (IS_ERR(ih)) {
printk(KERN_ERR SPY_INOTIFY "inotify_init() returned bad ptr\n");
err = PTR_ERR(ih);
goto out;
}

inotify_init_watch(&watch);

err = inotify_add_watch(ih, &watch, s_path.dentry->d_inode, MASK);
if (err < 0) {
printk(KERN_ERR SPY_INOTIFY
"inotify_add_watch() returned bad descriptor: %d\n", err);
goto clean_ih;
}

return 0;

clean_ih:
inotify_destroy(ih);

out:
return err;
}

void exit_sinotify(void)
{
inotify_rm_watch(ih, &watch);
inotify_destroy(ih);
printk(KERN_INFO SPY_INOTIFY "End spying\n");
}


module_init(init_sinotify);
module_exit(exit_sinotify);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ruslan Savchenko");
MODULE_DESCRIPTION("Spy-mode inotify");
Makefile
obj-m += spy_inotify.o

all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

1 comment:

mospehraict said...

Now just add calling some free virus checker like clamav and get yourself some linuxed kaspersky avp :)