def __init__(self, logfile=None, debug=False): self.__conns = {} # username:connection self.__logger = LoggerBuilder().build_logger("Database", logfile=logfile, debug=debug) self.__logger.info("The database has been initialized.") self.__demos = {} atexit.register(self.__confirm_demo_user_deletion)
def __init__(self, logfile=SERVER_LOG, debug=True): self.__db = Database(debug=debug, logfile=DB_LOG) self.__authenticated_users = {} self.__timers = {} self.__blacklist = {} self.__logger = LoggerBuilder().build_logger("Server", logfile=logfile, debug=debug) self.__logger.info("The server been initialized.")
class Server(object): def __init__(self, logfile=SERVER_LOG, debug=True): self.__db = Database(debug=debug, logfile=DB_LOG) self.__authenticated_users = {} self.__timers = {} self.__blacklist = {} self.__logger = LoggerBuilder().build_logger("Server", logfile=logfile, debug=debug) self.__logger.info("The server been initialized.") def register(self, uname, h, token): if not self.__check_credentials(uname, h): return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") self.__logger.info("We are registering " + uname + ".") creds = self.__db.get_user_credentials() if uname in creds: self.__logger.info("Registration for " + uname + " failed. Username already in use.") return m.Message(m.T_FAIL, {m.K_REASON: m.R_UNAME_TAKEN}, "Username already in use.") f = open(REG_TOKENS, "r") tokens = f.read() write_back = "" success = False result = m.Message(m.T_FAIL, {m.K_REASON: m.R_INVALID_TOKEN}, "Invalid token.") for t in tokens.split('\n'): if t == token and len(t) == 32: self.__db.store_user_credentials(uname, h) result = m.Message( m.T_ACK, {}, "User " + uname + " registered successfully.") success = True self.__logger.info("Registration for " + uname + " successful.") elif len(t) == 32: write_back += t + '\n' else: pass f.close() if success == False: self.__logger.info("Registration for " + uname + " failed. Invalid token.") else: f = open(REG_TOKENS, "w") f.write(write_back) return result def demo(self): self.__logger.info("We are requesting a demo session.") user = self.__db.generate_demo_user() if user == {}: self.__logger.info("We denied session. Demo user limit reached.") return m.Message(m.T_ERR, {m.K_REASON: m.R_LIMIT_REACHED}, "Demo user limit reached. Try again later.") else: uname = user[m.K_USER] self.__logger.info("We granted session " + uname + ".") session_token = self.__generate_session_token() user[m.K_SID] = session_token self.__authenticated_users[uname] = session_token self.__logger.debug("We have added " + uname + " into auth_users.") self.__set_reset_timer(uname) return m.Message(m.T_RESPONSE, user, "Demo session granted.") def authenticate(self, uname, h): self.__logger.info("We are authenticating user '" + uname + "': '" + h + "'...") creds = self.__db.get_user_credentials() if creds == {}: self.__logger.debug("Authentication failed. User " + uname + " not registered.") return m.Message(m.T_FAIL, {m.K_REASON: m.R_EMPTY}, "User not registered.") self.__logger.debug("The credentials are: " + str(creds)) if uname in creds.keys(): match = self.__match_safely(creds[uname], h) # Success if user is found and hash is correct. if match: session_token = self.__generate_session_token() self.__authenticated_users[uname] = session_token self.__logger.debug("We have added " + uname + " into auth_users.") self.__set_reset_timer(uname) self.__logger.debug("Authenticated users: " + self.__authenticated_users.__str__()) self.__logger.info(uname + "'s authentication successful.") return m.Message(m.T_RESPONSE, {m.K_SID: session_token}, "Authentication successful.") else: if uname in self.__authenticated_users.keys(): del self.__authenticated_users[uname] self.__logger.debug("Authentication failed. Invalid hash.") return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH}, "authentication failed.") else: self.__logger.debug("failed. User " + uname + " not registered.") return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH}, "user not registered.") def logout(self, uname): if uname in self.__authenticated_users.keys(): del self.__authenticated_users[uname] self.__logger.debug("We have removed " + uname + " from auth_users.") if uname in self.__timers.keys(): self.__timers[uname].die() del self.__timers[uname] self.__logger.debug("We have removed " + uname + " from timers.") self.__logger.info("User " + uname + " logged out.") if uname.startswith('demo_'): self.__db.delete_user(uname) self.__logger.debug("We have deleted demouser " + uname + ".") return m.Message(m.T_ACK, {}, "Bye.") def read_passwords(self, uname, sid): if self.__check_auth(uname, sid): pwd_dict = self.__db.read_passwords(uname) return m.Message(m.T_RESPONSE, pwd_dict, "Read successful.") else: return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH}, "Authentication required.") def store_password(self, uname, sid, id, pwd): if self.__check_auth(uname, sid): result = self.__db.add_password(uname, identifier=id, pwd=pwd) if result == True: return m.Message(m.T_ACK, {}, "Storage successful.") else: raise Exception("Store password faced unexpected db response.") else: return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH}, "Authentication required.") def remove_password(self, uname, sid, id): if self.__check_auth(uname, sid): result = self.__db.delete_password(uname, identifier=id) if result == True: return m.Message(m.T_ACK, {}, "Removal successful.") else: raise Exception( "Remove password faced unexpected db response.") else: return m.Message(m.T_FAIL, {m.K_REASON: m.R_NOT_AUTH}, "Authentication required.") def respond(self, request, ip): if ip in self.__blacklist.keys(): time_passed = time.time() - self.__blacklist[ip] if time_passed > BLACKLIST_DURATION: del self.__blacklist[ip] else: self.__blacklist[ip] = time.time() msg = m.Message( m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "You are in blacklist. If you think this is a mistake\nand you have done nothing wrong, contact\nthe administrator." ) self.__logger.info("We have reset the blaclist timer for " + ip + ".") return msg.toJson() msg = self.__construct_response(request) if msg.type == m.T_ERR: args = msg.args if m.K_REASON in args.keys() and args[m.K_REASON] == m.R_BLACKLIST: self.__blacklist[ip] = time.time() self.__logger.info("We have blacklisted " + ip + " for 5 minutes.") return msg.toJson() def __construct_response(self, request): try: msg = m.construct_message(request) except Exception as e: self.__logger.info("Exception in construct_message: " + e.__str__()) if msg: response = self.__exact_response(msg) return response else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") def __exact_response(self, msg): ''' Sorts the requests to right handlers according to type. If the client has been tampered with, it is blacklisted. ''' if msg.type == m.T_LOGIN: keys = msg.args.keys() if len(keys) == 2 and m.K_USER in keys and m.K_HASH in keys: return self.authenticate(uname=msg.args[m.K_USER], h=msg.args[m.K_HASH]) elif len(keys) == 2 and m.K_USER in keys and m.K_SID in keys: self.__timer_reset(uname=msg.args[m.K_USER]) self.__logger.debug("User " + msg.args[m.K_USER] + " still alive.") return m.Message(m.T_ACK, {}, "We have refreshed your login.") else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_GET: keys = msg.args.keys() if len(keys) == 2 and m.K_USER in keys and m.K_SID in keys: return self.read_passwords(msg.args[m.K_USER], msg.args[m.K_SID]) else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_PUT: keys = msg.args.keys() if len( keys ) == 4 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys and m.K_PWD in keys: return self.store_password(msg.args[m.K_USER], msg.args[m.K_SID], id=msg.args[m.K_ID], pwd=msg.args[m.K_PWD]) else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_REMOVE: keys = msg.args.keys() args = msg.args if len( keys ) == 3 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys: return self.remove_password(args[m.K_USER], args[m.K_SID], id=args[m.K_ID]) else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_LOGOUT: keys = msg.args.keys() args = msg.args if len(keys) == 1 and m.K_USER in keys: return self.logout(args[m.K_USER]) else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_REGISTER: keys = msg.args.keys() args = msg.args if len( keys ) == 3 and m.K_USER in keys and m.K_HASH in keys and m.K_TOKEN in keys: return self.register(args[m.K_USER], args[m.K_HASH], args[m.K_TOKEN]) else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_DEMO: keys = msg.args.keys() if len(keys) == 0: return self.demo() else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") else: return m.Message(m.T_ERR, {m.K_REASON: m.R_BLACKLIST}, "Welcome to blacklist.") def __check_auth(self, uname, sid): auth_users = self.__authenticated_users if uname in auth_users and self.__match_safely(sid, auth_users[uname]): return True else: self.__logger.debug("Check auth failed. User " + uname + " not authenticated.") return False def __match_safely(self, str1, str2): try: match = True if len(str1) != len(str2): return False for i in range(len(str1)): if str1[i] != str2[i]: match = False return match except Exception as e: self.__logger.debug("Exception caught in __match_safely:", e) return False def __generate_session_token(self): secret = rng.get_random_bytes(50) return secret.encode('base64').replace('\n', '') def __expire_user(self, uname): if uname in self.__authenticated_users.keys(): del self.__authenticated_users[uname] del self.__timers[uname] self.__logger.debug("We have removed " + uname + " from timers and auth_users.") def __timer_reset(self, uname): self.__timers[uname].reset() def __set_reset_timer(self, uname, time=300): # Set a timer in seconds how long user's authentication stays valid. self.__logger.debug("We have added " + uname + " into timers.") self.__timers[uname] = ExpirationTimer(uname, self.__expire_user, time) self.__timers[uname].start() def __check_credentials(self, uname, h): if self.__is_valid_id(uname) and self.__is_valid_hash(h): return True else: return False def __is_valid_id(self, id): normal = False for letter in id: if not (64 < ord(str(letter)) < 91 or 96 < ord(str(letter)) < 123 or 47 < ord(str(letter)) < 58): return False if 3 < len(id) < 16: return True else: return False def __is_valid_hash(self, h): if len(h) == 128: try: h.decode('hex') return True except: return False else: return False
class Database(object): def __init__(self, logfile=None, debug=False): self.__conns = {} # username:connection self.__logger = LoggerBuilder().build_logger("Database", logfile=logfile, debug=debug) self.__logger.info("The database has been initialized.") self.__demos = {} atexit.register(self.__confirm_demo_user_deletion) def __confirm_demo_user_deletion(self): for uname in self.__demos.keys(): self.delete_user(uname) def __get_db_conn(self, uname): ''' Returns an open connection object to the parameter user's database. If the connection is not open, it will be opened and returned. ''' self.__logger.debug("We are opening connection to db " + uname + ".") if not uname in self.__conns.keys(): conn = sqlite3.connect(uname) self.__conns[uname] = conn return conn else: return self.__conns[uname] def __close_db_conn(self, uname): ''' Closes the connection (if open) to parameter users database. ''' try: self.__logger.debug("We are closing connection to db " + uname + ".") self.__conns[uname].close() del self.__conns[uname] except Exception as e: self.__logger.error( "We caught an exception in close_conn:\nType: " + str(type(e)) + "\nArgs: " + e.__str__()) def __pad_data(self, data): while len(data) % 16 != 0: data = data + " " return data def store_user_credentials(self, uname, password_hash_hex): ''' Stores a user's credential info for authentication purposes. ''' conn = self.__get_db_conn(CREDENTIALS) cursor = conn.cursor() added = False updated = False #Create the table for credentials if it doesn't already exist. cursor.execute("create table if not exists " + CREDENTIALS + " (username text, password text)") #Insert into credentials table if user was not already there. if not cursor.execute( "select * from " + CREDENTIALS + " where username=?", (uname, )).fetchone(): cursor.execute("insert into " + CREDENTIALS + " values (?,?)", ( uname, password_hash_hex, )) conn.commit() added = True self.__logger.info("We have successfully registered " + uname + ".") else: cursor.execute( "update " + CREDENTIALS + " set password=? where username=?", ( password_hash_hex, uname, )) conn.commit() updated = True self.__logger.info("We have updated " + uname + "'s password in credentials library.") self.__close_db_conn(CREDENTIALS) if added: return ADDED elif updated: return UPDATED else: raise Exception( 'Store user credentials neither updated or added anything.') def get_user_credentials(self): conn = self.__get_db_conn(CREDENTIALS) # We are avoiding any complications in opening and closing the connection. result = self.__get_user_credentials(conn) self.__close_db_conn(CREDENTIALS) return result def __get_user_credentials(self, conn): ''' Reads user credentials and returns them in a dictionary. ''' cursor = conn.cursor() exists = cursor.execute( "select name from sqlite_master where type='table' and name='" + CREDENTIALS + "'").fetchall() if len(exists) == 0: self.__logger.info( "We don't have any registered users. Returning {}.") return {} credentials = cursor.execute("select * from " + CREDENTIALS).fetchall() dict_format = {} for tuple in credentials: dict_format[str(tuple[0])] = str(tuple[1]) credentials = dict_format self.__logger.info("We are reading credentials.") self.__logger.debug("Credentials: " + str(credentials)) return credentials def add_password(self, uname, identifier, pwd): ''' Stores a password in the users database. Opens a connection to the db if neccessary. ''' iv = rng.get_random_bytes(16) ec = AES.new(DB_KEY, AES.MODE_CBC, iv) #Encrypting the password for storage pwd = ec.encrypt(self.__pad_data(pwd)).encode('hex') conn = self.__get_db_conn(uname) cursor = conn.cursor() added = False #Create the table for the user if it doesn't already exist. cursor.execute( "create table if not exists sites (id text, iv text, password text)" ) #Insert into db if identifier is not already there. if not cursor.execute("select * from sites where id=?", (identifier.encode('hex'), )).fetchone(): #IV and ID encoded in hex for storage. self.__logger.debug("We are adding a new password " + identifier + " for user " + uname + ".") cursor.execute("insert into sites values (?,?,?)", ( identifier.encode('hex'), iv.encode('hex'), pwd, )) # Else update pwd value. else: cursor.execute("update sites set iv=? ,password=? where id=?", ( iv.encode('hex'), pwd, identifier.encode('hex'), )) self.__logger.debug("We have updated " + uname + "'s password " + identifier + ".") conn.commit() added = True self.__close_db_conn(uname) return added def read_passwords(self, uname): ''' Reads all the passwords of the parameter user and returns them in a list. ''' conn = self.__get_db_conn(uname) # We are avoiding any complications in opening and closing the connection. result = self.__read_passwords(conn, uname) self.__close_db_conn(uname) return result def __read_passwords(self, conn, uname): self.__logger.info("We are reading passwords for " + uname + ".") cursor = conn.cursor() exists = cursor.execute( "select name from sqlite_master where type='table' and name='sites'" ).fetchall() if len(exists) == 0: self.__logger.info("We have no passwords for " + uname + " Returning {}.") return {} passwords = cursor.execute("select * from sites").fetchall() self.__logger.info("Read for " + uname + " successful.") self.__logger.debug("Passwords:") result = {} for line in passwords: iv_hex = str(line[1]) id_hex = line[0] dc = AES.new(DB_KEY, AES.MODE_CBC, iv_hex.decode('hex')) password = dc.decrypt(line[2].decode('hex')).rstrip(' ') self.__logger.debug([id_hex.decode('hex'), iv_hex, password]) result[id_hex.decode('hex')] = password return result def delete_password(self, uname, identifier): self.__logger.info("We are trying to delete " + uname + "'s password " + identifier + ".") conn = self.__get_db_conn(uname) cursor = conn.cursor() removed = False exists = cursor.execute( "select name from sqlite_master where type='table' and name='sites'" ).fetchall() if len(exists) == 0: self.__logger.info("We have no passwords for " + uname + ". Returning False.") return False if cursor.execute("select * from sites where id=?", (identifier.encode('hex'), )).fetchone(): cursor.execute("delete from sites where id=?", (identifier.encode('hex'), )) conn.commit() removed = True self.__logger.info("We are deleting user " + uname + "'s password " + identifier) #If table is empty after removal, it is dropped. if not cursor.execute("select * from sites").fetchone(): cursor.execute("drop table sites") conn.commit() self.__logger.debug("We are deleting user " + uname + "'s empty table sites.") self.__close_db_conn(uname) return removed def delete_user(self, uname): self.__logger.info("We are deleting user " + uname + ".") conn = self.__get_db_conn(uname) cursor = conn.cursor() cursor.execute("drop table if exists sites") self.__logger.debug("We are dropping table sites if it exists.") conn.commit() try: os.remove(uname) except: pass self.__close_db_conn(uname) conn = self.__get_db_conn(CREDENTIALS) cursor = conn.cursor() removed = False exists = cursor.execute( "select name from sqlite_master where type='table' and name='" + CREDENTIALS + "'").fetchall() if len(exists) == 0: self.__logger.info( "We don't have any registered users. Returning False.") return False if cursor.execute("select * from " + CREDENTIALS + " where username=?", (uname, )).fetchone(): cursor.execute("delete from " + CREDENTIALS + " where username=?", (uname, )) conn.commit() removed = True self.__logger.info("We have deleted user " + uname + ".") #If table is empty after removal, it is dropped. if not cursor.execute("select * from " + CREDENTIALS).fetchone(): cursor.execute("drop table " + CREDENTIALS) conn.commit() self.__logger.debug("We are deleting empty table " + CREDENTIALS + ".") else: self.__logger.debug("We didn't delete " + uname + ", he's gone already.") self.__close_db_conn(CREDENTIALS) return removed def generate_demo_user(self): if len(self.__demos.keys()) > 10: return None demo = {} dname = "demo_" + str(len(self.__demos.keys())) dpass = rng.get_random_bytes(20).encode('hex') demo[m.K_USER] = dname demo[m.K_DEMO_PASS] = dpass self.__demos[dname] = dpass return demo
class Server(object): def __init__(self, logfile=SERVER_LOG, debug=True): self.__db = Database(debug=debug, logfile=DB_LOG) self.__authenticated_users = {} self.__timers = {} self.__blacklist = {} self.__logger = LoggerBuilder().build_logger("Server", logfile=logfile, debug=debug) self.__logger.info("The server been initialized.") def register(self, uname, h, token): if not self.__check_credentials(uname, h): return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") self.__logger.info("We are registering "+uname+".") creds = self.__db.get_user_credentials() if uname in creds: self.__logger.info("Registration for "+uname+" failed. Username already in use.") return m.Message(m.T_FAIL, {m.K_REASON:m.R_UNAME_TAKEN}, "Username already in use.") f = open(REG_TOKENS, "r") tokens = f.read() write_back = "" success = False result = m.Message(m.T_FAIL, {m.K_REASON:m.R_INVALID_TOKEN}, "Invalid token.") for t in tokens.split('\n'): if t == token and len(t) == 32: self.__db.store_user_credentials(uname, h) result = m.Message(m.T_ACK, {}, "User "+uname+" registered successfully.") success = True self.__logger.info("Registration for "+uname+" successful.") elif len(t) == 32: write_back += t+'\n' else: pass f.close() if success == False: self.__logger.info("Registration for "+uname+" failed. Invalid token.") else: f = open(REG_TOKENS, "w") f.write(write_back) return result def demo(self): self.__logger.info("We are requesting a demo session.") user = self.__db.generate_demo_user() if user == {}: self.__logger.info("We denied session. Demo user limit reached.") return m.Message(m.T_ERR, {m.K_REASON:m.R_LIMIT_REACHED}, "Demo user limit reached. Try again later.") else: uname = user[m.K_USER] self.__logger.info("We granted session "+uname+".") session_token = self.__generate_session_token() user[m.K_SID] = session_token self.__authenticated_users[uname] = session_token self.__logger.debug("We have added "+uname+ " into auth_users.") self.__set_reset_timer(uname) return m.Message(m.T_RESPONSE, user, "Demo session granted.") def authenticate(self, uname, h): self.__logger.info("We are authenticating user '"+uname+"': '"+h+"'...") creds = self.__db.get_user_credentials() if creds == {}: self.__logger.debug("Authentication failed. User "+uname+" not registered.") return m.Message(m.T_FAIL, {m.K_REASON : m.R_EMPTY}, "User not registered.") self.__logger.debug("The credentials are: "+str(creds)) if uname in creds.keys(): match = self.__match_safely(creds[uname], h) # Success if user is found and hash is correct. if match: session_token = self.__generate_session_token() self.__authenticated_users[uname] = session_token self.__logger.debug("We have added "+uname+ " into auth_users.") self.__set_reset_timer(uname) self.__logger.debug("Authenticated users: "+self.__authenticated_users.__str__()) self.__logger.info(uname+"'s authentication successful.") return m.Message(m.T_RESPONSE, {m.K_SID : session_token}, "Authentication successful.") else: if uname in self.__authenticated_users.keys(): del self.__authenticated_users[uname] self.__logger.debug("Authentication failed. Invalid hash.") return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "authentication failed.") else: self.__logger.debug("failed. User "+uname+" not registered.") return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "user not registered.") def logout(self, uname): if uname in self.__authenticated_users.keys(): del self.__authenticated_users[uname] self.__logger.debug("We have removed "+uname+" from auth_users.") if uname in self.__timers.keys(): self.__timers[uname].die() del self.__timers[uname] self.__logger.debug("We have removed "+uname+" from timers.") self.__logger.info("User "+uname+" logged out.") if uname.startswith('demo_'): self.__db.delete_user(uname) self.__logger.debug("We have deleted demouser "+uname+".") return m.Message(m.T_ACK, {}, "Bye.") def read_passwords(self, uname, sid): if self.__check_auth(uname, sid): pwd_dict = self.__db.read_passwords(uname) return m.Message(m.T_RESPONSE, pwd_dict, "Read successful.") else: return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "Authentication required.") def store_password(self, uname, sid, id, pwd): if self.__check_auth(uname, sid): result = self.__db.add_password(uname, identifier=id, pwd=pwd) if result == True: return m.Message(m.T_ACK, {}, "Storage successful.") else: raise Exception("Store password faced unexpected db response.") else: return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "Authentication required.") def remove_password(self, uname, sid, id): if self.__check_auth(uname, sid): result = self.__db.delete_password(uname, identifier=id) if result == True: return m.Message(m.T_ACK, {}, "Removal successful.") else: raise Exception("Remove password faced unexpected db response.") else: return m.Message(m.T_FAIL, {m.K_REASON : m.R_NOT_AUTH}, "Authentication required.") def respond(self, request, ip): if ip in self.__blacklist.keys(): time_passed = time.time() - self.__blacklist[ip] if time_passed > BLACKLIST_DURATION: del self.__blacklist[ip] else: self.__blacklist[ip] = time.time() msg = m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "You are in blacklist. If you think this is a mistake\nand you have done nothing wrong, contact\nthe administrator.") self.__logger.info("We have reset the blaclist timer for "+ip+".") return msg.toJson() msg = self.__construct_response(request) if msg.type == m.T_ERR: args = msg.args if m.K_REASON in args.keys() and args[m.K_REASON] == m.R_BLACKLIST: self.__blacklist[ip] = time.time() self.__logger.info("We have blacklisted "+ip+" for 5 minutes.") return msg.toJson() def __construct_response(self, request): try: msg = m.construct_message(request) except Exception as e: self.__logger.info("Exception in construct_message: "+e.__str__()) if msg: response = self.__exact_response(msg) return response else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") def __exact_response(self, msg): ''' Sorts the requests to right handlers according to type. If the client has been tampered with, it is blacklisted. ''' if msg.type == m.T_LOGIN: keys = msg.args.keys() if len(keys) == 2 and m.K_USER in keys and m.K_HASH in keys: return self.authenticate(uname=msg.args[m.K_USER], h=msg.args[m.K_HASH]) elif len(keys) == 2 and m.K_USER in keys and m.K_SID in keys: self.__timer_reset(uname=msg.args[m.K_USER]) self.__logger.debug("User "+msg.args[m.K_USER]+" still alive.") return m.Message(m.T_ACK, {}, "We have refreshed your login.") else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_GET: keys = msg.args.keys() if len(keys) == 2 and m.K_USER in keys and m.K_SID in keys: return self.read_passwords(msg.args[m.K_USER], msg.args[m.K_SID]) else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_PUT: keys = msg.args.keys() if len(keys) == 4 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys and m.K_PWD in keys: return self.store_password(msg.args[m.K_USER], msg.args[m.K_SID], id=msg.args[m.K_ID], pwd=msg.args[m.K_PWD]) else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_REMOVE: keys = msg.args.keys() args = msg.args if len(keys) == 3 and m.K_USER in keys and m.K_SID in keys and m.K_ID in keys: return self.remove_password(args[m.K_USER], args[m.K_SID], id=args[m.K_ID]) else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_LOGOUT: keys = msg.args.keys() args = msg.args if len(keys) == 1 and m.K_USER in keys: return self.logout(args[m.K_USER]) else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_REGISTER: keys = msg.args.keys() args = msg.args if len(keys) == 3 and m.K_USER in keys and m.K_HASH in keys and m.K_TOKEN in keys: return self.register(args[m.K_USER], args[m.K_HASH], args[m.K_TOKEN]) else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") elif msg.type == m.T_DEMO: keys = msg.args.keys() if len(keys) == 0: return self.demo() else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") else: return m.Message(m.T_ERR, {m.K_REASON:m.R_BLACKLIST}, "Welcome to blacklist.") def __check_auth(self, uname, sid): auth_users = self.__authenticated_users if uname in auth_users and self.__match_safely(sid, auth_users[uname]): return True else: self.__logger.debug("Check auth failed. User "+uname+" not authenticated.") return False def __match_safely(self, str1, str2): try: match = True if len(str1) != len(str2): return False for i in range(len(str1)): if str1[i] != str2[i] : match = False return match except Exception as e: self.__logger.debug("Exception caught in __match_safely:", e) return False def __generate_session_token(self): secret = rng.get_random_bytes(50) return secret.encode('base64').replace('\n', '') def __expire_user(self, uname): if uname in self.__authenticated_users.keys(): del self.__authenticated_users[uname] del self.__timers[uname] self.__logger.debug("We have removed "+uname+" from timers and auth_users.") def __timer_reset(self, uname): self.__timers[uname].reset() def __set_reset_timer(self, uname, time=300): # Set a timer in seconds how long user's authentication stays valid. self.__logger.debug("We have added "+uname+" into timers.") self.__timers[uname] = ExpirationTimer(uname, self.__expire_user, time) self.__timers[uname].start() def __check_credentials(self, uname, h): if self.__is_valid_id(uname) and self.__is_valid_hash(h): return True else: return False def __is_valid_id(self, id): normal = False for letter in id: if not (64 < ord(str(letter)) < 91 or 96 < ord(str(letter)) < 123 or 47 < ord(str(letter)) < 58): return False if 3 < len(id) < 16: return True else: return False def __is_valid_hash(self, h): if len(h) == 128: try: h.decode('hex') return True except: return False else: return False
class Database(object): def __init__(self, logfile=None, debug=False): self.__conns = {} # username:connection self.__logger = LoggerBuilder().build_logger("Database", logfile=logfile, debug=debug) self.__logger.info("The database has been initialized.") self.__demos = {} atexit.register(self.__confirm_demo_user_deletion) def __confirm_demo_user_deletion(self): for uname in self.__demos.keys(): self.delete_user(uname) def __get_db_conn(self, uname): ''' Returns an open connection object to the parameter user's database. If the connection is not open, it will be opened and returned. ''' self.__logger.debug("We are opening connection to db "+uname+".") if not uname in self.__conns.keys(): conn = sqlite3.connect(uname) self.__conns[uname] = conn return conn else: return self.__conns[uname] def __close_db_conn(self, uname): ''' Closes the connection (if open) to parameter users database. ''' try: self.__logger.debug("We are closing connection to db "+uname+".") self.__conns[uname].close() del self.__conns[uname] except Exception as e: self.__logger.error("We caught an exception in close_conn:\nType: "+str(type(e))+"\nArgs: "+e.__str__()) def __pad_data(self, data): while len(data)%16 != 0: data = data+" " return data def store_user_credentials(self, uname, password_hash_hex): ''' Stores a user's credential info for authentication purposes. ''' conn = self.__get_db_conn(CREDENTIALS) cursor = conn.cursor() added = False updated = False #Create the table for credentials if it doesn't already exist. cursor.execute("create table if not exists "+CREDENTIALS+" (username text, password text)") #Insert into credentials table if user was not already there. if not cursor.execute("select * from "+CREDENTIALS+" where username=?", (uname,)).fetchone(): cursor.execute("insert into "+CREDENTIALS+" values (?,?)", (uname, password_hash_hex,)) conn.commit() added = True self.__logger.info("We have successfully registered "+uname+".") else: cursor.execute("update "+CREDENTIALS+" set password=? where username=?", (password_hash_hex, uname,)) conn.commit() updated = True self.__logger.info("We have updated "+uname+"'s password in credentials library.") self.__close_db_conn(CREDENTIALS) if added: return ADDED elif updated: return UPDATED else: raise Exception('Store user credentials neither updated or added anything.') def get_user_credentials(self): conn = self.__get_db_conn(CREDENTIALS) # We are avoiding any complications in opening and closing the connection. result = self.__get_user_credentials(conn) self.__close_db_conn(CREDENTIALS) return result def __get_user_credentials(self, conn): ''' Reads user credentials and returns them in a dictionary. ''' cursor = conn.cursor() exists = cursor.execute("select name from sqlite_master where type='table' and name='"+CREDENTIALS+"'").fetchall() if len(exists) == 0: self.__logger.info("We don't have any registered users. Returning {}.") return {} credentials = cursor.execute("select * from "+CREDENTIALS).fetchall() dict_format = {} for tuple in credentials: dict_format[str(tuple[0])] = str(tuple[1]) credentials = dict_format self.__logger.info("We are reading credentials.") self.__logger.debug("Credentials: "+str(credentials)) return credentials def add_password(self, uname, identifier, pwd): ''' Stores a password in the users database. Opens a connection to the db if neccessary. ''' iv = rng.get_random_bytes(16) ec = AES.new(DB_KEY, AES.MODE_CBC, iv) #Encrypting the password for storage pwd = ec.encrypt(self.__pad_data(pwd)).encode('hex') conn = self.__get_db_conn(uname) cursor = conn.cursor() added = False #Create the table for the user if it doesn't already exist. cursor.execute("create table if not exists sites (id text, iv text, password text)") #Insert into db if identifier is not already there. if not cursor.execute("select * from sites where id=?", (identifier.encode('hex'),)).fetchone(): #IV and ID encoded in hex for storage. self.__logger.debug("We are adding a new password "+identifier+" for user "+uname+".") cursor.execute("insert into sites values (?,?,?)",(identifier.encode('hex'), iv.encode('hex'), pwd,)) # Else update pwd value. else: cursor.execute("update sites set iv=? ,password=? where id=?", (iv.encode('hex'), pwd, identifier.encode('hex'),)) self.__logger.debug("We have updated "+uname+"'s password "+identifier+".") conn.commit() added = True self.__close_db_conn(uname) return added def read_passwords(self, uname): ''' Reads all the passwords of the parameter user and returns them in a list. ''' conn = self.__get_db_conn(uname) # We are avoiding any complications in opening and closing the connection. result = self.__read_passwords(conn, uname) self.__close_db_conn(uname) return result def __read_passwords(self, conn, uname): self.__logger.info("We are reading passwords for "+uname+".") cursor = conn.cursor() exists = cursor.execute("select name from sqlite_master where type='table' and name='sites'").fetchall() if len(exists) == 0: self.__logger.info("We have no passwords for "+uname+" Returning {}.") return {} passwords = cursor.execute("select * from sites").fetchall() self.__logger.info("Read for "+uname+" successful.") self.__logger.debug("Passwords:") result = {} for line in passwords: iv_hex = str(line[1]) id_hex = line[0] dc = AES.new(DB_KEY, AES.MODE_CBC, iv_hex.decode('hex')) password = dc.decrypt(line[2].decode('hex')).rstrip(' ') self.__logger.debug([id_hex.decode('hex'), iv_hex, password]) result[id_hex.decode('hex')] = password return result def delete_password(self, uname, identifier): self.__logger.info("We are trying to delete "+uname+"'s password "+identifier+".") conn = self.__get_db_conn(uname) cursor= conn.cursor() removed = False exists = cursor.execute("select name from sqlite_master where type='table' and name='sites'").fetchall() if len(exists) == 0: self.__logger.info("We have no passwords for "+uname+". Returning False.") return False if cursor.execute("select * from sites where id=?", (identifier.encode('hex'),)).fetchone(): cursor.execute("delete from sites where id=?", (identifier.encode('hex'),)) conn.commit() removed = True self.__logger.info("We are deleting user "+uname+"'s password "+identifier) #If table is empty after removal, it is dropped. if not cursor.execute("select * from sites").fetchone(): cursor.execute("drop table sites") conn.commit() self.__logger.debug("We are deleting user "+uname+"'s empty table sites.") self.__close_db_conn(uname) return removed def delete_user(self, uname): self.__logger.info("We are deleting user "+uname+".") conn = self.__get_db_conn(uname) cursor= conn.cursor() cursor.execute("drop table if exists sites") self.__logger.debug("We are dropping table sites if it exists.") conn.commit() try: os.remove(uname) except: pass self.__close_db_conn(uname) conn = self.__get_db_conn(CREDENTIALS) cursor= conn.cursor() removed = False exists = cursor.execute("select name from sqlite_master where type='table' and name='"+CREDENTIALS+"'").fetchall() if len(exists) == 0: self.__logger.info("We don't have any registered users. Returning False.") return False if cursor.execute("select * from "+CREDENTIALS+" where username=?", (uname,)).fetchone(): cursor.execute("delete from "+CREDENTIALS+" where username=?", (uname,)) conn.commit() removed = True self.__logger.info("We have deleted user "+uname+".") #If table is empty after removal, it is dropped. if not cursor.execute("select * from "+CREDENTIALS).fetchone(): cursor.execute("drop table "+CREDENTIALS) conn.commit() self.__logger.debug("We are deleting empty table "+CREDENTIALS+".") else: self.__logger.debug("We didn't delete "+uname+", he's gone already.") self.__close_db_conn(CREDENTIALS) return removed def generate_demo_user(self): if len(self.__demos.keys()) > 10: return None demo = {} dname = "demo_"+str(len(self.__demos.keys())) dpass = rng.get_random_bytes(20).encode('hex') demo[m.K_USER] = dname demo[m.K_DEMO_PASS] = dpass self.__demos[dname] = dpass return demo