Esempio n. 1
0
def checkResolverType(resolver):
    """
    check if a resolver of the given type exists

    :param resolver: full qualified resolver name or optional with trailing
        conf like: ``useridresolver.PasswdIdResolver.IdResolver.etc_resl``

    :return: True or False
    """
    res = False
    ret = False

    # prepare
    reso = resolver.strip()
    reso = reso.replace("\"", "")

    # the fully qualified resolver
    if reso in context.get('resolver_clazzes'):
        res = context.get('resolver_clazzes').get(reso)
        ret = True
    else:
        # if the last argument is the configuration
        pack = reso.split('.')
        rtype = ".".join(pack[:-1])
        conf = pack[-1]

        # lookup, if there is a resolver definition
        if rtype in context.get('resolver_types'):
            res = "%s.%s" % (rtype, conf)
            ret = True
        # #
        else:
            # legacy support, where resolver is defined as
            #    "useridresolver.passwdresolver.mrealm"
            # so we only could rely only on the type definition e.g.
            #  'passwdresolver' as part of the string
            for res_id, res_type in context.get('resolver_types').iteritems():
                if res_type in reso:
                    res = "%s.%s" % (res_id, conf)
                    ret = True
                    break

    #  is resolver defined in the linotp config
    try:
        getResolverObject(res)
    except Exception as exx:
        log.warning("Failed to setup resolver %r: %r" % (res, exx))
        res = "%r" % exx
        ret = False

    return (ret, res)
Esempio n. 2
0
def getResolverInfo(resolvername):
    '''
    return the resolver info of the given resolvername

    :param resolvername: the requested resolver
    :type  resolvername: string

    :return : dict of resolver description
    '''
    resolver_dict = {}
    typ = ""
    resolvertypes = get_resolver_types()

    descr = {}

    conf = context.get('Config')
    # conf = getLinotpConfig()

    for entry in conf:

        for typ in resolvertypes:

            # get the typed values of the descriptor!
            resolver_conf = get_resolver_classConfig(typ)
            if typ in resolver_conf:
                descr = resolver_conf.get(typ).get('config', {})
            else:
                descr = resolver_conf

            if entry.startswith("linotp." + typ) and entry.endswith(resolvername):
                # the realm might contain dots "."
                # so take all after the 3rd dot for realm
                resolver = entry.split(".", 3)
                # An old entry without resolver name
                if len(resolver) <= 3:
                    break

                value = conf.get(entry)
                if resolver[2] in descr:
                    configEntry = resolver[2]
                    if descr.get(configEntry) == 'password':

                        # do we already have the decrypted pass?
                        if 'enc' + entry in conf:
                            value = conf.get('enc' + entry)
                        else:
                            # if no, we take the encpass and decrypt it
                            value = conf.get(entry)
                            try:
                                en = decryptPassword(value)
                                value = en
                            except:
                                log.info("Decryption of resolver passwd failed: compatibility issue?")

                resolver_dict[ resolver[2] ] = value
                # Dont check the other resolver types

                break

    return {"type": typ, "data": resolver_dict, "resolver": resolvername}
Esempio n. 3
0
def getResolverList(filter_resolver_type=None):
    '''
    Gets the list of configured resolvers

    :param filter_resolver_type: Only resolvers of the given type are returned
    :type filter_resolver_type: string
    :rtype: Dictionary of the resolvers and their configuration
    '''
    Resolvers = {}
    resolvertypes = get_resolver_types()

    conf = context.get('Config')
    # conf = getLinotpConfig()
    for entry in conf:

        for typ in resolvertypes:
            if entry.startswith("linotp." + typ):
                # the realm might contain dots "."
                # so take all after the 3rd dot for realm
                r = {}
                resolver = entry.split(".", 3)
                # An old entry without resolver name
                if len(resolver) <= 3:
                    break
                r["resolvername"] = resolver[3]
                r["entry"] = entry
                r["type"] = typ

                if (filter_resolver_type is None) or (filter_resolver_type and filter_resolver_type == typ):
                    Resolvers[resolver[3]] = r
                # Dont check the other resolver types
                break

    return Resolvers
Esempio n. 4
0
def get_resolver_types():
    """
    get the array of the registred resolvers

    :return: array of resolvertypes like 'passwdresolver'
    """
    return context.get('resolver_types').values()
Esempio n. 5
0
    def active_users_per_realm(self, realm=None):
        """
        get the number of users which are assigned to an active token in total
            or per realm and resolver
        :param realm: name of realm
        :return: dict with
                keys: resolvernames
                values: number of active token users
        """
        realminfo = context.get('Config').getRealms().get(realm)
        resolver_specs = realminfo.get('useridresolver', '')
        realmdict = {}

        for resolver_spec in resolver_specs:
            __, config_identifier = parse_resolver_spec(resolver_spec)
            act_users_per_resolver = Session.query(Token.LinOtpUserid,
                                                   Token.LinOtpIdResolver,
                                                   Token.LinOtpIdResClass,
                                                   Token.LinOtpIsactive)\
                .join(TokenRealm)\
                .join(Realm)\
                .filter(and_(
                            Token.LinOtpIsactive == True,
                            Token.LinOtpIdResClass == resolver_spec,
                            Realm.name == realm
                ))\
                .group_by(Token.LinOtpUserid, Token.LinOtpIdResolver,
                          Token.LinOtpIsactive, Token.LinOtpIdResClass)

            realmdict[config_identifier] = act_users_per_resolver.count()

        return realmdict
Esempio n. 6
0
def defineResolver(params):
    """
    set up a new resolver from request parameters

    :param params: dict of request parameters
    """

    typ = params['type']
    conf = params['name']
    resolver_clazz = None

    for clazz_name, clazz_type in context.get('resolver_types').items():
        if typ.lower() in clazz_type.lower():
            resolver_clazz = clazz_name

    if not resolver_clazz:
        raise Exception("no such resolver type '%r' defined!" % typ)

    resolver = Resolver()
    resolver.setDefinition(params)
    res = resolver.saveConfig()

    getResolverObject(resolver_clazz + '.' + conf)

    return res
Esempio n. 7
0
def getResolverClassName(resolver_type, resolver_name):

    res = ""
    for clazz_name, clazz_type in context.get('resolver_types').items():
        if clazz_type == resolver_type:
            res = "%s.%s" % (clazz_name, resolver_name)
            break

    return res
Esempio n. 8
0
    def setSoPin(self, soPin):
        """
        set the soPin of the token
            the soPin is encrypted and the encrypte value is stored in the
            Token model

        :param soPin: the special so pin
        """
        iv, enc_soPin = SecretObj.encrypt(soPin, hsm=context.get('hsm'))
        self.token.setSoPin(enc_soPin, iv)
Esempio n. 9
0
 def __init__(self):
     """
     the Migration hanlder relies on a crypto handler, which
     encrypts or decryptes data.
     The setup of the cryptohandler is delayed, as at startup, might
     not all data be available
     """
     self.salt = None
     self.crypter = None
     self.hsm = context.get('hsm')
Esempio n. 10
0
def closeResolvers():
    """
    hook to close the resolvers at the end of the request
    """
    try:
        for resolver in context.get('resolvers_loaded').values():
            if hasattr(resolver, 'close'):
                resolver.close()
    except Exception as exx:
            log.exception("Failed to close resolver in context %r" % exx)
    return
Esempio n. 11
0
def get_cls_identifier(config_identifier):

    """
    Returns the class identifier string for a existing resolver
    identified by config_identifier (or None, if config_identifier
    doesn't exist)
    """

    config = context.get('Config')
    cls_identifiers = context.get('resolver_classes').keys()

    for config_entry in config:

        if not config_entry.endswith(config_identifier):
            continue

        for cls_identifier in cls_identifiers:
            if config_entry.startswith('linotp.' + cls_identifier):
                return cls_identifier

    return None
Esempio n. 12
0
def similar_resolver_exists(config_identifier):
    """
    Signifies if a resolver identified by config_identifer
    exists in the configuration.

    :remark: matches case insensitive

    :returns: bool
    """

    config = context.get('Config')
    cls_identifiers = context.get('resolver_classes').keys()

    for config_entry in config:
        for cls_identifier in cls_identifiers:
            if config_entry.startswith('linotp.' + cls_identifier):
                __, __, entry_config_identifier = config_entry.rpartition('.')
                if entry_config_identifier.lower() == config_identifier.lower():
                    return True

    return False
Esempio n. 13
0
def deleteResolver(resolvername):
    '''
    delete a resolver and all related config entries

    :paramm resolvername: the name of the to be deleted resolver
    :type   resolvername: string
    :return: sucess or fail
    :rtype:  boelean

    '''
    res = False

    resolvertypes = get_resolver_types()
    conf = context.get('Config')
    # conf = getLinotpConfig()

    delEntries = []
    resolver_specs = set()

    for entry in conf:
        rest = entry.split(".", 3)
        lSplit = len(rest)
        if lSplit > 3:
            rConf = rest[lSplit - 1]
            if rConf == resolvername:
                if rest[0] == "linotp" or rest[0] == "enclinotp":
                    typ = rest[1]
                    if typ in resolvertypes:
                        delEntries.append(entry)
                        resolver_conf = get_resolver_class_config(typ)
                        resolver_class = resolver_conf.get(typ, {}).get('clazz')
                        fqn = ".".join([resolver_class, resolvername])
                        resolver_specs.add(fqn)

    if len(delEntries) > 0:
        try:
            for entry in delEntries:
                res = removeFromConfig(entry)
                res = True
        except Exception as e:
            log.exception("Deleting resolver %s failed. Exception was %r"
                          % (resolvername, e))
            res = False

    if res:
        # on success we can flush the caches
        for resolver_spec in resolver_specs:
            _flush_user_resolver_cache(resolver_spec)
            _delete_from_resolver_config_cache(resolver_spec)

    return res
Esempio n. 14
0
    def get_tranactionid_length():
        """
        get transaction_id length from config and check if it is in range
        :return: length of transaction id
        """
        transid_len = int(
            context.get(
            'Config', {}).get(
            'TransactionIdLength', Challenges.DefaultTransactionIdLength))

        if transid_len < 12 or transid_len > 17:
            raise Exception("TransactionIdLength must be between 12 and 17, "
                            "was %d" % transid_len)
        return transid_len
Esempio n. 15
0
    def checkOtp(self, anOtpVal, counter, window, options=None):
        '''
        checkOtp - validate the token otp against a given otpvalue

        :param anOtpVal: the to be verified otpvalue
        :type anOtpVal:  string

        :param counter: the counter state, that shoule be verified
        :type counter: int

        :param window: the counter +window, which should be checked
        :type window: int

        :param options: the dict, which could contain token specific info
        :type options: dict

        :return: the counter state or -1
        :rtype: int

        '''
        log.debug("[checkOtp] begin. start the otp verification with: otpval %r, counter %r , window %r , options %r " %
                   (anOtpVal, counter, window, options))

        otplen = self.token.LinOtpOtpLen

        #otime contains the previous verification time
        # the new one must be newer than this!
        otime = self.token.LinOtpCount
        secObj = self._get_secret_object()
        window = self.token.LinOtpCountWindow
        key, iv = self.token.getUserPin()
        secPinObj = SecretObj(key, iv, hsm=context.get('hsm'))

        log.debug("[checkOtp] otime %s", otime)

        mtimeOtp = mTimeOtp(secObj, secPinObj, otime, otplen)
        res = mtimeOtp.checkOtp(anOtpVal, window, options=options)

        if (res != -1):
            res = res - 1  ## later on this will be incremented by 1
        if res == -1:
            msg = "verification failed"
        else:
            msg = "verifiction was successful"

        log.debug("[checkOtp] %s :res %r" % (msg, res))
        return res
