Example #1
0
def _lookup_provider_policies(provider_type):
    """
    helper, to prevent deleting a provider while it is still used in a policy

    :param provider_type: the type of provider: sms or email
    :return: a dictionary with provider names as key and list of policy names
    """
    provider_policies = {}

    # lookup the policy action name
    provider_action_name = Policy_action_name.get(provider_type)
    if not provider_action_name:
        raise Exception('unknown provider_type for policy lookup! %r' %
                        provider_type)

    # now have a look at all authentication policies
    policies = getPolicy({
        'scope': 'authentication',
        "action": provider_action_name,
    })

    for policy in policies:
        provider_name = getPolicyActionValue(policies,
                                             provider_action_name,
                                             is_string=True)
        if provider_name not in provider_policies:
            provider_policies[provider_name] = []

        provider_policies[provider_name].append(policy)

    return provider_policies
Example #2
0
def is_phone_editable(user=""):
    """
    this function checks the policy scope=selfservice, action=edit_sms
    This is a int policy, while the '0' is a deny
    """
    # the default string is the OTP value
    ret = True
    realm = user.realm
    login = user.login

    policies = getPolicy(
        {
            "scope": "selfservice",
            "realm": realm,
            "action": "edit_sms",
            "user": login,
        }
    )

    edit_sms = get_action_value(
        policies, scope="selfservice", action="edit_sms", default=1
    )

    if edit_sms == 0:
        return False

    return True
Example #3
0
    def valid_facets(self, realm=None):
        """
        Show the JSON output for the valid facets configured by the enrollment
        policy 'u2f_valid_facets'. The form of the JSON output is specified by
        the FIDO Alliance.
        """
        if realm is None:
            realm = getDefaultRealm()

        # Get the valid facets as specified in the enrollment policy 'u2f_valid_facets'
        # for the specific realm
        get_policy_params = {
            'action': 'u2f_valid_facets',
            'scope': 'enrollment',
            'realm': realm
            }
        valid_facets_action_value = getPolicyActionValue(
                                        getPolicy(get_policy_params),
                                        'u2f_valid_facets',
                                        is_string=True)
        # the action value contains the semicolon-separated list of valid facets
        valid_facets = valid_facets_action_value.split(';')

        # Prepare the response
        response.content_type = 'application/fido.trusted-­apps+json'  # as specified by FIDO
        response_dict = {
            "trustedFacets": [{
                "version": {"major": 1, "minor": 0},
                "ids": []
                }]
            }
        for facet in valid_facets:
            facet = facet.strip()
            response_dict['trustedFacets'][0]['ids'].append(facet)
        return json.dumps(response_dict)
Example #4
0
def get_auth_smstext(user="", realm="", context=None):
    '''
    this function checks the policy scope=authentication, action=smstext
    This is a string policy
    The function returns the tuple (bool, string),
        bool: If a policy is defined
        string: the string to use
    '''
    # the default string is the OTP value
    ret = False
    smstext = "<otp>"

    pol = getPolicy(
        {
            'scope': 'authentication',
            'realm': realm,
            "action": "smstext"
        },
        context=context)

    if len(pol) > 0:
        smstext = getPolicyActionValue(pol, "smstext", String=True)
        log.debug("[get_auth_smstext] got the smstext = %s" % smstext)
        ret = True

    return ret, smstext
Example #5
0
def is_phone_editable(user=""):
    '''
    this function checks the policy scope=selfservice, action=edit_sms
    This is a int policy, while the '0' is a deny
    '''
    # the default string is the OTP value
    ret = True
    realm = user.realm
    login = user.login

    policies = getPolicy({
        'scope': 'selfservice',
        'realm': realm,
        "action": "edit_sms",
        "user": login
    })

    edit_sms = get_action_value(policies,
                                scope='selfservice',
                                action="edit_sms",
                                default=1)

    if edit_sms == 0:
        return False

    return True
