/*
 * N0Sp00f.c			Semplice modulo per evitare che qualche lama
 *				decida di usare il nostro sistema come hop di
 *				lancio per pacchetti IP spoofati. Intercetta
 *				la chiamata di sistema socketcall() per il
 *				parametro IP_HDRINCL passato a setsockopt(). 
 *				Purtroppo Linux ha le seguenti istruzioni in
 *				~/net/ipv4/af_inet.c:
 *
 *				case SOCK_RAW:
 *               			if (protocol == IPPROTO_RAW)
 *                       			sk->ip_hdrincl = 1;
 *
 *				che permettono di bypassare tranquillamente
 *				l'uso di setsockopt() ...
 *				In questo caso dovremo anche controllare in
 * 				ogni sys_sendto() per evitare lo spoof degli
 *				IP sorgente dalla nostra box. Comunque, ogni
 *				tentativo verra' loggato. Una comoda password
 *				da inserire in un file in /proc/net/ potra'
 *				servire al 'legittimo' root per operare sugli
 *				header dei pacchetti.
 *
 *				Implementazione per Linux 2.2.x
 *
 *                                           __NO__(C)2000 FuSyS [S0ftPj|BFi]
 *							   <fusys@s0ftpj.org>
 *
 * Compilate con:       	gcc -c -O2 -fomit-frame-pointer N0Sp00f.c
 * Installate con:      	insmod N0Sp00f.o <DEVICE=interfaccia>
 *
 * Credits:			LKMPG per /proc, pIGpEN per avermi spronato,
 *				i LAMAH di tutto il mondo per i DoS [se privi
 *				di ogni significato 'antagonista' ...],
 *				Gigi_Sull per tutti gli Aieeeee' =)
 *
 */

#define MODULE
#define __KERNEL__
#define CONFIG_PROC_FS
#include <linux/module.h>

#include <linux/types.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/if.h>
#include <linux/ip.h>
#include <linux/notifier.h>
#include <linux/inetdevice.h>
#include <linux/netdevice.h>
#include <sys/syscall.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>

#define PASS_LENGTH 	50
#define PASSWORD	"[S0ftPj|BFi]"
#define LKMNAME         "N0Sp00f"
#define LOG

int (*old_socketcall) (int, unsigned long *);
int (*old_query_module)(const char *, int, char *, size_t, size_t *) ;
extern void *sys_call_table[];
static char password[PASS_LENGTH];
char *DEVICE="eth0";
char Rip[15];
int errno;

MODULE_PARM(DEVICE, "s");

char *ntoa(unsigned long ip) {
        static char buff[18];
        char *p;
        p = (char *) &ip;
        sprintf(buff, "%d.%d.%d.%d",
                (p[0] & 255), (p[1] & 255), (p[2] & 255), (p[3] & 255));
        return(buff);
}

void getIPs() 
{
	struct device *dev;
	struct in_device *in_dev;
	struct in_ifaddr **ifap = NULL;
	struct in_ifaddr *ifa = NULL;
	
	dev =(struct device *)(dev_get(DEVICE));
	in_dev = dev->ip_ptr;
       	if ((in_dev=dev->ip_ptr) != NULL) {
               	for (ifap=&in_dev->ifa_list; (ifa=*ifap) != NULL; ifap=&ifa->ifa_next)
                       	if (strcmp(DEVICE, ifa->ifa_label) == 0)
                               	break;
       	}
	strncpy(Rip, ntoa(ifa->ifa_local), 15);
}

static ssize_t module_output(struct file *file, char *buf, size_t len, loff_t *offset)
{
  static int finished = 0;
  int i;
  char message[PASS_LENGTH+30];

  if (finished) {
    finished = 0;
    return 0;
  }
  sprintf(message, "N0SP00F Password\n");
  for(i=0; i<len && message[i]; i++)
    put_user(message[i], buf+i);
  finished = 1;
  return i;
}

static ssize_t module_input(struct file *file, const char *buf, size_t length, loff_t *offset)
{
  int i;

  for(i=0; i<PASS_LENGTH-1 && i<length; i++)
    get_user(password[i], buf+i);
  password[i] = '\0';
  return i;
}

static int module_permission(struct inode *inode, int op)
{
  if (current->euid == 0)
    return 0;
  return -EACCES;
}

int module_open(struct inode *inode, struct file *file)
{
  MOD_INC_USE_COUNT;
  return 0;
}

int module_close(struct inode *inode, struct file *file)
{
  MOD_DEC_USE_COUNT;
  return 0;
}

