Logo Search packages:      
Sourcecode: dazuko-source version File versions  Download package

dazuko.c

/* Dazuko. Allow file access control for 3rd-party applications.
   Copyright (C) 2002,2003 H+BEDV Datentechnik GmbH
   Written by Martin Ritter <mritter@antivir.de>
              John Ogness <jogness@antivir.de>

   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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include <linux/modversions.h>
#endif

#include <linux/kernel.h>
#include <linux/version.h>

#ifdef MODULE
#include <linux/module.h>
#endif

#ifndef KERNEL_VERSION
#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))
#endif

#ifdef DEBUG
#define DPRINTK(x) printk x
#else
#define DPRINTK(x)
#endif

#include <linux/init.h>

#include <linux/unistd.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>

#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif

#ifdef CONFIG_SMP
#ifndef __SMP__
#define __SMP__
#endif
#endif

#ifdef __SMP__
#include <asm/smplock.h>
#endif
#include "dazuko.h"

#define NUM_SLOT_LISTS  5
#define NUM_SLOTS 25

#define     SCAN_ON_OPEN            (access_mask & ON_OPEN)
#define     SCAN_ON_CLOSE           (access_mask & ON_CLOSE)
#define     SCAN_ON_EXEC            (access_mask & ON_EXEC)
#define     SCAN_ON_CLOSE_MODIFIED  (access_mask & ON_CLOSE_MODIFIED)

#define     FREE  0     /* the daemon is not ready */
#define     READY 1     /* a daemon waits for something to do */
#define     WAITING     2     /* a request is waiting to be served */
#define     WORKING     3     /* daemon is currently in action */
#define     DONE  4     /* daemon response is available */

#define     BROKEN      5     /* invalid state (interrupt from ready,waiting) */

#ifdef HIDDEN_SCT
void **sys_call_table;
void **get_sct();
extern asmlinkage long sys_close(unsigned int fd);
#else
extern void *sys_call_table[];
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
int dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos);
#else
ssize_t dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos);
#endif
int dazuko_device_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param);
int dazuko_device_open(struct inode *inode, struct file *file);
int dazuko_device_release(struct inode *inode, struct file *file);

struct path_t
{
      /* A node in a linked list of paths. Used
       * for the include and exclude lists. */

      struct path_t     *next;
      int         len;
      char        path[1];    /* this MUST be at the end of the struct */
};

struct hash_t
{
      /* A node in a linked list of filenames.
       * Used for the list of files to be
       * scanned on close. */

      struct hash_t     *next;
      struct file *file;
      int         dirty;
      int         namelen;
      char        name[1];    /* this MUST be at the end of the struct */
};

struct slot_t
{
      /* A representation of a daemon. It holds
       * all information about the daemon, the
       * file that is scanned, and the state of
       * the scanning process. */

      int               id;         
      int               pid;        /* pid of our daemon */
      int               state;
      int               response;
      int               event;
      int               o_flags;
      int               o_mode;
      int               kuid;       /* user id of the kernel process */
      int               kpid;       /* process id of the kernel process */
      int               filenamelength;   /* not including terminator */
      char              *filename;
      struct semaphore  mutex;
};

struct slot_list_container_t
{
      struct slot_list_t      *slot_list;
      struct semaphore  mutex;
};

struct slot_list_t
{
      atomic_t          use_count;
      struct slot_t           slots[NUM_SLOTS];
      char              reg_name[1];      /* this MUST be at the end of the struct */
};

struct dazuko_file_struct
{
      /* A structure designed for simple and
       * intelligent memory management when
       * doing filename lookups in the kernel. */

      const char        *user_filename;         /* userspace filename */
      int               should_scan;            /* already know we need to scan? */
      int               filename_length;  /* length of filename */
      char              *filename;        /* kernelspace filename */
      int               putname_filename; /* flag to clean up filename */
      int               full_filename_length;   /* length of filename */
      char              *full_filename;         /* kernelspace filename with full path */
      int               free_full_filename;     /* flag to clean up full_filename */
      struct dentry           *dentry;          /* used to get inode */
      int               dput_dentry;            /* flag to clean up dentry */
      char              *buffer;          /* used to get full path */
      int               free_page_buffer; /* flag to clean up buffer */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      struct nameidata  nd;               /* used to get full path */
      int               path_release_nd;  /* flag to clean up nd */
      struct vfsmount         *vfsmount;        /* used to get full path */
      int               mntput_vfsmount;  /* flag to clean up vfsmount */
#endif
};

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
static struct vfsmount        *orig_rootmnt = NULL;
#endif

static struct dentry                *orig_root = NULL;
static char                   access_mask = 7;
static struct slot_list_container_t slot_lists[NUM_SLOT_LISTS];
static struct path_t                *incl_paths = NULL;
static struct path_t                *excl_paths = NULL;
static struct hash_t                *hash = NULL;
static int                    dev_major = -1;
static rwlock_t                     lock_hash;
static rwlock_t                     lock_lists;
static atomic_t                     active;

#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
static asmlinkage long        (*original_sys_open)(const char *filename, int flags, int mode);
#endif
#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
static asmlinkage long        (*original_sys_close)(unsigned int fd);
#endif
#ifdef ON_CLOSE_MODIFIED_SUPPORT
static asmlinkage ssize_t     (*original_sys_write)(unsigned int fd, char *buf, unsigned int count);
#endif
#ifdef ON_EXEC_SUPPORT
static asmlinkage int         (*original_sys_execve)(struct pt_regs regs);
#endif

static struct file_operations fops = {
                              read: dazuko_device_read,     /* read */
                              ioctl: dazuko_device_ioctl,   /* ioctl */
                              open: dazuko_device_open,     /* open */
                              release: dazuko_device_release,     /* release */
                        };

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)

static DECLARE_WAIT_QUEUE_HEAD(wait_kernel_waiting_for_free_slot);
static DECLARE_WAIT_QUEUE_HEAD(wait_daemon_waiting_for_work);
static DECLARE_WAIT_QUEUE_HEAD(wait_kernel_waiting_while_daemon_works);
static DECLARE_WAIT_QUEUE_HEAD(wait_daemon_waiting_for_free);

#else

static struct wait_queue *wait_kernel_waiting_for_free_slot;
static struct wait_queue *wait_daemon_waiting_for_work;
static struct wait_queue *wait_kernel_waiting_while_daemon_works;
static struct wait_queue *wait_daemon_waiting_for_free;

/* The following code is taken directly from Linux in the file:
   include/linux/sched.h */

#ifndef __wait_event_interruptible
#define __wait_event_interruptible(wq, condition, ret)             \
do {                                             \
      struct wait_queue __wait;                        \
                                                 \
      __wait.task = current;                           \
      add_wait_queue(&wq, &__wait);                    \
      for (;;) {                                 \
            current->state = TASK_INTERRUPTIBLE;             \
            mb();                                \
            if (condition)                             \
                  break;                               \
            if (!signal_pending(current)) {                  \
                  schedule();                    \
                  continue;                      \
            }                                    \
            ret = -ERESTARTSYS;                        \
            break;                                     \
      }                                          \
      current->state = TASK_RUNNING;                         \
      remove_wait_queue(&wq, &__wait);                 \
} while (0)
#endif

#ifndef wait_event_interruptible
#define wait_event_interruptible(wq, condition)              \
({                                               \
      int __ret = 0;                                   \
      if (!(condition))                          \
            __wait_event_interruptible(wq, condition, __ret);\
      __ret;                                           \
})
#endif

#endif

static inline void dazuko_bzero(void *p, int len)
{
      /* "zero out" len bytes starting with p */

      char  *ptr = (char *)p;

      while (len--)
            *ptr++ = 0;
}

static inline int dazuko_slot_state(struct slot_t *s)
{
      int state;

/* DOWN */
      if (down_interruptible(&(s->mutex)) != 0)
            return -EINTR;

      state = s->state;

      up(&(s->mutex));
/* UP */

      return state;
}

static inline int __dazuko_change_slot_state(struct slot_t *s, int from_state, int to_state)
{
      /* Make a predicted state transition. We fail if it
       * is an unpredicted change. We can ALWAYS go to the
       * to_state if it is the same as from_state. Not SMP safe! */

      if (to_state != from_state)
      {
            /* make sure this is a predicted transition and there
             * is a daemon on this slot (pid != 0)*/
            if (s->state != from_state || !s->pid)
                  return 0;
      }

      s->state = to_state;

      /* handle appropriate wake_up's for basic
       * state changes */

      if (to_state == READY)
      {
            wake_up(&wait_kernel_waiting_for_free_slot);
      }
      else if (to_state == FREE)
      {
            wake_up(&wait_kernel_waiting_while_daemon_works);
            wake_up(&wait_daemon_waiting_for_free);
      }

      return 1;
}