Example #6
0
def get_single_auth_policy(policy_name, user=None, realms=None):
    """
    Retrieves a policy value and checks if the value is consistent
    across realms.

    :param policy_name: the name of the policy, e.g:
        * qrtoken_pairing_callback_url
        * qrtoken_pairing_callback_sms
        * qrtoken_challenge_response_url
        * qrtoken_challenge_response_sms

    :param realms: the realms that his policy should be effective in
    """

    action_values = []
    login = None
    ret = None

    if user and user.login and user.realm:
        realms = [user.realm]
        login = user.login

    if realms is None or len(realms) == 0:
        realms = ['/:no realm:/']

    params = {"scope": "authentication", 'action': policy_name}

    for realm in realms:
        params['realm'] = realm
        if login:
            params['user'] = login

        policy = getPolicy(params)
        action_value = getPolicyActionValue(policy,
                                            policy_name,
                                            is_string=True)
        if action_value:
            action_values.append(action_value)

    if len(action_values) > 1:
        for value in action_values:
            if value != action_values[0]:
                raise Exception('conflicting policy values %r found for '
                                'realm set: %r' % (action_values, realms))
    if action_values:
        ret = action_values[0]

    return ret
Example #7
0
File: u2f.py Project: soitun/LinOTP
    def valid_facets(self, realm=None):
        """
        Show the JSON output for the valid facets configured by the enrollment
        policy 'u2f_valid_facets'. The form of the JSON output is specified by
        the FIDO Alliance.
        """
        if realm is None:
            realm = getDefaultRealm()

        # Get the valid facets as specified in the enrollment policy 'u2f_valid_facets'
        # for the specific realm
        get_policy_params = {
            "action": "u2f_valid_facets",
            "scope": "enrollment",
            "realm": realm,
        }
        valid_facets_action_value = get_action_value(
            getPolicy(get_policy_params),
            scope="enrollment",
            action="u2f_valid_facets",
            default="",
        )
        # the action value contains the semicolon-separated list of valid
        # facets
        valid_facets = valid_facets_action_value.split(";")

        # Prepare the response
        response.content_type = (
            "application/fido.trusted-­apps+json"  # as specified by FIDO
        )
        response_dict = {
            "trustedFacets": [{
                "version": {
                    "major": 1,
                    "minor": 0
                },
                "ids": []
            }]
        }
        for facet in valid_facets:
            facet = facet.strip()
            response_dict["trustedFacets"][0]["ids"].append(facet)
        return json.dumps(response_dict)
Example #8
0
    def _is_valid_facet(self, origin):
        """
        check if origin is in the valid facets if the u2f_valid_facets policy is set.
        Otherwise check if the origin matches the previously saved origin

        :return:          boolean - True if supported, False if unsupported
        """
        is_valid = False

        # Get the valid facets as specified in the enrollment policy 'u2f_valid_facets'
        # for the specific realm
        valid_facets_action_value = ""
        realms = self.token.getRealmNames()
        if len(realms) > 0:
            get_policy_params = {
                "action": "u2f_valid_facets",
                "scope": "enrollment",
                "realm": realms[0],
            }
            policies = getPolicy(get_policy_params)
            valid_facets_action_value = get_action_value(
                policies,
                scope="enrollment",
                action="u2f_valid_facets",
                default="",
            )

        if valid_facets_action_value != "":
            # 'u2f_valid_facets' policy is set - check if origin is in valid facets list
            valid_facets = valid_facets_action_value.split(";")
            for facet in valid_facets:
                facet = facet.strip()
            if origin in valid_facets:
                is_valid = True
        else:
            # 'u2f_valid_facets' policy is empty or not set
            # check if origin matches the origin stored in the token info or save it if no origin
            # is stored yet
            appId = self._get_app_id()
            if appId == origin:
                is_valid = True

        return is_valid
Example #9
0
    def _is_valid_facet(self, origin):
        """
        check if origin is in the valid facets if the u2f_valid_facets policy is set.
        Otherwise check if the origin matches the previously saved origin

        :return:          boolean - True if supported, False if unsupported
        """
        is_valid = False

        # Get the valid facets as specified in the enrollment policy 'u2f_valid_facets'
        # for the specific realm
        valid_facets_action_value = ''
        realms = self.token.getRealmNames()
        if len(realms) > 0:
            get_policy_params = {
                'action': 'u2f_valid_facets',
                'scope': 'enrollment',
                'realm': realms[0]
            }
            valid_facets_action_value = getPolicyActionValue(
                getPolicy(get_policy_params), 'u2f_valid_facets', String=True)

        if valid_facets_action_value != '':
            # 'u2f_valid_facets' policy is set - check if origin is in valid facets list
            valid_facets = valid_facets_action_value.split(';')
            for facet in valid_facets:
                facet = facet.strip()
            if origin in valid_facets:
                is_valid = True
        else:
            # 'u2f_valid_facets' policy is empty or not set
            # check if origin matches the origin stored in the token info or save it if no origin
            # is stored yet
            appId = self.getFromTokenInfo('appId', None)
            if appId is None:
                self.addToTokenInfo('appId', origin)
                is_valid = True
            else:
                if origin == appId:
                    is_valid = True

        return is_valid
