#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/security.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/time.h>
#include "lsmlgi.h"
#include "lsmlgi_def.h"

/* for print_string */ 
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/version.h>

/* lsmlgi
 * An attempt at using state for access control.  Uses lsm framework
 * * Fri 16 Nov 2007 Thomas Uphill <uphill@ias.edu>
 * - creating inode memory cache and attempting to save state in there.
 * * Wed 7 Nov 2007 Thomas Uphill <uphill@ias.edu>
 * - keeping simple counters of read, write, delete operations
 * * Wed 10 Oct 2007 Thomas Uphill <uphill@ias.edu>
 * - initial module
 */

/*  Notes:
 * current is a task_struct
 * children should inherit the state of their parents
 * parents should experience the state of their children - budget of child should
 *	affect the parents.
 * should setup a state at login or at lsmlgi load time, keep that, then use that to determine if we are at the same state
 *  that is, setup a law where you can only delete files you created this session.
 * we need to use the superblock it seems, we want to store our state in the xattr of the inode.  even if only in memory
 */

/* should we print out debug messages */
static int debug = 1;

/* global definitions */
struct proc_dir_entry *lsmlgi_procfile;
struct kmem_cache *lsmlgi_inode_cache;

char *lsmlgi_uncompiled_law;
static unsigned long lsmlgi_law_size = 0;
struct law_struct *lsmlgi_law;
int lsmlgi_law_lines = 0;
int lsmlgi_law_init = 0;

/* flag to keep track of how we were registered during register_security */
static int secondary;

/* SID of this instance */
static int lsmlgi_initsid;

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("lsmlgi");
MODULE_AUTHOR("Thomas Uphill");
MODULE_VERSION("LSMLGI_VERSION");

int procfile_read(char *buffer,
		  char **buffer_location,
		  off_t offset,
		  int buffer_length,
		  int *eof,
		  void *data)
{
	int ret;
	char *lsmlgi_lawstring;
	if (offset > 0) {
		ret = 0;
	} else {
		// return useful information here.  eventually we would put the law here.
		lsmlgi_lawstring = lsmlgi_dumplaw();
		ret = sprintf(buffer, "sid: %i\nlaw:\n%s\n",lsmlgi_initsid,lsmlgi_lawstring);
	}
	return ret;
}

int procfile_write(struct file *file,
		   void *buffer,
		   unsigned long count,
		   void *data)
{
	lsmlgi_law_size = count;
	if (lsmlgi_law_size > LSMLGI_MAX_LAWSIZE) {
		lsmlgi_law_size = LSMLGI_MAX_LAWSIZE;
	}
	/* write data to the buffer */
	lsmlgi_law = kzalloc(sizeof(lsmlgi_law_size), GFP_KERNEL);
	if ( copy_from_user(lsmlgi_law, buffer, count)) {
		return -EFAULT;
	}
	lsmlgi_law_lines = lsmlgi_law_size / sizeof(struct law_struct);
	if (debug) printk(KERN_INFO "number of lines is %i\n",lsmlgi_law_lines);
	lsmlgi_law_init = 1;
	return lsmlgi_law_size;
}
static int lsmlgi_bprm_check_security (struct linux_binprm *bprm)
{
	/* its crashing here when root run's vi, that means root doesn't have
         * a security context put in an if to remove the crash 
 	 */
	int rc = 0;
	struct bprm_security_struct *bsec;
	struct inode_security_struct *isec;

	printk(KERN_INFO "lsmlgi_bprm_check_security %p\n",bprm);
	if (!bprm) return 0;
        if (debug) printk(KERN_DEBUG "file %s, e_uid = %d, e_gid = %d\n",bprm->filename, bprm->e_uid, bprm->e_gid);
	
	/* this is a bit weird, but it should work, we have to get the inode
	 * security struct for the bprm data
	 */
	
	bsec = bprm->security;
	if (!bsec)
		return 0;
	isec = bsec->isec;
	if (!isec)
		return 0;
	rc = lsmlgi_check_law(LSMLGI_EXEC, isec);
	if (rc) printk(KERN_INFO "lsmlgi_bprm_check_security: check_law %i\n",rc);
        return rc;
}

static int lsmlgi_ptrace (struct task_struct *parent,
			    struct task_struct *child)
{
	return 0;
}

static int lsmlgi_capget (struct task_struct *target,
			    kernel_cap_t *effective,
			    kernel_cap_t *inheritable,
			    kernel_cap_t *permitted)
{
	return 0;
}

static int lsmlgi_capset_check (struct task_struct *target,
				  kernel_cap_t *effective,
				  kernel_cap_t *inheritable,
				  kernel_cap_t *permitted)
{
	return 0;
}

static void lsmlgi_capset_set (struct task_struct *target,
				 kernel_cap_t *effective,
				 kernel_cap_t *inheritable,
				 kernel_cap_t *permitted)
{
	return;
}

static int lsmlgi_acct (struct file *file)
{
	return 0;
}

static int lsmlgi_capable (struct task_struct *tsk, int cap)
{
	if (cap_is_fs_cap (cap) ? tsk->fsuid == 0 : tsk->euid == 0)
		/* capability granted */
		return 0;

	/* capability denied */
	return -EPERM;
}

/*static int lsmlgi_sys_security (unsigned int id, unsigned int call,
				  unsigned long *args)
{
	return -ENOSYS;
}
*/
static int lsmlgi_quotactl (int cmds, int type, int id,
			      struct super_block *sb)
{
	return 0;
}

static int lsmlgi_quota_on (struct file *f)
{
	return 0;
}

static int lsmlgi_bprm_alloc_security (struct linux_binprm *bprm)
{
	// allocate security structure for this program
	struct bprm_security_struct *bsec;
	u32 newsid;
	
	// reserve the memory
	bsec = kzalloc(sizeof(struct bprm_security_struct), GFP_KERNEL);
	if (!bsec)
		return -ENOMEM;

	lsmlgi_newsid(&newsid);
	// assign the bprm structure to the security structure
	bsec->bprm = bprm;
	bsec->sid = lsmlgi_initsid;
	bsec->tsid = newsid;
	bsec->set = 0;

	// assign security for the bprm
	bprm->security = bsec;

	return 0;
}