static int dazuko_change_slot_state(struct slot_t *s, int from_state, int to_state, int release)
{
      /* SMP safe version of __dazuko_change_slot_state().
       * This should only be used if we haven't
       * already aquired slot.mutex. Use this function
       * with CAUTION, since the mutex may or may not
       * be released depending on the return value AND
       * on the value of the "release" argument. */

      int   success;

      /* if we are interrupted, report the state as unpredicted */
/* DOWN */
      if (down_interruptible(&(s->mutex)) != 0)
            return 0;

      success = __dazuko_change_slot_state(s, from_state, to_state);

      /* the mutex is released if the state change was
       * unpredicted or if the called wants it released */
      if (!success || release)
            up(&(s->mutex));
/* UP */
      return success;
}

static struct slot_t * _dazuko_find_slot(int pid, int release, struct slot_list_t *sl)
{
      /* Find the first slot with the same given
       * pid number. SMP safe. Use this function
       * with CAUTION, since the mutex may or may not
       * be released depending on the return value AND
       * on the value of the "release" argument. */

      int         i;
      struct slot_t     *s = NULL;

      if (sl == NULL)
      {
            printk("dazuko: invalid slot_list given (bug!)\n");
            return NULL;
      }

      for (i=0 ; i<NUM_SLOTS ; i++)
      {
            s = &(sl->slots[i]);
/* DOWN */
            /* if we are interrupted, we say that no
            * slot was found */
            if (down_interruptible(&(s->mutex)) != 0)
                  return NULL;

            if (s->pid == pid)
            {
                  /* we release the mutex only if the
                  * called wanted us to */
                  if (release)
                        up(&(s->mutex));
/* UP */
                  return s;
            }

            up(&(s->mutex));
/* UP */
      }

      return NULL;
}

static struct slot_t * dazuko_find_slot_and_slotlist(int pid, int release, struct slot_list_t *slist, struct slot_list_t **sl_result)
{
      struct slot_t           *s;
      int               i;
      struct slot_list_t      *sl;

      if (slist == NULL)
      {
            for (i=0 ; i<NUM_SLOT_LISTS ; i++)
            {
/* DOWN */
                  /* if we are interrupted, we say that no
                  * slot was found */
                  if (down_interruptible(&(slot_lists[i].mutex)) != 0)
                        return NULL;

                  sl = slot_lists[i].slot_list;

                  up(&(slot_lists[i].mutex));
/* UP */

                  if (sl != NULL)
                  {
                        s = _dazuko_find_slot(pid, release, sl);
                        if (s != NULL)
                        {
                              /* set the current slot_list */
                              if (sl_result != NULL)
                                    *sl_result = sl;

                              return s;
                        }
                  }
            }
      }
      else
      {
            return _dazuko_find_slot(pid, release, slist);
      }

      return NULL;
}

static inline struct slot_t * dazuko_find_slot(int pid, int release, struct slot_list_t *slist)
{
      struct slot_list_t      *sl;

      return dazuko_find_slot_and_slotlist(pid, release, slist, &sl);
}

static int dazuko_insert_path_fs(struct path_t **list, char *fs_path, int fs_len)
{
      /* Create a new path_t structure and insert it
       * into the linked list given (list argument).
       * The fs_len argument is to help speed things
       * up so we don't have to calculate the length
       * of fs_path. The fs_path argument is in
       * userspace! */

      struct path_t     *newitem;
      struct path_t     *tmp;

      /* create a new path_t structure making room for path also */
      newitem = (struct path_t *)kmalloc(sizeof(struct path_t) + fs_len, GFP_KERNEL);
      if (!newitem)
            return -EFAULT;

      /* We must copy the path from userspace to kernelspace. */

      if (copy_from_user(newitem->path, fs_path, fs_len) != 0)
      {
            kfree(newitem);
            return -EFAULT;
      }

      newitem->path[fs_len] = 0;

      while (newitem->path[fs_len] == 0)
      {
            fs_len--;
            if (fs_len == 0)
                  break;
      }

      newitem->len = fs_len;

      /* we want only absolute paths */
      if (newitem->path[0] != '/')
      {
            kfree(newitem);
            return -EINVAL;
      }

      /* check if this path already exists in the list */
      for (tmp=*list ; tmp ; tmp=tmp->next)
      {
            if (newitem->len == tmp->len)
            {
                  if (memcmp(newitem->path, tmp->path, tmp->len) == 0)
                  {
                        /* we already have this path */

                        kfree(newitem);

                        return 0;
                  }
            }
      }

      DPRINTK(("dazuko: adding %s %s\n", (list == &incl_paths) ? "incl" : "excl", newitem->path));

      /* add path_t to head of linked list */
/* LOCK */
      write_lock(&lock_lists);
      newitem->next = *list;
      *list = newitem;
      write_unlock(&lock_lists);
/* UNLOCK */

      return 0;
}

static void dazuko_remove_all_hash(void)
{
      /* Empty the hash linked list. */

      struct hash_t     *tmp;

/* LOCK */
      write_lock(&lock_hash);
      while (hash)
      {
            tmp = hash;
            hash = hash->next;

            kfree(tmp);
      }
      write_unlock(&lock_hash);
/* UNLOCK */
}

static void dazuko_remove_all_paths(void)
{
      /* Empty both include and exclude path_t
       * linked lists. */

      struct path_t     *tmp;

/* LOCK */
      write_lock(&lock_lists);

      /* empty include paths list */
      while (incl_paths)
      {
            tmp = incl_paths;
            incl_paths = incl_paths->next;

            DPRINTK(("dazuko: removing incl %s\n", tmp->path));

            kfree(tmp);
      }

      /* empty exclude paths list */
      while (excl_paths)
      {
            tmp = excl_paths;
            excl_paths = excl_paths->next;

            DPRINTK(("dazuko: removing excl %s\n", tmp->path));

            kfree(tmp);
      }

      write_unlock(&lock_lists);
/* UNLOCK */
}

int dazuko_unregister_daemon(void)
{
      /* We unregister the daemon by finding the
       * slot with the same slot->pid as the the
       * current process id, the daemon. */

      struct slot_t           *s;
      struct slot_list_t      *sl;

      DPRINTK(("dazuko: dazuko_unregister_daemon() [%d]\n", current->pid));

      /* find our slot and hold the mutex
       * if we find it */
/* DOWN? */
      s = dazuko_find_slot_and_slotlist(current->pid, 0, NULL, &sl);

      if (s == NULL)
      {
            printk("dazuko: daemon %d had no slot (possible bug)\n", current->pid);
            return -EPERM;
      }

/* DOWN */

      /* clearing the pid makes the slot available */
      s->pid = 0;

      /* reset slot state */
      __dazuko_change_slot_state(s, FREE, FREE);

      atomic_dec(&(sl->use_count));

      up(&(s->mutex));
/* UP */

      /* active should always be positive here, but
       * let's check just to be sure. ;) */
      if (atomic_read(&active) > 0)
      {
            /* active and the kernel usage counter
             * should always reflect how many daemons
             * are active */

#ifdef MODULE
            MOD_DEC_USE_COUNT;
#endif
            atomic_dec(&active);
      }
      else
      {
            printk("dazuko: active count error (possible bug)\n");
      }

      /* Wake up any kernel processes that are
       * waiting for an available slot. Remove
       * all the include and exclude paths
       * if there are no more daemons */

      if (atomic_read(&active) == 0)
      {
            /* clear out include and exclude paths */
            /* are we sure we want to do this? */
            dazuko_remove_all_paths();

            /* clear out hash nodes */
            dazuko_remove_all_hash();
      }

      wake_up(&wait_kernel_waiting_for_free_slot);
      wake_up(&wait_kernel_waiting_while_daemon_works);

      return 0;
}

static inline int dazuko_is_our_daemon(void)
{
      /* Check if the current process is one
       * of the daemons. */

      return (dazuko_find_slot(current->pid, 1, NULL) != NULL);
}

int dazuko_device_release(struct inode *inode, struct file *file)
{
      DPRINTK(("dazuko: dazuko_device_release() [%d]\n", current->pid));

      /* non-root daemons are ignored */
      if (current->uid != 0)
            return 0;

      /* do not unregister if the daemon is not registered */
      if (!dazuko_is_our_daemon())
            return 0;

      dazuko_unregister_daemon();

      return 0;
}

