def __init__(self, config_path=DEFAULT_CONFIG_PATH): if not os.path.isfile(config_path): raise DDTBError('No such file: %s' % config_path) try: config = configobj.ConfigObj(config_path) except configobj.ParseError, e: raise DDTBError(e)
def changeUserPassword(self, apikey, id, newPlaintextPassword): if self._checkAPIKey(apikey): login = self._userLogin(base64.b64decode(id).decode('utf-8')) if not login: return DDTBError('Client with login %s not found' % login) iv = self.crypt.get_new_iv() if self.db_key is not None: newPassword = base64.b64encode( self.crypt.encryptPassword( iv, base64.b64decode(newPlaintextPassword), self.db_key)) else: newPassword = newPlaintextPassword try: self.database.update_password(login, base64.b64encode(iv), newPassword) self.database.store.autoreload() self.sessions.manager.change_user_cache(login) logs.ipc.info( "Password for user with login %s successfully changed" % login) return True except DDTBError, e: logs.ipc.error("Password for user with login %s NOT changed" % login) return e
def removeUser(self, apikey, id): if self._checkAPIKey(apikey): #try: # killTunnel(self, apikey, login) #except: # pass login = self._userLogin(base64.b64decode(id).decode('utf-8')) if not login: return DDTBError('Client with login %s not found' % login) try: self.database.unregister_client(login=login.encode('utf-8')) self.database.store.autoreload() if self.sessions.manager.auth.user_cache.has_key(login): del self.sessions.manager.auth.user_cache[login] logs.ipc.debug('User %s removed from user cache.' % login) else: logs.ipc.error( "User with login %s was NOT found in user cache, could not remove." % (login)) return False logs.ipc.info("User with login %s successfully removed." % login) return True except DDTBError, e: print 'Error removing user %s: %s' % (login, e)
class DDTBDatabase(object): """ Database access class for database queries. """ def __init__(self, config): """ The connection parameter should be a valid storm ORM DB access string, for example: mysql://ddtb:ddtbpassword@localhost:3306/TB """ if re.search("[^a-zA-Z0-9_\+=_\-]", config.database.password): error = 'Database user ' + config.database.user + ' password contains illegal character (for example, "/" is not allowed).' raise DDTBError(error) self.database = create_database(config.database.connection) self.__store = None def __getattr__(self, attr): if attr == 'store': if self.__store is None: try: self.__store = Store(self.database) except OperationalError, e: raise DDTBError('Error connecting to database: %s' % e[1]) except: raise DDTBError('Error opening database store')
def register_client(self, login, name, iv, passwd, email, mobile, ipaddress, max_prefix, min_prefix): """ Register a new client entry to the database """ try: self.store.find(Client, Client.login == unicode(login))[0] raise DDTBError('Customer already registered: %s' % unicode(login)) except IndexError: pass client = Client() client.login = unicode(login) client.name = unicode(name) client.iv = unicode(iv) client.passwd = unicode(passwd) client.email = unicode(email) client.mobile = unicode(mobile) client.ipaddress = unicode(ipaddress) client.min_prefix = min_prefix client_max_prefix = max_prefix self.store.add(client) self.store.flush() self.store.commit() logs.auth.info('Registered client: %s (%s)' % (login, name)) self.__store = None return client.id
def init_system(self): for cmd in TTDB_INIT_COMMANDS: cmd = cmd % self.broker logs.ddtb.debug('Running: %s' % cmd) retval = call(cmd.split()) if retval != 0: raise DDTBError('Error running command %s' % cmd)
def __init__(self, config): DDTBAuthenticator.__init__(self, config) self.method = 'DIGEST-MD5' self.service = 'tsp' try: self.server = config.auth.server except KeyError: raise DDTBError('SASL server address not configured.')
def __getattr__(self, attr): if attr == 'store': if self.__store is None: try: self.__store = Store(self.database) except OperationalError, e: raise DDTBError('Error connecting to database: %s' % e[1]) except: raise DDTBError('Error opening database store')
def DDTBLoadAuthenticator(config): method = config.auth.method path = 'ddtb.authmethods.%s' % method classname = '%sAuthentication' % method m = __import__(path, globals(), fromlist=[method]) return getattr(m, classname)(config) try: pass except (ImportError, AttributeError): raise DDTBError('Error loading authenticator for %s' % method)
def __init__(self, name, data): DDTBConfigSection.__init__(self, name, data) if self.engine == 'mysql': try: self.connection = 'mysql://%(user)s:%(password)s@%(host)s:%(port)s/%(database)s' % self except KeyError: raise DDTBError( 'Database configuration missing required values.') else: raise NotImplementedError('Only MySQL is supported for now.')
def __init__(self,config): for k,v in config.items(): setattr(self,k,v) if not os.path.isdir(self.logdir): raise DDTBError('No such directory: %s' % self.logdir) try: ddtblog = os.path.join(self.logdir,'ddtb.log') self['ddtb'] = DDTBLogFile( program='ddtb', path=ddtblog, logformat=LOGFORMAT, level=logging.DEBUG, max_bytes=self.max_bytes, rotations=self.rotations ) except IOError,(ecode,emsg): raise DDTBError('Error opening %s: %s' % (ddtblog,emsg))
class DDTBConfig(dict): def __init__(self, config_path=DEFAULT_CONFIG_PATH): if not os.path.isfile(config_path): raise DDTBError('No such file: %s' % config_path) try: config = configobj.ConfigObj(config_path) except configobj.ParseError, e: raise DDTBError(e) except IOError, (ecode, emsg): raise DDTBError('Error opening %s: %s' % (config_path, emsg))
def __init__(self, database, address, allocation_size): """ Class to iterate possible address prefixes in given address pools, looking for unallocated pool. """ self.database = database try: self.address = address self.prefixes = SubnetPrefixIterator(address, allocation_size) self.allocation_size = allocation_size except ValueError, e: raise DDTBError('Error creating address pool: %s' % e)
def __init__(self, config): """ The connection parameter should be a valid storm ORM DB access string, for example: mysql://ddtb:ddtbpassword@localhost:3306/TB """ if re.search("[^a-zA-Z0-9_\+=_\-]", config.database.password): error = 'Database user ' + config.database.user + ' password contains illegal character (for example, "/" is not allowed).' raise DDTBError(error) self.database = create_database(config.database.connection) self.__store = None
def __init__(self, name, data): DDTBConfigSection.__init__(self, name, data) if not self.has_key('logdir'): self['logdir'] = DEFAULT_LOG_DIRECTORY if not self.has_key('rotations'): self['rotations'] = DEFAULT_LOG_ROTATIONS if not self.has_key('max_bytes'): self['max_bytes'] = DEFAULT_LOG_MAX_BYTES if not self.has_key('owner'): self['owner'] = DEFAULT_LOG_OWNER if not self.has_key('group'): self['group'] = DEFAULT_LOG_GROUP try: self['uid'] = pwd.getpwnam(str(self['owner']).strip()).pw_uid except KeyError: raise DDTBError('User for logs %s not found.' % self.owner) try: self['gid'] = grp.getgrnam(str(self['group']).strip()).gr_gid except KeyError: raise DDTBError('Group for logs %s not found.' % self.group) try: self['rotations'] = int(self['rotations']) if self.rotations < 0 or self.rotations > MAX_ROTATIONS: raise ValueError except ValueError: raise DDTBError('Invalid logging rotations value: %s' % self['rotations']) try: self['max_bytes'] = int(self['max_bytes']) if self.max_bytes < 1024 or self.max_bytes > LOG_BYTES_LIMIT: raise ValueError except ValueError: raise DDTBError('Invalid logging max_bytes value: %s' % self['max_bytes']) if not os.path.isdir(self['logdir']): raise DDTBError('No such directory: %s' % self.logdir)
def __init__(self, tunnel_manager, flags): self.tunnel_manager = tunnel_manager if type(flags) != dict: raise DDTBError('TSPTunnelConfig requires a dict as parameter') for k in TSP_MANDATORY_FLAGS: if not flags.has_key(k): raise DDTBError('Missing mandatory flag: %s' % k) self.update(flags) self['server_port'] = tunnel_manager.server_port (self['prefix_network'], self['prefix_mask']) = self['prefix'].split('/') # Example interface name: c0a00101_1234 self['interface'] = '%s_%04d' % (''.join([ '%02x' % int(x) for x in self['client_ipv4'].split('.') ]), int(self['client_port'])) # Ifindex is only initialized when the tunnel is configured or # deconfigure is attempted self['ifindex'] = None
def unregister_tunnel(self, tunnel_id): """ Remove registeration for given tunnel with given user ID """ try: tunnel = self.store.find(Tunnel, Tunnel.id == tunnel_id)[0] client_login = tunnel.client.login self.store.remove(tunnel) self.store.commit() logs.auth.info("Tunnel for client %s closed" % client_login) except IndexError: raise DDTBError('No such tunnel ID: %s' % tunnel_id) self.__store = None
def __init__(self, name, data): DDTBConfigSection.__init__(self, name, data) if not self.has_key('allocation_prefix'): raise DDTBError('Missing IPv6 client allocation prefix.') try: IPv6Address(self['allocation_prefix']).address except ValueError: raise DDTBError('Invalid IPv6 allocation prefix: %s' % self['allocation_prefix']) if not self.has_key('customer_prefix_size'): self['customer_prefix_size'] = DEFAULT_PREFIX_SIZE try: customer_prefix_size = int(self['customer_prefix_size']) if customer_prefix_size <= 1 or customer_prefix_size >= 2**7: raise ValueError self['customer_prefix_size'] = customer_prefix_size except ValueError: raise DDTBError('Invalid customer prefix size %s' % self['customer_prefix_size'])
def create_database_tables(self): """ Create empty database tables based on our database model. Note that an empty database with GRANT ALL ON <database>.* must be executed and user account created to DB before this can be done. We only create tables, not databases or accounts. """ for cmd in DDTB_MYSQL_TABLES: try: self.store.execute(cmd) except ProgrammingError: raise DDTBError('Error creating database tables') self.store.commit() self.__store = None
def configure(self): """ Configure a TSP tunnel interface and associated iptables and routing policy rules for it. """ logs.ddtb.info('Configuring tunnel interface %s' % self.interface) for cmd in TSP_TUNNEL_LINK_SETUP_COMMANDS: cmd = cmd % self # logs.ddtb.debug('Running cmd: %s' % cmd) retval = call(cmd.split()) if retval != 0: raise DDTBError('Error running command %s' % cmd) try: self['ifindex'] = '%s' % int(self.get_ifindex()) self['utun_table'] = '%s' % (int(self.ifindex) + TSP_RTABLE_OFFSET) except ValueError: logs.ddtb.debug('Interface was not configured: %s' % self.interface) raise for queue in TSP_MANGLE_QUEUES: params = dict(self) params['queue'] = queue cmd = TSP_MANGLE_COMMAND % params # logs.ddtb.debug('Running cmd: %s' % cmd) retval = call(cmd.split()) if retval != 0: raise DDTBError('Error running mangle command %s' % cmd) for cmd in TSP_TUNNEL_IPTABLES_ROUTING: cmd = cmd % self # logs.ddtb.debug('Running cmd: %s' % cmd) retval = call(cmd.split()) if retval != 0: raise DDTBError('Error running command: %s' % ' '.join(cmd))
def __init__(self, name, data): DDTBConfigSection.__init__(self, name, data) try: self['active'] = bool(self['active']) except KeyError: raise DDTBError('RPC "active" flag required') if self['active'] and not (self.has_key('apikey') or len(self['apikey'] == 0)): raise DDTBError('RPC api key required') try: self['rpcip'] = IPv4Address(self['rpcip']).ipaddress except ValueError: raise DDTBError('Invalid broker %s: %s' % (k, self[k])) try: port = int(self['port']) if port <= 0 or port >= 2**16: raise ValueError self['port'] = port except ValueError: raise DDTBError('Invalid XML-RPC IPC port: %s' % self['port'])
def update_password(self, login, iv, NewPasswd): try: self.store.find( Client, Client.login == unicode(login)).set(passwd=unicode(NewPasswd)) self.store.find(Client, Client.login == unicode(login)).set(iv=unicode(iv)) self.store.commit() self.__store = None return True except IndexError: raise DDTBError( 'Client with login %s not found, cannot update password' % login)
def unregister_client(self, login): """ Unregister a client entry from the database """ self.store.flush() try: client = self.store.find( Client, Client.login == unicode(login.decode('utf8')))[0] logs.auth.info('Unregistering client: %s (%s)' % (client.login, client.name)) self.store.remove(client) self.store.commit() except IndexError: raise DDTBError('No such client: %s' % login) self.__store = None
def userDetails(self, apikey, id): if self._checkAPIKey(apikey): login = self._userLogin(id) if not login: return DDTBError('Client with login %s not found' % login) client = self.database.client_details(login) # Easy client to dict conversion, casting doesn't work fieldList = ['login', 'name', 'email', 'mobile', 'ipaddress'] clientList = [ client.login, client.name, client.email, client.mobile, client.ipaddress ] return dict(zip(fieldList, clientList)) else: return TBRPCInvalidAPIKEYError('userDetails')
def get_ifindex(self): """ Return ifindex of tunnel interface, raise ValueError if not configured. """ cmd = ['ip', 'link', 'list', self.interface] p = Popen(cmd, stdout=PIPE, stderr=PIPE) stdout, stderr = p.communicate() if stdout.rstrip() == 'Device "%s" does not exist.' % self.interface: raise ValueError if p.returncode != 0: raise ValueError try: # Note - we return this as string, not int self.ifindex = stdout.split(':')[0] return self.ifindex except IndexError: raise DDTBError('Error splitting ifindex from %s' % stdout)
def register_prefix(self, client_id, prefix): """ Register a single prefix for given client_id """ self.store.flush() entry = Prefix() try: client = self.store.find(Client, Client.id == client_id)[0] except IndexError: raise DDTBError('No such client: %s' % client_id) entry.client_id = client.id entry.prefix = unicode(prefix.network) entry.prefixlen = prefix.mask entry.type = unicode(prefix.prefix_type) self.store.add(entry) self.store.commit() self.__store = None
def checkLogin(self, login, passwd): try: entry = self.store.find(Client, Client.login == unicode(login)).one() if not entry: logs.auth.info("Client with login %s not found." % login) return False elif not unicode(passwd) == entry.passwd: logs.auth.info( "Client with login %s tried wrong credentials." % login) return False logs.auth.info("Client with login %s authenticated." % login) return True except IndexError: raise DDTBError('Client with login %s not found (IndexError).' % login)
def find_next(self, user_id=None): """ Returns next prefix from this pool """ # TODO - think when to actually update this from DB, not every time. # In general, this may need lots of refactoring! self.update_db_prefix_cache() while True: try: ca = CustomerAllocation(self, self.prefixes.next(), user_id) try: existing = self[ca.network] if existing.user_id == user_id: # Return user's network return ca else: # Allocated to someone else continue except KeyError: self.database.register_prefix(user_id, ca) self[ca.network] = ca return ca except StopIteration: raise DDTBError('No more Ipv6 prefixes in %s' % self.address)
class DDTBLogs(dict): """ Initialize the logging facilities available for DDTB """ def __init__(self,config): for k,v in config.items(): setattr(self,k,v) if not os.path.isdir(self.logdir): raise DDTBError('No such directory: %s' % self.logdir) try: ddtblog = os.path.join(self.logdir,'ddtb.log') self['ddtb'] = DDTBLogFile( program='ddtb', path=ddtblog, logformat=LOGFORMAT, level=logging.DEBUG, max_bytes=self.max_bytes, rotations=self.rotations ) except IOError,(ecode,emsg): raise DDTBError('Error opening %s: %s' % (ddtblog,emsg)) try: sessionlog = os.path.join(self.logdir,'session.log') self['session'] = DDTBLogFile( program='session', path=sessionlog, logformat=LOGFORMAT, level=logging.DEBUG, max_bytes=self.max_bytes, rotations=self.rotations ) except IOError,(ecode,emsg): raise DDTBError('Error opening %s: %s' % (sessionlog,emsg))
def __init__(self, name, data): DDTBConfigSection.__init__(self, name, data) for opt in BROKER_REQUIRED_CONFIG_KEYS: if not self.has_key(opt): raise DDTBError('Missing broker configuration for %s' % opt) try: validate_hostname(self['hostname']) except ValueError: raise DDTBError('Invalid broker hostname: %s' % self['hostname']) for k in ['tunnelip', 'serverip']: try: self[k] = IPv4Address(self[k]).ipaddress except ValueError: raise DDTBError('Invalid broker %s: %s' % (k, self[k])) try: port = int(self['port']) if port <= 0 or port >= 2**16: raise ValueError self['port'] = port except ValueError: raise DDTBError('Invalid broker port: %s' % self['port']) if not self.has_key('cleanup_interval'): self['cleanup_interval'] = DEFAULT_CLEANUP_INTERVAL try: self['cleanup_interval'] = int(self['cleanup_interval']) except ValueError: raise DDTBError('Invalid broker cleanup_interval %s' % self['cleanup_interval']) if not self.has_key('keepalive_interval'): self['keepalive_interval'] = DEFAULT_KEEPALIVE_INTERVAL try: self['keepalive_interval'] = int(self['keepalive_interval']) except ValueError: raise DDTBError('Invalid broker keepalive_interval %s' % self['keepalive_interval'])