static int lsmlgi_bprm_set_security (struct linux_binprm *bprm)
{
	int rc=0;
	struct bprm_security_struct *bsec;
	struct task_security_struct *tsec;
	struct inode_security_struct *isec;

	if (current->security == NULL) {
		rc = lsmlgi_task_alloc_security(current);	
		if (rc) return rc;
	}
	bsec = bprm->security;
	if (bsec == NULL) {
		if (current != NULL) {
			if (debug) printk(KERN_INFO "There is no security for the bprm yet!%i\n",current->pid);
		} else {	
			if (debug) printk(KERN_INFO "No security and no task\n");
		}
		return 0;
	}

	// check if bsec was already set
	if (bsec->set)
		return 0;
	
	// assign task security to this bprm security
	if (current->security != NULL) {
		bsec->tsec = current->security;
		tsec = (struct task_security_struct *) current->security;
		lsmlgi_update_parent(current,LSMLGI_EXEC);

//		if (debug) printk(KERN_INFO "setting this programs security - task id %i\n",tsec->id);
	}
	if (bprm->file->f_dentry) 
		if (bprm->file->f_dentry->d_inode)
			isec = bprm->file->f_dentry->d_inode->i_security;	
	if (isec != NULL) {			
		printk(KERN_INFO "bprm_set_security: %s-%i-%i:%i:%i:%i\n",bprm->filename, bprm->e_uid,bprm->e_gid,isec->sid,isec->tsid,isec->fsid);
		rc = lsmlgi_check_law(LSMLGI_EXEC,isec);
		if (rc) printk(KERN_INFO "bprm_set_security: check_law %i\n",rc);
	}	
	
	/* Default to the current task SID */
	bsec->set = 1;
	return rc;
}

static void lsmlgi_bprm_free_security (struct linux_binprm *bprm)
{
	return;
}

/* static void lsmlgi_bprm_compute_creds (struct linux_binprm *bprm)
{
	return;
}
*/

static int lsmlgi_sb_alloc_security (struct super_block *sb)
{
	return 0;
}

static void lsmlgi_sb_free_security (struct super_block *sb)
{
	return;
}

static int lsmlgi_sb_statfs (struct super_block *sb)
{
	return 0;
}

static int lsmlgi_mount (char *dev_name, struct nameidata *nd, char *type,
			   unsigned long flags, void *data)
{
	return 0;
}

static int lsmlgi_check_sb (struct vfsmount *mnt, struct nameidata *nd)
{
	return 0;
}

static int lsmlgi_umount (struct vfsmount *mnt, int flags)
{
	return 0;
}

static void lsmlgi_umount_close (struct vfsmount *mnt)
{
	return;
}

static void lsmlgi_umount_busy (struct vfsmount *mnt)
{
	return;
}

static void lsmlgi_post_remount (struct vfsmount *mnt, unsigned long flags,
				   void *data)
{
	return;
}


static void lsmlgi_post_mountroot (void)
{
	return;
}

static void lsmlgi_post_addmount (struct vfsmount *mnt,
				    struct nameidata *nd)
{
	return;
}

static int lsmlgi_pivotroot (struct nameidata *old_nd,
			       struct nameidata *new_nd)
{
	return 0;
}

static void lsmlgi_post_pivotroot (struct nameidata *old_nd,
				     struct nameidata *new_nd)
{
	return;
}

static int lsmlgi_inode_alloc_security (struct inode *inode)
{
	// 01:36 - we need to alloc a security struct first, then maybe it'll actually do something
	struct inode_security_struct *isec;
	struct task_security_struct *tsec;

	/* allocate memory in the inode cache for this inode */
	isec = kmem_cache_alloc(lsmlgi_inode_cache, SLAB_KERNEL);
	if (!isec)
		return -ENOMEM;

	memset(isec, 0, sizeof(*isec));		/* clear the memory of isec */

	if (current->security == NULL)
		if(lsmlgi_task_alloc_security(current))
			return -ENOMEM;
	tsec = current->security;

	/* I'm not sure we are supposed to set things up here check hooks.c */
	init_MUTEX(&isec->sem);			/* initialise the semaphore */
	INIT_LIST_HEAD(&isec->list);
	isec->inode = inode;			/* point back to the inode */
	isec->sid = lsmlgi_initsid;		/* initialize sid to UNKNOWN */
	isec->tsid = tsec->tsid;			/* this tasks sid */
	lsmlgi_newsid(&isec->fsid);
	inode->i_security = isec;		/* point back to this inode_security */

	return 0;
}

static void lsmlgi_inode_free_security (struct inode *inode)
{
	struct inode_security_struct *isec = inode->i_security;
	if (inode->i_security == NULL) {
		/* there is no security to free here */
		return;
	}


	inode->i_security = NULL;

	/* deallocate memory in the inode cache for this inode */
	kmem_cache_free(lsmlgi_inode_cache, isec);
	return;
}

static int lsmlgi_inode_init_security(struct inode *inode, struct inode *dir,
				      char **name, void **value,
				      size_t *len)
{
	/* this is where we set the security context on disk */
	/* should read in the previous security here if defined ? */
	struct task_security_struct *tsec;
	struct inode_security_struct *isec;
	u32 sid = 0, tsid = 0, fsid = 0;
	char *namep = NULL, *context;
	char *valuep = NULL;
	int err;

	/* use inode security if set */
	if (inode->i_security != NULL) {
		isec = inode->i_security;
		sid = isec->sid;
		tsid = isec->tsid;
		fsid = isec->fsid;
	} else if (current->security == NULL) {
		err = lsmlgi_task_alloc_security(current);
		if (err) return err;
		tsec = current->security;
		sid = tsec->sid;
		tsid = tsec->tsid;
		lsmlgi_newsid(&fsid);
	}

	/* set the name */
	if (name) {
		namep = kstrdup(XATTR_LSMLGI_SUFFIX, GFP_KERNEL);
		if (!namep)
			return -ENOMEM;
		*name = namep;
	}

	/* set the sid's here */
	/* 11:36 - monday */
	if (value && len) {
		/* set the security context */
		context = kmalloc(LSMLGI_MAX_CONTEXTLEN, GFP_ATOMIC);
		if (!context)
			return -ENOMEM;
		//*len = sprintf(context,"%i:%i:%i",tsec->sid,tsec->tsid,tsec->fsid);
		*len = sprintf(context,"%i:%i:%i:",sid,tsid,fsid) + 1;
		valuep = kmalloc(sizeof(char) * *len, GFP_ATOMIC);
		memcpy(valuep,context,*len);
		kfree(context);
		*value = valuep;
//		*len = strlen(value) + 1;
//		printk(KERN_INFO "context = %s length = %i\n",(char *) *value,*len);
	}
	return 0;
}

//static int inode_security_set_sid(struct inode *inode, u32 sid)
//{
	/* set inode SID to sid */
//	struct inode_security_struct *isec = inode->i_security;

	/* use semaphore to control access to this part */