int dazuko_device_open(struct inode *inode, struct file *file)
{
      DPRINTK(("dazuko: dazuko_device_open() [%d]\n", current->pid));

      return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
int dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos)
#else
ssize_t dazuko_device_read(struct file *file, char *buffer, size_t length, loff_t *pos)
#endif
{
      /* Reading from the dazuko device simply
       * returns the device number. This is to
       * help out the daemon. */

      char  tmp[20];
      size_t      dev_major_len;

      DPRINTK(("dazuko: dazuko_device_read() [%d]\n", current->pid));

      /* non-root daemons are ignored */
      if (current->uid != 0)
            return 0;

      if (dev_major < 0)
            return -ENODEV;

      /* print dev_major to a string
       * and get length (with terminator) */
      dazuko_bzero(tmp, sizeof(tmp));

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,8)
            dev_major_len = snprintf(tmp, sizeof(tmp), "%d", dev_major) + 1;
      #else
            dev_major_len = sprintf(tmp, "%d", dev_major) + 1;
      #endif

      if (tmp[sizeof(tmp)-1] != 0)
      {
            printk("dazuko: failing device_read, device number overflow for dameon %d (dev_major=%d)\n", current->pid, dev_major);
            return -EFAULT;
      }

      if (length < dev_major_len)
            return -EINVAL;

      /* copy dev_major string to userspace */
      if (copy_to_user(buffer, tmp, dev_major_len) != 0)
            return -EFAULT;

      return dev_major_len;
}

static int dazuko_register_daemon(const char *reg_name, int string_length)
{
      const char        *p1;
      char              *p2;
      struct slot_t           *s;
      struct slot_list_t      *sl;
      int               i;

      DPRINTK(("dazuko: dazuko_register_daemon() [%d]\n", current->pid));

      if (reg_name == NULL)
            return -EPERM;

      /* Find the slot_list with the matching name. */

      for (i=0 ; i<NUM_SLOT_LISTS ; i++)
      {
/* DOWN */
            /* if we are interrupted, we say that it
            * was interrupted */
            if (down_interruptible(&(slot_lists[i].mutex)) != 0)
                  return -EINTR;

            sl = slot_lists[i].slot_list;

            up(&(slot_lists[i].mutex));
/* UP */

            if (sl != NULL)
            {
                  p1 = reg_name;
                  p2 = sl->reg_name;

                  while (*p1 == *p2)
                  {
                        if (*p1 == 0)
                              break;

                        p1++;
                        p2++;
                  }

                  if (*p1 == *p2)
                        break;
            }
      }

      if (i == NUM_SLOT_LISTS)
      {
            /* There is no slot_list with this name. We
             * need to make one. */

            sl = (struct slot_list_t *)kmalloc(sizeof(struct slot_list_t) + string_length, GFP_KERNEL);
            if (!sl)    
                  return -EFAULT;

            dazuko_bzero(sl, sizeof(struct slot_list_t) + string_length);
            atomic_set(&(sl->use_count), 0);

            p1 = reg_name;
            p2 = sl->reg_name;

            while (*p1)
            {
                  *p2 = *p1;

                  p1++;
                  p2++;
            }
            *p2 = 0;

            /* give each slot a unique id */
            for (i=0 ; i<NUM_SLOTS ; i++)
            {
                  sl->slots[i].id = i;
                  #ifdef init_MUTEX
                        init_MUTEX(&(sl->slots[i].mutex));
                  #else
                        sema_init(&(sl->slots[i].mutex), 1);
                  #endif
            }

            /* we need to find an empty slot */
            for (i=0 ; i<NUM_SLOT_LISTS ; i++)
            {
/* DOWN */
                  /* if we are interrupted, we need to cleanup
                  * and return error */
                  if (down_interruptible(&(slot_lists[i].mutex)) != 0)
                  {
                        kfree(sl);
                        return -EINTR;
                  }

                  if (slot_lists[i].slot_list == NULL)
                  {
                        slot_lists[i].slot_list = sl;

                        up(&(slot_lists[i].mutex));
/* UP */
                        break;
                  }

                  up(&(slot_lists[i].mutex));
/* UP */
            }

            if (i == NUM_SLOT_LISTS)
            {
                  /* no empty slot :( */
                  kfree(sl);
                  return -EBUSY;
            }
      }

      /* find a slot with pid 0 and hold the mutex
      * if we find one */
/* DOWN? */
      s = dazuko_find_slot(0, 0, sl);

      if (s == NULL)
            return -EBUSY;

/* DOWN */

      /* We have found a slot, so increment the active
       * variable and the kernel module use counter.
       * The module counter will always reflect the
       * number of daemons. */

#ifdef MODULE
      MOD_INC_USE_COUNT;
#endif
      atomic_inc(&active);

      s->pid = current->pid;

      atomic_inc(&(sl->use_count));

      /* the daemon is registered, but not yet
       * ready to receive files */
      __dazuko_change_slot_state(s, FREE, FREE);

      DPRINTK(("dazuko: slot[%d] assigned to daemon %d\n", s->id, current->pid));

      up(&(s->mutex));
/* UP */

      return 0;
}

