/*
 * megas.c, by pmsac@toxyn.org, 1998
 *
 * Just another ripp off from:
 * - cocain.c, by pmsac@toxyn.org, 1998, which is a rip off:
 *  - heroin.c, by Runar Jensen, zarq@opaque.org, 1998(?), from BugTraq
 *  - itf.c v0.8, by plaguez, dube0866@eurobretagne.fr, 1997, from Phrack52
 *
 * gcc -Wall -O3 -fomit-frame-pointer -c megas.c
 *
 */

#define MODULE
#define __KERNEL__
#define VERSION_COUNT sn199807290335

#include <linux/proc_fs.h>
#include <linux/module.h>
#include <linux/limits.h>
#include <sys/syscall.h>

/* Examine, Allow */
#define EXAMINE 0
#define ALLOW 1
char *TASKSTR[][2] = {
	{ "pine", NULL },
	{ "pine", "joe" },
	{ "bash", "ls" },
	{ "bash", "rmmod" },
	{ NULL, NULL }
};

#define PID 0
#define SIGDEBUG 31
#define SIGSANE 10
#define SIGCOUNT 0

extern void *sys_call_table[];

int errno;
int VERSION_COUNT = 0;
int debug = 0;
int __NR_myexecve;
#define TMPBINMAXLEN (PATH_MAX + 1)


int (*oldKill)(pid_t, int);
int (*oldExecve)(const char *, const char *[], const char *[]);
void cleanup_module(void);

#define DEBUG(X); if (debug) printk(X);
#define DEBUG2(X,Y); if (debug) printk(X,Y);
#define DEBUG3(X,Y,Z); if (debug) printk(X,Y,Z);



int newKill(pid_t pid, int sig) {
int ret;

	DEBUG("newKill()\n");
	
	if ((pid != PID) ||
	    ((sig != SIGCOUNT) &&
	     (sig != SIGDEBUG) &&
	     (sig != SIGSANE))) {
	    	DEBUG("-> oldKill()...\n");
		ret = (*oldKill)(pid, sig);
		if (ret == -1)
                        return (-errno);
		return(ret);
	}
	if (sig == SIGCOUNT) {
		DEBUG("-> Toggling usage count...\n");
		VERSION_COUNT ^= 1;
		if (VERSION_COUNT)
			MOD_INC_USE_COUNT;
		else
			MOD_DEC_USE_COUNT;
	}
	if (sig == SIGSANE) {
		DEBUG("-> Sanitizing module, pls remove by hand...\n");
		while (VERSION_COUNT != 0) {
			MOD_DEC_USE_COUNT;
			VERSION_COUNT--;
		}
		cleanup_module();
	}
	if (sig == SIGDEBUG) {
		DEBUG("-> Toggling debugging...\n");
		debug ^= 1;
	}

	DEBUG("-> Returning...\n");
        return(0);
}

int cryptic_execve(const char *filename, const char *argv[], const char *envp[])
{
long __res;
	__asm__ volatile ("int $0x80":"=a" (__res):"0"(__NR_myexecve), "b"((long) (filename)), "c"((long) (argv)), "d"((long) (envp)));
	return (int) __res;
}

int newExecve(const char *bin, const char *argv[], const char *envp[]) {
int ret;
int allow = 0;
int spotted = 0;
char *tmpBinName;


	DEBUG("newExecve()\n");

	DEBUG("-> Allocating kernel space...\n");
	tmpBinName = (char *) kmalloc(TMPBINMAXLEN, GFP_KERNEL);
	DEBUG("-> Copying to kernel space...\n");
	memcpy_fromfs(tmpBinName, bin, TMPBINMAXLEN);
	DEBUG2("-> bin == %s\n", tmpBinName);

	DEBUG("-> Examining list...\n");
	for (ret = 0; TASKSTR[ret][EXAMINE] != NULL; ret++) {
		if (TASKSTR[ret][ALLOW] == NULL) {
			DEBUG("-> Deny all (further ?)...\n");
			break;
		}
		if (strstr(current->comm, TASKSTR[ret][EXAMINE]) != NULL) {
			DEBUG3("-> Spotted... %s/%s\n", TASKSTR[ret][EXAMINE], TASKSTR[ret][ALLOW]);
			spotted = 1;
			if (strstr(tmpBinName, TASKSTR[ret][ALLOW]) != NULL) {
			    	DEBUG("-> List allowed...\n");
				allow = 1;
				break;
			}
		}
	}
	
	DEBUG("-> Deallocating kernel space...\n");
	
	if ((TASKSTR[ret][EXAMINE] == NULL) && !spotted) {
		DEBUG("-> Unlisted, allowing...\n");
		allow = 1;
	}

	if (allow) {
		DEBUG("-> Allowed, oldExecve()...\n");
		ret = (*cryptic_execve)(bin, argv, envp);
		return (ret);
	}
	
	DEBUG("-> Not allowed, -EPERM...\n");
	return(-EPERM);
}

int init_module(void) {

	DEBUG("init_module()\n");
	
	DEBUG("-> Replacing kill()...\n");
	oldKill = sys_call_table[SYS_kill];
	sys_call_table[SYS_kill] = newKill;

	DEBUG("-> Replacing execve()...\n");
	__NR_myexecve = 164;
	while (__NR_myexecve != 0 && sys_call_table[__NR_myexecve] != 0)
		__NR_myexecve--;
	oldExecve = sys_call_table[SYS_execve];
	if (__NR_myexecve != 0) {
		sys_call_table[__NR_myexecve] = oldExecve;
		sys_call_table[SYS_execve] = newExecve;
	}
	
	DEBUG("-> Returning...\n");
	return 0;
}

void cleanup_module(void) {

	DEBUG("cleanup_module()\n");
	
	DEBUG("-> Restoring kill()...\n");
	sys_call_table[SYS_kill] = oldKill;
	
	DEBUG("-> Restoring execve()...\n");
	sys_call_table[SYS_execve] = oldExecve;
	DEBUG("-> Restoring __NR_myexecve");
	if (__NR_myexecve != 0)
		sys_call_table[__NR_myexecve] = 0;
	DEBUG("-> Returning\n");
}