Esempio n. 16
0
    def lookup_challenges(serial=None, transid=None, filter_open=False):
        """
        database lookup to find all challenges belonging to a token and or
        if exist with a transaction state

        :param serial:   serial of the token
        :param transid:  transaction id, if None, all will be retrieved
        :param filter_open: check only for those challenges, which have not
                            been verified before
        :return:         return a list of challenge dict
        """
        log.debug('serial %r: transactionid %r', serial, transid)

        if transid is None and serial is None:
            log.debug(
                'Called without serial or transid! Returning all challenges')

        conditions = ()

        if transid:
            transid_len = int(
                context.get('Config').get('TransactionIdLength', 12) or 12)

            if len(transid) == transid_len:
                conditions += (and_(Challenge.transid == transid),)
            else:
                conditions += (and_(Challenge.transid.startswith(transid)),)

        if serial:
            conditions += (and_(Challenge.tokenserial == serial),)

        if filter_open is True:
            conditions += (and_(Challenge.session.like('%"status": "open"%')),)

        # SQLAlchemy requires the conditions in one arg as tupple
        condition = and_(*conditions)
        challenges = Session.query(Challenge).\
            filter(condition).order_by(desc(Challenge.id)).all()

        log.debug('%r', challenges)

        return challenges
Esempio n. 17
0
        def request_cacher(*args, **kwargs):

            cache_name = func_to_cache.__name__ + "_cache"
            if not request_context.get(cache_name, None):
                request_context[cache_name] = {}

            cache_key = key_generator(*args, **kwargs)

            log_message = ("[" + func_to_cache.__name__ + "]:" +
                           " getting output values from cache")
            if cache_key not in request_context[cache_name]:
                log_message = (
                    "[" + func_to_cache.__name__ + "]:" +
                    "output values not in cache, getting values from DB")

                request_context[cache_name][cache_key] = func_to_cache(
                    *args, **kwargs)

            log.info(log_message)
            return request_context[cache_name][cache_key]
Esempio n. 18
0
    def __after__(self, action):
        """
        """
        try:
            # finally create the audit entry
            Audit = request_context['Audit']
            audit = request_context.get('audit')

            c.audit.update(audit)
            Audit.log(c.audit)
            Session.commit()
            return request

        except Exception as exx:
            log.exception(exx)
            Session.rollback()
            return sendError(response, exx, context='after')

        finally:
            Session.close()
Esempio n. 19
0
def get_cls_identifier(config_identifier):
    """
    Returns the class identifier string for a existing resolver
    identified by config_identifier (or None, if config_identifier
    doesn't exist)
    """

    config = context.get('Config')
    cls_identifiers = resolver_registry.keys()

    for config_entry in config:

        if not config_entry.endswith(config_identifier):
            continue

        for cls_identifier in cls_identifiers:
            if config_entry.startswith('linotp.' + cls_identifier):
                return cls_identifier

    return None
Esempio n. 20
0
    def __after__(self, action):
        """
        """
        try:
            # finally create the audit entry
            Audit = request_context['Audit']
            audit = request_context.get('audit')

            c.audit.update(audit)
            Audit.log(c.audit)
            Session.commit()
            return request

        except Exception as exx:
            log.exception(exx)
            Session.rollback()
            return sendError(response, exx, context='after')

        finally:
            Session.close()
Esempio n. 21
0
def deleteResolver(resolvername):
    '''
    delete a resolver and all related config entries

    :paramm resolvername: the name of the to be deleted resolver
    :type   resolvername: string
    :return: sucess or fail
    :rtype:  boelean

    '''
    res = False

    resolvertypes = get_resolver_types()
    conf = context.get('Config')
    # conf = getLinotpConfig()

    delEntries = []

    for entry in conf:
        rest = entry.split(".", 3)
        lSplit = len(rest)
        if lSplit > 3:
            rConf = rest[lSplit - 1]
            if rConf == resolvername:
                if rest[0] == "linotp" or rest[0] == "enclinotp" :
                    typ = rest[1]
                    if typ in resolvertypes:
                        delEntries.append(entry)

    if len(delEntries) > 0:
        try:
            for entry in delEntries:
                res = removeFromConfig(entry)
                log.debug("[deleteResolver] removing key: %s" % entry)
                res = True
        except Exception as e:
            log.exception("deleteResolver: %r" % e)
            res = False


    return res
Esempio n. 22
0
def deleteResolver(resolvername):
    '''
    delete a resolver and all related config entries

    :paramm resolvername: the name of the to be deleted resolver
    :type   resolvername: string
    :return: sucess or fail
    :rtype:  boelean

    '''
    res = False

    resolvertypes = get_resolver_types()
    conf = context.get('Config')
    # conf = getLinotpConfig()

    delEntries = []

    for entry in conf:
        rest = entry.split(".", 3)
        lSplit = len(rest)
        if lSplit > 3:
            rConf = rest[lSplit - 1]
            if rConf == resolvername:
                if rest[0] == "linotp" or rest[0] == "enclinotp":
                    typ = rest[1]
                    if typ in resolvertypes:
                        delEntries.append(entry)

    if len(delEntries) > 0:
        try:
            for entry in delEntries:
                res = removeFromConfig(entry)
                log.debug("[deleteResolver] removing key: %s" % entry)
                res = True
        except Exception as e:
            log.exception("deleteResolver: %r" % e)
            res = False

    return res
Esempio n. 23
0
def get_resolver_class(cls_identifier):
    '''
    return the class object for a resolver type
    :param resolver_type: string specifying the resolver
                          fully qualified or abreviated
    :return: resolver object class
    '''

    # ## this patch is a bit hacky:
    # the normal request has a request context, where it retrieves
    # the resolver info from and preserves the loaded resolvers for reusage
    # But in case of a authentication request (by a redirect from a 401)
    # the caller is no std request and the context object is missing :-(
    # The solution is, to deal with local references, either to the
    # global context or to local data

    resolver_classes = context.get('resolver_classes')
    if resolver_classes is None:
        glo = getGlobalObject()
        resolver_classes = copy.deepcopy(glo.getResolverClasses())

    return resolver_classes.get(cls_identifier)
Esempio n. 24
0
def _get_hsm_obj_from_context(hsm=None):
    """Get the hsm from  LinOTP request context

    If no hsm parameter is given, we get the hsm from the LinOTP request context
    (var context) which was extended some time ago.

    :param hsm: hsm security object instance
    :return: return the hsm object
    :rtype:
    """

    if hsm:
        hsm_obj = hsm.get('obj')
    else:
        hsm_obj = context.get('hsm', {}).get('obj')

    if not hsm_obj:
        raise HSMException('no hsm defined in execution context!')

    if hsm_obj.isReady() is False:
        raise HSMException('hsm not ready!')
    return hsm_obj
Esempio n. 25
0
def similar_resolver_exists(config_identifier):
    """
    Signifies if a resolver identified by config_identifer
    exists in the configuration.

    :remark: matches case insensitive

    :returns: bool
    """

    config = context.get('Config')
    cls_identifiers = list(resolver_registry.keys())

    for config_entry in config:
        for cls_identifier in cls_identifiers:
            if config_entry.startswith('linotp.' + cls_identifier):
                __, __, entry_config_identifier = config_entry.rpartition('.')
                if entry_config_identifier.lower() == config_identifier.lower(
                ):
                    return True

    return False
Esempio n. 26
0
def _get_hsm_obj_from_context(hsm=None):
    """Get the hsm from  LinOTP request context

    If no hsm parameter is given, we get the hsm from the LinOTP request context
    (var context) which was extended some time ago.

    :param hsm: hsm security object instance
    :return: return the hsm object
    :rtype:
    """

    if hsm:
        hsm_obj = hsm.get('obj')
    else:
        hsm_obj = context.get('hsm', {}).get('obj')

    if not hsm_obj:
        raise HSMException('no hsm defined in execution context!')

    if hsm_obj.isReady() is False:
        raise HSMException('hsm not ready!')
    return hsm_obj
Esempio n. 27
0
def get_resolver_class(cls_identifier):
    '''
    return the class object for a resolver type
    :param resolver_type: string specifying the resolver
                          fully qualified or abreviated
    :return: resolver object class
    '''

    # ## this patch is a bit hacky:
    # the normal request has a request context, where it retrieves
    # the resolver info from and preserves the loaded resolvers for reusage
    # But in case of a authentication request (by a redirect from a 401)
    # the caller is no std request and the context object is missing :-(
    # The solution is, to deal with local references, either to the
    # global context or to local data

    resolver_classes = context.get('resolver_classes')
    if resolver_classes is None:
        glo = getGlobalObject()
        resolver_classes = copy.deepcopy(glo.getResolverClasses())

    return resolver_classes.get(cls_identifier)
Esempio n. 28
0
    def resolverinfo(self, realm):
        """
        get the resolvers for one realm and the number of users per resolver
        :param realm: the realm to query
        :return: dict with resolvernames as keys and number of users as value
        """
        realminfo = context.get('Config').getRealms().get(realm)
        resolver_specs = realminfo.get('useridresolver', '')
        realmdict = {}

        for resolver_spec in resolver_specs:
            __, config_identifier = parse_resolver_spec(resolver_spec)
            realmdict[config_identifier] = 0

        user = getUserFromParam({'realm': realm}, optionalOrRequired=True)
        users_iters = iterate_users(getUserListIterators({'realm': realm}, user))

        for next_one in users_iters:
            for key in realmdict:
                if key in next_one:
                    realmdict[key] += 1

        return realmdict
Esempio n. 29
0
    def resolverinfo(self, realm):
        """
        get the resolvers for one realm and the number of users per resolver
        :param realm: the realm to query
        :return: dict with resolvernames as keys and number of users as value
        """

        realminfo = context.get("Config").getRealms().get(realm)
        resolver_specs = realminfo.get("useridresolver", "")
        realmdict = {}

        for resolver_spec in resolver_specs:
            __, config_identifier = parse_resolver_spec(resolver_spec)
            realmdict[config_identifier] = 0

        user = getUserFromParam({"realm": realm})
        users = getUserList({"realm": realm, "username": "******"}, user)

        for next_one in users:
            resolver = next_one["useridresolver"].split(".")[-1]
            if resolver in realmdict:
                realmdict[resolver] += 1

        return realmdict
Esempio n. 30
0
    def resolverinfo(self, realm):
        """
        get the resolvers for one realm and the number of users per resolver
        :param realm: the realm to query
        :return: dict with resolvernames as keys and number of users as value
        """

        realminfo = context.get('Config').getRealms().get(realm)
        resolver_specs = realminfo.get('useridresolver', '')
        realmdict = {}

        for resolver_spec in resolver_specs:
            __, config_identifier = parse_resolver_spec(resolver_spec)
            realmdict[config_identifier] = 0

        user = getUserFromParam({'realm': realm})
        users = getUserList({'realm': realm, 'username': '******'}, user)

        for next_one in users:
            resolver = next_one['useridresolver'].split('.')[-1]
            if resolver in realmdict:
                realmdict[resolver] += 1

        return realmdict
Esempio n. 31
0
    def resolverinfo(self, realm):
        """
        get the resolvers for one realm and the number of users per resolver
        :param realm: the realm to query
        :return: dict with resolvernames as keys and number of users as value
        """

        realminfo = context.get('Config').getRealms().get(realm)
        resolver_specs = realminfo.get('useridresolver', '')
        realmdict = {}

        for resolver_spec in resolver_specs:
            __, config_identifier = parse_resolver_spec(resolver_spec)
            realmdict[config_identifier] = 0

        user = getUserFromParam({'realm': realm})
        users = getUserList({'realm': realm, 'username': '******'}, user)

        for next_one in users:
            resolver = next_one['useridresolver'].split('.')[-1]
            if resolver in realmdict:
                realmdict[resolver] += 1

        return realmdict
Esempio n. 32
0
 def request_context_test_iterator():
     # this will raise an error if it is called
     # outside of request_context_safety
     yield request_context.get('foo')