int dazuko_device_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long param)
{
      /* A daemon uses this function to interact with
       * the kernel. A daemon can set scanning parameters,
       * give scanning response, and get filenames to scan. */

      int               error;
      int               len;
      int               opt;
      struct access_t         *u_dazuko;
      struct slot_t           *s;
      char              *reg_name;
      int               i;

      /* non-root daemons are ignored */
      if (current->uid != 0)
            return 0;

      /* A macro is used to translate the cmd argument.
       * This keeps it compatible across various Linux
       * platforms. */
      switch (_IOC_NR(cmd))
      {
            case IOCTL_GET_AN_ACCESS:
                  /* The daemon is requesting a filename of a file
                   * to scan. This code will wait until a filename
                   * is available, or until we should be killed.
                   * (killing is done if any errors occur as well
                   * as when the user kills us) */

                  error = verify_area(VERIFY_WRITE, (void *)param, sizeof(struct access_t));
                  if (error)
                        return error;

                  u_dazuko = (struct access_t *)param;

tryagain:
                  /* find our slot */
                  s = dazuko_find_slot(current->pid, 1, NULL);

                  if (s == NULL)
                  {
                        i = dazuko_register_daemon("_COMPAT", 7);
                        if (i != 0)
                        {
                              printk("dazuko: unregistered daemon %d attempted to get an access\n", current->pid);
                              return -ESRCH;
                        }

                        s = dazuko_find_slot(current->pid, 1, NULL);
                        if (s == NULL)
                        {
                              printk("dazuko: unregistered daemon %d attempted to get an access\n", current->pid);
                              return -ESRCH;
                        }

                        printk("dazuko: warning: daemon %d is using a deprecated protocol\n", current->pid);
                  }

                  /* the daemon is now ready to receive a file */
                  dazuko_change_slot_state(s, READY, READY, 1);

                  if (wait_event_interruptible(wait_daemon_waiting_for_work, dazuko_slot_state(s) != READY) != 0)
                  {
                        /* The user has issued an interrupt.
                         * Return an error. The daemon should
                         * unregister itself. */

                        DPRINTK(("dazuko: daemon %d killed while waiting for work\n", current->pid));

                        if (dazuko_change_slot_state(s, READY, BROKEN, 1) || dazuko_change_slot_state(s, WAITING, BROKEN, 1))
                        {
                              wake_up(&wait_kernel_waiting_for_free_slot);
                              wake_up(&wait_kernel_waiting_while_daemon_works);
                        }

                        return -EINTR;
                  }

                  /* slot SHOULD now be in WAITING state */

                  /* we will be writing data to the slot, so
                   * we need to lock it */
/* DOWN */
                  if (down_interruptible(&(s->mutex)) != 0)
                  {
                        return -EINTR;
                  }

                  if (!__dazuko_change_slot_state(s, WAITING, WORKING))
                  {
                        /* State transition error. Try again., */

                        up(&(s->mutex));
/* UP */
                        goto tryagain;
                  }

                  /* Slot IS in WORKING state. Copy all the
                   * necessary information to userspace structure. */

                  if (copy_to_user(u_dazuko->filename, s->filename, s->filenamelength+1) != 0)
                  {
                        up(&(s->mutex));
/* UP */
                        return -EFAULT;
                  }

                  if (copy_to_user(&(u_dazuko->event), &(s->event), sizeof(int)) != 0)
                  {
                        up(&(s->mutex));
/* UP */
                        return -EFAULT;
                  }

                  if (copy_to_user(&(u_dazuko->o_flags), &(s->o_flags), sizeof(int)) != 0)
                  {
                        up(&(s->mutex));
/* UP */
                        return -EFAULT;
                  }

                  if (copy_to_user(&(u_dazuko->o_mode), &(s->o_mode), sizeof(int)) != 0)
                  {
                        up(&(s->mutex));
/* UP */
                        return -EFAULT;
                  }

                  if (copy_to_user(&(u_dazuko->uid), &(s->kuid), sizeof(int)) != 0)
                  {
                        up(&(s->mutex));
/* UP */
                        return -EFAULT;
                  }

                  if (copy_to_user(&(u_dazuko->pid), &(s->kpid), sizeof(int)) != 0)
                  {
                        up(&(s->mutex));
/* UP */
                        return -EFAULT;
                  }

                  up(&(s->mutex));
/* UP */
                  return 0;  /* no error */

            case IOCTL_RETURN_ACCESS:
                  /* The daemon has finished scanning a file
                   * and has the response to give. The daemon's
                   * slot should be in the WORKING state. */

                  error = verify_area(VERIFY_READ, (void *)param, sizeof(struct access_t));
                  if (error)
                  {
                        return error;
                  }

                  u_dazuko = (struct access_t *)param;

                  /* find our slot */
                  s = dazuko_find_slot(current->pid, 1, NULL);

                  if (s == NULL)
                  {
                        /* It appears the kernel isn't interested
                         * in us or our response. It gave our slot away! */

                        DPRINTK(("dazuko: daemon %d unexpectedly lost slot\n", current->pid));

                        return -EPERM;
                  }

                  /* we will be writing into the slot, so we
                   * need to lock it */
/* DOWN */
                  if (down_interruptible(&(s->mutex)) != 0)
                  {
                        return -EINTR;
                  }

                  if (!__dazuko_change_slot_state(s, WORKING, DONE))
                  {
                        /* The slot is in the wrong state. We will
                         * assume the kernel has cancelled the file
                         * access. */

                        DPRINTK(("dazuko: response from daemon %d on slot[%d] not needed\n", current->pid, s->id));

                        up(&(s->mutex));
/* UP */
                        return 0;
                  }

                  /* copy the response into the slot */
                  if (copy_from_user(&(s->response), &(u_dazuko->deny), sizeof(int)) != 0)
                  {
                        up(&(s->mutex));
/* UP */
                        return -EFAULT;
                  }

                  up(&(s->mutex));
/* UP */

                  /* wake up any kernel processes that are
                   * waiting for responses */
                  wake_up(&wait_kernel_waiting_while_daemon_works);

                  if (wait_event_interruptible(wait_daemon_waiting_for_free, dazuko_slot_state(s) != DONE) != 0)
                  {
                        /* The user has issued an interrupt.
                         * Return an error. The daemon should
                         * unregister itself. */

                        DPRINTK(("dazuko: daemon %d killed while waiting for response acknowledgement\n", current->pid));

                        return -EINTR;
                  }

                  return 0;

            case IOCTL_SET_OPTION:
                  /* The daemon wants to set a configuration
                   * option in the kernel. */

                  error = verify_area(VERIFY_READ, (void *)param, 2*sizeof(int));
                  if (error)
                        return error;

                  /* copy option type from userspace */
                  if (copy_from_user(&opt, (int *)param, sizeof(int)) != 0)
                        return -EPERM;

                  param += sizeof(int);

                  /* copy path length from userspace */
                  if (copy_from_user(&len, (int *)param, sizeof(int)) != 0)
                        return -EPERM;

                  /* sanity check */
                  if (len < 0 || len > 1024)
                        return -EPERM;

                  param += sizeof(int);

                  error = verify_area(VERIFY_READ, (void *)param, len);
                  if (error)
                        return error;

                  /* make sure we are already registered
                   * (or that we don't register twice) */

                  /* find our slot */
                  s = dazuko_find_slot(current->pid, 1, NULL);

                  switch (opt)
                  {
                        case REGISTER:
                              if (s != NULL)
                              {
                                    /* We are already registered! */

                                    printk("dazuko: daemon %d already assigned to slot[%d]\n", current->pid, s->id);

                                    return -EPERM;
                              }
                              break;

                        case UNREGISTER:
                              if (s == NULL)
                              {
                                    /* We are not registered! */

                                    return 0;
                              }
                              break;

                        default:
                              if (s == NULL)
                              {
                                    i = dazuko_register_daemon("_COMPAT", 7);
                                    if (i != 0)
                                    {
                                          printk("dazuko: unregistered daemon %d attempted access\n", current->pid);
                                          return -EPERM;
                                    }

                                    s = dazuko_find_slot(current->pid, 1, NULL);
                                    if (s == NULL)
                                    {
                                          printk("dazuko: unregistered daemon %d attempted access\n", current->pid);
                                          return -EPERM;
                                    }

                                    printk("dazuko: warning: daemon %d is using a deprecated protocol\n", current->pid);
                              }
                              break;
                  }

                  /* check option type and take the appropriate action */
                  switch (opt)
                  {
                        case SET_ACCESS_MASK:
                              if (copy_from_user(&access_mask, (char *)param, sizeof(char)) != 0)
                                    return -EFAULT;
                              break;

                        case ADD_INCLUDE_PATH:
                              error = dazuko_insert_path_fs(&incl_paths, (char *)param, len);
                              if (error)
                                    return error;
                              break;

                        case ADD_EXCLUDE_PATH:
                              error = dazuko_insert_path_fs(&excl_paths, (char *)param, len);
                              if (error)
                                    return error;
                              break;

                        case REGISTER:
                              /* We register the daemon by finding an
                               * unused slot (slot->pid=0) and setting
                               * the slot->pid to the proccess id of
                               * the current proccess, the daemon. */

                              reg_name = (char *)kmalloc(len + 1, GFP_KERNEL);
                              if (!reg_name)
                                    return -EFAULT;

                              /* We must copy the reg_name from userspace to kernelspace. */

                              if (copy_from_user(reg_name, (char *)param, len) != 0)
                              {
                                    kfree(reg_name);
                                    return -EFAULT;
                              }

                              reg_name[len] = 0;

                              error = dazuko_register_daemon(reg_name, len);
                              kfree(reg_name);

                              if (error != 0)
                                    return error;

                              break;

                        case REMOVE_ALL_PATHS:
                              dazuko_remove_all_paths();
                              break;

                        case UNREGISTER:
                              error = dazuko_unregister_daemon();
                              break;

                        default:
                              printk("dazuko: daemon %d requested unknown set %d (possible bug)\n", current->pid, opt);
                              break;
                  }
                  break;
            default:
                  printk("dazuko: daemon %d requested unknown device_ioctl %d (possible bug)\n", current->pid, _IOC_NR(cmd));
                  break;
      }

      return 0;
}

static struct slot_t * dazuko_get_and_hold_ready_slot(struct slot_list_t *sl)
{
      /* This is a simple search to find a
       * slot whose state is READY. This means
       * it is able to accept work. If a slot
       * is found, the slot.mutex is held so
       * it can be filled with work by the caller.
       * It is the responsibility of the caller
       * to RELEASE THE MUTEX. */

      int         i;
      struct slot_t     *s;

      for (i=0 ; i<NUM_SLOTS ; i++)
      {
            s = &(sl->slots[i]);
/* DOWN? */
            if (dazuko_change_slot_state(s, READY, WAITING, 0))
            {
/* DOWN */
                  return s;
            }
      }

      /* we didn't find a slot that is ready for work */

      return NULL;
}