//	down(&isec->sem);
//	isec->sid = lsmlgi_initsid;
//	isec->tsid = sid;
//	isec->initialized = 1;
//	up(&isec->sem);

//	return 0;
//}

static int lsmlgi_inode_create (struct inode *dir,
				  struct dentry *dentry,
				  int mask)
{
	/* this is the write operation
	 * check if the law allows the access
	 * then we update the parent
	 */

	int rc = 0;
	rc = lsmlgi_check_law(LSMLGI_WRITE, dir->i_security);	

	//if (rc) return rc;
	if (rc) printk(KERN_INFO "lsmlgi_inode_create: check_law %i\n",rc);

	if ( (current != NULL) && (current->pid > 1)) {
		lsmlgi_update_parent(current,LSMLGI_WRITE);
	}

	return rc;
}

static int lsmlgi_inode_link (struct dentry *old_dentry,
				struct inode *inode,
				struct dentry *new_dentry)
{
	return 0;
}

static int lsmlgi_inode_unlink (struct inode *dir, struct dentry *dentry)
{
	/* this is the delete operation
	 * check if the law allows the access
	 * then update the parent
	 */
	int rc = 0;

	struct inode *inode;
	inode = dentry->d_inode;

	rc = lsmlgi_check_law(LSMLGI_DEL, inode->i_security);
	if (rc) printk(KERN_INFO "lsmlgi_inode_unlink: check_law %i\n",rc);
	//if (rc) return rc;

	if ( (current != NULL) && (current->pid > 1)) {
		lsmlgi_update_parent(current,LSMLGI_DEL);
	} 
	return rc;
}


static int lsmlgi_inode_symlink (struct inode *inode, struct dentry *dentry,
				   const char *name)
{
	return 0;
}

static int lsmlgi_inode_mkdir (struct inode *inode,
				 struct dentry *dentry,
				 int mask)
{
	/* this is the write operation on a directory
	 * we just update write for now, but we could keep directories separate
	 */
	int rc = 0;
	rc = lsmlgi_check_law(LSMLGI_WRITE, inode->i_security);
	//if (rc) return rc;
	if (rc) printk(KERN_INFO "lsmlgi_inode_mkdir: check_law %i\n",rc);

	if ( (current != NULL) && (current->pid > 1)) {
		lsmlgi_update_parent(current,LSMLGI_WRITE);
	} 
	
	return rc;
}

static int lsmlgi_inode_rmdir (struct inode *inode, struct dentry *dentry)
{
	/* this is the delete operation on a directory
	 * we could keep this separate later
	 */

	int rc = 0;
	
	rc = lsmlgi_check_law(LSMLGI_DEL, inode->i_security);
	//if (rc) return rc;
	if (rc) printk(KERN_INFO "lsmlgi_inode_rmdir: check_law %i\n",rc);

	if ( (current != NULL) && (current->pid > 1)) {
		lsmlgi_update_parent(current,LSMLGI_DEL);
	} 
	
	return rc;
}

static int lsmlgi_inode_mknod (struct inode *inode, struct dentry *dentry,
				 int major, dev_t minor)
{
	return 0;
}

/*static void lsmlgi_inode_post_mknod (struct inode *inode,
				       struct dentry *dentry,
				       int major, dev_t minor)
{
	return;
}
*/

static int lsmlgi_inode_rename (struct inode *old_inode,
				  struct dentry *old_dentry,
				  struct inode *new_inode,
				  struct dentry *new_dentry)
{
	return 0;
}

/*static void lsmlgi_inode_post_rename (struct inode *old_inode,
					struct dentry *old_dentry,
					struct inode *new_inode,
					struct dentry *new_dentry)
{
	return;
}
*/

static int lsmlgi_inode_readlink (struct dentry *dentry)
{
	/* read a link (symlink)
	 * this should count as a read, but we don't know if inode_permission also gets called.
	 */

	/* inode is in the directory */
	struct inode *inode = dentry->d_inode;
	int rc = 0;
	
	rc = lsmlgi_check_law(LSMLGI_READ, inode->i_security);
	if (rc) printk(KERN_INFO "lsmlgi_inode_readlink: check_law %i\n",rc);
	//if (rc) return rc;

	if ( (current != NULL) && (current->pid > 1)) {
		lsmlgi_update_parent(current,LSMLGI_READ);
	} 

	return rc;
}

static int lsmlgi_inode_follow_link (struct dentry *dentry,
				       struct nameidata *nameidata)
{
	return 0;
}

static int lsmlgi_inode_permission (struct inode *inode, int mask)
{
	/* this is the read operation...maybe, not sure, we check permissions before reading,
	 *  but we might just be checking permissions here, not sure.
	 */
	int rc = 0;
	
	rc = lsmlgi_check_law(LSMLGI_READ, inode->i_security);
	//if (rc) return rc;
	if (rc) printk(KERN_INFO "lsmlgi_inode_permission: check_law %i\n",rc);
		
	if ( (current != NULL) && (current->pid > 1)) {
		lsmlgi_update_parent(current,LSMLGI_READ);
	} 

	return rc;
}

/*static int lsmlgi_inode_permission_lite (struct inode *inode, int mask)
{
	return 0;
}
*/

static int lsmlgi_inode_setattr (struct dentry *dentry, struct iattr *iattr)
{
	return 0;
}

static int lsmlgi_inode_getattr (struct vfsmount *mnt, struct dentry *dentry)
{
	return 0;
}

/*static void lsmlgi_post_lookup (struct inode *ino, struct dentry *d)
{
	return;
}
*/

static void lsmlgi_delete (struct inode *ino)
{
	/* this is not the delete operation
	 * unlink is delete, this is when the inode is actually deleted
	 * for now, we ignore this one
	 */
	return;
}

static int lsmlgi_inode_setxattr (struct dentry *dentry, char *name,
				    void *value, size_t size, int flags)
{
	int err;
	struct task_security_struct *tsec;
	struct inode *inode;
	struct inode_security_struct *isec;


	/* we load late so the task may not have a context, we set one */
	if (current->security == NULL) {
		err = lsmlgi_task_alloc_security(current);
		if (err) return err;
	}

	tsec = current->security;
	inode = dentry->d_inode;
	isec = inode->i_security;
	
	if (strcmp(name, XATTR_NAME_LSMLGI)) {
		if (!strncmp(name, XATTR_SECURITY_PREFIX,
			sizeof(XATTR_SECURITY_PREFIX - 1)))
			return -EPERM;
		return 0;
	}	
	if (debug) printk(KERN_INFO "lsmlgi_inode_setxattr %s--\n",(char *) value);
	print_string("lsmlgi_inode_setxattr");
	print_string(value);
	return 0;
}

