From 08bd8dc7de2b2c82d2d7544c78b236586303f4ab Mon Sep 17 00:00:00 2001 From: vhb Date: Thu, 16 Jul 2015 21:00:31 +0200 Subject: [PATCH 1/8] fix typo --- ns_auth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ns_auth.py b/ns_auth.py index 2fd1483..6be63c0 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -5,7 +5,7 @@ # All rights reserved # # Redistribution and use in source and binary forms, with or without -# modification, are permitted providing that the following conditions +# modification, are permitted providing that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. @@ -99,7 +99,7 @@ def loop(self): try: rdata = self._sock.recv(4096).decode('utf8').strip('\n') except: - raise NetsouConnectionError + raise NetsoulConnectionError if rdata == '': raise NetsoulConnectionError From 16ca40a0b4adee684ff467bc7e2a7e5b0c90528f Mon Sep 17 00:00:00 2001 From: vhb Date: Thu, 16 Jul 2015 21:03:05 +0200 Subject: [PATCH 2/8] fix pep8 --- ns_auth.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/ns_auth.py b/ns_auth.py index 6be63c0..656dda2 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -1,6 +1,4 @@ #!/usr/bin/env python3.3 - -#- # Copyright 2013-2014 Emmanuel Vadot # All rights reserved # @@ -35,11 +33,15 @@ import getopt import os + class NetsoulConnectionError(BaseException): '''Disconnected''' + class Netsoul: - def __init__(self, login, password=None, host='ns-server.epitech.net', port=4242, verbose=False): + + def __init__(self, login, password=None, + host='ns-server.epitech.net', port=4242, verbose=False): self._host = host self._port = port self._login = login @@ -60,14 +62,16 @@ def isauth_get(self): def connect(self): try: if self._verbose: - print ('Connecting to ' + self._host + ' on port ' + str(self._port)) + print ('Connecting to ' + self._host + ' on port ' + + str(self._port)) self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.connect((self._host, self._port)) self._isconnected = True return True except: if self._verbose: - print ("Couldn't connect to " + self._host + ' on port ' + str(self._port)) + print ("Couldn't connect to " + self._host + ' on port ' + + str(self._port)) raise NetsoulConnectionError def _ns_auth_ag(self): @@ -79,25 +83,31 @@ def _ns_auth_ag(self): def _ns_ext_user_log(self): data = self._salut.split(' ') challenge = hashlib.md5() - challenge.update(bytes(data[2] + '-' + data[3] + '/' + data[4] + self._password, 'utf8')) - self._buffer = bytes('ext_user_log ' + self._login + ' ' + challenge.hexdigest() + ' ' + data[4] + ' ' + urllib.parse.quote('NSLOG 0.1') + '\n', 'utf8') + challenge.update(bytes(data[2] + '-' + data[3] + '/' + data[4] + + self._password, 'utf8')) + self._buffer = bytes('ext_user_log ' + self._login + ' ' + + challenge.hexdigest() + ' ' + data[4] + ' ' + + urllib.parse.quote('NSLOG 0.1') + '\n', 'utf8') self._writefd.append(self._sock) self._next = self._ns_state def _ns_state(self): self._isauth = True - self._buffer = bytes('state actif:' + str(int(time.time())) + '\n', 'utf8') + self._buffer = bytes('state actif:' + str(int(time.time())) + + '\n', 'utf8') self._writefd.append(self._sock) self._next = None def loop(self): while 1: - readfd, writefd, exceptfd = select.select([self._sock], self._writefd,[]) + readfd, writefd, exceptfd = select.select([self._sock], + self._writefd, []) for i in readfd: if i == self._sock: try: - rdata = self._sock.recv(4096).decode('utf8').strip('\n') + rdata = self._sock.recv(4096).decode('utf8') \ + .strip('\n') except: raise NetsoulConnectionError @@ -127,6 +137,7 @@ def loop(self): self._sock.send(self._buffer) self._writefd.remove(self._sock) + def daemonize(): try: pid = os.fork() @@ -148,13 +159,15 @@ def daemonize(): sys.stderr.write("os.fork() failed: %s\n" + e.strerror) sys.exit(1) + def usage(): print ('Usage: ' + sys.argv[0] + ' [-u login] [-h] [-v]') sys.exit(0) if __name__ == '__main__': try: - opts, args = getopt.getopt(sys.argv[1:], 'dhvu:', ['help', 'verbose', 'user=']) + opts, args = getopt.getopt(sys.argv[1:], 'dhvu:', + ['help', 'verbose', 'user=']) except getopt.GetoptError as e: print (e) usage() From bd9a65027c0b1139a9eaa2e720c94a6be73e6ca1 Mon Sep 17 00:00:00 2001 From: vhb Date: Thu, 16 Jul 2015 21:16:50 +0200 Subject: [PATCH 3/8] fix wrong option check --- ns_auth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ns_auth.py b/ns_auth.py index 656dda2..def8bcc 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -183,7 +183,7 @@ def usage(): verbose = True elif o in ('-u', '--user'): user = a - elif o in ('-d'): + elif o in ('-d', ): daemon = False else: usage() From 74d802957a05d803d35cbd367508874c8900f18b Mon Sep 17 00:00:00 2001 From: vhb Date: Thu, 16 Jul 2015 21:41:10 +0200 Subject: [PATCH 4/8] add demonize option --- ns_auth.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ns_auth.py b/ns_auth.py index def8bcc..e998f67 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -161,13 +161,13 @@ def daemonize(): def usage(): - print ('Usage: ' + sys.argv[0] + ' [-u login] [-h] [-v]') + print ('Usage: ' + sys.argv[0] + ' [-u login] [-h] [-v] [-d]') sys.exit(0) if __name__ == '__main__': try: opts, args = getopt.getopt(sys.argv[1:], 'dhvu:', - ['help', 'verbose', 'user=']) + ['demonize', 'help', 'verbose', 'user=']) except getopt.GetoptError as e: print (e) usage() @@ -183,7 +183,7 @@ def usage(): verbose = True elif o in ('-u', '--user'): user = a - elif o in ('-d', ): + elif o in ('-d', '--demonize'): daemon = False else: usage() From a1fd6af1a0866ed6277c0f4166b17a3c84d59a75 Mon Sep 17 00:00:00 2001 From: vhb Date: Fri, 17 Jul 2015 01:09:27 +0200 Subject: [PATCH 5/8] add file handling --- ns_auth.py | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/ns_auth.py b/ns_auth.py index e998f67..6695e5c 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -68,7 +68,7 @@ def connect(self): self._sock.connect((self._host, self._port)) self._isconnected = True return True - except: + except Exception: if self._verbose: print ("Couldn't connect to " + self._host + ' on port ' + str(self._port)) @@ -164,41 +164,73 @@ def usage(): print ('Usage: ' + sys.argv[0] + ' [-u login] [-h] [-v] [-d]') sys.exit(0) + +def load_config_file(filename): + import configparser + + with open(filename, 'r') as f: + config = configparser.ConfigParser({ + 'login': None, + 'password': None, + 'host': None, + 'port': None, + }) + config.readfp(f) + return { + 'login': config.get('config', 'login'), + 'password': config.get('config', 'password'), + 'host': config.get('config', 'host'), + 'port': config.getint('config', 'port'), + } + + +def merge_dict(x, y): + z = x.copy() + z.update(y) + return z + if __name__ == '__main__': try: - opts, args = getopt.getopt(sys.argv[1:], 'dhvu:', - ['demonize', 'help', 'verbose', 'user=']) + opts, args = getopt.getopt(sys.argv[1:], 'dhvu:f:', + ['demonize', 'help', 'verbose', 'user=', + 'config-file=']) except getopt.GetoptError as e: print (e) usage() - verbose = False + options = {'verbose': False, 'login': getpass.getuser()} daemon = True - user = getpass.getuser() for o, a in opts: if o in ('-h', '--help'): usage() elif o in ('-v', '--verbose'): - verbose = True + options['verbose'] = True elif o in ('-u', '--user'): - user = a + options['login'] = a elif o in ('-d', '--demonize'): daemon = False + elif o in ('-f', '--config-file'): + options = merge_dict(options, load_config_file(a)) else: usage() - password = getpass.getpass() + if options.get('login', None) is None: + options['login'] = getpass.getuser() + if options.get('password', None) is None: + options['password'] = getpass.getpass() + + print(options, daemon) if daemon: daemonize() while 1: - ns = Netsoul(login=user, verbose=verbose, password=password) + ns = Netsoul(**options) try: ns.connect() ns.loop() except NetsoulConnectionError: - if verbose: + if options['verbose']: print ('Disconnected, retrying in 10 seconds') time.sleep(10) From 524f91266ecd50c2362ac162fcf3a337ce494cda Mon Sep 17 00:00:00 2001 From: vhb Date: Sat, 18 Jul 2015 14:25:30 +0200 Subject: [PATCH 6/8] refactor config file handling and add small cipher for password --- ns_auth.py | 107 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 20 deletions(-) diff --git a/ns_auth.py b/ns_auth.py index 6695e5c..22d9664 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -32,6 +32,7 @@ import sys import getopt import os +import configparser class NetsoulConnectionError(BaseException): @@ -42,8 +43,9 @@ class Netsoul: def __init__(self, login, password=None, host='ns-server.epitech.net', port=4242, verbose=False): + print(login, password, host, port) self._host = host - self._port = port + self._port = int(port) self._login = login self._password = password self._verbose = verbose @@ -165,23 +167,88 @@ def usage(): sys.exit(0) -def load_config_file(filename): - import configparser +class ConfigHandler: + class NetsoulPasswordEncryption: - with open(filename, 'r') as f: - config = configparser.ConfigParser({ - 'login': None, - 'password': None, - 'host': None, - 'port': None, - }) - config.readfp(f) - return { - 'login': config.get('config', 'login'), - 'password': config.get('config', 'password'), - 'host': config.get('config', 'host'), - 'port': config.getint('config', 'port'), - } + _BLOCK_SIZE = 32 + _PADDING = '8' + + def __init__(self, AES, base64): + self._AES = AES + self._base64 = base64 + s = socket.gethostname() + secret = s + (32 - len(s)) * '4' + self._c = self._AES.new(secret) + + def _pad(self, s): + return s + (self._BLOCK_SIZE - len(s) % self._BLOCK_SIZE) \ + * self._PADDING + + def encrypt(self, s): + tmp = self._c.encrypt(self._pad(s)) + return self._base64.b64encode(tmp).decode('ascii') + + def decrypt(self, s): + val = self._c.decrypt(self._base64.b64decode(s)).decode("utf-8") + val = val.rstrip(self._PADDING) + if len(val) != 8: + raise Exception("Canno't decrypt password, please rewrite it") + return val + + class NoEncryption: + def encrypt(self, s): + return s + + def decrypt(self, s): + raise Exception("Canno't decrypt the password, please rewrite it") + + _CONFIG_SECTION = 'config' + + def __init__(self, config_file_path): + self.config_file_path = config_file_path + + def get_config(self, name): + try: + return self.config.get(self._CONFIG_SECTION, name) + except configparser.NoOptionError: + return None + + def check_password(self): + p = self.get_config('password') + if p is None: + return None + try: + from Crypto.Cipher import AES + import base64 + + cipher = self.NetsoulPasswordEncryption(AES, base64) + except ImportError: + print('Warning: pycrypt not available, no encryption will be used') + cipher = self.NoEncryption() + if len(p) == 8: + # Need to be encrypted + encrypted = cipher.encrypt(p) + self.config.set(self._CONFIG_SECTION, 'password', encrypted) + with open(self.config_file_path, 'w') as f: + self.config.write(f) + return p + else: + # Need be decrypted + decrypted = cipher.decrypt(p) + return decrypted + + def load_config_file(self): + with open(self.config_file_path, 'r') as f: + self.config = configparser.ConfigParser() + self.config.readfp(f) + password = self.check_password() + value = { + 'login': self.get_config('login'), + 'password': password, + 'host': self.get_config('host'), + 'port': self.get_config('port'), + } + return value def merge_dict(x, y): @@ -189,6 +256,7 @@ def merge_dict(x, y): z.update(y) return z + if __name__ == '__main__': try: opts, args = getopt.getopt(sys.argv[1:], 'dhvu:f:', @@ -211,7 +279,8 @@ def merge_dict(x, y): elif o in ('-d', '--demonize'): daemon = False elif o in ('-f', '--config-file'): - options = merge_dict(options, load_config_file(a)) + c = ConfigHandler(a) + options = merge_dict(options, c.load_config_file()) else: usage() @@ -220,8 +289,6 @@ def merge_dict(x, y): if options.get('password', None) is None: options['password'] = getpass.getpass() - print(options, daemon) - if daemon: daemonize() From e3095ad62652cf977f84a9fc7cca451197ff1dcd Mon Sep 17 00:00:00 2001 From: vhb Date: Sat, 18 Jul 2015 14:37:14 +0200 Subject: [PATCH 7/8] use mac addr as seed for secret generation --- ns_auth.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ns_auth.py b/ns_auth.py index 22d9664..b445879 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -33,6 +33,9 @@ import getopt import os import configparser +from uuid import getnode as get_mac +import string +import random class NetsoulConnectionError(BaseException): @@ -170,14 +173,18 @@ def usage(): class ConfigHandler: class NetsoulPasswordEncryption: + _CHARS = string.ascii_uppercase + string.digits + _SECRET_SIZE = 32 _BLOCK_SIZE = 32 _PADDING = '8' def __init__(self, AES, base64): self._AES = AES self._base64 = base64 - s = socket.gethostname() - secret = s + (32 - len(s)) * '4' + seed = get_mac() + random.seed(seed) + secret = ''.join(random.choice(self._CHARS) for x in + range(self._SECRET_SIZE)) self._c = self._AES.new(secret) def _pad(self, s): From baf0e504605b3809cfad0615466f56c0fd04e37e Mon Sep 17 00:00:00 2001 From: vhb Date: Tue, 4 Aug 2015 22:33:25 +0200 Subject: [PATCH 8/8] fix sleep and add config example --- example.ini | 6 ++++++ ns_auth.py | 13 +++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 example.ini diff --git a/example.ini b/example.ini new file mode 100644 index 0000000..bee9bad --- /dev/null +++ b/example.ini @@ -0,0 +1,6 @@ +[config] +login = tonlog_x +password = tonpass +host = ns-server.epitech.net +port = 4242 + diff --git a/ns_auth.py b/ns_auth.py index b445879..17f72cf 100755 --- a/ns_auth.py +++ b/ns_auth.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3.3 +#!/usr/bin/env python3 # Copyright 2013-2014 Emmanuel Vadot # All rights reserved # @@ -46,7 +46,6 @@ class Netsoul: def __init__(self, login, password=None, host='ns-server.epitech.net', port=4242, verbose=False): - print(login, password, host, port) self._host = host self._port = int(port) self._login = login @@ -72,6 +71,7 @@ def connect(self): self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._sock.connect((self._host, self._port)) self._isconnected = True + self.time_to_sleep = 1 return True except Exception: if self._verbose: @@ -230,7 +230,7 @@ def check_password(self): cipher = self.NetsoulPasswordEncryption(AES, base64) except ImportError: - print('Warning: pycrypt not available, no encryption will be used') + print('Warning: pycrypt not available, no encryption will be used for password') cipher = self.NoEncryption() if len(p) == 8: # Need to be encrypted @@ -263,7 +263,6 @@ def merge_dict(x, y): z.update(y) return z - if __name__ == '__main__': try: opts, args = getopt.getopt(sys.argv[1:], 'dhvu:f:', @@ -299,6 +298,7 @@ def merge_dict(x, y): if daemon: daemonize() + time_to_sleep = 1 while 1: ns = Netsoul(**options) try: @@ -306,5 +306,6 @@ def merge_dict(x, y): ns.loop() except NetsoulConnectionError: if options['verbose']: - print ('Disconnected, retrying in 10 seconds') - time.sleep(10) + print ('Disconnected, retrying in {:d} seconds'.format(time_to_sleep)) + time.sleep(time_to_sleep) + time_to_sleep = time_to_sleep * 2