class Irc(object): """Handles a client connection to the IRC protocol""" def __init__(self, server, publisher, flood_prevention=1): self.server = server self.publisher = publisher self.flood_prevention = flood_prevention self._conn = Connection(server['host'], server['port'], server['ssl'], server['timeout']) # Internal connection # Timer to prevent flooding self.timer = Event() self.timer.set() # The canonical channels of IRC to subscribe / publish # Receives input to send to irc server self.sender = Queue() # Receives output to publish self.receiver = Queue() # Suscribe my output to receive data from connection self.publisher.subscribe(self.receiver, self._conn.receiver, Msg.from_msg) # Subscribe connection to send data from my input self.publisher.subscribe(self._conn.sender, self.sender, self._prevent_flood) @property def connected(self): return self._conn.connected def connect(self): if self.connected: return True self._conn.connect() if self.connected: return True else: return self._conn.state def disconnect(self): self._conn.disconnect() return self.connected def kill(self): """Completely terminate the irc connection""" self.publisher.unsubscribe(self.receiver, self._conn.receiver) self.publisher.unsubscribe(self._conn.sender, self.sender) self._conn.kill() def _prevent_flood(self, msg): """Used to prevent sending messages extremely quickly""" if self.flood_prevention > 0 and msg.cmd != 'PONG': self.timer.wait() self.timer.clear() gevent.spawn_later(self.flood_prevention, self.timer.set) return str(msg)
def main(): HOST, PORT, REVERSE, PUB_KEY_FILE, PRV_KEY_FILE, KN_HOSTS_FILE = read_conf( ) # Si se pasa dirección y/o puerto como argumento, sobreescribirlo addr, port = read_args() if addr: HOST = addr if port: PORT = port # Si existe el known_hosts, leerlo y convertirlo en una lista if os.path.isfile(KN_HOSTS_FILE): with open(KN_HOSTS_FILE, "rb") as f: known_hosts = f.read() known_hosts = known_hosts.split("\n") else: known_hosts = False # Creación del socket conn = Connection(HOST, PORT) try: if REVERSE == '1': conn.listen(timeout=10) else: conn.connect() keyring = Keyring() # Lee los ficheros de claves pub/priv with open(PRV_KEY_FILE, 'rb') as f: prv_key = f.read() with open(PUB_KEY_FILE, 'rb') as f: pub_key = f.read() # Envía su clave pública al servidor conn.send(pub_key) # Recibe la clave pública del servidor srv_pub_key = conn.receive() if srv_pub_key == ':ERR:': print '{}[!]{} ERROR: El servidor no reconoce tu clave pública.'.format( color.RED, color.END) sys.exit(1) # Comparación de clave pública recibida con las contenidas en el known_hosts if (not known_hosts) or (srv_pub_key not in known_hosts): add_srv_to_known_hosts = raw_input( "{}[!]{} WARNING: La clave pública de este servidor no se encuentra almacenada:\n{} \nSi lo desea, puede añadirla [y/n] >>> " .format(color.YELLOW, color.END, srv_pub_key)) if add_srv_to_known_hosts.lower() == "y": with open(KN_HOSTS_FILE, "ab") as f: f.write(srv_pub_key + "\n") # Envía firma de autenticación signature = keyring.sign(prv_key) conn.send(signature) srv_signature = conn.receive() # Si recibe error de autenticación, informa y cierra el programa if srv_signature == ':ERR:': print '{}[!]{} ERROR: La autenticación ha fallado'.format( color.RED, color.END) sys.exit(1) # Si logra autenticarse, comprueba la firma del servidor print '{}[+]{} Cliente autenticado correctamente'.format( color.GREEN, color.END) sign_valid = keyring.verify(srv_pub_key, srv_signature) if sign_valid: conn.send(':OK:') print '{}[+]{} Servidor autenticado correctamente'.format( color.GREEN, color.END) else: conn.send(':ERR:') print '{}[!]{} ERROR: La autenticación ha fallado'.format( color.RED, color.END) sys.exit(1) # Marca de sincronización sync = conn.receive() # Intercambio de clave de sesión mediante PKI session_key = keyring.genSessionKey() session_key_crypted = keyring.cipherRSA(session_key, srv_pub_key) conn.send(session_key_crypted) print '{}[+]{} Intercambiando clave de sesión...'.format( color.BLUE, color.END) # Una vez establecida e intercambiada la clave de sesión, asociamos el keyring a la conexión keyring.session_key = session_key conn.keyring = keyring shell = Shell(conn) shell.start() except conn.timeout: print '\n{}[!]{} El servidor está desconectado.\n'.format( color.RED, color.END)
def main(): initial_path = os.path.dirname(os.path.abspath(__file__)) HOST, PORT, REVERSE, PUB_KEY_FILE, PRV_KEY_FILE, KN_HOSTS_FILE = read_conf( ) # Comprobar que exista un fichero con la clave pública del cliente if os.path.isfile(KN_HOSTS_FILE): with open(KN_HOSTS_FILE, "rb") as f: known_hosts = f.read() known_hosts = known_hosts.split("\n") else: print '[!] ERROR: El fichero {} debe existir.'.format(KN_HOSTS_FILE) sys.exit(1) while True: time.sleep(1) # Creación del socket conn = Connection(HOST, PORT) try: if REVERSE == '1': conn.connect() else: conn.listen(timeout=5) keyring = Keyring() # Lee los ficheros de claves pub/priv with open(PRV_KEY_FILE, 'rb') as f: prv_key = f.read() with open(PUB_KEY_FILE, 'rb') as f: pub_key = f.read() # Recibe la clave pública del cliente cli_pub_key = conn.receive() # Si no reconoce la clave pub del cliente, informa de que se cierra la conexión if cli_pub_key not in known_hosts: conn.send(':ERR:') sys.exit(1) # Si reconoce la clave pub del cliente, le envía su clave pública conn.send(pub_key) # Recibe firma de autenticación del cliente y la comprueba cli_signature = conn.receive() sign_valid = keyring.verify(cli_pub_key, cli_signature) # Si es válida, envía su firma de autenticación if sign_valid: signature = keyring.sign(prv_key) conn.send(signature) else: conn.send(':ERR:') sys.exit(1) # Si el cliente no acepta la firma, cierra el programa auth = conn.receive() if auth == ':ERR:': sys.exit(1) # Marca de sincronización conn.send(':SYNC:') # Intercambio de clave de sesión mediante PKI session_key_crypted = conn.receive() session_key = keyring.decipherRSA(session_key_crypted, prv_key) # Una vez establecida e intercambiada la clave de sesión, asociamos el keyring a la conexión keyring.session_key = session_key conn.keyring = keyring shell = Shell(conn) shell.start() os.chdir(initial_path) # Por si el cliente se cambió de directorio except: pass