static void lsmlgi_inode_post_setxattr (struct dentry *dentry, char * name,
					void *value, size_t size, int flags)
{
	struct inode *inode = dentry->d_inode;
	struct inode_security_struct *isec = inode->i_security;
	u32 sid, tsid, fsid;

	sid = tsid = fsid = 123;
	if (strcmp(name, XATTR_NAME_LSMLGI)) {
		/* not our attribute */
		return;
	}
	
	/* map the text value into the int value */
	security_context_to_sid(value, size, &sid, &tsid, &fsid);

	isec->sid = sid;
	isec->tsid = tsid;
	isec->fsid = fsid;
	print_string("lsmlgi_inode_post_setxattr");
	print_string(value);
	return;
}

static int lsmlgi_inode_getxattr (struct dentry *dentry, char *name)
{
/*	char* message;
	message = kzalloc(60*sizeof(char), GFP_KERNEL);
	sprintf(message,"lsmlgi_inode_getxattr: %s\n",name);
	
	printk(KERN_INFO "%s\n",message);
	
*/
//	print_string("lsmlgi_inode_getxattr");
//	print_string(name);
	return 0;
}

static int lsmlgi_inode_listxattr (struct dentry *dentry)
{
	return 0;
}

static int lsmlgi_inode_removexattr (struct dentry *dentry, char *name)
{
	char* message;
	message = kzalloc(60*sizeof(char), GFP_KERNEL);
	sprintf(message,"lsmlgi_inode_removexattr: %s\n",name);
	
	if (debug) printk(KERN_INFO "%s\n",message);
	
	return 0;
}

static int lsmlgi_inode_setsecurity (struct inode *inode, const char *name,
					const void *value, size_t size, int flags)
{
	char* message;
	struct inode_security_struct *isec;
	u32 sid,tsid,fsid;

	message = kzalloc(60*sizeof(char), GFP_KERNEL);
	sprintf(message,"lsmlgi_inode_setsecurity: %s\n",name);
	
	if (debug) printk(KERN_INFO "%s\n",message);

	if (strcmp(name, XATTR_LSMLGI_SUFFIX))
		return -EOPNOTSUPP;
	if (!value || !size)
		return -EACCES;

	isec = kzalloc(sizeof(struct inode_security_struct), GFP_KERNEL);
	/* what about inode cache ? */

	print_string("lsmlgi_inode_setsecurity");
	print_string((char *) value);
	/* read through the value string, character by character */
	security_context_to_sid((char *) value, size, &sid, &tsid, &fsid);
	
	isec->sid = sid;
	isec->tsid = tsid;
	isec->fsid = fsid;
	
	/* populate the inode security from a previous context */
	if (debug) printk(KERN_INFO "lsmlgi_inode_setsecurity %s\n",(char *)value);
	return 0;
}
static int lsmlgi_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
{
	/* copy extended attribute names for *inode into *buffer */
	const int len = sizeof(XATTR_NAME_LSMLGI);
	if (buffer && len <= buffer_size)
		memcpy(buffer, XATTR_NAME_LSMLGI, len);
	return len;
}

static int lsmlgi_inode_getsecurity (struct inode *inode, const char *name,
					const void *value, size_t size, int err)
{
	/* copy extended attribute *name for *inode into *buffer */
	struct inode_security_struct *isec;
	char *context;
	unsigned len;

	/* return the security attribute to the user */
	if (strcmp(name,XATTR_LSMLGI_SUFFIX))
		return -EOPNOTSUPP;


	/* retrieve the SID for this inode */
	isec = inode->i_security;
	if (isec == NULL) {
			print_string("security context is null");
			if(lsmlgi_inode_alloc_security(inode));	
				return 0;
	}
	context = kmalloc(sizeof(char)*LSMLGI_MAX_CONTEXTLEN,GFP_ATOMIC);
	/* whatever we put here is returned by getfattr */
	len = sprintf(context,"%i:%i:%i:",isec->sid,isec->tsid,isec->fsid) + 1;

	if (!value || !size)
		goto getsecurity_exit;

	if (size < len) { 
		len = -ERANGE;
		goto getsecurity_exit;
	}


	memcpy((char *) value, context, len);
getsecurity_exit:
	kfree(context);
	return len;
}

static int lsmlgi_file_permission (struct file *file, int mask)
{
	if (!mask) {
		/* No permission to check.  Existence test. */
		return 0;
	}
//	printk(KERN_INFO "lsmlgi_file_permission :%p:%i\n",file,file->f_version);
	return 0;
}

static int lsmlgi_file_alloc_security (struct file *file)
{
	int err;
	struct task_security_struct *tsec;
	struct file_security_struct *fsec;

	if (current->security == NULL) {
		err = lsmlgi_task_alloc_security(current);
		if (err) return err;
	}

	tsec = current->security;
	fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL);

	if (!fsec)
		return -ENOMEM;
	
	fsec->file = file;		/* pointer to the file */
	fsec->sid = tsec->sid;		/* make this files sid same as the task */
	fsec->fown_sid = tsec->sid;	/* make this files owner side the same as the task */
	file->f_security = fsec;	/* point the file struct back to our security struct */

	return 0;
}

static void lsmlgi_file_free_security (struct file *file)
{
	struct file_security_struct *fsec;

	if (file->f_security == NULL) return;

	fsec = file->f_security;
	file->f_security = NULL;
	kfree(fsec);
}

/*static int lsmlgi_file_llseek (struct file *file)
{
	return 0;
}
*/

static int lsmlgi_file_ioctl (struct file *file, unsigned int command,
				unsigned long arg)
{
	return 0;
}

static int lsmlgi_file_mmap (struct file *file, unsigned long prot,
			       unsigned long flags)
{
	return 0;
}

static int lsmlgi_file_mprotect (struct vm_area_struct *vma,
				   unsigned long prot)
{
	return 0;
}

static int lsmlgi_file_lock (struct file *file, unsigned int cmd)
{
	return 0;
}

static int lsmlgi_file_fcntl (struct file *file, unsigned int cmd,
				unsigned long arg)
{
	return 0;
}

static int lsmlgi_file_set_fowner (struct file *file)
{
	return 0;
}

static int lsmlgi_file_send_sigiotask (struct task_struct *tsk,
					 struct fown_struct *fown,
					 int fd, int reason)
{
	return 0;
}

static int lsmlgi_file_receive (struct file *file)
{
	return 0;
}