static int dazuko_run_daemon_on_slotlist(int event, char *filename, int filenamelength, int o_flags, int o_mode, int prev_response, struct slot_list_t *sl)
{
      /* This is the main function called by the kernel
       * to work with a daemon. */

      int         rc;
      int         pid;
      struct slot_t     *s;

begin:
      /* we initialize the slot value because
       * we cannot guarentee that it will be
       * assigned a new value BEFORE !active
       * is checked */
      s = NULL;

      /* wait for a slot to become ready */
      if (wait_event_interruptible(wait_kernel_waiting_for_free_slot, ((s = dazuko_get_and_hold_ready_slot(sl)) != NULL) || (atomic_read(&active) == 0) || (atomic_read(&(sl->use_count)) == 0)) != 0)
      {
            /* The kernel process was killed while
             * waiting for a slot to become ready.
             * This is fine. */

            DPRINTK(("dazuko: kernel process %d killed while waiting for free slot\n", current->pid));

            return -1;  /* user interrupted */
      }

      /* Make sure we have a slot. We may have
       * gotten past the last wait because we
       * are no longer active. */

      if (s == NULL)
      {
            /* We were no longer active. We don't
             * need to initiate a daemon. This also
             * means we never acquired the lock. */

            return 0;  /* allow access */
      }

/* DOWN */

      /* the slot is already locked at this point */

      /* grab the daemon's pid */
      pid = s->pid;

      /* At this point we have a locked slot. It IS
       * sitting in the WAITING state, waiting for
       * us to give it some work. */
      
      /* set up the slot to do work */
      s->filename = filename;
      s->event = event;
      s->response = prev_response;
      s->kuid = current->uid;
      s->kpid = current->pid;
      s->o_flags = o_flags;
      s->o_mode = o_mode;
      s->filenamelength = filenamelength;

      /* we are done modifying the slot */
      up(&(s->mutex));
/* UP */

      /* wake up any daemons waiting for work */
      wake_up(&wait_daemon_waiting_for_work);

      /* wait until the daemon is finished with the slot */
      if (wait_event_interruptible(wait_kernel_waiting_while_daemon_works, dazuko_slot_state(s) != WAITING && dazuko_slot_state(s) != WORKING) != 0)
      {
            /* The kernel process was killed while
             * waiting for a daemon to process the file.
             * This is fine. */

            DPRINTK(("dazuko: kernel process %d killed while waiting for daemon response\n", current->pid));

            /* change the slot's state to let the
             * daemon know we are not interested
             * in a response */
            dazuko_change_slot_state(s, FREE, FREE, 1);

            return -1;  /* user interrupted */
      }

      /* we are working with the slot, so
       * we need to lock it */
/* DOWN */
      if (down_interruptible(&(s->mutex)) != 0)
      {
            return -1;  /* user interrupted */
      }

      /* make sure this is the right daemon */
      if (s->pid != pid)
      {
            /* This is a different daemon than
             * the one we assigned work to.
             * We need to scan again. */
            up(&(s->mutex));
/* UP */
            goto begin;
      }

      /* The slot should now be in the DONE state. */
      if (!__dazuko_change_slot_state(s, DONE, FREE))
      {
            /* The daemon was killed while scanning.
             * We need to scan again. */

            up(&(s->mutex));
/* UP */
            goto begin;
      }

      /* grab the response */
      rc = s->response;

      up(&(s->mutex));
/* UP */

      /* CONGRATULATIONS! You successfully completed a full state cycle! */

      return rc;
}

static int dazuko_run_daemon(int event, char *filename, int filenamelength, int o_flags, int o_mode)
{
      struct slot_list_t      *sl;
      int               i;
      int               rc = 0;
      int               error;

      for (i=0 ; i<NUM_SLOT_LISTS ; i++)
      {
/* DOWN */
            /* if we are interrupted, we report error */
            if (down_interruptible(&(slot_lists[i].mutex)) != 0)
                  return -EINTR;

            sl = slot_lists[i].slot_list;

            up(&(slot_lists[i].mutex));
/* UP */

            if (sl != NULL)
            {
                  error = dazuko_run_daemon_on_slotlist(event, filename, filenamelength, o_flags, o_mode, rc, sl);

                  if (error < 0)
                  {
                        /* most likely user interrupt */
                        rc = error;
                        break;
                  }
                  else if (error > 0)
                  {
                        /* this daemon wants access blocked */
                        rc = 1;
                  }
            }
      }

      return rc;
}

static int dazuko_is_selected(char *filename, int len)
{
      /* Check if the given filename (with path) is
       * under our include directories but not under
       * the exclude directories. */

      struct path_t     *path;

      /* If we are interrupted here, we will report that
       * this file is not selected. This will make the
       * kernel allow normal access. Is this dangerous? */
/* LOCK */
      read_lock(&lock_lists);

      /* check if filename is under our include paths */
      for (path=incl_paths ; path ; path=path->next)
      {
            /* the include item must be at least as long as the given filename */
            if (path->len < len)
            {
                  /* the include item should match the beginning of the given filename */
                  if (memcmp(path->path, filename, path->len) == 0)
                        break;
            }
      }

      /* If we didn't find a path, it isn't in our
       * include directories. It can't be one of
       * the selected files to scan. */
      if (!path)
      {
            read_unlock(&lock_lists);
/* UNLOCK */
            return 0;
      }

      /* check if filename is under our exclude paths */
      for (path=excl_paths ; path ; path=path->next)
      {
            /* the exclude item must be at least as long as the given filename */
            if (path->len < len)
            {
                  /* the exclude item should match the beginning of the given filename */
                  if (memcmp(path->path,filename,path->len) == 0)
                        break;
            }
      }

      read_unlock(&lock_lists);
/* UNLOCK */

      /* If we got a path, then we are supposed
       * to exclude this file for scanning. */
      if (path)
            return 0;

      /* if we made it this far, it is a selected file to scan */

      return 1;
}

#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
static int dazuko_add_hash(struct file *file, char *filename, int len)
{
      /* Add the given file and filename to the linked list
       * of files to scan once they are closed. */

      struct hash_t     *h;

      /* create a new hash_t structure making room for name also */
      h = (struct hash_t *)kmalloc(sizeof(struct hash_t) + len, GFP_KERNEL);
      if (!h)     
            return -EFAULT;

      /* fill in structure items */

      h->file = file;
      h->dirty = 0;
      h->namelen = len;
      memcpy(h->name, filename, len);
      h->name[len] = 0;

      /* add the new hash_t item to the head of the
       * hast_t linked list */

/* LOCK */
      write_lock(&lock_hash);
      h->next = hash;
      hash = h;
      write_unlock(&lock_hash);
/* UNLOCK */
      return 0;
}
#endif

#ifdef ON_CLOSE_MODIFIED_SUPPORT
/* Code based on code from: Swade 12/08/02: Move dirty to end of list */
static void dazuko_mark_hash_dirty(struct file *file)
{
      struct hash_t     *h = NULL;
      struct hash_t     *entry = NULL;
      struct hash_t     *prev = NULL;
      struct hash_t     *prev_entry = NULL;

/* LOCK */
      write_lock(&lock_hash);

      for (h=hash ; h ; h=h->next)
      {
            /* not found if hit first dirty entry */
            if (h->dirty)
            {
                  entry = NULL;
                  break;
            }

            /* since these are file* and not
             * strings, we can compare them
             * directly */
            if (h->file == file)
            {
                  prev_entry = prev;
                  entry = h;
                  break;
            }

            prev = h;
      } 

      if (entry)
      {
            if (!entry->dirty)
            {
                  /* mark as dirty */
                  entry->dirty = 1;

                  /* If we already are last entry or next
                   * entry dirty, we don't need to move */

                  if (entry->next)
                  {
                        if (!entry->next->dirty)
                        {
                              for (h=entry->next ; h ; h=h->next)
                              {
                                    if (h->dirty)
                                          break;

                                    prev = h;
                              }

                              /* remove from current position */
                              if (prev_entry)
                                    prev_entry->next = entry->next;
                              else
                                    hash = entry->next;

                              if (prev == NULL)
                              {
                                    /* insert as first item */
                                    entry->next = hash;
                                    hash = entry;
                              }
                              else if (h)
                              {
                                    /* insert before h (after prev) */
                                    entry->next = prev->next;
                                    prev->next = entry;
                              }
                              else
                              {
                                    /* insert as last item (after prev) */
                                    entry->next = NULL;
                                    prev->next = entry;
                              }
                        }
                  }
            }
      }

      write_unlock(&lock_hash);
/* UNLOCK */

}
#endif

#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
static struct hash_t *dazuko_get_hash(struct file *file)
{
      /* Find the given file within our list
       * and then remove it from the list and
       * return it. */

      struct hash_t     *prev;
      struct hash_t     *cur;

/* LOCK */
      write_lock(&lock_hash);

      prev = NULL;
      cur = hash;
      while (cur)
      {
            /* since these are file* and not
             * strings, we can compare them
             * directly */
            if (cur->file == file)
            {
                  /* remove the item from the list */
                  if (!prev)
                        hash = cur->next;
                  else
                        prev->next = cur->next;
                  break;
            }

            prev = cur;
            cur = cur->next;
      }

      write_unlock(&lock_hash);
/* UNLOCK */

      return cur;
}
#endif