Esempio n. 33
0
    def challenge(self, data, session='', typ='raw', challenge=None):
        '''
        the challenge method is for creating an transaction / challenge object

        remark: the transaction has a maximum lifetime and a reference to
                the OcraSuite token (serial)

        :param data:     data, which is the base for the challenge or None
        :type data:     string or None
        :param session:  session support for ocratokens
        :type session:  string
        :type typ:      define, which kind of challenge base should be used
                         could be raw - take the data input as is
                              (extract chars accordind challenge definition Q)
                         or random    - will generate a random input
                         or hased     - will take the hash of the input data

        :return:    challenge response containing the transcation id and
                    the challenge for the ocrasuite
        :rtype :    tuple of (transId(string), challenge(string))

        '''

        s_data = 'None'
        s_session = 'None'
        s_challenge = 'None'
        if data is not None:
            s_data = data
        if session is not None:
            s_session = session
        if challenge is None:
            s_challenge = challenge

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        if not data:
            typ = 'random'

        if challenge is None:
            if typ == 'raw':
                challenge = ocraSuite.data2rawChallenge(data)
            elif typ == 'random':
                challenge = ocraSuite.data2randomChallenge(data)
            elif typ == 'hash':
                challenge = ocraSuite.data2hashChallenge(data)

        serial = self.getSerial()
        counter = self.getOtpCount()

        # set the pin onyl in the compliant hashed mode
        pin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            pin = secObj.getKey()

        try:
            param = {}
            param['C'] = counter
            param['Q'] = challenge
            param['P'] = pin
            param['S'] = session
            if ocraSuite.T is not None:
                now = datetime.datetime.now()
                stime = now.strftime("%s")
                itime = int(stime)
                param['T'] = itime
            ''' verify that the data is compliant with the OcraSuitesuite
                and the client is able to calc the otp
            '''
            c_data = ocraSuite.combineData(**param)
            ocraSuite.compute(c_data)

        except Exception as ex:
            log.exception(
                "[OcraTokenClass] Failed to create ocrasuite challenge")
            raise Exception('[OcraTokenClass] Failed to create ocrasuite'
                            'challenge: %r' % (ex))

        #  save the object
        digits = '0123456789'
        transid = ''
        transactionIdLen = 12

        try:
            transactionIdLen = int(getFromConfig("OcraDefaultSuite", '12'))
        except:
            transactionIdLen = 12
            log.debug("[OcraTokenClass] Failed to set transactionId length"
                      " from config - using fallback %d" % (transactionIdLen))

        #  create a non exisiting challenge
        try:
            while True:
                for _c in range(0, transactionIdLen):
                    transid += urandom.choice(digits)

                chall = OcraTokenClass.getTransaction(transid)
                if chall is None:
                    break

            ddata = ''
            if data is not None:
                ddata = data

            chall = OcraChallenge(transid=transid,
                                  tokenserial=serial,
                                  challenge=typ + ':' + challenge,
                                  data=typ + ':' + ddata)
            chall.save()

        except Exception as ex:
            #  this might happen if we have a db problem or
            # the uniqnes constrain does not fit
            log.exception("[OcraTokenClass] Failed to create challenge")
            raise Exception('[OcraTokenClass] Failed to create challenge'
                            ' object: %s' % (ex))

        realms = []
        tokenrealms = self.token.getRealms()
        for realm in tokenrealms:
            realms.append(realm.name)

        url = get_qrtan_url(realms)

        return (transid, challenge, True, url)
Esempio n. 34
0
    def checkUserPass(self, user, passw, options=None):
        """
        :param user: the to be identified user
        :param passw: the identification pass
        :param options: optional parameters, which are provided
                    to the token checkOTP / checkPass

        :return: tuple of True/False and optional information
        """

        # the upper layer will catch / at least should ;-)

        opt = None
        serial = None
        resolverClass = None
        uid = None
        user_exists = False

        if user is not None and not user.is_empty:
            # the upper layer will catch / at least should
            try:
                (uid, _resolver, resolverClass) = getUserId(
                                                    user, check_existance=True)
                user_exists = True
            except Exception as _exx:
                pass_on = context.get('Config').get(
                    'linotp.PassOnUserNotFound', False)
                if pass_on and 'true' == pass_on.lower():
                    g.audit['action_detail'] = (
                        'authenticated by PassOnUserNotFound')
                    return (True, opt)
                else:
                    g.audit['action_detail'] = 'User not found'
                    return (False, opt)

        # if we have an user, check if we forward the request to another server
        if user_exists and get_auth_forward_on_no_token(user) is False:
            servers = get_auth_forward(user)
            if servers:
                res, opt = ForwardServerPolicy.do_request(servers, env,
                                                          user, passw, options)
                return res, opt

        # ------------------------------------------------------------------ --

        th = TokenHandler()

        # ------------------------------------------------------------------ --

        # auto asignement with otp only if user has no active token

        auto_assign_otp_return = th.auto_assign_otp_only(
                                                    otp=passw,
                                                    user=user,
                                                    options=options)

        if auto_assign_otp_return is True:
            return (True, None)

        # ------------------------------------------------------------------ --

        token_type = None
        if options:
            token_type = options.get('token_type', None)

        # ------------------------------------------------------------------ --

        # if there is a serial provided in the parameters, it overwrites the
        # token selection by user

        query_user = user
        if options and 'serial' in  options and options['serial']:
            serial = options['serial']
            query_user = None

        # ------------------------------------------------------------------ --

        tokenList = getTokens4UserOrSerial(
                               query_user,
                               serial,
                               token_type=token_type,
                               read_for_update=True
                               )

        if len(tokenList) == 0:
            g.audit['action_detail'] = 'User has no tokens assigned'

            # here we check if we should to autoassign and try to do it
            auto_assign_return = th.auto_assignToken(passw, user)
            if auto_assign_return is True:
                # We can not check the token, as the OTP value is already used!
                # but we will auth the user....
                return (True, opt)

            auto_enroll_return, opt = th.auto_enrollToken(passw, user,
                                                          options=options)
            if auto_enroll_return is True:
                # we always have to return a false, as
                # we have a challenge tiggered
                return (False, opt)

            pass_on = context.get('Config').get('linotp.PassOnUserNoToken',
                                                False)
            if pass_on and 'true' == pass_on.lower():
                g.audit['action_detail'] = 'authenticated by PassOnUserNoToken'
                return (True, opt)

            # Check if there is an authentication policy passthru

            if get_auth_passthru(user):
                log.debug('user %r has no token. Checking for '
                          'passthru in realm %r' % (user.login, user.realm))
                y = getResolverObject(resolverClass)
                g.audit['action_detail'] = 'Authenticated against Resolver'
                if y.checkPass(uid, passw):
                    return (True, opt)

            # Check alternatively if there is an authentication
            # policy passOnNoToken
            elif get_auth_passOnNoToken(user):
                log.info('user %r has not token. PassOnNoToken'
                         ' set - authenticated!')
                g.audit['action_detail'] = (
                    'Authenticated by passOnNoToken policy')
                return (True, opt)

            # if we have an user, check if we forward the request to another server
            elif get_auth_forward_on_no_token(user) is True:
                servers = get_auth_forward(user)
                if servers:
                    res, opt = ForwardServerPolicy.do_request(
                                            servers, env, user, passw, options)
                    return res, opt

            return False, opt

        if passw is None:
            raise ParameterError("Missing parameter:pass", id=905)

        (res, opt) = self.checkTokenList(
            tokenList, passw, user, options=options)

        return (res, opt)
Esempio n. 35
0
    def create_challenge(token, options=None, challenge_id=None, id_postfix=''):
        """
        dedicated method to create a challenge to support the implementation
        of challenge policies in future

        :param options: optional parameters for token specific tokens
                        eg. request a signed challenge
        :return: a tuple of  (boolean, and a dict, which contains the
                 {'challenge' : challenge} description)
        """

        # response dict, describing the challenge reply
        challenge = {}
        # the allocated db challenge object
        challenge_obj = None
        retry_counter = 0
        reason = None

        hsm = context['hsm'].get('obj')

        id_length = int(
            context.get('Config', None).get('TransactionIdLength', 12)) - \
                        len(id_postfix)

        while True:
            try:
                if not challenge_id:
                    transactionid = "%s%s" % (
                    Challenge.createTransactionId(length=id_length), id_postfix)
                else:
                    transactionid = challenge_id

                num_challenges = Session.query(Challenge). \
                    filter(Challenge.transid == transactionid).count()

                if num_challenges == 0:
                    challenge_obj = Challenge(transid=transactionid,
                                              tokenserial=token.getSerial())
                if challenge_obj is not None:
                    break

            except Exception as exce:
                log.info("Failed to create Challenge: %r", exce)
                reason = exce

            # prevent an unlimited loop
            retry_counter = retry_counter + 1
            if retry_counter > 100:
                log.info(
                    "Failed to create Challenge for %d times: %r -quiting!",
                    retry_counter, reason)
                raise Exception('Failed to create challenge %r' % reason)

        expired_challenges, valid_challenges = Challenges.get_challenges(token)

        # carefully create a new challenge
        try:

            # we got a challenge object allocated and initialize the challenge
            (res, open_transactionid, message, attributes) = \
                token.initChallenge(transactionid,
                                    challenges=valid_challenges,
                                    options=options)

            if res is False:
                # if a different transid is returned, this indicates, that there
                # is already an outstanding challenge we can refere to
                if open_transactionid != transactionid:
                    transactionid = open_transactionid

            else:
                # in case the init was successfull, we preserve no the
                # challenge data to support the implementation of a blocking
                # based on the previous stored data
                challenge_obj.setChallenge(message)
                challenge_obj.save()

                (res, message, data, attributes) = \
                    token.createChallenge(transactionid, options=options)

                if res is True:
                    # persist the final challenge data + message
                    challenge_obj.setChallenge(message)
                    challenge_obj.setData(data)
                    challenge_obj.signChallenge(hsm)
                    challenge_obj.save()
                else:
                    transactionid = ''
                    reason = Exception(message)

        except Exception as exce:
            reason = exce
            res = False

        # if something goes wrong with the challenge, remove it
        if res is False and challenge_obj is not None:
            try:
                log.debug("deleting session")
                Session.delete(challenge_obj)
                Session.commit()
            except Exception as exx:
                log.debug("deleting session failed: %r" % exx)
                try:
                    Session.expunge(challenge_obj)
                    Session.commit()
                except Exception as exx:
                    log.debug("expunge session failed: %r" % exx)

        # in case that create challenge fails, we must raise this reason
        if reason is not None:
            message = "%r" % reason
            log.error("Failed to create or init challenge %r " % reason)
            raise reason

        # prepare the response for the user
        if transactionid is not None:
            challenge['transactionid'] = transactionid

        if message is not None:
            challenge['message'] = message

        if attributes is not None and type(attributes) == dict:
            challenge.update(attributes)

        return (res, challenge)
Esempio n. 36
0
def deleteResolver(resolvername):
    """
    delete a resolver and all related config entries

    :paramm resolvername: the name of the to be deleted resolver
    :type   resolvername: string
    :return: sucess or fail
    :rtype:  boolean

    """

    if resolvername == current_app.config["ADMIN_RESOLVER_NAME"]:
        raise DeleteForbiddenError(
            f"default admin resolver {resolvername} is not allowed to be removed!"
        )

    res = False

    resolvertypes = get_resolver_types()
    conf = context.get("Config")

    delEntries = []
    resolver_specs = set()

    for entry in conf:
        rest = entry.split(".", 3)
        lSplit = len(rest)
        if lSplit > 3:
            rConf = rest[lSplit - 1]
            if rConf == resolvername:
                if rest[0] == "linotp" or rest[0] == "enclinotp":
                    typ = rest[1]
                    if typ in resolvertypes:
                        delEntries.append(entry)
                        resolver_conf = get_resolver_class_config(typ)
                        resolver_class = resolver_conf.get(typ, {}).get(
                            "clazz"
                        )
                        fqn = ".".join([resolver_class, resolvername])
                        resolver_specs.add(fqn)

    if len(delEntries) > 0:
        try:
            for entry in delEntries:
                res = removeFromConfig(entry)
                res = True
        except Exception as exx:
            log.error(
                "Deleting resolver %s failed. Exception was %r",
                resolvername,
                exx,
            )
            res = False

    if res:
        # on success we can flush the caches
        for resolver_spec in resolver_specs:
            _flush_user_resolver_cache(resolver_spec)
            _delete_from_resolver_config_cache(resolver_spec)

    return res