Example #10
0
def is_email_editable(user=""):
    '''
    this function checks the policy scope=selfservice, action=edit_email
    This is a int policy, while the '0' is a deny
    '''
    ret = True
    realm = user.realm
    login = user.login

    policies = getPolicy({'scope': 'selfservice',
                          'realm': realm,
                          "action": "edit_email",
                          "user": login},)

    if policies:
        edit_email = getPolicyActionValue(policies, "edit_email")
        if edit_email == 0:
            ret = False

    return ret
Example #11
0
def do_forward_failcounter(token):
    """
    this function checks the for the policy

        scope=authentication,
        action=forwardtoken:no_failcounter_forwarding

    defining if the target token failcounter should be incremented / reseted

    :param serial: the token serial number, which allows to derive the
                   realm(s) and owner from
    :return: boolean
    """
    boolean = True

    owner = get_token_owner(token)
    if owner and owner.realm:
        realms = [owner.realm]
    else:
        realms = getTokenRealms(token.getSerial())

    if not realms:
        realms = ["*"]

    for realm in realms:
        params = {
            "scope": "authentication",
            "realm": realm,
            "action": "forwardtoken:no_failcounter_forwarding",
        }

        if owner and owner.login:
            params["user"] = owner.login

        pol = getPolicy(params)

        if pol:
            boolean = False
            break

    return boolean
Example #12
0
def do_forward_failcounter(token):
    '''
    this function checks the for the policy

        scope=authentication,
        action=forwardtoken:no_failcounter_forwarding

    defining if the target token failcounter should be incremented / reseted

    :param serial: the token serial number, which allows to derive the
                   realm(s) and owner from
    :return: boolean
    '''
    boolean = True

    owner = get_token_owner(token)
    if owner and owner.realm:
        realms = [owner.realm]
    else:
        realms = getTokenRealms(token.getSerial())

    if not realms:
        realms = ['*']

    for realm in realms:
        params = {
            'scope': 'authentication',
            'realm': realm,
            'action': "forwardtoken:no_failcounter_forwarding"
        }

        if owner and owner.login:
            params['user'] = owner.login

        pol = getPolicy(params)

        if pol:
            boolean = False
            break

    return boolean
Example #13
0
def is_phone_editable(user="", context=None):
    '''
    this function checks the policy scope=selfservice, action=edit_sms
    This is a int policy, while the '0' is a deny
    '''
    # the default string is the OTP value
    ret = True
    realm = user.realm
    login = user.login

    policies = getPolicy(
        {
            'scope': 'selfservice',
            'realm': realm,
            "action": "edit_sms",
            "user": login
        },
        context=context)
    if policies:
        edit_sms = getPolicyActionValue(policies, "edit_sms")
        if edit_sms == 0:
            ret = False

    return ret
