diff options
Diffstat (limited to '.scripts/retiolum.py')
-rwxr-xr-x | .scripts/retiolum.py | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/.scripts/retiolum.py b/.scripts/retiolum.py new file mode 100755 index 00000000..99da7aae --- /dev/null +++ b/.scripts/retiolum.py @@ -0,0 +1,312 @@ +#!/usr/bin/python2 +import sys, os, time, socket, subprocess, thread, random, Queue, binascii, logging #these should all be in the stdlib +import sqlite3 +from Crypto.PublicKey import RSA +from optparse import OptionParser + +def pub_encrypt(netname, hostname_t, text): #encrypt data with public key + conn = sqlite3.connect("/etc/tinc/" + netname + "/hosts.sqlite") + c = conn.cursor() + hostname_tupel = [hostname_t] + pubkey = "" + try: + c.execute("select r_pub from hosts where hostname=?", hostname_tupel) + except: + logging.error("RSA_Encryption: Database error") + return -1 + for i in c: + pubkey += i[0] + c.close + rsa_pub = RSA.importKey(pubkey) + enc_text = rsa_pub.encrypt(text, 0) #seems like RSA_encrypt needs no random + return(binascii.b2a_base64(enc_text[0])) + +def priv_decrypt(netname, enc_data): #decrypt data with private key + raw_privkey = open("/etc/tinc/" + netname + "/rsa_key.priv", "r") + r_privkey = raw_privkey.readlines() + privkey = "" + for i in xrange(len(r_privkey)): + privkey += r_privkey[i] + raw_privkey.close() + + + rsa_priv = RSA.importKey(privkey) + dec_text = rsa_priv.decrypt(binascii.a2b_base64(enc_data)) + return(dec_text) + +def database2hostfiles(netname): #make hostsfiles from database + conn = sqlite3.connect("/etc/tinc/" + netname + "/hosts.sqlite") + c = conn.cursor() + c.execute("select * from hosts") + for i in c: + host = open("/etc/tinc/" + netname + "/hosts/" + i[0], "w") + host.write(i[2]) + host.write(i[3]) + host.write(i[1]) + host.write(i[5]) + host.close() + c.close() + +def address2hostfile(netname, hostname, address): #adds address to hostsfile or restores it if address is empty + tupel = [hostname,] + conn = sqlite3.connect("/etc/tinc/" + netname + "/hosts.sqlite") + c = conn.cursor() + c.execute("select * from hosts where hostname=?", tupel) + for i in c: + host = open("/etc/tinc/" + netname + "/hosts/" + i[0], "w") + if address != "": + host.write("Address = " + address + "\n") + host.write(i[2]) + host.write(i[3]) + host.write(i[1]) + host.write(i[5]) + host.close() + c.close() + logging.info("sending ALRM to tinc deamon!") + tincd_ALRM = subprocess.call(["tincd -n " + netname + " --kill=HUP" ],shell=True) + +def findhostinlist(hostslist, hostname, ip): #finds host + ip in list + for line in xrange(len(hostslist)): + if hostname == hostslist[line][0] and ip == hostslist[line][1]: + return line + return -1 #nothing found + +def getHostname(netname): + tconf = open("/etc/tinc/" + netname + "/tinc.conf", "r") + feld = tconf.readlines() + tconf.close() + for x in feld: + if x.startswith("Name"): + return str(x.partition("=")[2].lstrip().rstrip("\n")) + + print("hostname not found!") + return -1 #nothing found + + +####Thread functions + + +def sendthread(netname, hostname, sendfifo, ghostmode): #send to multicast, sends keep alive packets + while True: + try: + #{socket init start + ANY = "0.0.0.0" + SENDPORT = 23542 + MCAST_ADDR = "224.168.2.9" + MCAST_PORT = 1600 + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) #initalize socket with udp + sock.bind((ANY,SENDPORT)) #now bound to Interface and Port + sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) #activate multicast + #}socket init end + + if ghostmode == 0: + + i = 9 + + while True: + i += 1 + if not sendfifo.empty(): + sock.sendto(sendfifo.get(), (MCAST_ADDR,MCAST_PORT) ) + logging.info("send: sending sendfifo") + else: + time.sleep(1) + if i == 10: + sock.sendto("#Stage1#" + netname + "#" + hostname + "#", (MCAST_ADDR,MCAST_PORT) ) + logging.debug("send: sending keep alive") + i = 0 + else: + while True: + if not sendfifo.empty(): + sock.sendto(sendfifo.get(), (MCAST_ADDR,MCAST_PORT) ) + logging.info("send: sending sendfifo") + else: + time.sleep(1) + + except: + logging.error("send: socket init failed") + time.sleep(10) + + + +def recvthread(netname, hostname, timeoutfifo, authfifo): #recieves input from multicast, send them to timeout or auth + while True: + try: + ANY = "0.0.0.0" + MCAST_ADDR = "224.168.2.9" + MCAST_PORT = 1600 + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) #create a UDP socket + sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #allow multiple sockets to use the same PORT number + sock.bind((ANY,MCAST_PORT)) #Bind to the port that we know will receive multicast data + sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 255) #tell the kernel that we are a multicast socket + + + status = sock.setsockopt(socket.IPPROTO_IP, + socket.IP_ADD_MEMBERSHIP, #Tell the kernel that we want to add ourselves to a multicast group + socket.inet_aton(MCAST_ADDR) + socket.inet_aton(ANY)); #The address for the multicast group is the third param + + while True: + while True: + + try: + data, addr = sock.recvfrom(1024) + ip, port = addr + break + except socket.error, e: + pass + + logging.debug("recv: got data") + dataval = data.split("#") + if dataval[0] == "": + if dataval[2] == netname: + if dataval[1] == "Stage1": + if dataval[3] != hostname: + timeoutfifo.put(["tst", dataval[3], ip]) + logging.info("recv: got Stage1: writing data to timeout") + if dataval[1] == "Stage2": + if dataval[3] == hostname: + authfifo.put([dataval[1], dataval[3], ip, dataval[4]]) + logging.info("recv: got Stage2: writing data to auth") + if dataval[1] == "Stage3": + if dataval[3] != hostname: + authfifo.put([dataval[1], dataval[3], ip, dataval[4]]) + logging.info("recv: got Stage3: writing data to auth") + except: + logging.error("recv: socket init failed") + time.sleep(10) + +def timeoutthread(netname, timeoutfifo, authfifo): #checks if the hostname is already in the list, deletes timeouted nodes + hostslist = [] #hostname, ip, timestamp + + while True: + if not timeoutfifo.empty(): + curhost = timeoutfifo.get() + if curhost[0] == "add": + hostslist.append([curhost[1], curhost[2], time.time()]) + address2hostfile(netname, curhost[1], curhost[2]) + logging.info("adding host to hostslist") + elif curhost[0] == "tst": + line = findhostinlist(hostslist, curhost[1], curhost[2]) + if line != -1: + hostslist[line][2] = time.time() + logging.debug("timeout: refreshing timestamp") + else: + authfifo.put(["Stage1", curhost[1], curhost[2]]) + logging.info("timeout: writing to auth") + + else: + i = 0 + while i < len(hostslist): + if time.time() - hostslist[i][2] > 60: + address2hostfile(netname, hostslist[i][0], "") + del hostslist[i] + logging.info("timeout: deleting dead host") + else: + i += 1 + time.sleep(2) + +def auththread(netname, hostname, authfifo, sendfifo, timeoutfifo): #manages authentication with clients (bruteforce sensitve, should be fixed) + authlist = [] #hostname, ip, Challenge, timestamp + + + while True: + if not authfifo.empty(): + logging.debug("auth: authfifo is not empty") + curauth = authfifo.get() + if curauth[0] == "Stage1": + line = findhostinlist(authlist, curauth[1], curauth[2]) + if line == -1: + challengenum = random.randint(0,65536) + encrypted_message = pub_encrypt(netname, curauth[1], "#" + hostname + "#" + str(challengenum) + "#") + authlist.append([curauth[1], curauth[2], challengenum, time.time()]) + else: + encrypted_message = pub_encrypt(netname, authlist[line][0], "#" + hostname + "#" + str(authlist[line][2]) + "#") + if encrypted_message == -1: + logging.info("auth: RSA Encryption Error") + else: + sendtext = "#Stage2#" + netname + "#" + curauth[1] + "#" + encrypted_message + "#" + sendfifo.put(sendtext) + logging.info("auth: got Stage1 sending now Stage2") + + if curauth[0] == "Stage2": + dec_message = priv_decrypt(netname, curauth[3]) + splitmes = dec_message.split("#") + if splitmes[0] == "": + encrypted_message = pub_encrypt(netname, splitmes[1], "#" + splitmes[2] + "#") + if encrypted_message == -1: + logging.error("auth: RSA Encryption Error") + else: + sendtext = "#Stage3#" + netname + "#" + curauth[1] + "#" + encrypted_message + "#" + sendfifo.put(sendtext) + logging.info("auth: got Stage2 sending now Stage3") + + if curauth[0] == "Stage3": + line = findhostinlist(authlist, curauth[1], curauth[2]) + if line != -1: + dec_message = priv_decrypt(netname, curauth[3]) + splitmes = dec_message.split("#") + logging.info("auth: checking challenge") + if splitmes[0] == "" and splitmes[1] == str(authlist[line][2]): + timeoutfifo.put(["add", curauth[1], curauth[2]]) + del authlist[line] + logging.info("auth: Stage3 checked, sending now to timeout") + else: + logging.error("auth: challenge failed") + + else: + i = 0 + while i < len(authlist): + if time.time() - authlist[i][3] > 120: + del authlist[i] + logging.info("auth: deleting timeoutet auth") + else: + i += 1 + time.sleep(1) + +#Program starts here! +#netname = "retiolum" +#hostname = "miefda901" + +parser = OptionParser() +parser.add_option("-n", "--netname", dest="netname", help="the netname of the tinc network") +parser.add_option("-H", "--hostname", dest="hostname", default="default" , help="your nodename, if not given, it will try too read it from tinc.conf") +parser.add_option("-d", "--debug", dest="debug", default="0", help="debug level: 0,1,2,3 if empty debug level=0") +parser.add_option("-g", "--ghost", action="store_true", dest="ghost", default=False, help="deactivates active sending, keeps you anonymous in the public network") +(option, args) = parser.parse_args() + +if option.netname == None: + parser.error("Netname is required, use -h for help!") +if option.hostname == "default": + option.hostname = getHostname(option.netname) + +hostname = option.hostname +netname = option.netname + + +#Logging stuff +LEVELS = {'3' : logging.DEBUG, + '2' : logging.INFO, + '1' : logging.ERROR, + '0' : logging.CRITICAL} + +level_name = option.debug +level = LEVELS.get(level_name, logging.NOTSET) +logging.basicConfig(level=level) + +wget = subprocess.call(["wget vpn.miefda.org/hosts.sqlite -O /etc/tinc/" + netname + "/hosts.sqlite"], shell=True) +database2hostfiles(netname) +start_tincd = subprocess.call(["tincd -n " + netname ],shell=True) + +sendfifo = Queue.Queue() #sendtext +authfifo = Queue.Queue() #Stage{1, 2, 3} hostname ip enc_data +timeoutfifo = Queue.Queue() #State{tst, add} hostname ip + +thread_recv = thread.start_new_thread(recvthread, (netname, hostname, timeoutfifo, authfifo)) +thread_send = thread.start_new_thread(sendthread, (netname, hostname, sendfifo, option.ghost)) +thread_timeout = thread.start_new_thread(timeoutthread, (netname, timeoutfifo, authfifo)) +thread_auth = thread.start_new_thread(auththread, (netname, hostname, authfifo, sendfifo, timeoutfifo)) + +##dirty while function, SHOULD BE IMPROVED +while True: + time.sleep(10) |