Esempio n. 37
0
    def _check(self, param):
        """
        basic check function, that can be used by different controllers

        :param param: dict of all caller parameters
        :type param: dict

        :return: Tuple of True or False and opt
        :rtype: Tuple(boolean, opt)

        """
        opt = None

        options = {}

        # put everything in the options but the user, pass, init
        options.update(param)
        for para in ["pass", "user", "init"]:
            if para in options:
                del options[para]

        passw = param.get("pass")
        user = getUserFromParam(param)

        # support for challenge verification
        challenge = param.get("challenge")
        if challenge is not None:
            options = {}
            options["challenge"] = challenge

        g.audit["user"] = user.login
        realm = user.realm or getDefaultRealm()
        g.audit["realm"] = realm

        # AUTHORIZATION Pre Check
        # we need to overwrite the user.realm in case the
        # user does not exist in the original realm (setrealm-policy)
        user.realm = set_realm(user.login, realm, exception=True)
        check_user_authorization(user.login, user.realm, exception=True)

        vh = ValidationHandler()
        (ok, opt) = vh.checkUserPass(user, passw, options=options)

        g.audit.update(request_context.get("audit", {}))
        g.audit["success"] = ok

        if ok:
            # AUTHORIZATION post check
            check_auth_tokentype(g.audit["serial"], exception=True, user=user)
            check_auth_serial(g.audit["serial"], exception=True, user=user)

        # add additional details
        if is_auth_return(ok, user=user):
            if opt is None:
                opt = {}
            if ok:
                opt["realm"] = g.audit.get("realm")
                opt["user"] = g.audit.get("user")
                opt["tokentype"] = g.audit.get("token_type")
                opt["serial"] = g.audit.get("serial")
            else:
                opt["error"] = g.audit.get("action_detail")

        return (ok, opt)
Esempio n. 38
0
    def checkOtp(self, passw, counter, window, options=None):
        '''
        checkOtp - standard callback of linotp to verify the token

        :param passw:      the passw / otp, which has to be checked
        :type passw:       string
        :param counter:    the start counter
        :type counter:     int
        :param  window:    the window, in which the token is valid
        :type  window:     int
        :param options:    options contains the transaction id, eg. if check_t
                           checks one transaction this will support
                           assynchreonous otp checks (when check_t is used)
        :type options:     dict

        :return:           verification counter or -1
        :rtype:            int (-1)

        '''

        ret = -1

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        # if we have no transactionid given through the options,
        # we have to retrieve the eldest challenge for this ocra token
        serial = self.getSerial()
        challenges = []

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            defTimeWindow = int(getFromConfig("ocra.timeWindow", 180))
            window = (int(self.getFromTokenInfo(
                    'timeWindow', defTimeWindow)) / ocraSuite.T)

            defTimeShift = int(getFromConfig("ocra.timeShift", 0))
            timeShift = int(self.getFromTokenInfo("timeShift", defTimeShift))

        if options is None:
            challenges = OcraTokenClass.getTransactions4serial(
                                                        serial,
                                                        currentOnly=True)

        elif options is not None:
            if type(options).__name__ != 'dict':
                err = ('[chekOtp] "options" not of type dict! %r' %
                       type(options))
                log.error(err)
                raise Exception(err)

            if 'transactionid' in options:
                transid = options.get('transactionid')
                challenges.append(OcraTokenClass.getTransaction(transid))

            elif 'challenge' in options:
                challenges.append(options)

            # due to the added options in checkUserPass, we have to extend
            # the logic here:
            # if no challenges found in between but we have a serial, we catch
            # the open challenges by serial (s.o.)
            if len(challenges) == 0:
                challenges = OcraTokenClass.getTransactions4serial(
                                                        serial,
                                                        currentOnly=True)

        if len(challenges) == 0:
            #  verify that there has already been a challenge
            challenges = OcraTokenClass.getTransactions4serial(serial)
            if len(challenges) > 0:
                err = 'No current transaction found!'
                ret = -1
                return ret
            else:
                err = 'No open transaction found!'
                log.info(err)
                if type(options) == dict and 'transactionid' in options:
                    raise Exception(err)
                ret = -1
                return ret

        for ch in challenges:
            challenge = {}

            if isinstance(ch, dict):
                #  transaction less checkOtp
                self.transId = 0
                challenge.update(ch)

            elif type(ch) == OcraChallenge:
                #  preserve transaction context, so we could use this in
                # the status callback
                self.transId = ch.transid
                challenge['challenge'] = ch.challenge
                challenge['transid'] = ch.transid
                challenge['session'] = ch.session

            ret = ocraSuite.checkOtp(passw, counter, window, challenge,
                                     pin=ocraPin, options=options,
                                     timeshift=timeShift)

            if ret != -1:
                break

        if -1 == ret:
            #  autosync: test if two consecutive challenges +
            # it's counter match
            ret = self.autosync(ocraSuite, passw, challenge)

        return ret
Esempio n. 39
0
def getResolverList(filter_resolver_type=None, config=None):
    '''
    Gets the list of configured resolvers

    :param filter_resolver_type: Only resolvers of the given type are returned
    :type filter_resolver_type: string
    :rtype: Dictionary of the resolvers and their configuration
    '''
    Resolvers = {}
    resolvertypes = get_resolver_types()

    if not config:
        conf = context.get('Config')
    else:
        conf = config

    for entry in conf:

        for typ in resolvertypes:
            if entry.startswith("linotp." + typ):
                # the realm might contain dots "."
                # so take all after the 3rd dot for realm
                r = {}
                resolver = entry.split(".", 3)

                # An old entry without resolver name
                if len(resolver) <= 3:
                    break
                r["resolvername"] = resolver[3]
                r["entry"] = entry
                r["type"] = typ

                readonly_entry = '.'.join([resolver[0], resolver[1],
                                           'readonly', resolver[3]])

                if readonly_entry in conf:
                    readonly = False
                    try:
                        readonly = boolean(conf[readonly_entry])
                    except Exception as _exx:
                        log.info("Failed to convert 'readonly' attribute"
                                 " %r:%r",
                                 readonly_entry, conf[readonly_entry])

                    if readonly:
                        r["readonly"] = True
                #
                # this is a patch for a hack:
                #
                # as entry, the first found resolver is shown
                # as the PasswdResolver only has one entry, this always
                # has been 'fileName', which now as could be 'readonly'
                # thus we skip the readonly entry:

                key = resolver[2]
                if key == "readonly":
                    continue

                if ((filter_resolver_type is None) or
                        (filter_resolver_type and
                         filter_resolver_type == typ)):
                    Resolvers[resolver[3]] = r
                # Dont check the other resolver types
                break

    return Resolvers
Esempio n. 40
0
def getResolverInfo(resolvername, passwords=False):
    '''
    return the resolver info of the given resolvername

    :param resolvername: the requested resolver
    :type  resolvername: string

    :return : dict of resolver description
    '''

    result = {"type": None, "data": {}, "resolver": resolvername}

    linotp_config = context.get('Config')
    resolver_types = get_resolver_types()

    # --------------------------------------------------------------------- --

    # lookup, which resolver type is associated with this resolver name

    for config_entry in linotp_config:

        if config_entry.endswith("." + resolvername):

            # check if this is a resolver definition, starting with linotp.
            # and continuing with a resolver type

            part = config_entry.split('.')

            if (len(part) > 3 and part[0] == 'linotp' and
                part[1] in resolver_types):

                resolver_type = part[1]
                break

    else:
        return result

    # now we can load the resolver config unsing the resolver class

    resolver_cls = get_resolver_class(resolver_type)

    if resolver_cls is None:
        raise Exception("no such resolver type '%r' defined!" %
                        resolver_type)

    res_conf, _missing = resolver_cls.filter_config(linotp_config,
                                                    resolvername)

    # --------------------------------------------------------------------- --

    # now prepare the resolver config output, which should contain
    #
    # - no global entries, starting with 'linotp.'
    # - adjusted passwords
    # - all values as text

    for key in res_conf.keys():

        # suppress global config entries

        if key.startswith("linotp."):
            del res_conf[key]
            continue

        # should passwords be displayed?
        if key in resolver_cls.crypted_parameters:
            if not passwords:
                res_conf[key] = encryptPassword(res_conf[key])

        # as we have in the resolver config typed values, this might
        # lead to some trouble. so we prepare for output comparison
        # the string representation

        if (not isinstance(res_conf[key], str) and
            not isinstance(res_conf[key], unicode)):
            res_conf[key] = "%r" % res_conf[key]

    if 'readonly' in res_conf:
        readonly = False
        try:
            readonly = boolean(res_conf['readonly'])
        except Exception:
            log.info("Failed to convert 'readonly' attribute %r:%r",
                     resolvername, res_conf['readonly'])

        if readonly:
            result["readonly"] = True

    result["type"] = resolver_type
    result["data"] = res_conf

    return result
Esempio n. 41
0
    def create_challenge(token, options=None, challenge_id=None, id_postfix=''):
        """
        dedicated method to create a challenge to support the implementation
        of challenge policies in future

        :param options: optional parameters for token specific tokens
                        eg. request a signed challenge
        :return: a tuple of  (boolean, and a dict, which contains the
                 {'challenge' : challenge} description)
        """

        # response dict, describing the challenge reply
        challenge = {}
        # the allocated db challenge object
        challenge_obj = None
        retry_counter = 0

        reason = None
        ReasonException = Exception()

        hsm = context['hsm'].get('obj')

        id_length = int(
            context.get('Config', None).get('TransactionIdLength', 12)) - \
                        len(id_postfix)

        while True:
            try:
                if not challenge_id:
                    transactionid = "%s%s" % (
                    Challenge.createTransactionId(length=id_length), id_postfix)
                else:
                    transactionid = challenge_id

                num_challenges = Session.query(Challenge). \
                    filter(Challenge.transid == transactionid).count()

                if num_challenges == 0:
                    challenge_obj = Challenge(transid=transactionid,
                                              tokenserial=token.getSerial())
                if challenge_obj is not None:
                    break

            except Exception as exce:
                log.exception("Failed to create Challenge: %r", exce)
                reason = "%r" % exce
                ReasonException = exce

            # prevent an unlimited loop
            retry_counter = retry_counter + 1
            if retry_counter > 100:
                log.info(
                    "Failed to create Challenge for %d times: %r -quiting!",
                    retry_counter, reason)
                raise Exception('Failed to create challenge %r' % reason)

        expired_challenges, valid_challenges = Challenges.get_challenges(token)

        # carefully create a new challenge
        try:

            # we got a challenge object allocated and initialize the challenge
            (res, open_transactionid, message, attributes) = \
                token.initChallenge(transactionid,
                                    challenges=valid_challenges,
                                    options=options)

            if res is False:
                # if a different transid is returned, this indicates, that there
                # is already an outstanding challenge we can refere to
                if open_transactionid != transactionid:
                    transactionid = open_transactionid

            else:
                # in case the init was successfull, we preserve no the
                # challenge data to support the implementation of a blocking
                # based on the previous stored data
                challenge_obj.setChallenge(message)
                challenge_obj.save()

                (res, message, data, attributes) = \
                    token.createChallenge(transactionid, options=options)

                if res is True:
                    # persist the final challenge data + message
                    challenge_obj.setChallenge(message)
                    challenge_obj.setData(data)
                    challenge_obj.signChallenge(hsm)
                    challenge_obj.save()
                else:
                    transactionid = ''
                    reason = message
                    ReasonException = Exception(message)

        except Exception as exce:
            log.exception("Failed to create Challenge: %r", exce)
            reason = "%r" % exce
            ReasonException = exce
            res = False

        # if something goes wrong with the challenge, remove it
        if res is False and challenge_obj is not None:
            try:
                log.debug("deleting session")
                Session.delete(challenge_obj)
                Session.commit()
            except Exception as exx:
                log.debug("deleting session failed: %r" % exx)
                try:
                    Session.expunge(challenge_obj)
                    Session.commit()
                except Exception as exx:
                    log.debug("expunge session failed: %r" % exx)

        # in case that create challenge fails, we must raise this reason
        if reason is not None:
            log.exception("Failed to create Challenge: %r", ReasonException)
            log.error("Failed to create or init challenge %r ", reason)
            raise ReasonException

        # prepare the response for the user
        if transactionid is not None:
            challenge['transactionid'] = transactionid

        if message is not None:
            challenge['message'] = message

        if attributes is not None and type(attributes) == dict:
            challenge.update(attributes)

        #
        # add token specific info like tokentype and serial
        #

        challenge["linotp_tokenserial"] = token.getSerial()
        challenge["linotp_tokentype"] = token.type

        return (res, challenge)
