Example #1
0
    def authenticate(self, passw, user, options=None):
        """
        do the authentication on base of password / otp and user and
        options, the request parameters.

        Here we contact the other LinOTP server to validate the OtpVal.

        :param passw: the password / otp
        :param user: the requesting user
        :param options: the additional request parameters

        :return: tupple of (success, otp_count - 0 or -1, reply)

        """
        log.debug("authenticate")

        res = False
        otp_counter = -1
        reply = None

        otpval = passw

        ## should we check the pin localy??
        if self.check_pin_local():
            (res, pin, otpval) = split_pin_otp(self, passw, user,
                                               options=options)

            res = check_pin(self, pin, user=user, options=options)
            if res is False:
                return (res, otp_counter, reply)

        (res, otp_count, reply) = self.do_request(otpval, user=user)

        return (res, otp_count, reply)
Example #2
0
    def is_challenge_request(self, passw, user, options=None):
        """
        This method checks, if this is a request, that triggers a challenge.
        It depends on the way, the pin is checked - either locally or remote

        :param passw: password, which might be pin or pin+otp
        :type passw: string
        :param user: The user from the authentication request
        :type user: User object
        :param options: dictionary of additional request parameters
        :type options: dict

        :return: true or false
        """

        request_is_valid = False

        if self.check_pin_local():
            pin_match = check_pin(self, passw, user=user, options=options)
            if pin_match is True:
                request_is_valid = True

        elif self.isRemoteChallengeRequest:
            request_is_valid = True

        return request_is_valid
Example #3
0
    def is_challenge_request(self, passw, user, options=None):
        '''
        check, if the request would start a challenge

        - default: if the passw contains only the pin, this request would
        trigger a challenge

        - in this place as well the policy for a token is checked

        :param passw: password, which might be pin or pin+otp
        :param options: dictionary of additional request parameters

        :retrun: returns true or false
        '''

        request_is_valid = False
        # # do we need to call the
        # (res, pin, otpval) = split_pin_otp(self, passw, user, options=options)
        realms = self.token.getRealmNames()
        if trigger_sms(realms, context=self.context):
            if 'check_s' in options.get('scope', {}) and 'challenge' in options:
                request_is_valid = True
                return request_is_valid

        pin_match = check_pin(self, passw, user=user, options=options)
        if pin_match is True:
            request_is_valid = True

        return request_is_valid
Example #4
0
    def is_challenge_response(self, passw, user, options=None, challenges=None):
        '''
        check, if the request contains the result of a challenge

        :param passw: password, which might be pin or pin+otp
        :param user: the requesting user
        :param options: dictionary of additional request parameters

        :return: returns true or false
        '''

        challenge_response = False

        if "state" in options or "transactionid" in options:
            challenge_response = True

        # # it as well might be a challenge response,
        # # if the passw is longer than the pin
        if challenge_response == False:
            (res, pin, otpval) = split_pin_otp(self, passw, user=user,
                                                            options=options)
            if res >= 0:
                res = check_pin(self, pin, user=user, options=options)
                if res == True and len(otpval) > 0:
                    challenge_response = True

        return challenge_response
Example #5
0
    def update(self, param, reset_failcount=False):
        self.setSyncWindow(0)
        self.setOtpLen(32)
        self.setCounterWindow(0)

        tdesc = getParam(param, "description", optional)
        if tdesc is not None:
            self.token.setDescription(tdesc)

        # requested_phase must be either "registration1" or "registration2"
        # current_phase is either "registration" or "authentication"
        requested_phase = getParam(param, "phase", optional)
        current_phase = self.getFromTokenInfo("phase", None)

        if requested_phase == "registration1" and current_phase is None:
            # This initial registration phase triggers a challenge
            # which is sent to the FIDO U2F compatible client device

            # Set the optional token pin in this first phase
            pin = getParam(param, "pin", optional)
            if pin is not None:
                TokenClass.setPin(self, pin)

            # preserve the registration state
            self.addToTokenInfo("phase", "registration")
            self.token.LinOtpIsactive = False
        elif requested_phase == "registration2" and current_phase == "registration":
            # Check the token pin
            pin = getParam(param, "pin", optional)
            if pin is None:
                pin = ""
            if check_pin(self, pin) is False:
                log.error("Wrong token pin!")
                raise ValueError("Wrong token pin!")
        # check for set phases which are not "registration1" or "registration2"
        elif requested_phase != "registration2" and requested_phase is not None:
            log.error("Wrong phase parameter!")
            raise Exception("Wrong phase parameter!")
        # only allow empty phase parameters once the token is registered successfully
        elif current_phase != "authentication" and requested_phase is None:
            log.error("Wrong phase parameter!")
            raise Exception("Wrong phase parameter!")
        # only allow "registration2" if the token already completed "registration1"
        elif current_phase != "registration" and requested_phase == "registration2":
            log.error(
                "Phase 'registration2' requested but we are not in the correct phase \
                to process the request."
            )
            raise Exception(
                "Phase 'registration2' requested but we are not in the correct phase \
                to process the request."
            )
        else:
            log.error('Unknown "phase" and "current_phase" parameter combination!')
            raise Exception('Unknown "phase" and "current_phase" parameter combination!')
