Source code for linotp.lib.security.provider

# -*- coding: utf-8 -*-
#
#    LinOTP - the open source solution for two factor authentication
#    Copyright (C) 2010 - 2014 LSE Leading Security Experts GmbH
#
#    This file is part of LinOTP server.
#
#    This program is free software: you can redistribute it and/or
#    modify it under the terms of the GNU Affero General Public
#    License, version 3, as published by the Free Software Foundation.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the
#               GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
#    E-mail: linotp@lsexperts.de
#    Contact: www.linotp.org
#    Support: www.lsexperts.de
#
""" the security provider is a dynamic handler for the security
    relevant tasks like
       random, crypt, decrypt, sign
"""

import thread
import time
import logging
import traceback

from linotp.lib.crypt import zerome
from linotp.lib.error import HSMException

DEFAULT_KEY = 0
CONFIG_KEY = 1
TOKEN_KEY = 2
VALUE_KEY = 3


log = logging.getLogger(__name__)

[docs]class SecurityProvider(object): ''' the Security provider is the singleton in the server who provides the security modules to run security relevant methods - read the hsm configurations - set up a pool of hsm modules - bind a hsm to one session - free the hsm from session after usage as session identifier the thread id is used ''' def __init__(self, secLock): ''' setup the SecurityProvider, which is called on server startup from the app_globals init :param secLock: RWLock() to support server wide locking :type secLock: RWLock :return: - ''' self.config = {} self.security_modules = {} self.activeOne = 'default' self.hsmpool = {} self.rwLock = secLock self.max_retry = 5 def __createDefault__(self, config): ''' create a backward compatible default security provider :param config: ''' keyFile = '/home/work/project/pylons/linotp2/v2.4old/dev/linotpd/src/secKey' if config.has_key('linotpSecretFile'): keyFile = config.get('linotpSecretFile') self.config['default'] = {'pinHandle' : TOKEN_KEY, 'passHandle' : CONFIG_KEY, 'valueHandle' : VALUE_KEY, 'defaultHandle' : DEFAULT_KEY, 'crypted' : 'FALSE', 'file' : keyFile, 'module' : 'linotp.lib.security.default.DefaultSecurityModule', 'poolsize' : 20, } self.config['err'] = {'pinHandle' : TOKEN_KEY, 'passHandle' : CONFIG_KEY, 'valueHandle' : VALUE_KEY, 'defaultHandle' : DEFAULT_KEY, 'crypted' : 'FALSE', 'file' : keyFile, 'module' : 'linotp.lib.security.default.ErrSecurityModule', 'poolsize' : 20, }
[docs] def load_config(self, config): ''' load the security modules configuration ''' try: ## load backward compatible defaults self. __createDefault__(config) for key in config: ## lookup, which is the active security module if key == 'linotpActiveSecurityModule': self.activeOne = config.get(key) log.debug("[SecurityProvider:load_config] setting active security module: %s" % self.activeOne) if key.startswith('linotpSecurity'): entry = key.replace('linotpSecurity.', '') try: (id, val) = entry.split('.') except Exception as e: error = ('[SecurityProvider:load_config] failed to ' 'identify config entry: %s ' % (unicode(key))) log.error(error) log.error("[SecurityProvider:load_config] %s" % traceback.format_exc()) raise HSMException(error, id=707) if self.config.has_key(id): id_config = self.config.get(id) id_config[val] = config.get(key) else: self.config[id] = {val:config.get(key) } except Exception as e: log.error("[load_config] failed to identify module: %r " % e) error = "failed to identify module: %s " % unicode(e) raise HSMException(error, id=707) ## now create for each module a pool of hsm objects self.rwLock.acquire_write() try: for id in self.config: self.createHSMPool(id) finally: self.rwLock.release() return
[docs] def loadSecurityModule(self, id=None): ''' return the specified security module :param id: identifier for the security module (from the configuration) :type id: String or None :return: None or the created object :rtype: security module ''' ret = None if id is None: id = self.activeOne log.debug("[loadSecurityModule] Loading module %s" % id) if self.config.has_key(id) == False: return ret config = self.config.get(id) if config.has_key('module') == False: return ret module = config.get('module') methods = ["encrypt", "decrypt", "random", "setup_module"] method = "" parts = module.split('.') className = parts[-1] packageName = '.'.join(parts[:-1]) mod = __import__(packageName, globals(), locals(), [className]) klass = getattr(mod, className) for method in methods: if hasattr(klass, method) == False: error = ("[loadSecurityModule] Security Module %s misses the " "following interface: %s" % (unicode(module), unicode(method))) log.error(error) raise NameError(error) ret = klass(config) self.security_modules[id] = ret log.debug("[loadSecurityModule] returning %r" % ret) return ret
def _getHsmPool_(self, hsm_id): ret = None if self.hsmpool.has_key(hsm_id): ret = self.hsmpool.get(hsm_id) return ret
[docs] def setupModule(self, hsm_id, config=None): ''' setupModule is called during runtime to define the config parameters like passw or connection strings ''' self.rwLock.acquire_write() try: pool = self._getHsmPool_(hsm_id) if pool is None: error = ("[setupModule] failed to retieve pool " "for hsm_id: %s" % (unicode(hsm_id))) log.error(error) raise HSMException(error, id=707) for entry in pool: hsm = entry.get('obj') hsm.setup_module(config) self.activeOne = hsm_id except Exception as e: error = "[setupModule] failed to load hsm : %s" % (unicode(e)) log.error(error) log.error("[setupModule] %s" % traceback.format_exc()) raise HSMException(error, id=707) finally: self.rwLock.release() return self.activeOne
[docs] def createHSMPool(self, hsm_id=None, *args, **kw): ''' setup a pool of secutity provider ''' pool = None ## amount has to be taken from the hsm-id config if hsm_id == None: ids = self.config else: if self.config.has_key(hsm_id): ids = [] ids.append(hsm_id) else: error = "[createHSMPool] failed to find hsm_id: %s" % (unicode(hsm_id)) log.error(error) raise HSMException(error, id=707) for id in ids: pool = self._getHsmPool_(id) log.debug("[createHSMPool] already got this pool: %r" % pool) if pool is None: ## get the number of entries from the hsd (id) config conf = self.config.get(id) amount = int(conf.get('poolsize', 10)) log.debug("[createHSMPool] creating pool for %r with size %r" % (id, amount)) pool = [] for i in range(0, amount): error = '' hsm = None try: hsm = self.loadSecurityModule(id) except Exception as e: error = u"%r" % e log.error("[createHSMPool] %r " % (e)) log.error("[createHSMPool] %s" % traceback.format_exc()) pool.append({'obj': hsm , 'session': 0, 'error':error}) self.hsmpool[id] = pool return pool
def _findHSM4Session(self, pool, sessionId): found = None ## find session for hsm in pool: hsession = hsm.get('session') if hsession == sessionId: found = hsm return found def _createHSM4Session(self, pool, sessionId): found = None for hsm in pool: hsession = hsm.get('session') if unicode(hsession) == u'0': hsm['session'] = sessionId found = hsm break return found def _freeHSMSession(self, pool, sessionId): hsm = None for hsm in pool: hsession = hsm.get('session') if unicode(hsession) == unicode(sessionId): hsm['session'] = 0 break return hsm
[docs] def dropSecurityModule(self, hsm_id=None, sessionId=None): found = None if hsm_id is None: hsm_id = self.activeOne if sessionId is None: sessionId = unicode(thread.get_ident()) if self.config.has_key(hsm_id) == False: error = ('[SecurityProvider:dropSecurityModule] no config found ' 'for hsm with id %s ' % (unicode(hsm_id))) log.error(error) raise HSMException(error, id=707) return None ## find session try: pool = self._getHsmPool_(hsm_id) self.rwLock.acquire_write() found = self._findHSM4Session(pool, sessionId) if found is None: log.info('[SecurityProvider:dropSecurityModule] could not bind ' 'hsm to session %r ' % hsm_id) else: self._freeHSMSession(pool, sessionId) finally: self.rwLock.release() return True
[docs] def getSecurityModule(self, hsm_id=None, sessionId=None): found = None if hsm_id is None: hsm_id = self.activeOne if sessionId is None: sessionId = unicode(thread.get_ident()) if self.config.has_key(hsm_id) == False: error = ('[SecurityProvider:getSecurityModule] no config found for ' 'hsm with id %s ' % (unicode(hsm_id))) log.error(error) raise HSMException(error, id=707) retry = True tries = 0 locked = False while retry == True: try: pool = self._getHsmPool_(hsm_id) self.rwLock.acquire_write() locked = True ## find session found = self._findHSM4Session(pool, sessionId) if found is not None: ## if session is ok - return self.rwLock.release() locked = False retry = False log.debug("[getSecurityModule] using existing pool session %s" % found) return found.get('obj') else: ## create new entry log.debug("[getSecurityModule] getting new Session (%s) " "from pool %s" % (sessionId, pool)) found = self._createHSM4Session(pool, sessionId) self.rwLock.release() locked = False if found is None: tries += 1 log.warning('try %d: could not bind hsm to session - ' 'going to sleep for %r' % (tries, 10 * tries)) time.sleep(10 * tries) if tries >= self.max_retry: error = ('[SecurityProvider:getSecurityModule] ' 'max_retry %d: could not bind hsm to ' 'session - going to sleep for %r' % tries, 10 * tries) log.error(error) raise Exception(error) retry = True else: retry = False finally: if locked == True: self.rwLock.release() return found #return self.loadSecurityModule(id)
[docs]def main(): ## hook for local provider test sep = SecurityProvider() sep.load_config({}) sep.createHSMPool('default') sep.setupModule('default', {'passwd' : 'test123'}) ## runtime catch an hsm for session hsm = sep.getSecurityModule() passwo = 'password' encpass = hsm.encryptPassword(passwo) passw = hsm.decryptPassword(encpass) zerome(passw) hsm2 = sep.getSecurityModule(sessionId='session2') passwo = 'password' encpass = hsm2.encryptPassword(passwo) passw = hsm2.decryptPassword(encpass) zerome(passw) ## session shutdown sep.dropSecurityModule(sessionId='session2') sep.dropSecurityModule() return True
if __name__ == '__main__': main() #eof###########################################################################