Esempio n. 42
0
    def checkUserPass(self, user, passw, options=None):
        """
        :param user: the to be identified user
        :param passw: the identification pass
        :param options: optional parameters, which are provided
                    to the token checkOTP / checkPass

        :return: tuple of True/False and optional information
        """

        # the upper layer will catch / at least should ;-)

        opt = None
        serial = None
        resolverClass = None
        uid = None
        user_exists = False

        if user:
            # the upper layer will catch / at least should
            try:
                (uid, _resolver,
                 resolverClass) = getUserId(user, check_existance=True)
                user_exists = True
            except Exception as _exx:
                pass_on = context.get("Config").get(
                    "linotp.PassOnUserNotFound", False)
                if pass_on and pass_on.lower() == "true":
                    g.audit[
                        "action_detail"] = "authenticated by PassOnUserNotFound"
                    return (True, opt)
                else:
                    g.audit["action_detail"] = "User not found"
                    return (False, opt)

        # if we have an user, check if we forward the request to another server
        if user_exists and not get_auth_forward_on_no_token(user):
            servers = get_auth_forward(user)
            if servers:
                log.info("forwarding auth request for user {} to {}".format(
                    user, servers))
                res, opt = ForwardServerPolicy.do_request(
                    servers, env, user, passw, options)
                log.info("result of auth request for user {}: ({}, {})".format(
                    user, res, opt))
                g.audit["action_detail"] = "Forwarded, result {}".format(res)
                return res, opt
            else:
                log.info(
                    "NOT forwarding auth request for user {} (no servers)".
                    format(user))
                g.audit["action_detail"] = "Not forwarded (no servers)"
        else:
            log.info(
                "NOT forwarding auth request for user {} "
                "(get_auth_forward_on_no_token returned False)".format(user))

        # ------------------------------------------------------------------ --

        th = TokenHandler()

        # ------------------------------------------------------------------ --

        # auto asignement with otp only if user has no active token

        auto_assign_otp_return = th.auto_assign_otp_only(otp=passw,
                                                         user=user,
                                                         options=options)

        if auto_assign_otp_return:
            return (True, None)

        # ------------------------------------------------------------------ --

        token_type = None
        if options:
            token_type = options.get("token_type", None)

        # ------------------------------------------------------------------ --

        # if there is a serial provided in the parameters, it overwrites the
        # token selection by user

        query_user = user
        if options and "serial" in options and options["serial"]:
            serial = options["serial"]
            query_user = None

        # ------------------------------------------------------------------ --

        tokenList = getTokens4UserOrSerial(query_user,
                                           serial,
                                           token_type=token_type,
                                           read_for_update=True)

        if len(tokenList) == 0:
            g.audit["action_detail"] = "User has no tokens assigned"

            # here we check if we should to autoassign and try to do it
            auto_assign_return = th.auto_assignToken(passw, user)
            if auto_assign_return:
                # We can not check the token, as the OTP value is already used!
                # but we will auth the user....
                return (True, opt)

            auto_enroll_return, opt = th.auto_enrollToken(passw,
                                                          user,
                                                          options=options)
            if auto_enroll_return:
                # we always have to return a false, as
                # we have a challenge tiggered
                return (False, opt)

            pass_on = context.get("Config").get("linotp.PassOnUserNoToken",
                                                False)
            if pass_on and pass_on.lower() == "true":
                g.audit["action_detail"] = "authenticated by PassOnUserNoToken"
                return (True, opt)

            # Check if there is an authentication policy passthru

            if get_auth_passthru(user):
                log.debug(
                    "user %r has no token. Checking for passthru in realm %r",
                    user.login,
                    user.realm,
                )
                y = getResolverObject(resolverClass)
                g.audit["action_detail"] = "Authenticated against Resolver"
                if y.checkPass(uid, passw):
                    return (True, opt)

            # Check alternatively if there is an authentication
            # policy passOnNoToken
            elif get_auth_passOnNoToken(user):
                log.info("user %r has not token. PassOnNoToken"
                         " set - authenticated!")
                g.audit[
                    "action_detail"] = "Authenticated by passOnNoToken policy"
                return (True, opt)

            # if we have an user, check if we forward the request to another
            # server
            elif get_auth_forward_on_no_token(user):
                servers = get_auth_forward(user)
                if servers:
                    log.info(
                        "forwarding auth request for user {} to {}".format(
                            user, servers))
                    res, opt = ForwardServerPolicy.do_request(
                        servers, env, user, passw, options)
                    log.info(
                        "result of auth request for user {}: ({}, {})".format(
                            user, res, opt))
                    g.audit["action_detail"] = "Forwarded, result {}".format(
                        res)
                    return res, opt
                else:
                    log.info(
                        "NOT forwarding auth request for user {} (no servers)".
                        format(user))
                    g.audit["action_detail"] = "Not forwarded (no servers)"

            return False, opt

        if passw is None:
            raise ParameterError("Missing parameter:pass", id=905)

        (res, opt) = self.checkTokenList(tokenList,
                                         passw,
                                         user,
                                         options=options)

        return (res, opt)
Esempio n. 43
0
    def migrate_resolver(self, src=None, target=None, filter_serials=None):
        """
        support the migration of owned tokens from one resolver to a new one

        the idea is:
        - get all tokens from one resolver
        - for each token, the the owner
        - from the owner get the login name
        - with the login name get the uid from the target resolver
        - update the new_id in the token

        """

        _ = context['translate']

        ret = {}

        if not src or not target:
            raise Exception("Missing src or target resolver defintion!")

        audit = context.get('audit')
        now = datetime.now()
        stime = now.strftime("%s")

        audit['action_detail'] = ("migration from %s to %s"
                                  % (src['resolvername'],
                                     target['resolvername']))

        ret['src'] = src
        ret['target'] = target
        ret['value'] = False
        ret['message'] = ''

        search = getResolverClassName(src['type'], src['resolvername'])
        target_resolver = getResolverClassName(target['type'],
                                               target['resolvername'])

        # get all tokens of src resolver
        tokens = self._get_tokens_for_resolver(search, serials=filter_serials)

        num_migration = 0
        serials = set()
        for token in tokens:
            serial = token.get('LinOtpTokenSerialnumber')
            userid = token.get('LinOtpUserid')
            resolverC = token.get('LinOtpIdResClass')
            # now do the lookup of the uid in the
            # src resolver to get the login
            uInfo = getUserInfo(userid, '', resolverC)

            login = uInfo.get('username')
            try:
                y = getResolverObject(target_resolver)
                uid = y.getUserId(login)
                if not uid:
                    log.warning("User %s not found in target resolver %r",
                                login, target_resolver)
                    continue

                token.LinOtpIdResClass = target_resolver
                token.LinOtpUserid = uid
                # TODO: adjust
                token.LinOtpIdResolver = target['type']
                Session.add(token)

                num_migration += 1
                serials.add(serial)

            except Exception as exx:
                log.exception("Faild to set new resolver data for token %s: %r"
                              % (serial, exx))

        ret['value'] = True
        ret['message'] = (_("%d tokens of %d migrated")
                            % (num_migration, len(tokens)))
        log.info(ret['message'])
        audit['info'] = "[%s] %s" % (stime, ret['message'])
        audit['serial'] = ",".join(list(serials))
        audit['success'] = True
        context['audit'] = audit

        return ret
Esempio n. 44
0
            return res

        ##
        # AUTOSYNC starts here
        ##

        counter = self.token.getOtpCounter()
        syncWindow = self.token.getSyncWindow()
        if ocraSuite.T is not None:
            syncWindow = syncWindow / 10

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            timeShift = int(self.getFromTokenInfo("timeShift", 0))

        # timeStepping    = int(ocraSuite.T)

        tinfo = self.getTokenInfo()

        # autosync does only work, if we have a token info, where the
        # last challenge and the last sync-counter is stored
Esempio n. 45
0
    def autosync(self, ocraSuite, passw, challenge):
        '''
        try to resync a token automaticaly, if a former and the current
        request failed

        :param  ocraSuite: the ocraSuite of the current Token
        :type  ocraSuite: ocra object
        :param  passw:
        '''
        res = -1

        autosync = False

        try:
            confsetting = getFromConfig("AutoResync")
            if confsetting is None:
                autosync = False
            elif "true" == confsetting.lower():
                autosync = True
            elif "false" == confsetting.lower():
                autosync = False
        except Exception as ex:
            log.exception('Ocra: autosync check undefined %r' % (ex))
            return res

        ' if autosync is not enabled: do nothing '
        if autosync is False:
            return res

        ##
        # AUTOSYNC starts here
        ##

        counter = self.token.getOtpCounter()
        syncWindow = self.token.getSyncWindow()
        if ocraSuite.T is not None:
            syncWindow = syncWindow / 10

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            timeShift = int(self.getFromTokenInfo("timeShift", 0))

        # timeStepping    = int(ocraSuite.T)

        tinfo = self.getTokenInfo()

        # autosync does only work, if we have a token info, where the
        # last challenge and the last sync-counter is stored
        # if no tokeninfo, we start with a autosync request, thus start the
        # lookup in the sync window

        if 'lChallenge' not in tinfo:
            # run checkOtp, with sync window for the current challenge
            log.info('[OcraToken:autosync] initial sync')
            count_0 = -1
            try:
                otp0 = passw
                count_0 = ocraSuite.checkOtp(otp0,
                                             counter,
                                             syncWindow,
                                             challenge,
                                             pin=ocraPin,
                                             timeshift=timeShift)
            except Exception as ex:
                log.exception('Ocra: Error during autosync0: %r' % (ex))

            if count_0 != -1:
                tinfo['lChallenge'] = {'otpc': count_0}
                self.setTokenInfo(tinfo)
                log.info('[OcraToken:autosync] initial sync - success: %r' %
                         count_0)

            res = -1

        else:
            # run checkOtp, with sync window for the current challenge
            count_1 = -1
            try:
                otp1 = passw
                count_1 = ocraSuite.checkOtp(otp1,
                                             counter,
                                             syncWindow,
                                             challenge,
                                             pin=ocraPin,
                                             timeshift=timeShift)
            except Exception as ex:
                log.exception('Ocra: Error during autosync1: %r' % (ex))

            if count_1 == -1:
                del tinfo['lChallenge']
                self.setTokenInfo(tinfo)
                log.info('[OcraToken:autosync] sync failed! Not a valid pass'
                         ' in scope (%r)' % (otp1))
                res = -1
            else:
                # run checkOtp, with sync window for the old challenge
                lChallange = tinfo.get('lChallenge')
                count_0 = lChallange.get('otpc')

                if ocraSuite.C is not None:
                    #  sync the counter based ocra token
                    if count_1 - count_0 < 2:
                        self.setOtpCount(count_1)
                        res = count_1

                if ocraSuite.T is not None:
                    #  sync the timebased ocra token
                    if count_1 - count_0 < ocraSuite.T * 2:
                        # calc the new timeshift !
                        log.debug("[autosync] the counter %r matches: %r" %
                                  (count_1,
                                   datetime.datetime.fromtimestamp(count_1)))

                        currenttime = int(time.time())
                        new_shift = (count_1 - currenttime)

                        tinfo['timeShift'] = new_shift
                        self.setOtpCount(count_1)
                        res = count_1

                #  if we came here, the old challenge is not required anymore
                del tinfo['lChallenge']
                self.setTokenInfo(tinfo)

        return res