Example #6
0
    def checkResponse4Challenge(self, user, passw, options=None, challenges=None):
        """
        This method verifies if the given ``passw`` matches any existing ``challenge``
        of the token.

        It then returns the new otp_counter of the token and the
        list of the matching challenges.

        In case of success the otp_counter needs to be > 0.
        The matching_challenges is passed to the method
        :py:meth:`~linotp.lib.tokenclass.TokenClass.challenge_janitor`
        to clean up challenges.

        :param user: the requesting user
        :type user: User object
        :param passw: the password (pin+otp)
        :type passw: string
        :param options:  additional arguments from the request, which could be token specific
        :type options: dict
        :param challenges: A sorted list of valid challenges for this token.
        :type challenges: list
        :return: tuple of (otpcounter and the list of matching challenges)
        """
        otp_counter = -1
        transid = None
        matching = None
        matching_challenges = []

        # fetch the transactionid
        if "transactionid" in options:
            transid = options.get("transactionid", None)

        # check if the transactionid is in the list of challenges
        if transid is not None:
            for challenge in challenges:
                if Challenges.is_same_transaction(challenge, transid):
                    matching = challenge
                    break
            if matching is not None:
                # Split pin from otp and check the resulting pin and otpval
                (pin, otpval) = self.splitPinPass(passw)
                if not check_pin(self, pin, user=user, options=options):
                    otpval = passw
                # The U2F checkOtp functions needs to know the saved challenge
                # to compare the received challenge value to the saved one,
                # thus we add the transactionid to the options
                options["transactionid"] = transid
                options["challenges"] = challenges
                otp_counter = check_otp(self, otpval, options=options)
                if otp_counter >= 0:
                    matching_challenges.append(matching)

        return (otp_counter, matching_challenges)
Example #7
0
    def is_challenge_request(self, passw, user, options=None):
        """
        check if the request would start a challenge

        - default: if the passw contains only the pin, this request would
        trigger a challenge

        - in this place as well the policy for a token is checked

        :param passw: password, which might be pin or pin+otp
        :param options: dictionary of additional request parameters

        :return: returns true or false
        """
        return check_pin(self, passw, user=user, options=options)
Example #8
0
    def checkResponse4Challenge(self, user, passw, options=None, challenges=None):
        """
        verify the response of a previous challenge

        :param user:     the requesting user
        :param passw:    the to be checked pass (pin+otp)
        :param options:  options an additional argument, which could be token
                          specific
        :param challenges: the list of challenges, where each challenge is
                            described as dict
        :return: tuple of (otpcounter and the list of matching challenges)

        do the standard check for the response of the challenge +
        change the tokeninfo data of the last challenge
        """
        otp_count = -1
        matching = []

        tok = super(SmsTokenClass, self)
        counter = self.getOtpCount()
        window = self.getOtpCountWindow()

        now = datetime.datetime.now()
        timeScope = self.loadLinOtpSMSValidTime()

        otp_val = passw

        # # fallback: do we have pin+otp ??
        (res, pin, otp) = split_pin_otp(self, passw, user=user,
                                                            options=options)

        if res >= 0:
            res = check_pin(self, pin, user=user, options=options)
            if res == True:
                otp_val = otp

        for challenge in challenges:
            otp_count = self.checkOtp(otp_val, counter, window,
                                                            options=options)
            if otp_count > 0:
                matching.append(challenge)
                break

        return (otp_count, matching)
Example #9
0
    def is_challenge_request(self, passw, user, options=None):
        '''
        This method checks, if this is a request, that triggers a challenge.

        :param passw: password, which might be pin or pin+otp
        :type passw: string
        :param user: The user from the authentication request
        :type user: User object
        :param options: dictionary of additional request parameters
        :type options: dict

        :return: true or false
        '''

        request_is_valid = False

        pin_match = check_pin(self, passw, user=user, options=options)
        if pin_match is True:
            request_is_valid = True

        return request_is_valid