static inline int dazuko_get_filename_length(char *filename)
{
      /* Get the length of the filename. There is
       * currently a DAZUKO_FILENAME_MAX_LENGTH maximum size restriction
       * on filenames. :( */

      int len;

      for (len=0 ; len<DAZUKO_FILENAME_MAX_LENGTH && filename[len]; len++);

      if (len == DAZUKO_FILENAME_MAX_LENGTH)
      {
            printk("dazuko: filename too long (%s)\n", filename);

            filename[DAZUKO_FILENAME_MAX_LENGTH] = 0;
      }

      return len;
}

static int dazuko_get_dentry(struct dazuko_file_struct *kfs)
{
      /* We get the appropriate structures in order
       * to acquire the inode and store them in the
       * dazuko_file_struct structure. */

      /* make sure we really need to get the filename */
      if (!kfs->putname_filename)
      {
            /* grab filename from filename cache */
            kfs->filename = (char *)getname(kfs->user_filename);

            /* make sure it is a valid name */
            if (IS_ERR(kfs->filename))
                  return 0;

            /* the name will need to be put back */
            kfs->putname_filename = 1;
      }

      /* get filename length and make sure it isn't too long */
      kfs->filename_length = dazuko_get_filename_length(kfs->filename);
      if (kfs->filename_length == DAZUKO_FILENAME_MAX_LENGTH)
            return 0;

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            dazuko_bzero(&(kfs->nd), sizeof(struct nameidata));

            /* initialize nameidata structure for finding file data */
            if (!path_init(kfs->filename, LOOKUP_FOLLOW | LOOKUP_POSITIVE, &(kfs->nd)))
                  return 0;

            if (!kfs->path_release_nd)
            {
                  /* find file data and fill it in nameidata structure */
                  if (path_walk(kfs->filename, &(kfs->nd)))  /* !=0 -> error */
                        return 0;

                  /* the nameidata will need to be released */
                  kfs->path_release_nd = 1;
            }

            /* get a local copy of the dentry to make kernel version
             * compatibility code eaiser to read */

            /* make sure we don't already have a dentry */
            if (!kfs->dput_dentry)
            {
                  kfs->dentry = dget(kfs->nd.dentry);

                  /* the dentry will need to be put back */
                  kfs->dput_dentry = 1;
            }
      }
      #else
      {
            if (!kfs->dput_dentry)
            {
                  kfs->dentry = lookup_dentry(kfs->filename, NULL, 1);
                  if (IS_ERR(kfs->dentry))
                        return 0;

                  /* the dentry will need to be put back */
                  kfs->dput_dentry = 1;
            }
      }
      #endif

      /* check if this file has no inode */
      if (kfs->dentry->d_inode == NULL)
            return 0;

      /* if we made it this far, we got the inode */

      return 1;
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
static char * __d_path(struct dentry *dentry, struct dentry *root, char *buffer, int buflen)
{
      /* Copy of d_path from linux/dcache.c but using
       * a given root instead of the current root. */

      char * end = buffer+buflen;
      char * retval;

      *--end = '\0';
      buflen--;
      if (dentry->d_parent != dentry && list_empty(&dentry->d_hash)) {
            buflen -= 10;
            end -= 10;
            memcpy(end, " (deleted)", 10);
      }

      /* Get '/' right */
      retval = end-1;
      *retval = '/';

      for (;;) {
            struct dentry * parent;
            int namelen;

            if (dentry == root)
                  break;
            dentry = dentry->d_covers;
            parent = dentry->d_parent;
            if (dentry == parent)
                  break;
            namelen = dentry->d_name.len;
            buflen -= namelen + 1;
            if (buflen < 0)
                  break;
            end -= namelen;
            memcpy(end, dentry->d_name.name, namelen);
            *--end = '/';
            retval = end;
            dentry = parent;
      }
      return retval;
}
#endif

static int dazuko_get_full_filename(struct dazuko_file_struct *kfs)
{
      /* Get the filename with the full path appended
       * to the beginning. */

      char        *temp;
      struct dentry     *root;

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            struct vfsmount   *rootmnt;
      #endif

      /* check if we need to allocate a buffer */
      if (!kfs->free_page_buffer)
      {
            /* get pre-requisites for d_path function */
            kfs->buffer = (char *)__get_free_page(GFP_USER);

            /* the buffer will need to be freed */
            kfs->free_page_buffer = 1;
      }

      root = dget(orig_root);

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            /* make sure we don't already have a vfsmount */
            if (!kfs->mntput_vfsmount)
            {
                  kfs->vfsmount = mntget(kfs->nd.mnt);

                  /* the vfsmount will need to be put back */
                  kfs->mntput_vfsmount = 1;
            }

            /* build new filename with path included, using temp */

            rootmnt = mntget(orig_rootmnt);

            spin_lock(&dcache_lock);
            temp = __d_path(kfs->dentry, kfs->vfsmount, root, rootmnt, kfs->buffer, PAGE_SIZE);
            spin_unlock(&dcache_lock);

            mntput(rootmnt);
      }
      #else
      {
            /* build new filename with path included, using temp */

            temp = __d_path(kfs->dentry, root, kfs->buffer, PAGE_SIZE);
      }
      #endif

      dput(root);

      /* make sure we really got a new filename */
      if (!temp)
            return 0;

      /* make sure we don't already have a full_filename */
      if (!kfs->free_full_filename)
      {
            /* get new filename length and make sure it isn't too long */
            kfs->full_filename_length = dazuko_get_filename_length(temp);
            if (kfs->full_filename_length == DAZUKO_FILENAME_MAX_LENGTH)
                  return 0;

            kfs->full_filename = (char *)kmalloc(kfs->full_filename_length + 1, GFP_KERNEL);

            /* the char array will need to be freed */
            kfs->free_full_filename = 1;

            memcpy(kfs->full_filename, temp, kfs->full_filename_length + 1);
      }

      /* we have a filename with the full path */

      return 1;
}

static void dazuko_file_struct_critical_cleanup(struct dazuko_file_struct *kfs)
{
      /* Delete all the flagged structures from the
       * given dazuko_file_struct and reset all critical
       * values back to 0. */

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            if (kfs->mntput_vfsmount)
            {
                  mntput(kfs->vfsmount);
                  kfs->mntput_vfsmount = 0;
            }
      }
      #endif

      if (kfs->free_page_buffer)
      {
            free_page((unsigned long)kfs->buffer);
            kfs->free_page_buffer = 0;
      }

      if (kfs->dput_dentry)
      {
            dput(kfs->dentry);
            kfs->dput_dentry = 0;
      }

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            if (kfs->path_release_nd)
            {
                  path_release(&(kfs->nd));
                  kfs->path_release_nd = 0;
            }
      }
      #endif

      if (kfs->putname_filename)
      {
            putname(kfs->filename);
            kfs->putname_filename = 0;
      }
}

static void dazuko_file_struct_cleanup(struct dazuko_file_struct *kfs)
{
      kfs->should_scan = 0;

      dazuko_file_struct_critical_cleanup(kfs);

      if (kfs->free_full_filename)
      {
            kfree(kfs->full_filename);
            kfs->free_full_filename = 0;
      }
}

static int dazuko_should_scan(struct dazuko_file_struct *kfs)
{
      /* Check if we are supposed to scan this file.
       * This checks for all the correct file types,
       * permissions, and if it is within the desired
       * paths to scan. */

      int result = 0;

      /* check if we already know if we scan this file */
      switch (kfs->should_scan)
      {
            /* case 0 means that we do not know yet. This is a little
             * confusing, because 0 represents uninitialized. However,
             * the should_scan variable is used in this function ONLY
             * so this optimization shouldn't cause any problems. */

            case 1:
                  /* we already know it should be scanned */
                  return 1;

            case 2:
                  /* we already know it should not be scanned */
                  return 0;
      }

      /* make sure we can get an inode */
      if (dazuko_get_dentry(kfs))
      {
            /* make sure we have a regular file */
            if (S_ISREG(kfs->dentry->d_inode->i_mode))
            {
                  /* make sure the file is readable */
                  if (permission(kfs->dentry->d_inode, MAY_READ) == 0)
                  {
                        /* make sure we can get the full path */
                        if (dazuko_get_full_filename(kfs))
                        {
                              /* check if the filename is within our include
                              * directories but not our exclude directories */

                              if (dazuko_is_selected(kfs->full_filename, kfs->full_filename_length))
                              {
                                    /* If we made it this far, we are supposed
                                     * to scan this file. We mark it so that
                                     * any further immediate inquiries don't have
                                     * to do all this work all over again. */
 
                                    /* yes, should be scanned */
                                    kfs->should_scan = 1;

                                    result = 1;
                              }
                              else
                              {
                                    /* We will still mark it so that any further
                                     * immediate inquiries don't have to do all
                                     * this work all over again. */

                                    /* no, should not be scanned */
                                    kfs->should_scan = 2;
                              }
                        }
                  }
            }
      }

      dazuko_file_struct_critical_cleanup(kfs);

      return result;
}