Esempio n. 46
0
    def checkUserPass(self, user, passw, options=None):
        """
        :param user: the to be identified user
        :param passw: the identifiaction pass
        :param options: optional parameters, which are provided
                    to the token checkOTP / checkPass

        :return: tuple of True/False and optional information
        """

        log.debug('entering function checkUserPass(%r)' % user.login)
        # the upper layer will catch / at least should ;-)

        opt = None
        serial = None
        resolverClass = None
        uid = None
        audit = context['audit']
        user_exists = False

        if user is not None and (user.isEmpty() is False):
            # the upper layer will catch / at least should
            try:
                (uid, _resolver, resolverClass) = getUserId(user)
                user_exists = True
            except:
                pass_on = context.get('Config').get(
                    'linotp.PassOnUserNotFound', False)
                if pass_on and 'true' == pass_on.lower():
                    audit['action_detail'] = (
                        'authenticated by PassOnUserNotFound')
                    return (True, opt)
                else:
                    audit['action_detail'] = 'User not found'
                    return (False, opt)

        # if we have an user, check if we forward the request to another server
        if user_exists:
            servers = get_auth_forward(user)
            if servers:
                res, opt = ForwardServerPolicy.do_request(
                    servers, env, user, passw, options)
                return res, opt

        tokenList = linotp.lib.token.getTokens4UserOrSerial(user, serial)

        if len(tokenList) == 0:
            audit['action_detail'] = 'User has no tokens assigned'

            # here we check if we should to autoassign and try to do it
            log.debug('about to check auto_assigning')

            th = TokenHandler()
            auto_assign_return = th.auto_assignToken(passw, user)
            if auto_assign_return is True:
                # We can not check the token, as the OTP value is already used!
                # but we will auth the user....
                return (True, opt)

            auto_enroll_return, opt = th.auto_enrollToken(passw,
                                                          user,
                                                          options=options)
            if auto_enroll_return is True:
                # we always have to return a false, as
                # we have a challenge tiggered
                return (False, opt)

            pass_on = context.get('Config').get('linotp.PassOnUserNoToken',
                                                False)
            if pass_on and 'true' == pass_on.lower():
                audit['action_detail'] = 'authenticated by PassOnUserNoToken'
                return (True, opt)

            #  Check if there is an authentication policy passthru
            from linotp.lib.policy import get_auth_passthru
            if get_auth_passthru(user):
                log.debug('user %r has no token. Checking for '
                          'passthru in realm %r' % (user.login, user.realm))
                y = getResolverObject(resolverClass)
                audit['action_detail'] = 'Authenticated against Resolver'
                if y.checkPass(uid, passw):
                    return (True, opt)

            #  Check if there is an authentication policy passOnNoToken
            from linotp.lib.policy import get_auth_passOnNoToken
            if get_auth_passOnNoToken(user):
                log.info('user %r has not token. PassOnNoToken'
                         ' set - authenticated!')
                audit['action_detail'] = (
                    'Authenticated by passOnNoToken policy')
                return (True, opt)

            return (False, opt)

        if passw is None:
            raise ParameterError(u"Missing parameter:pass", id=905)

        (res, opt) = self.checkTokenList(tokenList,
                                         passw,
                                         user,
                                         options=options)
        log.debug('return of __checkTokenList: %r ' % (res, ))

        return (res, opt)
Esempio n. 47
0
    def migrate_resolver(self, src=None, target=None, filter_serials=None):
        """
        support the migration of owned tokens from one resolver to a new one

        the idea is:
        - get all tokens from one resolver
        - for each token, the the owner
        - from the owner get the login name
        - with the login name get the uid from the target resolver
        - update the new_id in the token

        """

        _ = context['translate']

        ret = {}

        if not src or not target:
            raise Exception("Missing src or target resolver defintion!")

        audit = context.get('audit')
        now = datetime.now()
        stime = now.strftime("%s")

        audit['action_detail'] = (
            "migration from %s to %s" %
            (src['resolvername'], target['resolvername']))

        ret['src'] = src
        ret['target'] = target
        ret['value'] = False
        ret['message'] = ''

        search = getResolverClassName(src['type'], src['resolvername'])
        target_resolver = getResolverClassName(target['type'],
                                               target['resolvername'])

        # get all tokens of src resolver
        tokens = self._get_tokens_for_resolver(search, serials=filter_serials)

        num_migration = 0
        serials = set()
        for token in tokens:
            serial = token.get('LinOtpTokenSerialnumber')
            userid = token.get('LinOtpUserid')
            resolverC = token.get('LinOtpIdResClass')
            # now do the lookup of the uid in the
            # src resolver to get the login
            uInfo = getUserInfo(userid, '', resolverC)

            login = uInfo.get('username')
            try:
                y = getResolverObject(target_resolver)
                uid = y.getUserId(login)
                if not uid:
                    log.warning("User %s not found in target resolver %r",
                                login, target_resolver)
                    continue

                token.LinOtpIdResClass = target_resolver
                token.LinOtpUserid = uid
                # TODO: adjust
                token.LinOtpIdResolver = target['type']
                Session.add(token)

                num_migration += 1
                serials.add(serial)

            except Exception as exx:
                log.exception(
                    "Faild to set new resolver data for token %s: %r" %
                    (serial, exx))

        ret['value'] = True
        ret['message'] = (_("%d tokens of %d migrated") %
                          (num_migration, len(tokens)))
        log.info(ret['message'])
        audit['info'] = "[%s] %s" % (stime, ret['message'])
        audit['serial'] = ",".join(list(serials))
        audit['success'] = True
        context['audit'] = audit

        return ret
Esempio n. 48
0
def getResolverInfo(resolvername):
    '''
    return the resolver info of the given resolvername

    :param resolvername: the requested resolver
    :type  resolvername: string

    :return : dict of resolver description
    '''
    resolver_dict = {}
    typ = ""
    resolvertypes = get_resolver_types()

    descr = {}

    conf = context.get('Config')
    # conf = getLinotpConfig()

    for entry in conf:

        for typ in resolvertypes:

            # get the typed values of the descriptor!
            resolver_conf = get_resolver_class_config(typ)
            if typ in resolver_conf:
                descr = resolver_conf.get(typ).get('config', {})
            else:
                descr = resolver_conf

            if entry.startswith("linotp." +
                                typ) and entry.endswith(resolvername):
                # the realm might contain dots "."
                # so take all after the 3rd dot for realm
                resolver = entry.split(".", 3)
                # An old entry without resolver name
                if len(resolver) <= 3:
                    break

                value = conf.get(entry)
                if resolver[2] in descr:
                    configEntry = resolver[2]
                    if descr.get(configEntry) == 'password':

                        # do we already have the decrypted pass?
                        if 'enc' + entry in conf:
                            value = conf.get('enc' + entry)
                        else:
                            # if no, we take the encpass and decrypt it
                            value = conf.get(entry)
                            try:
                                en = decryptPassword(value)
                                value = en
                            except:
                                log.info(
                                    "Decryption of resolver passwd failed: compatibility issue?"
                                )

                resolver_dict[resolver[2]] = value
                # Dont check the other resolver types

                break

    return {"type": typ, "data": resolver_dict, "resolver": resolvername}
Esempio n. 49
0
    def _check(self, param):
        '''
        basic check function, that can be used by different controllers

        :param param: dict of all caller parameters
        :type param: dict

        :return: Tuple of True or False and opt
        :rtype: Tuple(boolean, opt)

        '''
        opt = None

        options = {}

        # put everything in the options but the user, pass, init
        options.update(param)
        for para in ["pass", "user", "init"]:
            if options.has_key(para):
                del options[para]

        passw = param.get("pass")
        user = getUserFromParam(param)

        # support for ocra application challenge verification
        challenge = param.get("challenge")
        if challenge is not None:
            options = {}
            options['challenge'] = challenge

        c.audit['user'] = user.login
        realm = user.realm or getDefaultRealm()
        c.audit['realm'] = realm

        # AUTHORIZATION Pre Check
        # we need to overwrite the user.realm in case the
        # user does not exist in the original realm (setrealm-policy)
        user.realm = set_realm(user.login, realm, exception=True)
        check_user_authorization(user.login, user.realm, exception=True)

        if isSelfTest() is True:
            initTime = param.get("init")
            if initTime is not None:
                if options is None:
                    options = {}
                options['initTime'] = initTime
        vh = ValidationHandler()
        (ok, opt) = vh.checkUserPass(user, passw, options=options)

        c.audit.update(request_context.get('audit'))
        c.audit['success'] = ok

        if ok:
            # AUTHORIZATION post check
            check_auth_tokentype(c.audit['serial'], exception=True, user=user)
            check_auth_serial(c.audit['serial'], exception=True, user=user)

        # add additional details
        if is_auth_return(ok, user=user):
            if opt is None:
                opt = {}
            if ok:
                opt['realm'] = c.audit.get('realm')
                opt['user'] = c.audit.get('user')
                opt['tokentype'] = c.audit.get('token_type')
                opt['serial'] = c.audit.get('serial')
            else:
                opt['error'] = c.audit.get('action_detail')

        return (ok, opt)
Esempio n. 50
0
    def checkOtp(self, passw, counter, window, options=None):
        '''
        checkOtp - standard callback of linotp to verify the token

        :param passw:      the passw / otp, which has to be checked
        :type passw:       string
        :param counter:    the start counter
        :type counter:     int
        :param  window:    the window, in which the token is valid
        :type  window:     int
        :param options:    options contains the transaction id, eg. if check_t
                           checks one transaction this will support
                           assynchreonous otp checks (when check_t is used)
        :type options:     dict

        :return:           verification counter or -1
        :rtype:            int (-1)

        '''

        ret = -1

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        # if we have no transactionid given through the options,
        # we have to retrieve the eldest challenge for this ocra token
        serial = self.getSerial()
        challenges = []

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            defTimeWindow = int(getFromConfig("ocra.timeWindow", 180))
            window = (int(self.getFromTokenInfo('timeWindow', defTimeWindow)) /
                      ocraSuite.T)

            defTimeShift = int(getFromConfig("ocra.timeShift", 0))
            timeShift = int(self.getFromTokenInfo("timeShift", defTimeShift))

        if options is None:
            challenges = OcraTokenClass.getTransactions4serial(
                serial, currentOnly=True)

        elif options is not None:
            if type(options).__name__ != 'dict':
                err = ('[chekOtp] "options" not of type dict! %r' %
                       type(options))
                log.error(err)
                raise Exception(err)

            if 'transactionid' in options:
                transid = options.get('transactionid')
                challenges.append(OcraTokenClass.getTransaction(transid))

            elif 'challenge' in options:
                challenges.append(options)

            # due to the added options in checkUserPass, we have to extend
            # the logic here:
            # if no challenges found in between but we have a serial, we catch
            # the open challenges by serial (s.o.)
            if len(challenges) == 0:
                challenges = OcraTokenClass.getTransactions4serial(
                    serial, currentOnly=True)

        if len(challenges) == 0:
            #  verify that there has already been a challenge
            challenges = OcraTokenClass.getTransactions4serial(serial)
            if len(challenges) > 0:
                err = 'No current transaction found!'
                ret = -1
                return ret
            else:
                err = 'No open transaction found!'
                log.info(err)
                if type(options) == dict and 'transactionid' in options:
                    raise Exception(err)
                ret = -1
                return ret

        for ch in challenges:
            challenge = {}

            if isinstance(ch, dict):
                #  transaction less checkOtp
                self.transId = 0
                challenge.update(ch)

            elif type(ch) == OcraChallenge:
                #  preserve transaction context, so we could use this in
                # the status callback
                self.transId = ch.transid
                challenge['challenge'] = ch.challenge
                challenge['transid'] = ch.transid
                challenge['session'] = ch.session

            ret = ocraSuite.checkOtp(passw,
                                     counter,
                                     window,
                                     challenge,
                                     pin=ocraPin,
                                     options=options,
                                     timeshift=timeShift)

            if ret != -1:
                break

        if -1 == ret:
            #  autosync: test if two consecutive challenges +
            # it's counter match
            ret = self.autosync(ocraSuite, passw, challenge)

        return ret