Example #14
0
    def getInitDetail(self, params, user=None):
        """
        to complete the token normalisation, the response of the initialisation
        should be built by the token specific method, the getInitDetails
        """
        response_detail = {}

        info = self.getInfo()
        response_detail.update(info)
        response_detail['serial'] = self.getSerial()

        # get requested phase
        requested_phase = getParam(params, "phase", optional=False)

        if requested_phase == "registration1":
            # We are in registration phase 1
            # We create a 32 bytes otp key (from urandom)
            # which is used as the registration challenge
            challenge = base64.urlsafe_b64encode(binascii.unhexlify(self._genOtpKey_(32)))
            self.addToTokenInfo('challenge', challenge)

            # save the appId to the TokenInfo
            # An appId passed as parameter is preferred over an appId defined in a policy
            appId = ''
            if 'appid' in params:
                appId = params.get('appid')
            else:
                # No appId passed as parameter - fall back to the policy
                # Get the appId as specified in the enrollment policy 'u2f_app_id'
                # for the specific realm
                # If the token has multiple realms, the appIds are checked for conflicts.
                # It could be discussed whether the token should use the appId of the default
                # realm, when the token is not attached to any realms
                realms = self.token.getRealmNames()
                for realm in realms:
                    get_policy_params = {
                        'action': 'u2f_app_id',
                        'scope': 'enrollment',
                        'realm': realm
                        }
                    policies = getPolicy(get_policy_params, context=self.context)
                    policy_value = getPolicyActionValue(policies,
                                                        'u2f_app_id',
                                                        String=True
                                                        )
                    # Check for appId conflicts
                    if appId and policy_value:
                        if appId != policy_value:
                            log.error("Conflicting appId values in u2f policies.")
                            raise Exception("Conflicting appId values in u2f policies.")
                    appId = policy_value

            if not appId:
                log.error("No appId defined.")
                raise Exception("No appId defined.")
            self.addToTokenInfo('appId', appId)

            # create U2F RegisterRequest object and append it to the response as 'message'
            appId = self._get_app_id()
            register_request = {'challenge': challenge,
                                'version': 'U2F_V2',
                                'appId': appId
                                }
            response_detail['registerrequest'] = register_request

        elif requested_phase == "registration2":
            # We are in registration phase 2
            # process the data generated by the u2f compatible token device
            registerResponse = ""

            otpkey = None
            if 'otpkey' in params:
                otpkey = params.get('otpkey')

            if otpkey is not None:
                # otpkey holds the JSON RegisterResponse object as specified by the FIDO Alliance
                try:
                    registerResponse = json.loads(otpkey)
                except ValueError as ex:
                    log.exception("Invalid JSON format - value error %r", (ex))
                    raise Exception('Invalid JSON format')

                self._handle_client_errors(registerResponse)

                try:
                    registrationData = registerResponse['registrationData']
                    clientData = registerResponse['clientData']
                except AttributeError as ex:
                    log.exception(
                        "Couldn't find keyword in JSON object - attribute error %r ", (ex))
                    raise Exception("Couldn't find keyword in JSON object")

                # registrationData and clientData are urlsafe base64 encoded
                # correct padding errors (length should be multiples of 4)
                # fill up the registrationData with '=' to the correct padding
                registrationData = registrationData + \
                    ('=' * (4 - (len(registrationData) % 4)))
                clientData = clientData + ('=' * (4 - (len(clientData) % 4)))
                registrationData = base64.urlsafe_b64decode(
                    registrationData.encode('ascii'))
                clientData = base64.urlsafe_b64decode(clientData.encode('ascii'))

                # parse the raw registrationData according to the specification
                (userPublicKey, keyHandle, X509cert, signature) = \
                    self._parseRegistrationData(registrationData)

                # check the received clientData object
                if not self._checkClientData(
                    clientData, 'registration', self.getFromTokenInfo('challenge', None)):
                    raise ValueError("Received invalid clientData object. Aborting...")

                # prepare the applicationParameter and challengeParameter needed for
                # verification of the registration signature
                appId = self._get_app_id()
                applicationParameter = sha256(appId).digest()
                challengeParameter = sha256(clientData).digest()

                # verify the registration signature
                self._validateRegistrationSignature(applicationParameter,
                                                    challengeParameter,
                                                    keyHandle,
                                                    userPublicKey,
                                                    X509cert,
                                                    signature
                                                    )

                # save the key handle and the user public key in the Tokeninfo field for
                # future use
                self.addToTokenInfo('keyHandle', base64.urlsafe_b64encode(keyHandle))
                self.addToTokenInfo('publicKey', base64.urlsafe_b64encode(userPublicKey))
                self.addToTokenInfo('counter', '0')
                self.addToTokenInfo('phase', 'authentication')
                # remove the registration challenge from the token info
                self.removeFromTokenInfo('challenge')
                # Activate the token
                self.token.LinOtpIsactive = True
            else:
                log.error("No otpkey set!")
                raise ValueError("No otpkey set")
        else:
            log.error("Unsupported phase: %s", requested_phase)
            raise Exception("Unsupported phase: %s", requested_phase)

        return response_detail