#ifdef ON_EXEC_SUPPORT
asmlinkage int dazuko_sys_execve(struct pt_regs regs)
{
      /* The kernel wants to execute the given file.
       * Because the given structure contains stack
       * address information, we can't simply call
       * the default standard execve. Instead we
       * have to manually inline the standard execve
       * call. */

      struct dazuko_file_struct     kfs;
      char                    *filename;
      int                     error = 0;

      /* check if we are supposed to do scanning */
      if ((atomic_read(&active) == 0) || !SCAN_ON_EXEC)
            goto standard;

      /* start with a clean dazuko_file_struct */
      dazuko_bzero(&kfs, sizeof(struct dazuko_file_struct));

      kfs.user_filename = (char *)regs.ebx;

      /* make sure we should scan this file */
      if (dazuko_should_scan(&kfs))
            error = dazuko_run_daemon(ON_EXEC, kfs.full_filename, kfs.full_filename_length, 0, 0);

      dazuko_file_struct_cleanup(&kfs);

      if (error > 0)
      {
            /* virus found and not cleaned */

            return -EPERM;
      }
      else if (error < 0)
      {
            /* user interrupted */

            return -EINTR;
      }

      /* call the standard execve function */

      /* We cannot simply call the original version of execvc
       * because the parameter contains stack information and
       * the call will push the execvc call onto a new stack
       * level and seg fault. :( */

standard:
      /* The following code only works on i386 machines.
       * It is directly copied from Linux in the file:
       * arch/i386/kernel/process.c */

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            filename = getname((char *) regs.ebx);
            error = PTR_ERR(filename);
            if (IS_ERR(filename))
                  goto out;
            error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
            if (error == 0)
                  current->ptrace &= ~PT_DTRACE;
            putname(filename);
out:
            return error;
      }
      #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,20)
      {
            #ifdef __SMP__
                  lock_kernel();
            #endif
            filename = getname((char *) regs.ebx);
            error = PTR_ERR(filename);
            if (IS_ERR(filename))
                  goto out;
            error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
            if (error == 0)
                  current->ptrace &= ~PT_DTRACE;
            putname(filename);
out:
            #ifdef __SMP__
                  unlock_kernel();
            #endif
            return error;
      }
      #else
      {
            #ifdef __SMP__
                  lock_kernel();
            #endif
            filename = getname((char *) regs.ebx);
            error = PTR_ERR(filename);
            if (IS_ERR(filename))
                  goto out;
            error = do_execve(filename, (char **) regs.ecx, (char **) regs.edx, &regs);
            if (error == 0)
                  current->flags &= ~PF_DTRACE;
            putname(filename);
out:
            #ifdef __SMP__
                  unlock_kernel();
            #endif
            return error;
      }
      #endif


}
#endif

#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
asmlinkage long   dazuko_sys_open(const char *filename, int flags, int mode)
{
      /* The kernel wants to open the given filename
       * with the given flags and mode. The dazuko_file_struct
       * is used to handle the tricky job of cleaning
       * up the many pieces of memory that may or may
       * not be allocated. */

      struct dazuko_file_struct     kfs;
      int               error = 0;
      int               fd;

      /* Check if we are supposed to do scanning. Even
       * if we don't scan on open, we need to keep going
       * if we are supposed to scan on close. */
      if ((atomic_read(&active) == 0) || filename == NULL || !(SCAN_ON_OPEN | SCAN_ON_CLOSE | SCAN_ON_CLOSE_MODIFIED))
      {
            return original_sys_open(filename, flags, mode);
      }

      /* do not scan if it is our scan daemon
       * opening the file */
      if (dazuko_is_our_daemon())
      {
            return original_sys_open(filename, flags, mode);
      }

      /* start with a clean dazuko_file_struct */
      dazuko_bzero(&kfs, sizeof(struct dazuko_file_struct));

      kfs.user_filename = filename;

      /* make sure we are supposed to scan files on open and
      * that we aren't truncating this file on open (truncating
      * the file will delete any contents, so no worry for viruses. */
      if (SCAN_ON_OPEN)
      {
            /* make sure we should scan this file */
            if (dazuko_should_scan(&kfs))
            {
                  error = dazuko_run_daemon(ON_OPEN, kfs.full_filename, kfs.full_filename_length, flags, mode);
            }
      }

      if (error > 0)
      {
            /* virus found and not cleaned */

            fd = -EPERM;
      }
      else if (error < 0)
      {
            /* user interrupted */

            fd = -EINTR;
      }
      else
      {
            /* call the standard open function */
            fd = original_sys_open(filename, flags, mode);

            /* if the file was opened and we are interested
            * in scanning on close, add this file to our hash_t list */

            if ((atomic_read(&active) != 0) && fd > 0 && fd < NR_OPEN)
            {
                  if (SCAN_ON_CLOSE || (SCAN_ON_CLOSE_MODIFIED && (flags & (O_RDWR | O_WRONLY))))
                  {
                        /* make sure we should scan this file */
                        if (dazuko_should_scan(&kfs))
                        {
                              #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
                              {
                                    read_lock(&current->files->file_lock);
                              }
                              #endif

                              dazuko_add_hash(current->files->fd[fd], kfs.full_filename, kfs.full_filename_length);

                              #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
                              {
                                    read_unlock(&current->files->file_lock);
                              }
                              #endif
                        }
                  }
            }
      }

      dazuko_file_struct_cleanup(&kfs);

      return fd;
}
#endif

#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
asmlinkage long   dazuko_sys_close(unsigned int fd)
{
      /* The kernel wants to close the given file
       * descriptor. */

      int         error;
      struct hash_t     *h = NULL;
      struct file *file = NULL;

      /* do not scan if it is our scan daemon
       * closing the file */
      if (dazuko_is_our_daemon())
      {
            return original_sys_close(fd);
      }

      /* If it is a valid file descriptor, see if it is
       * in our list of files to scan on close. If it is,
       * it will be removed from the list also. */

      if ((atomic_read(&active) != 0) && (SCAN_ON_CLOSE || SCAN_ON_CLOSE_MODIFIED) && fd > 0 && fd < NR_OPEN)
      {
            #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            {
                  read_lock(&current->files->file_lock);
            }
            #endif

            /* grab the file* for possible later use */
            file = current->files->fd[fd];

            #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            {
                  read_unlock(&current->files->file_lock);
            }
            #endif
      }

      /* call the standard close function */
      error = original_sys_close(fd);

      if (!error && (atomic_read(&active) != 0) && (SCAN_ON_CLOSE || SCAN_ON_CLOSE_MODIFIED) && fd > 0 && fd < NR_OPEN)
      {
            /* find hash entry and remove it from list */
            h = dazuko_get_hash(file);

            /* if we found the file in our list and the file was
            * successfully closed, we need to scan it */
            if (h)
            {
                  /* determine if we are scanning on close and/or close_modified */

                  /* note that modified has priority over just close */

                  if (SCAN_ON_CLOSE_MODIFIED && h->dirty)
                        dazuko_run_daemon(ON_CLOSE_MODIFIED, h->name, h->namelen, 0, 0);
                  else if (SCAN_ON_CLOSE)
                        dazuko_run_daemon(ON_CLOSE, h->name, h->namelen, 0, 0);

                  /* clean up the hash_t structure */
                  kfree(h);
            }
      }

      return error;
}
#endif

#ifdef ON_CLOSE_MODIFIED_SUPPORT
asmlinkage ssize_t dazuko_sys_write(unsigned int fd, char *buf, unsigned int count)
{
      /* The kernel wants to write to the given file
       * descriptor. */

      int         num;
      struct file *file = NULL;

      /* do not track if it is our scan daemon
       * writing the file */
      if (dazuko_is_our_daemon())
      {
            return original_sys_write(fd, buf, count);
      }

      /* Check if this file is in our list of files to
       * be cleaned on close. It will not be removed.
       * We only have to do this if we are scanning on
       * close.*/

      if ((atomic_read(&active) != 0) && SCAN_ON_CLOSE_MODIFIED && fd > 0 && fd < NR_OPEN)
      {
            #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            {
                  read_lock(&current->files->file_lock);
            }
            #endif

            /* Grab a copy of the file* "just in case*. */
            file = current->files->fd[fd];

            #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            {
                  read_unlock(&current->files->file_lock);
            }
            #endif
      }
      
      /* call the standard write function */
      num = original_sys_write(fd, buf, count);
      
      /* if we actually wrote something and we found the
       * file in our list, set it as dirty */

      if (num > 0 && file)
      {
            /* Swade 4/24/02: Move to end of clean list */
            dazuko_mark_hash_dirty(file);
      }

      return num;
}
#endif