Esempio n. 51
0
    def checkUserPass(self, user, passw, options=None):
        """
        :param user: the to be identified user
        :param passw: the identifiaction pass
        :param options: optional parameters, which are provided
                    to the token checkOTP / checkPass

        :return: tuple of True/False and optional information
        """

        log.debug('entering function checkUserPass(%r)'
                  % user.login)
        # the upper layer will catch / at least should ;-)

        opt = None
        serial = None
        resolverClass = None
        uid = None
        audit = context['audit']
        user_exists = False

        if user is not None and (user.isEmpty() == False):
            # the upper layer will catch / at least should
            try:
                (uid, _resolver, resolverClass) = getUserId(user)
                user_exists = True
            except:
                pass_on = context.get('Config').get(
                                            'linotp.PassOnUserNotFound', False)
                if pass_on and 'true' == pass_on.lower():
                    audit['action_detail'] = (
                                        'authenticated by PassOnUserNotFound')
                    return (True, opt)
                else:
                    audit['action_detail'] = 'User not found'
                    return (False, opt)

        # if we have an user, check if we forward the request to another server
        if user_exists:
            from linotp.lib.policy import get_auth_forward
            servers = get_auth_forward(user)
            if servers:
                if 'radius://' in servers:
                    rad = RadiusRequest(servers=servers)
                    res, opt = rad.do_request(servers, user, passw, options)
                    return res, opt
                elif 'http://' in servers or 'https://' in servers:
                    http = HttpRequest(servers=servers)
                    res, opt = http.do_request(user, passw, options)
                    return res, opt

        tokenList = linotp.lib.token.getTokens4UserOrSerial(user, serial)

        if len(tokenList) == 0:
            audit['action_detail'] = 'User has no tokens assigned'

            # here we check if we should to autoassign and try to do it
            log.debug('about to check auto_assigning')

            th = TokenHandler()
            auto_assign_return = th.auto_assignToken(passw, user)
            if auto_assign_return is True:
                # We can not check the token, as the OTP value is already used!
                # but we will auth the user....
                return (True, opt)

            auto_enroll_return, opt = th.auto_enrollToken(passw, user,
                                                            options=options)
            if auto_enroll_return is True:
                # we always have to return a false, as
                # we have a challenge tiggered
                return (False, opt)

            pass_on = context.get('Config').get('linotp.PassOnUserNoToken',
                                                         False)
            if pass_on and 'true' == pass_on.lower():
                audit['action_detail'] = 'authenticated by PassOnUserNoToken'
                return (True, opt)

            #  Check if there is an authentication policy passthru
            from linotp.lib.policy import get_auth_passthru
            if get_auth_passthru(user):
                log.debug('user %r has no token. Checking for '
                          'passthru in realm %r' % (user.login, user.realm))
                y = getResolverObject(resolverClass)
                audit['action_detail'] = 'Authenticated against Resolver'
                if y.checkPass(uid, passw):
                    return (True, opt)

            #  Check if there is an authentication policy passOnNoToken
            from linotp.lib.policy import get_auth_passOnNoToken
            if get_auth_passOnNoToken(user):
                log.info('user %r has not token. PassOnNoToken'
                         ' set - authenticated!')
                audit['action_detail'] = (
                    'Authenticated by passOnNoToken policy')
                return (True, opt)

            return (False, opt)

        if passw is None:
            raise ParameterError(u"Missing parameter:pass", id=905)

        (res, opt) = self.checkTokenList(
            tokenList, passw, user, options=options)
        log.debug('return of __checkTokenList: %r ' % (res,))

        return (res, opt)
Esempio n. 52
0
def getResolverInfo(resolvername, passwords=False):
    '''
    return the resolver info of the given resolvername

    :param resolvername: the requested resolver
    :type  resolvername: string

    :return : dict of resolver description
    '''

    result = {"type": None, "data": {}, "resolver": resolvername}

    linotp_config = context.get('Config')
    resolver_types = get_resolver_types()

    # --------------------------------------------------------------------- --

    # lookup, which resolver type is associated with this resolver name

    for config_entry in linotp_config:

        if config_entry.endswith("." + resolvername):

            # check if this is a resolver definition, starting with linotp.
            # and continuing with a resolver type

            part = config_entry.split('.')

            if (len(part) > 3 and part[0] == 'linotp'
                    and part[1] in resolver_types):

                resolver_type = part[1]
                break

    else:
        return result

    # now we can load the resolver config unsing the resolver class

    resolver_cls = get_resolver_class(resolver_type)

    if resolver_cls is None:
        raise Exception("no such resolver type '%r' defined!" % resolver_type)

    res_conf, _missing = resolver_cls.filter_config(linotp_config,
                                                    resolvername)

    # --------------------------------------------------------------------- --

    # now prepare the resolver config output, which should contain
    #
    # - no global entries, starting with 'linotp.'
    # - adjusted passwords
    # - all values as text

    for key in list(res_conf.keys()):

        # suppress global config entries

        if key.startswith("linotp."):
            del res_conf[key]
            continue

        # should passwords be displayed?
        if key in resolver_cls.crypted_parameters:

            # we have to be sure that we only have encrypted data objects for
            # secret data
            if not isinstance(res_conf[key], EncryptedData):
                raise Exception('Encrypted Data Object expected')

            # if parameter password is True, we need to unencrypt
            if passwords:
                res_conf[key] = res_conf[key].get_unencrypted()

        # as we have in the resolver config typed values, this might
        # lead to some trouble. so we prepare for output comparison
        # the string representation

        if not isinstance(res_conf[key], str):
            res_conf[key] = "%r" % res_conf[key]

    if 'readonly' in res_conf:
        readonly = False
        try:
            readonly = boolean(res_conf['readonly'])
        except Exception:
            log.info("Failed to convert 'readonly' attribute %r:%r",
                     resolvername, res_conf['readonly'])

        if readonly:
            result["readonly"] = True

    result["type"] = resolver_type
    result["data"] = res_conf

    return result
Esempio n. 53
0
def getResolverList(filter_resolver_type=None, config=None):
    """
    Gets the list of configured resolvers

    :param filter_resolver_type: Only resolvers of the given type are returned
    :type filter_resolver_type: string
    :rtype: Dictionary of the resolvers and their configuration
    """
    Resolvers = {}
    resolvertypes = get_resolver_types()

    local_admin_resolver = current_app.config["ADMIN_RESOLVER_NAME"]

    admin_resolvers = get_admin_resolvers()

    if not config:
        conf = context.get("Config")
    else:
        conf = config

    for entry in conf:

        for typ in resolvertypes:
            if entry.startswith("linotp." + typ):
                # the resolver might contain dots "." so take
                # all after the 3rd dot for the resolver name
                r = {}
                resolver = entry.split(".", 3)

                # An old entry without resolver name
                if len(resolver) <= 3:
                    break
                r["resolvername"] = resolver[3]
                r["entry"] = entry
                r["type"] = typ

                # return the resolver spec, which is required to define a realm
                resolver_cls = get_resolver_class(typ)
                r["spec"] = resolver_cls.db_prefix + "." + resolver[3]
                r["admin"] = resolver[3] in admin_resolvers

                # set the immutable flag if its the local_admin_resolver
                r["immutable"] = local_admin_resolver == resolver[3]

                readonly_entry = ".".join(
                    [resolver[0], resolver[1], "readonly", resolver[3]]
                )

                if readonly_entry in conf:
                    readonly = False
                    try:
                        readonly = boolean(conf[readonly_entry])
                    except Exception as _exx:
                        log.info(
                            "Failed to convert 'readonly' attribute %r:%r",
                            readonly_entry,
                            conf[readonly_entry],
                        )

                    if readonly:
                        r["readonly"] = True
                #
                # this is a patch for a hack:
                #
                # as entry, the first found resolver is shown
                # as the PasswdResolver only has one entry, this always
                # has been 'fileName', which now as could be 'readonly'
                # thus we skip the readonly entry:

                key = resolver[2]
                if key == "readonly":
                    continue

                if (filter_resolver_type is None) or (
                    filter_resolver_type and filter_resolver_type == typ
                ):
                    Resolvers[resolver[3]] = r
                # Dont check the other resolver types
                break

    return Resolvers
Esempio n. 54
0
    def resync(self, otp1, otp2, options=None):
        '''
        - for the resync to work, we take the last two transactions and
          their challenges
        - for each challenge, we search forward the sync window length

        '''

        ret = False
        challenges = []

        o_challenges = OcraTokenClass.getTransactions4serial(self.getSerial())
        for challenge in o_challenges:
            challenges.append(challenge)

        #  check if there are enough challenges around
        if len(challenges) < 2:
            return False

        challenge1 = {}
        challenge2 = {}

        if options is None:
            ch1 = challenges[0]
            challenge1['challenge'] = ch1.challenge
            challenge1['transid'] = ch1.transid
            challenge1['session'] = ch1.session

            ch2 = challenges[1]
            challenge2['challenge'] = ch2.challenge
            challenge2['transid'] = ch2.transid
            challenge2['session'] = ch2.session

        else:
            if 'challenge1' in options:
                challenge1['challenge'] = options.get('challenge1')
            if 'challenge2' in options:
                challenge2['challenge'] = options.get('challenge2')

        if len(challenge1) == 0 or len(challenge2) == 0:
            error = "No challenges found!"
            log.info('[OcraTokenClass:resync] %s' % (error))
            raise Exception('[OcraTokenClass:resync] %s' % (error))

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        syncWindow = self.token.getSyncWindow()
        if ocraSuite.T is not None:
            syncWindow = syncWindow / 10

        counter = self.token.getOtpCounter()

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            timeShift = int(self.getFromTokenInfo("timeShift", 0))

        try:

            count_1 = ocraSuite.checkOtp(otp1,
                                         counter,
                                         syncWindow,
                                         challenge1,
                                         pin=ocraPin,
                                         timeshift=timeShift)
            if count_1 == -1:
                log.info('Ocra resync: lookup for first otp value failed!')
                ret = False
            else:
                count_2 = ocraSuite.checkOtp(otp2,
                                             counter,
                                             syncWindow,
                                             challenge2,
                                             pin=ocraPin,
                                             timeshift=timeShift)
                if count_2 == -1:
                    log.info(
                        'Ocra resync: lookup for second otp value failed!')
                    ret = False
                else:
                    if ocraSuite.C is not None:
                        if count_1 + 1 == count_2:
                            self.setOtpCount(count_2)
                            ret = True

                    if ocraSuite.T is not None:
                        if count_1 - count_2 <= ocraSuite.T * 2:
                            #  callculate the timeshift
                            date = datetime.datetime.fromtimestamp(count_2)
                            log.info('Ocra resync: Syncing token to new '
                                     'timestamp %r' % (date))

                            now = datetime.datetime.now()
                            stime = now.strftime("%s")
                            timeShift = count_2 - int(stime)
                            self.addToTokenInfo('timeShift', timeShift)
                            ret = True

        except Exception as ex:
            log.exception('[OcraTokenClass:resync] unknown error: %r' % (ex))
            raise Exception('[OcraTokenClass:resync] unknown error: %s' % (ex))

        return ret
Esempio n. 55
0
def check_pin(token, passw, user=None, options=None):
    '''
    check the provided pin w.r.t. the policy definition

    :param passw: the to be checked pass
    :param user: if otppin==1, this is the user, which resolver should
                 be checked
    :param options: the optional request parameters

    :return: boolean, if pin matched True
    '''
    res = False

    otppin_mode = _get_otppin_mode(get_pin_policies(user))

    if 1 == otppin_mode:
        # We check the Users Password as PIN
        log.debug("pin policy=1: checking the users password as pin")
        # this should not be the case
        if not options:
            options = {}

        selfservice_state = context.get('selfservice', {}).get('state', '')
        if selfservice_state in ['credentials_verified', 'challenge_triggered']:
            return True

        if 'pin_match' not in options:
            options['pin_match'] = {}

        hashed_passw = sha256(passw.encode('utf-8')).hexdigest()

        # if password already found, we can return result again
        if hashed_passw in options['pin_match']:
            log.debug("check if password already checked! %r " %
                      options['pin_match'][hashed_passw])
            return options['pin_match'][hashed_passw]

        # if a password already matched, this one will fail
        if 'found' in options['pin_match']:
            log.debug("check if password already found but its not this one!")
            return False

        if user is None or not user.login:
            log.info("fail for pin policy == 1 with user = None")
            res = False
        else:
            (uid, _resolver, resolver_class) = getUserId(user)
            resolver = getResolverObject(resolver_class)
            if resolver.checkPass(uid, passw):
                log.debug("Successfully authenticated user %r." % uid)
                res = True
            else:
                log.info("user %r failed to authenticate." % uid)

        # we register our result
        key = sha256(passw.encode('utf-8')).hexdigest()
        options['pin_match'][key] = res
        # and register the success, to shorten lookups after
        # already one positive was found
        if res is True:
            options['pin_match']['found'] = True

        return res

    elif otppin_mode == 2:
        # NO PIN should be entered atall
        log.debug("[__checkToken] pin policy=2: checking no pin")
        return len(passw) == 0

    elif otppin_mode == 3:
        # ignore pin or password

        log.debug("[__checkToken] pin policy=3: ignoreing pin")

        if token.type in ['spass']:
            return token.checkPin(passw, options=options)

        return True

    else:
        # old stuff: We check The fixed OTP PIN
        log.debug("[__checkToken] pin policy=0: checkin the PIN")
        return token.checkPin(passw, options=options)