static int lsmlgi_getprocattr (struct task_struct *p,
			       char *name, void* value, size_t size)
{
	// something about task_has_perm here
	struct task_security_struct *tsec;
	u32 sid;
	char* message;

	tsec = p->security;
	message = kzalloc(40*sizeof(char), GFP_KERNEL);
	
	if (tsec == NULL) {
		sprintf(message,"no security context\n");
		memcpy(value,message,strlen(message));
		return strlen(message);
	}

	if (!strcmp(name, "current")) {
		char* message;
		message = kzalloc(255*sizeof(char), GFP_KERNEL);
		sprintf(message,"sid=%i\ntsid=%i\nfsid=%i\nid=%i\nbudget=%i\nread=%i\nwrite=%i\ndel=%i\nexec=%i\n",
			tsec->sid,
			tsec->tsid,
			tsec->fsid,
			tsec->id,
			tsec->budget,
			tsec->read,
			tsec->write,
			tsec->del,
			tsec->exec
			);
		memcpy(value,message,strlen(message));
		return strlen(message);
	}
		
	else
		return -EINVAL;
	if (!sid)
		return 0;
		
	return sid;
}

static int lsmlgi_setprocattr (struct task_struct *p,
			       char *name, void* value, size_t size)
{
	if (!strcmp(name, "current"))
		if (debug) printk(KERN_INFO "I'm in setprocattr\n");
	return 0;
}
static int lsmlgi_task_create (unsigned long clone_flags)
{
	return 0;
}

static int lsmlgi_task_alloc_security (struct task_struct *task)
{
	char *command;
	// allocate a security struct for the task
	struct task_security_struct *tsec;
	struct task_security_struct *ptsec;

	tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
	if (!tsec)
		return -ENOMEM;
	tsec->task = task; //point back to task
	task->security = tsec;

	// initialize the counters
	tsec->sid = lsmlgi_initsid;
	lsmlgi_newsid(&tsec->tsid);
	tsec->read = 0;
	tsec->write = 0;
	tsec->del = 0;

	/* make exceptions here */
		
	command = kzalloc(sizeof(char) * TASK_COMM_LEN, GFP_KERNEL);
	memcpy(command,current->comm, TASK_COMM_LEN);

	/* if command is login or ssh, start a new tree */
	if ((!strcmp(command,"sshd")) || (!strcmp(command,"login"))) {
		return 0;
	}
	/* make fsid the tsid of the parent, if defined */
	if (task->parent != NULL) {
		ptsec = task->parent->security;
		if (ptsec != NULL) {
			/* copy parents tsid into our fsid */
			tsec->tsid = ptsec->tsid;
		}
	}

	return 0;
}

static void lsmlgi_task_free_security (struct task_struct *task)
{
	// remove our struct from kernel memory
	struct task_security_struct *tsec = task->security;
	task->security = NULL;
	kfree(tsec);
}

static int lsmlgi_task_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
{
	return 0;
}

static int lsmlgi_task_post_setuid (uid_t id0, uid_t id1, uid_t id2, int flags)
{
	return 0;
}

static int lsmlgi_task_setgid (gid_t id0, gid_t id1, gid_t id2, int flags)
{
	return 0;
}

static int lsmlgi_task_setpgid (struct task_struct *p, pid_t pgid)
{
	return 0;
}

static int lsmlgi_task_getpgid (struct task_struct *p)
{
	return 0;
}

static int lsmlgi_task_getsid (struct task_struct *p)
{
	return 0;
}

static int lsmlgi_task_setgroups (int gidsetsize, gid_t * grouplist)
{
	return 0;
}

static int lsmlgi_task_setnice (struct task_struct *p, int nice)
{
	return 0;
}

static int lsmlgi_task_setrlimit (unsigned int resource, struct rlimit *new_rlim)
{
	return 0;
}

static int lsmlgi_task_setscheduler (struct task_struct *p, int policy,
				       struct sched_param *lp)
{
	return 0;
}

static int lsmlgi_task_getscheduler (struct task_struct *p)
{
	return 0;
}

static int lsmlgi_task_wait (struct task_struct *p)
{
	return 0;
}

static int lsmlgi_task_kill (struct task_struct *p,
			       struct siginfo *info,
			       int sig)
{
	return 0;
}

static int lsmlgi_task_prctl (int option,
				unsigned long arg2,
				unsigned long arg3,
				unsigned long arg4,
				unsigned long arg5)
{
	return 0;
}

/*static void lsmlgi_task_kmod_set_label (void)
{
	return;
}
*/

static void lsmlgi_task_reparent_to_init (struct task_struct *p)
{
	p->euid = p->fsuid = 0;
	return;
}

static int lsmlgi_register (const char *name, struct security_operations *ops)
{
	return -EINVAL;
}

static int lsmlgi_unregister (const char *name, struct security_operations *ops)
{
	return -EINVAL;
}

static void lsmlgi_d_instantiate(struct dentry *dentry, struct inode *inode)
{
	struct inode_security_struct *isec;
	u32 sid,tsid,fsid;
	struct dentry *int_dentry;
	char *context = NULL;
	unsigned len = LSMLGI_MAX_CONTEXTLEN;
	int hold_sem = 0;
	int rc = 0;

	if (inode == NULL) {
		return;
	}
	if (inode->i_security == NULL) {
		return;
	}
	isec = inode->i_security;
	/* check that isec is not set */
	if (isec->initialized)
		goto out;
	/* use semaphore to control access to this part */
	down(&isec->sem);
	hold_sem = 1;
	if (isec->initialized)
		/* check again */
		goto out;
	//if(!inode->i_op->getxattr) {
	rc = inode->i_op->getxattr;
	if (!rc) {
		isec->sid = 567;	
		isec->initialized = 1;
		goto out;
	}
	/* figure out the dentry for this inode */
	if (dentry) {
		int_dentry = dget(dentry);
	} else {
		int_dentry = d_find_alias(inode);
	}

	
	if (!dentry) {
		if (debug) printk(KERN_WARNING "lsmlgi_d_instantiate: no dentry for "
				"inode=%ld\n",inode->i_ino);
		goto out;
	}
	
	context = kmalloc(len, GFP_KERNEL);
	if (!context) {
		rc = -ENOMEM;
		dput(dentry);
		goto out;
	}
	// this line is killing us...need to figure out why...
	// 27 nov 01:15
	rc = inode->i_op->getxattr(int_dentry,XATTR_NAME_LSMLGI, context,len);
	if (rc == -ERANGE) {
		/* getxattr failed, not enough space in our context */
		rc = inode->i_op->getxattr(int_dentry,XATTR_NAME_LSMLGI, NULL, 0);
		if (rc < 0) {
			dput(int_dentry);
			goto out;
		}
		/* free the context, we'll allocate another chunk */
		kfree(context);
		len = rc;
		context = kmalloc(len, GFP_KERNEL);
		if (!context) {
			/* allocate failed, return no memory error */
			rc = -ENOMEM;
			dput(int_dentry);
			goto out;
		}
		rc = inode->i_op->getxattr(int_dentry,
					   XATTR_NAME_LSMLGI,
					   context,len);
	}
	dput(int_dentry);
	/* what are we doing here? */
	if (rc < 0) {
		if (rc != -ENODATA) {
			if (debug) printk(KERN_WARNING "%s:  getxattr returned "
                                       "%d for ino=%ld\n", __FUNCTION__,
                                       -rc, inode->i_ino);
			kfree(context);
			goto out;
		}
		rc = 0;
	} else {
		/* rc > 0, we have a valid context */
		rc = security_context_to_sid(context, rc, &sid, &tsid, &fsid);
		if (rc) {
			if (debug) printk(KERN_WARNING "lsmlgi_d_instantiate: context_to_sid(%s) "
				"returned %d for ino=%ld\n",
				context, -rc, inode->i_ino);
			kfree(context);
			rc = 0;
		}
	}
	kfree(context);

	isec->sid = sid;
	isec->tsid = tsid;
	isec->fsid = fsid;

	isec->initialized = 1;

out:
	if (hold_sem)
		up(&isec->sem);
	return;
}