#ifdef HIDDEN_SCT
static void** dazuko_get_sct()
{
      unsigned long     ptr;
      extern int  loops_per_jiffy;
      unsigned long     *p;

      for (ptr=(unsigned long)&loops_per_jiffy ; ptr<(unsigned long)&boot_cpu_data ; ptr+=sizeof(void *))
      {
            p = (unsigned long *)ptr;
            if (p[6] == (unsigned long)sys_close)
            {
                  return (void **)p;
            }
      }

      return NULL;
}
#endif

int __init dazuko_init(void)
{
      /* Called insmod when inserting the module. */

      int   i;

#ifdef HIDDEN_SCT
      sys_call_table = dazuko_get_sct();
      if (sys_call_table == NULL)
      {
            printk("dazuko: panic (sys_call_table == NULL)\n");
            return -1;
      }
#endif

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            rwlock_init(&lock_hash);
            rwlock_init(&lock_lists);
      }
      #else
      {
            lock_hash = RW_LOCK_UNLOCKED;
            lock_lists = RW_LOCK_UNLOCKED;
      }
      #endif

      dazuko_bzero(&slot_lists, sizeof(slot_lists));
      for (i=0 ; i<NUM_SLOT_LISTS ; i++)
      {
            #ifdef init_MUTEX
                  init_MUTEX(&(slot_lists[i].mutex));
            #else
                  sema_init(&(slot_lists[i].mutex), 1);
            #endif
      }

      atomic_set(&active, 0);

      /* Make sure we have a valid task_struct. */

      if (current == NULL)
      {
            printk("dazuko: panic (current == NULL)\n");
            return -1;
      }
      if (current->fs == NULL)
      {
            printk("dazuko: panic (current->fs == NULL)\n");
            return -1;
      }
      if (current->fs->root == NULL)
      {
            printk("dazuko: panic (current->root == NULL)\n");
            return -1;
      }
      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            if (current->fs->rootmnt == NULL)
            {
                  printk("dazuko: panic (current->rootmnt == NULL)\n");
                  return -1;
            }
      }
      #endif

      /* register the dazuko device */
#ifdef CONFIG_DEVFS_FS
      dev_major = devfs_register_chrdev(0, DEVICE_NAME, &fops);
      devfs_register(NULL, DEVICE_NAME, DEVFS_FL_DEFAULT,
            dev_major, 0, S_IFCHR | S_IRUSR | S_IWUSR,
            &fops, NULL);
#else
      dev_major = register_chrdev(0, DEVICE_NAME, &fops);
#endif
      if (dev_major < 0)
      {
            printk("dazuko: unable to register device chrdev, err=%d\n", dev_major);
            return dev_major;
      }

      /* Grab the current root. This is assumed to be the real.
       * If it is not the real root, we could have problems
       * looking up filenames. */

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
      {
            read_lock(&current->fs->lock);
            orig_rootmnt = current->fs->rootmnt;
      }
      #endif

      orig_root = current->fs->root;

      #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
            read_unlock(&current->fs->lock);
      #endif

      /* do a file syncronization on all devices (IMPORTANT!) and replace system calls */
      #ifdef __SMP__
            lock_kernel();
      #endif

      fsync_dev(0);

#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      /* replace the system call entries with our entries */
      DPRINTK(("dazuko: hooked sys_open\n"));
      original_sys_open = sys_call_table[__NR_open];
      sys_call_table[__NR_open] = dazuko_sys_open;
#endif
      
#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      DPRINTK(("dazuko: hooked sys_close\n"));
      original_sys_close = sys_call_table[__NR_close];
      sys_call_table[__NR_close] = dazuko_sys_close;
#endif

#ifdef ON_CLOSE_MODIFIED_SUPPORT
      DPRINTK(("dazuko: hooked sys_write\n"));
      original_sys_write = sys_call_table[__NR_write];
      sys_call_table[__NR_write] = dazuko_sys_write;
#endif

#ifdef ON_EXEC_SUPPORT
      DPRINTK(("dazuko: hooked sys_execve\n"));
      original_sys_execve = sys_call_table[__NR_execve];
      sys_call_table[__NR_execve] = dazuko_sys_execve;
#endif

      #ifdef __SMP__
            unlock_kernel();
      #endif
      /* done syncing and replacing */

      /* initialization complete */

      printk("dazuko: loaded, version=%s, dev_major=%d\n", VERSION, dev_major);

      return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
void __exit dazuko_exit(void)
#else
void dazuko_exit(void)
#endif
{
      /* Called by rmmod when removing the module. */

      int   error;
      int   i;

      dazuko_remove_all_paths();
      dazuko_remove_all_hash();

      /* do a file syncronization on all devices (IMPORTANT!) and replace system calls */
      #ifdef __SMP__
            lock_kernel();
      #endif

      fsync_dev(0);

      /* check if it is still our entries in the sytem call table */
#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      if (sys_call_table[__NR_open] != dazuko_sys_open)
            printk("dazuko: open system call not correct (system may be left in an unstable state!)\n");
#endif
#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      if (sys_call_table[__NR_close] != dazuko_sys_close)
            printk("dazuko: close system call not correct (system may be left in an unstable state!)\n");
#endif
#ifdef ON_CLOSE_MODIFIED_SUPPORT
      if (sys_call_table[__NR_write] != dazuko_sys_write)
            printk("dazuko: write system call not correct (system may be left in an unstable state!)\n");
#endif
#ifdef ON_EXEC_SUPPORT
      if (sys_call_table[__NR_execve] != dazuko_sys_execve)
            printk("dazuko: execve system call not correct (system may be left in an unstable state!)\n");
#endif

      /* return original system calls (we HOPE no one has played with the table) */

#if defined(ON_OPEN_SUPPORT) || defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      sys_call_table[__NR_open] = original_sys_open;
#endif
#if defined(ON_CLOSE_SUPPORT) || defined(ON_CLOSE_MODIFIED_SUPPORT)
      sys_call_table[__NR_close] = original_sys_close;
#endif
#ifdef ON_CLOSE_MODIFIED_SUPPORT
      sys_call_table[__NR_write] = original_sys_write;
#endif
#ifdef ON_EXEC_SUPPORT
      sys_call_table[__NR_execve] = original_sys_execve;
#endif

      #ifdef __SMP__
            unlock_kernel();
      #endif
      /* done syncing and replacing */

#ifdef CONFIG_DEVFS_FS
      error = devfs_unregister_chrdev(dev_major, DEVICE_NAME);
      devfs_unregister(devfs_find_handle(NULL, DEVICE_NAME, dev_major, 0, DEVFS_SPECIAL_CHR, 0));
#else
      error = unregister_chrdev(dev_major, DEVICE_NAME);
#endif
      if (error < 0)
      {
            printk("dazuko: error unregistering chrdev, err=%d\n", error);
      }

      for (i=0 ; i<NUM_SLOT_LISTS ; i++)
      {
            if (slot_lists[i].slot_list != NULL)
            {
                  if (atomic_read(&(slot_lists[i].slot_list->use_count)) != 0)
                        printk("dazuko: slot_list count for daemon %d was not 0 (possible bug)\n", current->pid);

                  kfree(slot_lists[i].slot_list);
                  slot_lists[i].slot_list = NULL;
            }
      }

      printk("dazuko: unloaded, version=%s\n", VERSION);
}

#ifdef MODULE

int init_module(void)
{
      return dazuko_init();
}

void cleanup_module(void)
{
      dazuko_exit();
}

MODULE_AUTHOR("H+BEDV Datentechnik GmbH <linux_support@antivir.de>");
MODULE_DESCRIPTION("allow 3rd-party file access control");
#ifdef MODULE_LICENSE
MODULE_LICENSE("GPL");
#else
static const char __module_license[] __attribute__((section(".modinfo"))) = "license=GPL";
#endif

EXPORT_NO_SYMBOLS;

#else

module_init(dazuko_init);
module_exit(dazuko_exit);
/* module_init(int dazuko_init(void)); */
/* module_exit(void dazuko_exit(void)); */

#endif

Generated by  Doxygen 1.6.0   Back to index