def authenticate(self, passw, user, options=None): """ This is the method that verifies single shot authentication like they are done with push button tokens. It is a high level interface to support as well other tokens, which do not have a pin and otp seperation - they could overwrite this method **remarks:** we have to call the global methods (check_pin,++) as they take the pin policies into account :param passw: the passw which could be pin+otp :type passw: string :param user: The authenticating user :type user: User object :param options: dictionary of additional request parameters :type options: (dict) :return: returns tuple true or false for the pin match, the otpcounter (int) and the reply (dict) that will be added as additional information in the JSON response of ``/validate/check``. """ pin_match = False otp_counter = -1 reply = None (res, pin, otpval) = split_pin_otp(self, passw, user, options=options) if res != -1: pin_policies = get_pin_policies(user) if 1 in pin_policies: otp_counter = check_otp(self, otpval, options=options) if otp_counter >= 0: pin_match = check_pin(self, pin, user=user, options=options) if not pin_match: otp_counter = -1 else: pin_match = check_pin(self, pin, user=user, options=options) if pin_match is True: otp_counter = check_otp(self, otpval, options=options) # for special token that have no otp like passwordtoken if not self.auth_info and pin_match is True and otp_counter == 0: self.auth_info = {"auth_info": [("pin_length", len(passw))]} return (pin_match, otp_counter, reply)
def authenticate(self, passw, user, options=None): ''' This is the method that verifies single shot authentication like they are done with push button tokens. It is a high level interface to support as well other tokens, which do not have a pin and otp seperation - they could overwrite this method **remarks:** we have to call the global methods (check_pin,++) as they take the pin policies into account :param passw: the passw which could be pin+otp :type passw: string :param user: The authenticating user :type user: User object :param options: dictionary of additional request parameters :type options: (dict) :return: returns tuple true or false for the pin match, the otpcounter (int) and the reply (dict) that will be added as additional information in the JSON response of ``/validate/check``. ''' pin_match = False otp_counter = -1 reply = None (res, pin, otpval) = split_pin_otp(self, passw, user, options=options) if res != -1: pin_policies = get_pin_policies(user) if 1 in pin_policies: otp_counter = check_otp(self, otpval, options=options) if otp_counter >= 0: pin_match = check_pin( self, pin, user=user, options=options) if not pin_match: otp_counter = -1 else: pin_match = check_pin(self, pin, user=user, options=options) if pin_match is True: otp_counter = check_otp(self, otpval, options=options) # for special token that have no otp like passwordtoken if not self.auth_info and pin_match is True and otp_counter == 0: self.auth_info = {'auth_info': [('pin_length', len(passw))]} return (pin_match, otp_counter, reply)
def split_pin_otp(token, passw, user=None, options=None): """ split the pin and the otp fron the given password :param token: the corresponding token :param passw: the to be splitted password :param user: the tokenuser :param options: currently not used, but might be forwarded to the token.splitPinPass :return: tuple of (split status, pin and otpval) """ pin_policies = get_pin_policies(user) policy = 0 if 0 in pin_policies or "token_pin" in pin_policies: # old stuff: We check The fixed OTP PIN log.debug('pin policy=0: checkin the PIN') (pin, otp) = token.splitPinPass(passw) elif 1 in pin_policies or "password" in pin_policies: log.debug('pin policy=1: checking the users password as pin') # split the passw into password and otp value (pin, otp) = token.splitPinPass(passw) policy = 1 elif 2 in pin_policies or "only_otp" in pin_policies: # NO PIN should be entered at all log.debug('pin policy=2: checking no pin') (pin, otp) = ('', passw) token.auth_info = { 'auth_info': [('pin_length', 0), ('otp_length', len(passw))] } policy = 2 else: # old stuff: We check The fixed OTP PIN log.debug('pin policy=0: checkin the PIN') (pin, otp) = token.splitPinPass(passw) res = policy return (res, pin, otp)
def split_pin_otp(token, passw, user=None, options=None): """ split the pin and the otp from the given password :param token: the corresponding token :param passw: the to be split password :param user: the token user :param options: currently not used, but might be forwarded to the token.splitPinPass :return: tuple of (split status, pin and otpval) """ otppin_mode = _get_otppin_mode(get_pin_policies(user)) if 0 == otppin_mode: # old stuff: We check The fixed OTP PIN log.debug('pin policy=0: checking the PIN') (pin, otp) = token.splitPinPass(passw) return 0, pin, otp elif 1 == otppin_mode: log.debug('pin policy=1: checking the users password as pin') # split the passw into password and otp value (pin, otp) = token.splitPinPass(passw) return 1, pin, otp elif 2 == otppin_mode: # NO PIN should be entered at all log.debug('pin policy=2: checking no pin') (pin, otp) = ('', passw) token.auth_info = { 'auth_info': [('pin_length', 0), ('otp_length', len(passw))] } return 2, pin, otp elif 3 == otppin_mode: # no pin should be checked log.debug('pin policy=3: ignoring the pin') (pin, otp) = token.splitPinPass(passw) return 3, pin, otp
def split_pin_otp(token, passw, user=None, options=None): """ split the pin and the otp from the given password :param token: the corresponding token :param passw: the to be split password :param user: the token user :param options: currently not used, but might be forwarded to the token.splitPinPass :return: tuple of (split status, pin and otpval) """ otppin_mode = _get_otppin_mode(get_pin_policies(user)) if 0 == otppin_mode: # old stuff: We check The fixed OTP PIN log.debug('pin policy=0: checking the PIN') (pin, otp) = token.splitPinPass(passw) return 0, pin, otp elif 1 == otppin_mode: log.debug('pin policy=1: checking the users password as pin') # split the passw into password and otp value (pin, otp) = token.splitPinPass(passw) return 1, pin, otp elif 2 == otppin_mode: # NO PIN should be entered at all log.debug('pin policy=2: checking no pin') (pin, otp) = ('', passw) token.auth_info = {'auth_info': [('pin_length', 0), ('otp_length', len(passw))]} return 2, pin, otp elif 3 == otppin_mode: # no pin should be checked log.debug('pin policy=3: ignoring the pin') (pin, otp) = token.splitPinPass(passw) return 3, pin, otp
def finish_invalid_tokens(self): """""" invalid_tokens = self.invalid_tokens user = self.user for tok in invalid_tokens: # count all token accesses if tok.count_auth_max > 0: tok.inc_count_auth() tok.statusValidationFail() Challenges.finish_challenges(tok, success=False) pin_policies = get_pin_policies(user) or [] if 1 in pin_policies: action_detail = "wrong user password -1" else: action_detail = "wrong otp pin -1" return (False, None, action_detail)
def finish_invalid_tokens(self): """ """ invalid_tokens = self.invalid_tokens user = self.user for tok in invalid_tokens: # count all token accesses if tok.count_auth_max > 0: tok.inc_count_auth() tok.statusValidationFail() Challenges.finish_challenges(tok, success=False) pin_policies = get_pin_policies(user) or [] if 1 in pin_policies: action_detail = "wrong user password -1" else: action_detail = "wrong otp pin -1" return (False, None, action_detail)
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)
def check_status(self, transid=None, user=None, serial=None, password=None, use_offline=False): """ check for open transactions - for polling support :param transid: the transaction id where we request the status from :param user: the token owner user :param serial: or the serial we are searching for :param password: the pin/password for authorization the request :param use_offline: on success the offline info is returned :return: tuple of success and detail dict """ expired, challenges = Challenges.get_challenges(None, transid=transid) # remove all expired challenges if expired: Challenges.delete_challenges(None, expired) if not challenges: return False, None # there is only one challenge per transaction id # if not multiple challenges, where transaction id is the parent one reply = {} pin_policies = get_pin_policies(user) if 1 in pin_policies: pin_match = check_pin(None, password, user=user, options=None) if not pin_match: return False, None involved_tokens = [] transactions = {} for ch in challenges: # only look for challenges that are not compromised if not Challenges.verify_checksum(ch): continue # is the requester authorized serial = ch.getTokenSerial() tokens = getTokens4UserOrSerial(serial=serial) if not tokens: continue involved_tokens.extend(tokens) # as one challenge belongs exactly to only one token, # we take this one as the token token = tokens[0] if 1 not in pin_policies: pin_match = check_pin(token, password, user=user, options=None) if not pin_match: ret = False continue ret = True trans_dict = {} trans_dict['received_count'] = ch.received_count trans_dict['received_tan'] = ch.received_tan trans_dict['valid_tan'] = ch.valid_tan trans_dict['message'] = ch.challenge trans_dict['status'] = ch.getStatus() token_dict = {'serial': serial, 'type': token.type} # 1. check if token supports offline at all supports_offline_at_all = token.supports_offline_mode # 2. check if policy allows to use offline authentication if user is not None and user.login and user.realm: realms = [user.realm] else: realms = token.getRealms() offline_is_allowed = supports_offline(realms, token) if not ch.is_open() and ch.valid_tan and \ supports_offline_at_all and \ offline_is_allowed and \ use_offline: token_dict['offline_info'] = token.getOfflineInfo() trans_dict['token'] = token_dict transactions[ch.transid] = trans_dict if transactions: reply['transactions'] = transactions return ret, reply
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 = {} 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)