Esempio n. 56
0
    def _check(self, param):
        '''
        basic check function, that can be used by different controllers

        :param param: dict of all caller parameters
        :type param: dict

        :return: Tuple of True or False and opt
        :rtype: Tuple(boolean, opt)

        '''
        opt = None

        options = {}

        ## put everythin in the options but the user, pass, init
        options.update(param)
        for para in ["pass", "user", "init"]:
            if options.has_key(para):
                del options[para]

        passw = getParam(param, "pass", optional)
        user = getUserFromParam(param, optional)

        # support for ocra application challenge verification
        challenge = getParam(param, "challenge", optional)
        if challenge is not None:
            options = {}
            options['challenge'] = challenge

        c.audit['user'] = user.login
        realm = user.realm or getDefaultRealm()
        c.audit['realm'] = realm

        # AUTHORIZATION Pre Check
        # we need to overwrite the user.realm in case the user does not exist in the original realm (setrealm-policy)
        user.realm = set_realm(user.login, realm, exception=True)
        check_user_authorization(user.login, user.realm, exception=True)

        if isSelfTest() is True:
            initTime = getParam(param, "init", optional)
            if initTime is not None:
                if options is None:
                    options = {}
                options['initTime'] = initTime
        vh = ValidationHandler()
        (ok, opt) = vh.checkUserPass(user, passw, options=options)

        c.audit.update(request_context.get('audit'))
        c.audit['success'] = ok

        if ok:
            # AUTHORIZATION post check
            check_auth_tokentype(c.audit['serial'], exception=True, user=user)
            check_auth_serial(c.audit['serial'], exception=True, user=user)

        # add additional details
        if is_auth_return(ok, user=user):
            if opt is None:
                opt = {}
            if ok:
                opt['realm'] = c.audit.get('realm')
                opt['user'] = c.audit.get('user')
                opt['tokentype'] = c.audit.get('token_type')
                opt['serial'] = c.audit.get('serial')
            else:
                opt['error'] = c.audit.get('action_detail')

        return (ok, opt)
Esempio n. 57
0
def getResolverList(filter_resolver_type=None, config=None):
    '''
    Gets the list of configured resolvers

    :param filter_resolver_type: Only resolvers of the given type are returned
    :type filter_resolver_type: string
    :rtype: Dictionary of the resolvers and their configuration
    '''
    Resolvers = {}
    resolvertypes = get_resolver_types()

    if not config:
        conf = context.get('Config')
    else:
        conf = config

    for entry in conf:

        for typ in resolvertypes:
            if entry.startswith("linotp." + typ):
                # the realm might contain dots "."
                # so take all after the 3rd dot for realm
                r = {}
                resolver = entry.split(".", 3)

                # An old entry without resolver name
                if len(resolver) <= 3:
                    break
                r["resolvername"] = resolver[3]
                r["entry"] = entry
                r["type"] = typ

                readonly_entry = '.'.join(
                    [resolver[0], resolver[1], 'readonly', resolver[3]])

                if readonly_entry in conf:
                    readonly = False
                    try:
                        readonly = boolean(conf[readonly_entry])
                    except Exception as _exx:
                        log.info(
                            "Failed to convert 'readonly' attribute"
                            " %r:%r", readonly_entry, conf[readonly_entry])

                    if readonly:
                        r["readonly"] = True
                #
                # this is a patch for a hack:
                #
                # as entry, the first found resolver is shown
                # as the PasswdResolver only has one entry, this always
                # has been 'fileName', which now as could be 'readonly'
                # thus we skip the readonly entry:

                key = resolver[2]
                if key == "readonly":
                    continue

                if ((filter_resolver_type is None) or
                    (filter_resolver_type and filter_resolver_type == typ)):
                    Resolvers[resolver[3]] = r
                # Dont check the other resolver types
                break

    return Resolvers
Esempio n. 58
0
    def resync(self, otp1, otp2, options=None):
        '''
        - for the resync to work, we take the last two transactions and
          their challenges
        - for each challenge, we search forward the sync window length

        '''

        ret = False
        challenges = []

        o_challenges = OcraTokenClass.getTransactions4serial(self.getSerial())
        for challenge in o_challenges:
            challenges.append(challenge)

        #  check if there are enough challenges around
        if len(challenges) < 2:
            return False

        challenge1 = {}
        challenge2 = {}

        if options is None:
            ch1 = challenges[0]
            challenge1['challenge'] = ch1.challenge
            challenge1['transid'] = ch1.transid
            challenge1['session'] = ch1.session

            ch2 = challenges[1]
            challenge2['challenge'] = ch2.challenge
            challenge2['transid'] = ch2.transid
            challenge2['session'] = ch2.session

        else:
            if 'challenge1' in options:
                challenge1['challenge'] = options.get('challenge1')
            if 'challenge2' in options:
                challenge2['challenge'] = options.get('challenge2')

        if len(challenge1) == 0 or len(challenge2) == 0:
            error = "No challenges found!"
            log.info('[OcraTokenClass:resync] %s' % (error))
            raise Exception('[OcraTokenClass:resync] %s' % (error))

        secObj = self._get_secret_object()
        ocraSuite = OcraSuite(self.getOcraSuiteSuite(), secObj)

        syncWindow = self.token.getSyncWindow()
        if ocraSuite.T is not None:
            syncWindow = syncWindow / 10

        counter = self.token.getOtpCounter()

        # set the ocra token pin
        ocraPin = ''
        if ocraSuite.P is not None:
            key, iv = self.token.getUserPin()
            secObj = SecretObj(key, iv, hsm=context.get('hsm'))
            ocraPin = secObj.getKey()

            if ocraPin is None or len(ocraPin) == 0:
                ocraPin = ''

        timeShift = 0
        if ocraSuite.T is not None:
            timeShift = int(self.getFromTokenInfo("timeShift", 0))

        try:

            count_1 = ocraSuite.checkOtp(otp1, counter, syncWindow,
                                         challenge1, pin=ocraPin,
                                         timeshift=timeShift)
            if count_1 == -1:
                log.info('Ocra resync: lookup for first otp value failed!')
                ret = False
            else:
                count_2 = ocraSuite.checkOtp(otp2, counter, syncWindow,
                                             challenge2, pin=ocraPin,
                                             timeshift=timeShift)
                if count_2 == -1:
                    log.info('Ocra resync: lookup for second otp value failed!')
                    ret = False
                else:
                    if ocraSuite.C is not None:
                        if count_1 + 1 == count_2:
                            self.setOtpCount(count_2)
                            ret = True

                    if ocraSuite.T is not None:
                        if count_1 - count_2 <= ocraSuite.T * 2:
                            #  callculate the timeshift
                            date = datetime.datetime.fromtimestamp(count_2)
                            log.info('Ocra resync: Syncing token to new '
                                     'timestamp %r' % (date))

                            now = datetime.datetime.now()
                            stime = now.strftime("%s")
                            timeShift = count_2 - int(stime)
                            self.addToTokenInfo('timeShift', timeShift)
                            ret = True

        except Exception as ex:
            log.exception('[OcraTokenClass:resync] unknown error: %r' % (ex))
            raise Exception('[OcraTokenClass:resync] unknown error: %s' % (ex))

        return ret
Esempio n. 59
0
    def __init__(self,
                 page=None,
                 psize=None,
                 sort=None,
                 sortdir=None,
                 realms=None,
                 status=None,
                 date=None):
        """
        constructor of Tokeniterator, which gathers all conditions to build
        a sqalchemy query - iterator

        :param page:     page number
        :type  page:     int
        :param psize:    how many entries per page
        :type  psize:    int
        :param sort:     sort field definition
        :type  sort:     string
        :param sortdir:  sort direction: ascending or descending
        :type  sortdir:  string
        :param realms:   reports from which realms will be shown
        :type realms:    list
        :param status:   filter reports by status like active, unassigned
        :type status:    list
        :param date:     only show entries newer than date
        :type date:      strin gin format 'yyyy-mm-dd'

        :return: - nothing / None
        """
        self.page = 1
        self.pages = 1
        if not isinstance(realms, (list, tuple)):
            realms = realms.split(',')
        if not isinstance(status, (list, tuple)):
            status = status.split(',')

        realm_cond = tuple()
        for realm in realms:
            realm_cond += (or_(Reporting.realm == realm), )

        status_cond = tuple()
        for stat in status:
            status_cond += (or_(Reporting.parameter == stat), )

        date_cond = tuple()
        if date:
            date_cond += (and_(Reporting.timestamp >= date), )

        conds = (
            and_(*date_cond),
            or_(*realm_cond),
            or_(*status_cond),
        )

        if sort is None:
            order = Reporting.timestamp
        elif sort == 'event':
            order = Reporting.event
        elif sort == 'realm':
            order = Reporting.realm
        elif sort == 'parameter':
            order = Reporting.parameter
        elif sort == 'value':
            order = Reporting.value
        elif sort == 'count':
            order = Reporting.count
        elif sort == 'detail':
            order = Reporting.detail
        elif sort == 'description':
            order = Reporting.description
        elif sort == 'session':
            order = Reporting.session
        else:
            order = Reporting.timestamp

        #  care for the result sort order
        if sortdir is not None and sortdir == "desc":
            order = order.desc()
        else:
            order = order.asc()

        # query database for all reports
        self.reports = Session.query(Reporting).filter(
            *conds).order_by(order).distinct()
        self.report_num = self.reports.count()
        self.pagesize = self.report_num

        #  care for the result pageing
        if page is not None:
            try:
                if psize is None:
                    pagesize = \
                        int(request_context.get('Config').get('pagesize', 50))
                else:
                    pagesize = int(psize)
            except Exception as exce:
                log.debug('Reporting: Problem with pagesize detected. '
                          'Exception was: %r' % exce)
                pagesize = 20

            try:
                the_page = int(page) - 1
            except Exception as exce:
                log.debug('Reporting: Problem with page detected. '
                          'Exception was %r' % exce)
                the_page = 0

            if the_page < 0:
                the_page = 0

            start = the_page * pagesize
            stop = (the_page + 1) * pagesize

            self.page = the_page + 1
            fpages = float(self.report_num) / float(pagesize)
            self.pages = int(fpages)
            if fpages - self.pages > 0:
                self.pages += 1
            self.pagesize = pagesize
            self.reports = self.reports.slice(start, stop)
Esempio n. 60
0
def getResolverInfo(resolvername, passwords=False):
    '''
    return the resolver info of the given resolvername

    :param resolvername: the requested resolver
    :type  resolvername: string

    :return : dict of resolver description
    '''

    result = {"type": None, "data": {}, "resolver": resolvername}

    resolver_dict = {}
    descr = {}

    resolver_entries = {}
    resolvertypes = get_resolver_types()

    linotp_config = context.get('Config')

    for typ in resolvertypes:
        for config_entry in linotp_config:
            if (config_entry.startswith("linotp." + typ)
                    and config_entry.endswith(resolvername)):
                resolver_entries[config_entry] = linotp_config.get(
                    config_entry)

    if not resolver_entries:
        return result

    resolver_parts = resolver_entries.keys()[0].split('.')

    #
    # TODO: remove legacy code: An old entry without resolver name
    #
    if len(resolver_parts) <= 3:
        return result

    #
    # get the type descriptions for the resolver type
    #

    resolver_type = resolver_parts[1]
    resolver_conf = get_resolver_class_config(resolver_type)

    if resolver_type in resolver_conf:
        resolver_descr = resolver_conf.get(resolver_type).get('config', {})
    else:
        resolver_descr = resolver_conf

    #
    # build up the resolver dictionary
    #

    for key, value in resolver_entries.items():
        resolver_key = key.split(".")[2]

        if resolver_key in resolver_descr:

            if (resolver_descr.get(resolver_key) == 'password'
                    and passwords is True):

                # do we already have the decrypted pass?
                if 'enc%s' % key in linotp_config:
                    value = linotp_config.get('enc%s' % key)
                else:
                    # if no, we take the entry and try to de crypt it
                    value = linotp_config.get(key)

                    try:
                        value = decryptPassword(value)
                    except Exception as exc:
                        log.exception(
                            "Decryption of resolver entry "
                            "failed: %r", exc)

        resolver_dict[resolver_key] = value

    result["type"] = resolver_type
    result["data"] = resolver_dict

    return result