Example #15
0
    def getInitDetail(self, params, user=None):
        """
        to complete the token normalisation, the response of the initialisation
        should be built by the token specific method, the getInitDetails
        """
        response_detail = {}

        info = self.getInfo()
        response_detail.update(info)
        response_detail["serial"] = self.getSerial()

        # get requested phase
        try:
            requested_phase = params["phase"]
        except KeyError:
            raise ParameterError("Missing parameter: 'phase'")

        if requested_phase == "registration1":
            # We are in registration phase 1
            # We create a 32 bytes otp key (from urandom)
            # which is used as the registration challenge
            challenge = base64.urlsafe_b64encode(
                binascii.unhexlify(self._genOtpKey_(32))
            )
            self.addToTokenInfo("challenge", challenge.decode("ascii"))

            # save the appId to the TokenInfo
            # An appId passed as parameter is preferred over an appId defined
            # in a policy
            appId = ""
            if "appid" in params:
                appId = params.get("appid")
            else:
                # No appId passed as parameter - fall back to the policy
                # Get the appId as specified in the enrollment policy 'u2f_app_id'
                # for the specific realm
                # If the token has multiple realms, the appIds are checked for conflicts.
                # It could be discussed whether the token should use the appId of the default
                # realm, when the token is not attached to any realms
                realms = self.token.getRealmNames()
                for realm in realms:
                    get_policy_params = {
                        "action": "u2f_app_id",
                        "scope": "enrollment",
                        "realm": realm,
                    }
                    policies = getPolicy(get_policy_params)
                    policy_value = get_action_value(
                        policies,
                        scope="enrollment",
                        action="u2f_app_id",
                        default="",
                    )

                    # Check for appId conflicts
                    if appId and policy_value:
                        if appId != policy_value:
                            raise Exception(
                                "Conflicting appId values in u2f policies."
                            )
                    appId = policy_value

            if not appId:
                raise Exception("No appId defined.")
            self.addToTokenInfo("appId", appId)

            # create U2F RegisterRequest object and append it to the response
            # as 'message'
            appId = self._get_app_id()
            register_request = {
                "challenge": challenge.decode("ascii"),
                "version": "U2F_V2",
                "appId": appId,
            }
            response_detail["registerrequest"] = register_request

        elif requested_phase == "registration2":
            # We are in registration phase 2
            # process the data generated by the u2f compatible token device
            registerResponse = ""

            otpkey = None
            if "otpkey" in params:
                otpkey = params.get("otpkey")

            if otpkey is not None:
                # otpkey holds the JSON RegisterResponse object as specified by
                # the FIDO Alliance
                try:
                    registerResponse = json.loads(otpkey)
                except ValueError as ex:
                    raise Exception("Invalid JSON format")

                self._handle_client_errors(registerResponse)

                try:
                    registrationData = registerResponse["registrationData"]
                    clientData = registerResponse["clientData"]
                except AttributeError as ex:
                    raise Exception("Couldn't find keyword in JSON object")

                # registrationData and clientData are urlsafe base64 encoded
                # correct padding errors (length should be multiples of 4)
                # fill up the registrationData with '=' to the correct padding
                registrationData = registrationData + (
                    "=" * (4 - (len(registrationData) % 4))
                )
                clientData = clientData + ("=" * (4 - (len(clientData) % 4)))
                registrationData = base64.urlsafe_b64decode(
                    registrationData.encode("ascii")
                )
                clientData = base64.urlsafe_b64decode(
                    clientData.encode("ascii")
                )

                # parse the raw registrationData according to the specification
                (
                    userPublicKey,
                    keyHandle,
                    x509cert,
                    signature,
                ) = self._parseRegistrationData(registrationData)

                # check the received clientData object
                if not self._checkClientData(
                    clientData,
                    "registration",
                    self.getFromTokenInfo("challenge", None),
                ):
                    raise ValueError(
                        "Received invalid clientData object. Aborting..."
                    )

                # prepare the applicationParameter and challengeParameter needed for
                # verification of the registration signature
                appId = self._get_app_id()
                applicationParameter = sha256(appId.encode("utf-8")).digest()
                challengeParameter = sha256(clientData).digest()

                # verify the registration signature
                self._validateRegistrationSignature(
                    applicationParameter,
                    challengeParameter,
                    keyHandle,
                    userPublicKey,
                    x509cert,
                    signature,
                )

                # save the key handle and the user public key in the Tokeninfo field for
                # future use
                self.addToTokenInfo(
                    "keyHandle",
                    base64.urlsafe_b64encode(keyHandle).decode("ascii"),
                )
                self.addToTokenInfo(
                    "publicKey",
                    base64.urlsafe_b64encode(userPublicKey).decode("ascii"),
                )
                self.addToTokenInfo("counter", "0")
                self.addToTokenInfo("phase", "authentication")
                # remove the registration challenge from the token info
                self.removeFromTokenInfo("challenge")
                # Activate the token
                self.token.LinOtpIsactive = True
            else:
                raise ValueError("No otpkey set")
        else:
            raise Exception("Unsupported phase: %s", requested_phase)

        return response_detail