static struct file_operations N0SP00F_fops =
  {
    NULL, module_output, module_input, NULL, NULL, NULL,
    NULL, module_open, NULL, module_close,
  };

static struct inode_operations N0SP00F_iops =
  {
    &N0SP00F_fops, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
    NULL, NULL, NULL, module_permission
  };

static struct proc_dir_entry N0SP00F =
  {
    0, 7, "N0SP00F", S_IFREG | S_IRUGO | S_IWUSR,
    1, 0, 0, 50, &N0SP00F_iops, NULL
  };

int new_socketcall(int call, unsigned long *args)
{
	int socket;
        unsigned long *sargs = args;
	unsigned long a0, a1, a2;
	void *buf;
	struct iphdr *ip;

	if(call == SYS_SETSOCKOPT) {
	   if(sargs[2] == IP_HDRINCL) {
		if(!strstr(password, PASSWORD)) {
				printk(KERN_INFO 
				"<N0Sp00f> IP_HDRINCL: %s with UID:%d and TTY:%s\n",
				current->comm, current->uid,
				current->tty->driver.driver_name);
			return -EPERM;
		}
	   }
	   return socket = (*old_socketcall) (call, args);
	}
	else if(call == SYS_SENDTO) {
		get_user(a0, sargs);
                get_user(a1, sargs + 1);
		get_user(a2, sargs + 2);
		buf = (void*)kmalloc(a2, GFP_KERNEL);
		copy_from_user(buf, (void *) a1, a2);
		ip = (struct iphdr *)(void *)buf ;
		if(ip->ihl == 5 && ip->version == 4) {
		   if(!strstr(password, PASSWORD)) {
			if(!strstr(Rip, (ntoa(ip->saddr)))) {
			#ifdef LOG
				printk(KERN_INFO
				"<N0Sp00f> sys_sendto\(): %s with UID:%d, TTY:%s and IP: %s\n",
				current->comm, current->uid, 
				current->tty->driver.driver_name, ntoa(ip->saddr));
			#else
				printk(KERN_INFO
				"<N0Sp00f> sys_sendto\(): %s with UID:%d, TTY:%s\n",
				current->comm, current->uid,current->tty->driver.driver_name);			
			#endif
				return -EPERM;
			}
		   }
		}
	}
	return socket = (*old_socketcall) (call, args);
}

int new_query_module(const char *name, int which, char *buf, size_t bufsize,
        size_t *ret)
{
        int res;
        int cnt;
        char *ptr, *match;

        res = (*old_query_module)(name, which, buf, bufsize, ret);

        if(res == -1)
                return(-errno);

        if(which != QM_MODULES)
                return(res);

        ptr = buf;

        for(cnt = 0; cnt < *ret; cnt++) {
                if(!strcmp(LKMNAME, ptr)) {
                        match = ptr;
                        while(*ptr)
                                ptr++;
                        ptr++;
                        memcpy(match, ptr, bufsize - (ptr - (char *)buf));
                        (*ret)--;
                        return(res);
                }
                while(*ptr)
                        ptr++;
                ptr++;
        }

        return(res);
}

void ttycredit(char *str)
{
	struct tty_struct *mytty;

	if((mytty = current->tty) != NULL) {
		(*(mytty->driver).write)(mytty, 0, str, strlen(str));
	}
}
	
int init_module(void)
{
        EXPORT_NO_SYMBOLS;

	getIPs();
	old_socketcall = sys_call_table[SYS_socketcall];
	sys_call_table[SYS_socketcall] = (void *) new_socketcall;
        old_query_module = sys_call_table[SYS_query_module];
        sys_call_table[SYS_query_module]=(void *)new_query_module;
	ttycredit("\n\033[1;34m---[ \033[1;32mN0Sp00f\033[1;34m");
	ttycredit(" Linux 2.2.x LKM by FuSyS [S0ftPj|BFi] ]---\033[0m\r\n\r\n");
	printk(KERN_INFO "Loading N0Sp00f to protect bypassing %s\n", Rip);
	return proc_register(proc_net, &N0SP00F);
}

void cleanup_module(void)
{
	proc_unregister(proc_net, N0SP00F.low_ino);
	sys_call_table[SYS_socketcall] = old_socketcall;
        sys_call_table[SYS_query_module] = old_query_module;
	ttycredit("\n\033[1;34m Modulo N0Sp00f Disattivato\033[0m\r\n\r\n");
	printk(KERN_INFO "Modulo N0Sp00f Disattivato\n");
}
