Fix n stuff

This commit is contained in:
Simone Margaritelli 2017-04-17 15:31:47 +02:00
parent 7caa119361
commit 0c4aa8a1a7
6 changed files with 122 additions and 71 deletions

16
main.py
View file

@ -1,14 +1,20 @@
#!/usr/bin/python
import os
import logging
from opensnitch.packetqueue import PacketQueue
logging.basicConfig(format='[%(asctime)s] (%(levelname)s) %(message)s',level=logging.DEBUG)
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
q = PacketQueue()
from opensnitch.snitch import Snitch
snitch = Snitch()
try:
q.start()
logging.info( "OpenSnitch running with pid %d." % os.getpid() )
snitch.start()
except KeyboardInterrupt, e:
pass
print "\n\nStopping ..."
logging.info( "Quitting ..." )
q.stop()
snitch.stop()

View file

@ -5,10 +5,11 @@ from socket import inet_ntoa
class Connection:
def __init__( self, payload ):
self.data = payload.get_data()
self.data = payload
self.pkt = ip.IP( self.data )
self.src_addr = inet_ntoa( self.pkt.src )
self.dst_addr = inet_ntoa( self.pkt.dst )
self.hostname = None
self.src_port = None
self.dst_port = None
self.proto = None

32
opensnitch/dns.py Normal file
View file

@ -0,0 +1,32 @@
import logging
from threading import Lock
from scapy.all import *
class DNSCollector:
def __init__(self):
self.lock = Lock()
self.hosts = { '127.0.0.1': 'localhost' }
def is_dns_response(self, packet):
if DNSRR in packet and packet.qd is not None and packet.an is not None:
return True
else:
return False
def add_response( self, packet ):
with self.lock:
hostname = packet.qd.qname
address = packet.an.rdata
if hostname.endswith('.'):
hostname = hostname[:-1]
logging.debug( "DNS[%s] = %s" % ( address, hostname ) )
self.hosts[address] = hostname
def get_hostname( self, address ):
with self.lock:
if address in self.hosts:
return self.hosts[address]
else:
logging.debug( "No hostname found for address %s" % address )
return address

View file

@ -1,59 +0,0 @@
import os
import nfqueue
from socket import AF_INET, AF_INET6, inet_ntoa
from threading import Lock
from opensnitch.ui import UI
from opensnitch.connection import Connection
from opensnitch.rule import Rules
class PacketQueue:
lock = Lock()
rules = Rules()
fw_rules = ( "OUTPUT -t mangle -m conntrack --ctstate NEW -j NFQUEUE --queue-num 0 --queue-bypass",
"INPUT --protocol udp --sport 53 -j NFQUEUE --queue-num 0 --queue-bypass" )
@staticmethod
def get_verdict( c ):
verdict = PacketQueue.rules.get_verdict(c)
if verdict is None:
with PacketQueue.lock:
( verdict, apply_for_all ) = UI.prompt_user( c.app.name,
c.app_path,
None,
c.dst_addr,
c.dst_port,
c.proto )
PacketQueue.rules.add_rule( c, verdict, apply_for_all )
return verdict
@staticmethod
def pkt_callback(pkt):
conn = Connection(pkt)
verd = nfqueue.NF_ACCEPT
if conn.proto is not None:
verd = PacketQueue.get_verdict( conn )
pkt.set_verdict(verd)
return 1
# TODO: Support IPv6!
def __init__( self ):
self.q = nfqueue.queue()
self.q.set_callback( PacketQueue.pkt_callback )
self.q.fast_open(0, AF_INET)
self.q.set_queue_maxlen(2*1024)
def start(self):
for r in PacketQueue.fw_rules:
os.system( "iptables -I %s" % r )
self.q.try_run()
def stop(self):
for r in PacketQueue.fw_rules:
os.system( "iptables -D %s" % r )
self.q.unbind(AF_INET)
self.q.close()

71
opensnitch/snitch.py Normal file
View file

@ -0,0 +1,71 @@
import os
import logging
import nfqueue
from socket import AF_INET, AF_INET6, inet_ntoa
from threading import Lock
from scapy.all import *
from opensnitch.ui import UI
from opensnitch.connection import Connection
from opensnitch.dns import DNSCollector
from opensnitch.rule import Rules
class Snitch:
IPTABLES_RULES = ( "OUTPUT -t mangle -m conntrack --ctstate NEW -j NFQUEUE --queue-num 0 --queue-bypass", )
# TODO: Support IPv6!
def __init__( self ):
self.lock = Lock()
self.rules = Rules()
self.dns = DNSCollector()
self.q = nfqueue.queue()
self.q.set_callback( self.pkt_callback )
self.q.fast_open(0, AF_INET)
self.q.set_queue_maxlen(2*1024)
def get_verdict(self,c):
verdict = self.rules.get_verdict(c)
if verdict is None:
with self.lock:
c.hostname = self.dns.get_hostname(c.dst_addr)
( verdict, apply_for_all ) = UI.prompt_user(c)
self.rules.add_rule( c, verdict, apply_for_all )
return verdict
def pkt_callback(self,pkt):
verd = nfqueue.NF_ACCEPT
try:
data = pkt.get_data()
packet = IP(data)
if self.dns.is_dns_response(packet):
self.dns.add_response(packet)
else:
conn = Connection(data)
if conn.proto is not None:
verd = self.get_verdict( conn )
except Exception as e:
print e
pkt.set_verdict(verd)
return 1
def start(self):
for r in Snitch.IPTABLES_RULES:
logging.debug( "Applying iptables rule '%s'" % r )
os.system( "iptables -I %s" % r )
self.q.try_run()
def stop(self):
for r in Snitch.IPTABLES_RULES:
logging.debug( "Deleting iptables rule '%s'" % r )
os.system( "iptables -D %s" % r )
self.q.unbind(AF_INET)
self.q.close()

View file

@ -3,14 +3,14 @@ import nfqueue
class UI:
@staticmethod
def prompt_user( app_name, app_path, app_icon, d_addr, d_port, proto ):
def prompt_user( c ):
title = 'OpenSnitch'
msg = "%s (%s) wants to connect to %s on %s port %s" % ( \
app_name,
app_path,
d_addr,
proto.upper(),
d_port )
c.app.name,
c.app_path,
c.hostname,
c.proto.upper(),
c.dst_port )
choices = [ 'Allow Once',
'Allow Forever',
'Allow All',