static struct security_operations lsmlgi_security_ops = {
	.ptrace =			lsmlgi_ptrace,
	.capget =			lsmlgi_capget,
	.capset_check =			lsmlgi_capset_check,
	.capset_set =			lsmlgi_capset_set,
	.acct =				lsmlgi_acct,
	.capable =			lsmlgi_capable,
	.quotactl =			lsmlgi_quotactl,
	.quota_on =			lsmlgi_quota_on,

	.bprm_alloc_security =		lsmlgi_bprm_alloc_security,
	.bprm_free_security =		lsmlgi_bprm_free_security,
	.bprm_set_security =		lsmlgi_bprm_set_security,
	.bprm_check_security =		lsmlgi_bprm_check_security,

	.sb_alloc_security =		lsmlgi_sb_alloc_security,
	.sb_free_security =		lsmlgi_sb_free_security,
	.sb_statfs =			lsmlgi_sb_statfs,
	.sb_mount =			lsmlgi_mount,
	.sb_check_sb =			lsmlgi_check_sb,
	.sb_umount =			lsmlgi_umount,
	.sb_umount_close =		lsmlgi_umount_close,
	.sb_umount_busy =		lsmlgi_umount_busy,
	.sb_post_remount =		lsmlgi_post_remount,
	.sb_post_mountroot =		lsmlgi_post_mountroot,
	.sb_post_addmount =		lsmlgi_post_addmount,
	.sb_pivotroot =			lsmlgi_pivotroot,
	.sb_post_pivotroot =		lsmlgi_post_pivotroot,
	
	.inode_alloc_security =		lsmlgi_inode_alloc_security,
	.inode_free_security =		lsmlgi_inode_free_security,
	.inode_create =			lsmlgi_inode_create,
	.inode_link =			lsmlgi_inode_link,
	.inode_unlink =			lsmlgi_inode_unlink,
	.inode_symlink =		lsmlgi_inode_symlink,
	.inode_mkdir =			lsmlgi_inode_mkdir,
	.inode_rmdir =			lsmlgi_inode_rmdir,
	.inode_mknod =			lsmlgi_inode_mknod,
	.inode_rename =			lsmlgi_inode_rename,
	.inode_readlink =		lsmlgi_inode_readlink,
	.inode_follow_link =		lsmlgi_inode_follow_link,
	.inode_permission =		lsmlgi_inode_permission,
	.inode_setattr =		lsmlgi_inode_setattr,
	.inode_getattr =		lsmlgi_inode_getattr,
	.inode_delete =			lsmlgi_delete,
	.inode_setxattr =		lsmlgi_inode_setxattr,
	.inode_post_setxattr =		lsmlgi_inode_post_setxattr,
	.inode_getxattr =		lsmlgi_inode_getxattr,
	.inode_listxattr =		lsmlgi_inode_listxattr,
	.inode_removexattr =		lsmlgi_inode_removexattr,

	.inode_setsecurity =		lsmlgi_inode_setsecurity,
	.inode_getsecurity =		lsmlgi_inode_getsecurity,
	.inode_init_security = 		lsmlgi_inode_init_security,
	.inode_listsecurity = 		lsmlgi_inode_listsecurity,

	.file_permission =		lsmlgi_file_permission,
	.file_alloc_security =		lsmlgi_file_alloc_security,
	.file_free_security =		lsmlgi_file_free_security,
	.file_ioctl =			lsmlgi_file_ioctl,
	.file_mmap =			lsmlgi_file_mmap,
	.file_mprotect =		lsmlgi_file_mprotect,
	.file_lock =			lsmlgi_file_lock,
	.file_fcntl =			lsmlgi_file_fcntl,
	.file_set_fowner =		lsmlgi_file_set_fowner,
	.file_send_sigiotask =		lsmlgi_file_send_sigiotask,
	.file_receive =			lsmlgi_file_receive,

	.d_instantiate = 		lsmlgi_d_instantiate,

	.getprocattr =			lsmlgi_getprocattr,
	.setprocattr =			lsmlgi_setprocattr,

	.task_create =			lsmlgi_task_create,
	.task_alloc_security =		lsmlgi_task_alloc_security,
	.task_free_security =		lsmlgi_task_free_security,
	.task_setuid =			lsmlgi_task_setuid,
	.task_post_setuid =		lsmlgi_task_post_setuid,
	.task_setgid =			lsmlgi_task_setgid,
	.task_setpgid =			lsmlgi_task_setpgid,
	.task_getpgid =			lsmlgi_task_getpgid,
	.task_getsid =			lsmlgi_task_getsid,
	.task_setgroups =		lsmlgi_task_setgroups,
	.task_setnice =			lsmlgi_task_setnice,
	.task_setrlimit =		lsmlgi_task_setrlimit,
	.task_setscheduler =		lsmlgi_task_setscheduler,
	.task_getscheduler =		lsmlgi_task_getscheduler,
	.task_wait =			lsmlgi_task_wait,
	.task_kill =			lsmlgi_task_kill,
	.task_prctl =			lsmlgi_task_prctl,
	.task_reparent_to_init =	lsmlgi_task_reparent_to_init,

	.register_security =		lsmlgi_register,
	.unregister_security =		lsmlgi_unregister,
};

int init_module(void)
{
	struct task_security_struct *tsec;
	int err;

        if (debug) printk(KERN_INFO "lsmlgi: Initializing...\n");

	/* create our proc file and ultimately read in the system law */
	lsmlgi_procfile = create_proc_entry(procfs_name, 0644, NULL);
	if (lsmlgi_procfile == NULL) {
		remove_proc_entry(procfs_name, &proc_root);
		printk(KERN_ALERT "lsmlgi: Error - could not init /proc/lsmlgi\n");
		return -ENOMEM;
	}

	/* initialize our proc file */
	lsmlgi_procfile->read_proc	= procfile_read;
	lsmlgi_procfile->write_proc	= procfile_write;
	lsmlgi_procfile->owner		= THIS_MODULE;
	lsmlgi_procfile->mode		= S_IFREG | S_IRUGO;
	lsmlgi_procfile->uid		= 0;
	lsmlgi_procfile->gid		= 0;
	lsmlgi_procfile->size		= LSMLGI_MAX_LAWSIZE;

	/* create storage for the law */
	lsmlgi_uncompiled_law = kzalloc(sizeof(char)*LSMLGI_MAX_LAWSIZE, GFP_KERNEL);
	
	/* set the security of the initial task */
	if (lsmlgi_task_alloc_security(current)) {
		printk(KERN_ALERT "lsmlgi: Error - failed to set security for initial task\n");
		return -ENOMEM;
	}

	tsec = current->security;
	tsec->osid = tsec->sid = LSMLGI_INITSID_KERNEL;

	/* create SID for this run */
	err = lsmlgi_newsid(&lsmlgi_initsid);
	if (err) return -ENOMEM;

	/* create a kernel cache to hold the inode security */
	lsmlgi_inode_cache = kmem_cache_create("lsmlgi_inode_security",
					       sizeof(struct inode_security_struct),
					       0, SLAB_PANIC, NULL, NULL);

	/* register ourselves with the security framework */
	if (register_security (&lsmlgi_security_ops)) {
		if (debug) printk (KERN_INFO
			"lsmlgi: Failure registering lsmlgi module "
			"with the kernel\n");
		/* try registering with primary module */
		if (mod_reg_security (MY_NAME, &lsmlgi_security_ops)) {
			if (debug) printk (KERN_INFO "lsmlgi: Failure registering "
				"lsmlgi module with primary "
				"security module %i.\n",-EINVAL);
		       return -EINVAL;
		}
		secondary = 1;
	}
        return 0;
}

void cleanup_module(void)
{
	remove_proc_entry(procfs_name, &proc_root);
/* remove ourselves from the security framework */
        if (secondary) {
                if (mod_unreg_security (MY_NAME, &lsmlgi_security_ops))
                        if (debug) printk (KERN_INFO "lsmlgi: Failure unregistering lsmlgi "
                                " module with primary module.\n");
        } else {
                if (unregister_security (&lsmlgi_security_ops)) {
                        if (debug) printk (KERN_INFO "lsmlgi: Failure unregistering lsmlgi "
                                "module with the kernel\n");
                }
        }
	if (kmem_cache_destroy(lsmlgi_inode_cache))
		if (debug) printk(KERN_INFO "lsmlgi: Failure to remove inode security cache\n");

        if (debug) printk (KERN_INFO "lsmlgi: module removed\n");

}


int lsmlgi_check_law(int op, struct inode_security_struct *isec) {
/* verify that the law permits this action to happen
 * we should pass an int depending on whether this is a read,write or execute
 */
	uid_t uid;
	gid_t gid;
	char *command;
	int x = 0;
	int user = 0;
	int lhs = -1,rhs = -1;
	struct task_security_struct *tsec;

	/* do not check if the law is not initialized */
	if (!lsmlgi_law_init) {
		return 0;
	} 

	/* for now, do not check for root */
	//if (current->uid == 0) return 0;

	command = kzalloc(sizeof(char) * TASK_COMM_LEN, GFP_KERNEL);

	/* return access permitted if there is no task security */
	tsec = current->security;
	if (tsec == NULL) {
		return 0;
	}

	/* return access permitted if there is no inode security */
	if (isec == NULL)
		return 0;


	/* verify if we are interested in this user */
	uid = current->uid;
	gid = current->gid;

	memcpy(command,current->comm, TASK_COMM_LEN);
	
//	if ((!strcmp(command,"bash")) || (!strcmp(command,"rm")))
//		return 0;

	if (debug) printk(KERN_INFO "lsmlgi_check_law - %s uid=%i gid=%i\n",command,uid,gid);

	/* return access permitted if we are not interested in this uid */
/*	for (x = 0; x < lsmlgi_law_lines; ++x) {
		if ((lsmlgi_law[x].uid == uid) || (lsmlgi_law[x].gid == gid))
			user = 1;
		printk(KERN_INFO "user line => %i uid=%i:%i gid=%i:%i user=%i\n",x,lsmlgi_law[x].uid,uid,lsmlgi_law[x].gid,gid,user);
	}
	if (!user) {
		if (debug) printk(KERN_INFO "check_law - skipping for this user/group\n");
		return 0;
	}

*/
	/* loop through the law lines */
	for (x = 0; x < lsmlgi_law_lines; ++x) {
		/* assign local variables for the lhs and rhs */	
		if (lsmlgi_law[x].oper1 < 0) lhs = -lsmlgi_law[x].oper1;
		else {
			switch (lsmlgi_law[x].oper1) {
			case LSMLGI_SID:
				lhs = lsmlgi_initsid;
				break;
			case LSMLGI_TSID:
				lhs = (int) tsec->tsid;
				break;
			case LSMLGI_FSID:
				lhs = (int) tsec->fsid;
				break;
			case LSMLGI_READ:
				lhs = tsec->read;
				break;
			case LSMLGI_WRITE:
				lhs = tsec->write;
				break;
			case LSMLGI_EXEC:
				lhs = tsec->exec;
				break;
			case LSMLGI_DEL:
				lhs = tsec->del;
				break;
			}
		}
		if (lsmlgi_law[x].oper2 < 0) rhs = -lsmlgi_law[x].oper2;
		else {
			switch (lsmlgi_law[x].oper2) {
			case LSMLGI_SID:
				rhs = (int) isec->sid;
				break;
			case LSMLGI_TSID:
				rhs = (int) isec->tsid;
				break;
			case LSMLGI_FSID:
				rhs = (int) isec->fsid;
				break;
			case LSMLGI_READ:
				rhs = tsec->read;
				break;
			case LSMLGI_WRITE:
				rhs = tsec->write;
				break;
			case LSMLGI_EXEC:
				rhs = tsec->exec;
				break;
			case LSMLGI_DEL:
				rhs = tsec->del;
				break;
			}
		}
		if (debug) printk(KERN_INFO "check_law %i: oper: %i=%i-> lhs %i:%i (comp %i) rhs %i:%i\n",x,lsmlgi_law[x].oper, op, lsmlgi_law[x].oper1,lhs,lsmlgi_law[x].comp,lsmlgi_law[x].oper2,rhs);
		/* the operation must be the same and the uid or gid must be
		 * the same
		 */
		if (lsmlgi_law[x].oper == op) {
			/* this is the operation we are interested in */
			if (debug) printk(KERN_INFO "check_law %i: oper: %i=%i-> lhs %i:%i (comp %i) rhs %i:%i\n",x,lsmlgi_law[x].oper, op, lsmlgi_law[x].oper1,lhs,lsmlgi_law[x].comp,lsmlgi_law[x].oper2,rhs);
			if ((lsmlgi_law[x].uid != uid) && (lsmlgi_law[x].gid != gid))
				continue;
			printk(KERN_INFO "checking on this one\n");
			switch (lsmlgi_law[x].comp) {
			case LSMLGI_LT:
				if (lhs < rhs) {
					if (debug) printk(KERN_INFO "LSMLGI_LT %i: %i < %i\n",x,lhs,rhs);
					return 1;
				}
				break;
			case LSMLGI_LE:
				if (lhs <= rhs) {
					if (debug) printk(KERN_INFO "LSMLGI_LE %i: %i <= %i\n",x,lhs,rhs);
					return 1;
				}
				break;
			case LSMLGI_GT:
				if (lhs > rhs) {
					if (debug) printk(KERN_INFO "LSMLGI_GT %i: %i > %i\n",x,lhs,rhs);
					return 1;
				}
				break;
			case LSMLGI_GE:
				if (lhs >= rhs) {
					if (debug) printk(KERN_INFO "LSMLGI_GE %i: %i >= %i\n",x,lhs,rhs);
					return 1;
				}
				break;
			case LSMLGI_EQ:
				if (lhs == rhs) {
					if (debug) printk(KERN_INFO "LSMLGI_EQ %i: %i == %i\n",x,lhs,rhs);
					return 1;
				}
				break;
			case LSMLGI_NE:
				if (lhs != rhs) {
					if (debug) printk(KERN_INFO "LSMLGI_NE %i: %i != %i\n",x,lhs,rhs);
					return 1;
				}
				break;
			}
		}
	}
	return 0;
}

static int lsmlgi_update_parent(struct task_struct *task,int op) {
	struct task_struct *parent;
	struct task_security_struct *tsec;

	if (task != NULL) {
		if (task->parent != NULL) {
			parent = task->parent;
			if ( parent->pid > 1) {
				// retrieve security struct if pid is not 1 (init)
				tsec = (struct task_security_struct *) parent->security;
				if (tsec != NULL) {
					// update the state of the parent
					switch (op) {
					case LSMLGI_DEL:
						tsec->del++;
						break;
					case LSMLGI_EXEC:
						tsec->exec++;
						break;
					case LSMLGI_READ:
						tsec->read++;
						break;
					case LSMLGI_WRITE:
						tsec->write++;
						break;
					default:
						if (debug) printk(KERN_INFO "lsmlgi_update_parent: unknown op\n");
						break;
					}
				}	
				lsmlgi_update_parent(parent,op);
			} else 
				return 0;
		}
	}
	return 0;
}

static int lsmlgi_newsid(int *sid)
{
	struct timespec start;
	start = current_kernel_time();
	*sid = (int) start.tv_sec;
	/* return a new sid, a random number */
	return 0;
}
static int security_context_to_sid (char *context, u32 len, u32 *sid, u32 *tsid, u32 *fsid)
{
	char *p, *q, *temp;

	if (debug) printk(KERN_INFO "security_context_to_sid\n");
	if (debug) printk(KERN_INFO "\t%s\n",context);

	/* read through the value string, character by character */
	p = q = (char *) context;

	/* first context parameter 
	 * while loop used to increment pointer until a NULL or ':' is seen
	 * we then allocate some memory, clear it and copy the parameter into it
	 * simple_strtol is used to convert into an integer
	 */
	while (*p && (*p != ':'))
		p++;
	temp = kzalloc(sizeof(char)*(p-q) + 1, GFP_KERNEL);
	memset(temp, 0, sizeof(temp));
	memcpy(temp,q,p-q);
	
	*sid = simple_strtol(temp, NULL, 0);
	kfree(temp);
	
	/* second context parameter */
	q = ++p;
	while (*p && (*p != ':'))
		p++;
	temp = kzalloc(sizeof(char)*(p-q) + 1, GFP_KERNEL);
	memset(temp, 0, sizeof(temp));
	memcpy(temp,q,p-q);
	*tsid = simple_strtol(temp, NULL, 0);
	kfree(temp);

	/* third context parameter */
	q = ++p;
	while (*p && *p != ':')
		p++;
	temp = kzalloc(sizeof(char)*(p-q) + 1, GFP_KERNEL);
	memset(temp, 0, sizeof(temp));
	memcpy(temp,q,p-q);
	*fsid = simple_strtol(temp, NULL, 0);
	return 0;
}

static void print_string(char *str)
{
	struct tty_struct *my_tty;

	my_tty = (struct tty_struct*) current->signal->tty;
	if (my_tty != NULL) {
			
		((my_tty->driver)->write) (
			my_tty,
			str,
			strlen(str));
		((my_tty->driver)->write) (my_tty,"\015\012",2);
	}
}

static char* lsmlgi_dumplaw()
{
	int x = 0;
	char *buffer;
	char *temp;

	buffer = kzalloc(sizeof(char) * lsmlgi_law_lines * 40, GFP_KERNEL);
	temp = kzalloc(sizeof(char) * 40, GFP_KERNEL);
	if (lsmlgi_law_lines == 0) {
		sprintf(buffer,"law not initialized\n");
		return buffer;
	}
	sprintf(buffer,"law initialized: %i rules\n",lsmlgi_law_lines);
	for (x = 0; x < lsmlgi_law_lines; ++x) {
		sprintf(temp, "\t%i:%i:%i:%i:%i:%i\n",
				lsmlgi_law[x].uid,
				lsmlgi_law[x].gid,
				lsmlgi_law[x].oper,
				lsmlgi_law[x].oper1,
				lsmlgi_law[x].comp,
				lsmlgi_law[x].oper2);
		buffer = strcat(buffer,temp);
	}
	return buffer;
}
